javajavafxjavafx-8javafx-3dcsg

How to convert a MeshView to a CSG object using JCSG library in JavaFX


I'm using the JCSG library for JavaFX.

I have some MeshView objects that I want to convert them into CSG objects, is there a way to achieve this?


Solution

  • The easiest way to combine a javafx.scene.shape.Mesh object with a CSG one, providing you have a TriangleMesh is converting the triangular faces to polygons (eu.mihosoft.vrl.v3d.Polygon).

    Once you have a CSG object you can perform the regular operations on it, and then you can export it back to a MeshView for instance.

    The problem with primitive shapes (Box, Sphere, ...) is that you don't have access to their TriangleMesh. So you can go to F(X)yz library and pick any of the available 3D shapes.

    For example, let's use a FrustumMesh object.

    You can easily create one:

    FrustumMesh cone = new FrustumMesh(1,0.2,4,2);
    

    Cone

    And you will have access to its mesh: cone.getMesh().

    Now we need to convert this TriangleMesh into List<Polygon>. For that we can create this utility class:

    public class Mesh2CSG {
        /**
         * Loads a CSG from TriangleMesh.
         * @param mesh
         * @return CSG
         * @throws IOException if loading failed
         */
        public static CSG mesh2CSG(MeshView mesh) throws IOException {
            return mesh2CSG(mesh.getMesh());
        }
        public static CSG mesh2CSG(Mesh mesh) throws IOException {
    
            List<Polygon> polygons = new ArrayList<>();
            List<Vector3d> vertices = new ArrayList<>();
            if(mesh instanceof TriangleMesh){
                // Get faces
                ObservableFaceArray faces = ((TriangleMesh)mesh).getFaces();
                int[] f=new int[faces.size()];
                faces.toArray(f);
    
                // Get vertices
                ObservableFloatArray points = ((TriangleMesh)mesh).getPoints();
                float[] p = new float[points.size()];
                points.toArray(p);
    
                // convert faces to polygons
                for(int i=0; i<faces.size()/6; i++){
                    int i0=f[6*i], i1=f[6*i+2], i2=f[6*i+4];
                    vertices.add(new Vector3d(p[3*i0], p[3*i0+1], p[3*i0+2]));
                    vertices.add(new Vector3d(p[3*i1], p[3*i1+1], p[3*i1+2]));
                    vertices.add(new Vector3d(p[3*i2], p[3*i2+1], p[3*i2+2]));
                    polygons.add(Polygon.fromPoints(vertices));
                    vertices = new ArrayList<>();
                }
            }
    
            return CSG.fromPolygons(new PropertyStorage(),polygons);
        }
    }
    

    With this method, you can get a CSG cone:

    CSG coneCSG = Mesh2CSG.mesh2CSG(cone.getMesh());
    

    So you can combine it with other CSG forms:

    CSG cube = new Cube(2).toCSG().color(Color.RED);
    CSG union = cube.union(coneCSG);
    

    and get back to a JavaFX mesh to view it:

    MeshView unionMesh = coneCSG.toJavaFXMesh().getAsMeshViews().get(0);
    

    Cone and Box

    This is the full sample class (providing you have on your classpath the FXyzLib.jar and JCSG.jar dependencies):

    public class FXyzJCSG extends Application {
        private double mousePosX, mousePosY;
        private double mouseOldX, mouseOldY;
        private final Rotate rotateX = new Rotate(-20, Rotate.X_AXIS);
        private final Rotate rotateY = new Rotate(-20, Rotate.Y_AXIS);
    
        @Override
        public void start(Stage primaryStage) throws IOException {
    
            FrustumMesh cone = new FrustumMesh(1,0.2,4,2);
            cone.setDrawMode(DrawMode.LINE);
            cone.setTextureModeNone(Color.ROYALBLUE);
    
            CSG coneCSG = Mesh2CSG.mesh2CSG(cone.getMesh());
    
            CSG cube = new Cube(2).toCSG().color(Color.RED);
            CSG union = cube.union(coneCSG);
    
            MeshView unionMesh = union.toJavaFXMesh().getAsMeshViews().get(0);
    //        unionMesh.setDrawMode(DrawMode.LINE);
            PerspectiveCamera camera = new PerspectiveCamera(true);
            camera.getTransforms().addAll (rotateX, rotateY, new Translate(0, 0, -10));
    
            Group root3D = new Group(camera,unionMesh);
    
            SubScene subScene = new SubScene(root3D, 600, 400, true, SceneAntialiasing.BALANCED);
            subScene.setFill(Color.AQUAMARINE);
            subScene.setCamera(camera);
    
            Scene scene = new Scene(new StackPane(subScene), 600, 400);
            scene.setOnMousePressed(me -> {
                mouseOldX = me.getSceneX();
                mouseOldY = me.getSceneY();
            });
            scene.setOnMouseDragged(me -> {
                mousePosX = me.getSceneX();
                mousePosY = me.getSceneY();
                rotateX.setAngle(rotateX.getAngle()-(mousePosY - mouseOldY));
                rotateY.setAngle(rotateY.getAngle()+(mousePosX - mouseOldX));
                mouseOldX = mousePosX;
                mouseOldY = mousePosY;
            });
    
            primaryStage.setTitle("FXyz & JCSG - JavaFX 3D");
            primaryStage.setScene(scene);
            primaryStage.show();
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }