JavaFX: Add a Mesh Object to a 3D World
The objects you can most simply add to your virtual JavaFX world are created using the three built-in 3D shape classes that come with JavaFX: Cylinder, Box, and Sphere. For more complex objects, you must use the TriangleMesh class to create the object based on a connected series of triangles.
Here, create one of the simplest of all mesh objects: the four-sided pyramid pictured here. Visually, a four-sided pyramid has a total of five faces: the four triangular side faces and the square base. But in a JavaFX triangle mesh, squares are not allowed, only triangles. So the pyramid actually consists of six faces: the four triangular side faces and two adjacent triangles that make up the face.
This section presents pretty conceptually difficult information. If you haven’t studied meshes in a Computer Graphics class, be prepared to read through the following paragraphs several times before it starts to make sense.
If it still doesn’t make sense, grab a latte, pull out a sheet of graph paper, and start doodling. Drawing the pyramid with your own hand will help your understanding. Try using a pencil.
To get the pyramid started, call the TriangleMesh constructor like this:
TriangleMesh pyramidMesh = new TriangleMesh();
To complete the pyramid, you need to populate three collections that define the geometry of the mesh. These collections hold the points, the faces, and the texture coordinates that define the shape.
Start with the texture coordinate collection, because you can pretty much ignore it for this simple pyramid. Texture coordinates are useful when you’re using a material that contains an image that should be stretched in a specific way over the framework of the mesh. They allow you to associate a specific x-, y-coordinate in the image with each corner of each face.
Unfortunately, you can’t simply leave out the texture coordinates even if you don’t need them, so you must load at least one coordinate. Do that with this line of code:
Now move on to the other two collections. The next is a collection of the vertices (that is, corners) that defines the shape. Your square pyramid has five vertices, which you can envision as the top, the front corner (the point nearest you), the left corner, the back corner, and the right corner. These vertices are numbered 0, 1, 2, 3, and 4.
Given the height h and the length s of each side of the pyramid, you can calculate the x-, y-, and z-coordinates for each vertex using the following formulas:
|1||Front||0||h||–s / 2|
|2||Left||–s / 2||h||0|
|3||Back||s / 2||h||0|
|4||Right||0||h||s / 2|
With all that as background, here’s the code to create the Points collection:
float h = 150; // Height float s = 300; // Side pyramidMesh.getPoints().addAll( 0, 0, 0, // Point 0 - Top 0, h, -s/2, // Point 1 - Front -s/2, h, 0, // Point 2 - Left s/2, h, 0, // Point 3 - Back 0, h, s/2 // Point 4 - Right );
The final collection defines the faces. The faces are defined by specifying the index of each vertex that makes up each face. For example, the front left face is a triangle whose three vertices are the top, the front, and the left. The indexes for these three vertices are 0, 2, and 1.
There are a total of six triangles in the pyramid, and their faces are defined by the following points:
|Face||Point 1||Point 2||Point 3|
Although it may not be evident from this table, the order in which the faces appear is critical to the success of the mesh. In general, the faces are listed in a counter-clockwise and downward order. Thus, the four side faces wrap around the pyramid in counter-clockwise order. They’re followed by the two bottom faces.
Each face in the Faces collection is represented by three pairs of numbers, each of which represents the index of one of the vertices of the triangle and the index of the corresponding texture coordinate.
Because you have only one item in the Texture Coordinate collection, the second number in each pair will always be zero. Thus, the sequence 0, 0, 2, 0, 1, 0 defines the front left face: The vertex indexes are 0, 2, and 1, and the texture coordinate indexes are all 0.
Here’s the code to load the Faces collection:
pyramidMesh.getFaces().addAll( 0,0, 2,0, 1,0, // Front left face 0,0, 1,0, 3,0, // Front right face 0,0, 3,0, 4,0, // Back right face 0,0, 4,0, 2,0, // Back left face 4,0, 1,0, 2,0, // Bottom rear face 4,0, 3,0, 1,0 // Bottom front face );
After the three collections of the mesh are ready, the rest of the code fleshes out the pyramid by adding a Phong material, translates the pyramid to get it off the center of the scene, and adds the pyramid to the root:
MeshView pyramid = new MeshView(pyramidMesh); pyramid.setDrawMode(DrawMode.FILL); pyramid.setMaterial(blueStuff); pyramid.setTranslateX(200); pyramid.setTranslateY(100); pyramid.setTranslateZ(200); root.getChildren().add(pyramid);