This is a 3D projection layer for FXGL and JavaFX. Instead of using a full 3D renderer, it keeps mesh data inside normal FXGL components, projects it through a custom camera class, and draws the output with JavaFX Polygon nodes while remaining compatible with the FXGL entity structure.
Limitations: The renderer is built for planar, non-intersecting polygons. OBJ material files and UV rendering are outside of the current render scope.
./mvnw clean javafx:runThe project uses Java 25, FXGL 25, and JavaFX 21.0.6.
Use WASD for camera-relative horizontal movement, Q/E for vertical movement, and the arrow keys to rotate the view.
Register the renderer as an FXGL engine service:
settings.addEngineService(RenderService.class);Load the included chicken OBJ:
FXGL.spawn("objObject", new SpawnData()
.put("filepath", "src/main/resources/at/htl/fxglprojection/chicken.obj")
.put("position", new Vec3D(0, -95, 200))
);Switch renderer modes:
RenderService renderer = FXGL.getService(RenderService.class);
renderer.setColorMode(ColorMode.NORMALS);
renderer.setDepthMode(DepthMode.AVERAGE);Move or tune the camera:
Camera3DProjection camera = renderer.getCamera();
camera.translatePosition(new Vec3D(0, 0, 1));
camera.rotateView(new Vec3D(0, 1, 0));
camera.setFocalLength(320);-
OBJ loading: Loads OBJ vertices, normals, faces.
-
Backface culling: Removes polygons facing away from the camera.
-
Quaternion rotation: Quaternions for camera and mesh rotation to avoid gimbal lock.
-
Camera-space movement: Movement follows the current camera view direction.
-
Custom projection: Projects 3D mesh data into 2D JavaFX
Polygonnodes. -
Mesh transforms: Allows applying scale, rotation, and translation.
-
Depth sorting: Selectable depth sorting approaches for ordering projected polygons.
-
Color modes: Selectable coloring approaches for projected polygons:
ORIGINAL: Polygon fill color.NORMALS: Normals to RGB.QUANTIZED_NORMALS: Rounded normals to RGB.
-
Node reuse: Reuses JavaFX
Polygonnodes between frames.
-
ObjectFactory- Defines the
objObjectspawn type. It reads the OBJ path fromSpawnData, parses it intoMeshData, creates a transform, and builds an entity with the resultingTransform3DComponentandMesh3DComponent.
- Defines the
-
ObjParser- Loads OBJ files into the internal mesh format. It supports
v,vn, andf v/vt/vndefinitions, including negative indices.
- Loads OBJ files into the internal mesh format. It supports
-
Transform3DComponent- Stores position, scale, and quaternion rotation. The quaternion is converted into a rotation matrix during preprocessing.
-
Mesh3DComponent- Holds the mesh for an entity. When FXGL adds or removes it, the component registers or unregisters itself with
ObjectRegistry.
- Holds the mesh for an entity. When FXGL adds or removes it, the component registers or unregisters itself with
-
ObjectRegistry- Keeps the current list of renderable mesh components to avoid searching the FXGL world every frame.
-
MeshDataandPolygon3DMeshDatais a polygon container,Polygon3Dstores vertices, one flat normal, and a fill color.
-
GeometryPreprocessor- Converts mesh vertices into world-space vertices by applying scale, rotation, and translation from the mesh
Transform3DComponent. Also rotates normals.
- Converts mesh vertices into world-space vertices by applying scale, rotation, and translation from the mesh
-
Camera3DProjection- Stores camera position, focal length, movement/rotation speed, and quaternion view rotation. It projects world-space points into camera space, then into 2D + depth.
-
RenderService- Runs every frame; takes preprocessed meshes from the
GeometryPreprocessor, initiates projection, orders visible polygons, and manages the render layer.
- Runs every frame; takes preprocessed meshes from the
-
PolygonProjector- Projects world-space polygons with
Camera3DProjection, applies backface culling, and calculates depth values for sorting.
- Projects world-space polygons with
-
PolygonNodeManager- Keeps JavaFX
Polygonnodes in sync with currently visible polygons, including color modes and node reuse.
- Keeps JavaFX
Camera3DProjection.projectPoint() subtracts the camera position, transforms the point into camera space using the inverse camera rotation matrix and camera position, and applies perspective projection:
x' = focalLength * x / z
y' = focalLength * y / z
Camera rotation is stores as a quaternion.
-
Backface culling:
PolygonProjectordetects faces pointing away from the camera view direction and stops them from being drawn.
-
Frustum culling:
PolygonProjectordetects faces outside the camera view and skips their drawing step.
-
Shared projected vertices:
- In
PolygonProjector, repeated vertices across polygons are cached and projected once.
- In
-
Mesh asset cache:
MeshAssetCachecachesMeshDataobtained from an OBJ file to prevent repeated parsing.
-
Processed mesh cache:
GeometryPreprocessorstores processed meshes byMesh3DComponentand recomputes them only when the mesh data or transform changes.
-
Shared transformed vertices:
- During preprocessing, repeated source vertices are transformed once and reused across polygons. This matters for OBJ meshes, where many faces share vertices.
-
JavaFX node reuse:
RenderServicekeeps track of existing JavaFXPolygonnodes, updating them each frame, and removing them only once they are no longer visible/culled.
This project is licensed under the MIT License.