4

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?

shA.t
  • 16,580
  • 5
  • 54
  • 111
Jean-Baptiste-B
  • 325
  • 1
  • 3
  • 9

1 Answers1

6

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);
    }
}
José Pereda
  • 44,311
  • 7
  • 104
  • 132
  • Nice answer! The converter method should be added to JCSG. – miho Apr 28 '15 at 12:42
  • Thanks @miho, both frameworks (FXyz and JCSG) could benefit from some interface that allows easy interaction between the two of them. Boolean operations are really a nice feature for FXyz, while JCSG could benefit from its collection of enhanced 3D shapes. – José Pereda Apr 28 '15 at 12:49
  • Thanks so much José Pereda, I really appreciate it. Thanks also miho for the amazing work on CSG. @José you said that JCSG could benefit from the enhanced collection of 3D shapes from FXyz, well we have made a pull request on github to add some new 3D shapes to FXyz – Jean-Baptiste-B Apr 29 '15 at 13:43
  • Yes, we saw that, we'll try to merge it as soon as possible. – José Pereda Apr 29 '15 at 13:54
  • I've just fixed a bug in `mesh2CSG()`, have a look if you already were using it. – José Pereda Apr 30 '15 at 16:44
  • @José, thanks so much, I'll change it immediately, I noticed that the old one didn't work with some FXyz primitives such as the TorusMesh, the SpheroidMesh, and the CapsuleMesh – Jean-Baptiste-B Apr 30 '15 at 19:22
  • I had wrong size arrays, maybe it created wrong polygons. Let me know if it works now. By the way, could you accept the answer, to help others looking for similar solutions? – José Pereda Apr 30 '15 at 19:25
  • @José it works with most of the MeshView objects like ConeMesh, PyramidMesh TrapezoidMesh TetrahedronMesh etc , but it seems that it doesn't work with CapsuleMesh TorusMesh SpheroidMesh – Jean-Baptiste-B May 01 '15 at 00:08
  • Those work for me. What kind of errors do you have? Maybe we can discuss this through other channels... – José Pereda May 01 '15 at 09:49