Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make sense of JavaFX triangle mesh?

Tags:

java

javafx

Just trying to make sense of JavaFX documentation re triangle mesh. This code works and draws a rectangle.

public class Shape3DRectangle extends TriangleMesh {

    public Shape3DRectangle(float Width, float Height) {
        float[] points = {
            -Width/2,  Height/2, 0, // idx p0
            -Width/2, -Height/2, 0, // idx p1
             Width/2,  Height/2, 0, // idx p2
             Width/2, -Height/2, 0  // idx p3
        };
        float[] texCoords = {
            1, 1, // idx t0
            1, 0, // idx t1
            0, 1, // idx t2
            0, 0  // idx t3
        };
        /**
         * points:
         * 1      3
         *  -------   texture:
         *  |\    |  1,1    1,0
         *  | \   |    -------
         *  |  \  |    |     |
         *  |   \ |    |     |
         *  |    \|    -------
         *  -------  0,1    0,0
         * 0      2
         *
         * texture[3] 0,0 maps to vertex 2
         * texture[2] 0,1 maps to vertex 0
         * texture[0] 1,1 maps to vertex 1
         * texture[1] 1,0 maps to vertex 3
         * 
         * Two triangles define rectangular faces:
         * p0, t0, p1, t1, p2, t2 // First triangle of a textured rectangle 
         * p0, t0, p2, t2, p3, t3 // Second triangle of a textured rectangle
         */
        int[] faces = {
            2, 2, 1, 1, 0, 0,
            2, 2, 3, 3, 1, 1
        };

        this.getPoints().setAll(points);
        this.getTexCoords().setAll(texCoords);
        this.getFaces().setAll(faces);
    }
}

The last 3 lines of the comment come from Class TriangleMesh. My problem is that I do not see a match between their definition of the faces array and the faces array in the code. Thus don't understand how to use faces array in general for other cases. Should it not be this instead:

        int[] faces = {
            2, 3, 0, 2, 1, 0,
            2, 3, 1, 0, 3, 1
        };

But then nothing renders of the rectangle. What am I missing and what should go into the faces array in general?

like image 582
ajeh Avatar asked Dec 19 '22 21:12

ajeh


1 Answers

Explanation of how the face direction works

The direction in which the face is defined is important. In the working sample, the point of the first face is 2, 1, 0 (i.e. the triangle is defined in a counter-clockwise order). In the faces array you suggest, the first face is 2, 0, 1 (clockwise). A face which is defined in a clockwise manner faces away from the viewer. A face which is defined in a counter-clockwise manner faces toward the viewer.

How to make your mesh visible

If you take your suggested face definition and rotate the mesh 180 degrees round the Y axis, you will see the face. Without rotation of the mesh, you will by default be looking at the back of the face, and the back of the mesh will be culled (i.e. not visible).

Another way to see your mesh without rotation is to set the cull face of the Mesh to CullFace.NONE, then the back of the mesh will show (though as it will just show as black rather than a shaded region).

A note on the comments in your supplied code

The comment in your code sample is slightly misleading, it should reflect the the actual face definition rather than a face definition which doesn't really work as expected.

Documentation request change suggestion

IMO, the TriangleMesh documentation should be enhanced, I logged a change request in the JavaFX issue tracker against the runtime project to request this enhancement.

Change request for documentation to highlight ordering of points in mesh faces is:

JDK-8122785 Document importance of TriangleMesh order of elements

Umbrella change request for documentation of the Java 8 3D API is:

JDK-8101810 Finish javadoc for FX 8 3D API

Demo

Here is a sample test harness you can use for playing around with this concepts in order to understand them better.

import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.*;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.*;
import javafx.scene.shape.*;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;

// drag the mouse over the rectangle to rotate it.
public class RectangleViewer extends Application {
 
    double anchorX, anchorY, anchorAngle;
 
    private PerspectiveCamera addCamera(Scene scene) {
        PerspectiveCamera perspectiveCamera = new PerspectiveCamera(false);
        scene.setCamera(perspectiveCamera);
        return perspectiveCamera;
    }
 
    public static void main(String[] args) {
        launch(args);
    }
 
    @Override
    public void start(Stage primaryStage) {
        final MeshView rect = new MeshView(
                new Shape3DRectangle(200, 200)
        );
        rect.setMaterial(new PhongMaterial(Color.DARKGREEN));
        rect.setRotationAxis(Rotate.Y_AXIS);
        rect.setTranslateX(250);
        rect.setTranslateY(250);
// try commenting this line out to see what it's effect is . . .
        rect.setCullFace(CullFace.NONE);

        final Group root = new Group(rect);
        final Scene scene = new Scene(root, 500, 500, true);
 
        scene.setOnMousePressed(new EventHandler<MouseEvent>() {
            @Override public void handle(MouseEvent event) {
                anchorX = event.getSceneX();
                anchorY = event.getSceneY();
                anchorAngle = rect.getRotate();
            }
        });
 
        scene.setOnMouseDragged(new EventHandler<MouseEvent>() {
            @Override public void handle(MouseEvent event) {
                rect.setRotate(anchorAngle + anchorX - event.getSceneX());
            }
        });


        addCamera(scene);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public class Shape3DRectangle extends TriangleMesh {

        public Shape3DRectangle(float width, float height) {
            float[] points = {
                    -width/2,  height/2, 0, // idx p0
                    -width/2, -height/2, 0, // idx p1
                    width/2,  height/2, 0, // idx p2
                    width/2, -height/2, 0  // idx p3
            };
            float[] texCoords = {
                    1, 1, // idx t0
                    1, 0, // idx t1
                    0, 1, // idx t2
                    0, 0  // idx t3
            };
            /**
             * points:
             * 1      3
             *  -------   texture:
             *  |\    |  1,1    1,0
             *  | \   |    -------
             *  |  \  |    |     |
             *  |   \ |    |     |
             *  |    \|    -------
             *  -------  0,1    0,0
             * 0      2
             *
             * texture[3] 0,0 maps to vertex 2
             * texture[2] 0,1 maps to vertex 0
             * texture[0] 1,1 maps to vertex 1
             * texture[1] 1,0 maps to vertex 3
             *
             * Two triangles define rectangular faces:
             * p0, t0, p1, t1, p2, t2 // First triangle of a textured rectangle
             * p0, t0, p2, t2, p3, t3 // Second triangle of a textured rectangle
             */

// if you use the co-ordinates as defined in the above comment, it will be all messed up
//            int[] faces = {
//                    0, 0, 1, 1, 2, 2,
//                    0, 0, 2, 2, 3, 3
//            };

// try defining faces in a counter-clockwise order to see what the difference is.
//            int[] faces = {
//                    2, 2, 1, 1, 0, 0,
//                    2, 2, 3, 3, 1, 1
//            };

// try defining faces in a clockwise order to see what the difference is.
            int[] faces = {
                    2, 3, 0, 2, 1, 0,
                    2, 3, 1, 0, 3, 1
            };

            this.getPoints().setAll(points);
            this.getTexCoords().setAll(texCoords);
            this.getFaces().setAll(faces);
        }
    }
} 

Demo Output

Front and back faces of the rectangle mesh when the rectangle is drawn to be by default facing away the viewer (by defining a face in a clockwise direction) and with a CullFace.NONE setting (win7 jdkb115).

backfront

FAQ

Why is it Height/2

I define the model co-ordinates so that in this way because, in this case, I want the center of the 3D shape I am rotating to be the centroid of the shape. That way I can rotate the shape around its centroid without performing additional translations. See pivot point rotation to see the additional translations required to rotate about an arbitrary pivot point.

is the coordinates origin the bottom right?

There are multiple coodinates involved. There are coordinates for the model which has a local coordinate system (see node documentation):

The Node class defines a traditional computer graphics "local" coordinate system in which the x axis increases to the right and the y axis increases downwards. The concrete node classes for shapes provide variables for defining the geometry and location of the shape within this local coordinate space.

There are coordinates for the scene which the camera operates in. In a 3D perspective application, the camera is a PerspectiveCamera. The information on how it effects co-ordinates is:

By default, this camera is located at center of the scene and looks along the positive z-axis. The coordinate system defined by this camera has its origin in the upper left corner of the panel with the Y-axis pointing down and the Z axis pointing away from the viewer (into the screen). If a PerspectiveCamera node is added to the scene graph, the transformed position and orientation of the camera will define the position of the camera and the direction that the camera is looking.

There are also texture co-ordinates in the mesh. These are completely different and map points on the texture map to vertices on your model:

The term texCoord is used to indicate a single pair of 2D texture coordinates (u, v) for a single vertex, while the term texCoords (plural) is used to indicate sets of texture coordinates for multiple vertices.

like image 190
jewelsea Avatar answered Dec 28 '22 05:12

jewelsea