Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to 3D print JavaFX 3D scene?

I have been working on this scientific application in JavaFX. The program basically generates data from an algorithm and visualizes them into spheres that intersect. enter image description here

I would like to 3D print part of this model(the colored shapes). Is there a way exporting the JavaFX scene? Even if it means exporting it into a file such as .stl or .obj and importing into Blender for further refinement or file conversion.

like image 686
lazycody Avatar asked Jul 16 '16 18:07

lazycody


People also ask

What is 3D shape in JavaFX?

3D Shape. In general, a 3D shape is a geometrical figure that can be drawn on the XYZ plane. These include a Cylinder, Sphere and a Box. Each of the above mentioned 3D shape is represented by a class and all these classes belong to the package javafx.scene.shape.

What is the print API in JavaFX?

JavaFX added support for printing nodes through the Print API in the javafx.print package. The API consists of the following classes: Instances of the above-listed classes represent different parts of the printing process. For example, a Printer represents a printer that can be used for printing jobs.

How to use Phong shaded material in JavaFX?

The PhongMaterial class of the package javafx.scene.paint is a sub class of this class and provides 7 properties that represent a Phong shaded material. You can apply all these type of materials to the surface of a 3D shape using the setter methods of these properties.

What is the use of setdrawmode () property in JavaFX?

It is the property is of the type DrawMode and it represents the drawing mode used to draw the current 3D shape. You can choose the draw mode to draw a 3D shape using the method setDrawMode () as follows − In JavaFX, you can choose two draw modes to draw a 3D shape, which are −


1 Answers

If you want to export your 3D model to .obj you need to get access to the TriangleMesh of each shape, so you can export vertices and faces.

Currently, using built-in JavaFX 3D shapes like Sphere, this is not possible, since their meshes are not exposed.

Also, there is no built-in export method.

So the solution will be creating from the scratch your 3D shapes and providing an export method, or using third party solutions.

If you have a look at the F(X)yz library, it solves part of your problems, since it already provides:

  • Several 3D shapes with full access to their meshes info
  • Export to .obj and .mtl for any given 3D shape.

Unfortunately it doesn't allow for now exporting multiple shapes into one single .obj file, but that's something that can be easily added.

This is a quick implementation of a Group exporter, that will export all the MeshView in the group and their diffuse color to a single obj file and a single mtl file.

public class GroupOBJWriter {

    private final String newline = System.getProperty("line.separator");
    private float[] points0, texCoord0;
    private int[] faces0, sm0;
    private BufferedWriter writer = null;
    private final String fileName;
    private String diffuseMap;
    private String diffuseColor = "0.0 0.0 0.0"; // black
    private final Group group;

    public GroupOBJWriter(Group group, String fileName){
        this.group = group;
        this.fileName = fileName;
    }

    public void exportMesh(){
        File objFile = new File(fileName + ".obj");
        try{
            writer = new BufferedWriter(new FileWriter(objFile));
            writer.write("mtllib " + fileName + ".mtl" + newline);

            AtomicInteger counter = new AtomicInteger();
            AtomicInteger vCounter = new AtomicInteger();

            group.getChildren().stream()
                .filter(MeshView.class::isInstance)
                .map(s -> (TriangleMesh) ((MeshView) s).getMesh())
                .forEach(mesh -> {
                    try{
                        writer.write("# Material" + newline);
                        int count = counter.getAndIncrement();
                        writer.write("usemtl " + fileName + "-" + count + ""+newline);

                        points0 = new float[mesh.getPoints().size()];
                        mesh.getPoints().toArray(points0);
                        List<Point3D> points1 = IntStream.range(0, points0.length/3)
                            .mapToObj(i -> new Point3D(points0[3*i], points0[3*i+1], points0[3*i+2]))
                            .collect(Collectors.toList());

                        writer.write("# Vertices (" + points1.size() + ") for shape " + count + "" + newline);
                        points1.forEach(p -> {
                            try {
                                writer.write("v " + p.x + " " + p.y + " " + p.z + "" + newline);
                            } catch (IOException ex) {
                                System.out.println("Error writting vertex "+ex);
                            }
                        });
                        writer.write(newline);

                        texCoord0 = new float[mesh.getTexCoords().size()];
                        mesh.getTexCoords().toArray(texCoord0);

                        List<Point2D> texCoord1 = IntStream.range(0, texCoord0.length/2)
                                .mapToObj(i -> new Point2D(texCoord0[2*i], texCoord0[2*i+1]))
                                .collect(Collectors.toList());

                        writer.write("# Textures Coordinates (" + texCoord1.size() + ") for shape " + count + "" + newline);
                        texCoord1.forEach(t->{
                            try {
                                // objimporter u->u, v->(1-v)
                                writer.write("vt " + ((float) t.getX()) + " " + ((float) (1d - t.getY())) + "" +newline);
                            } catch (IOException ex) {
                                System.out.println("Error writting texture coordinate " + ex);
                            }
                        });
                        writer.write(newline);

                        faces0 = new int[mesh.getFaces().size()];
                        mesh.getFaces().toArray(faces0);
                        List<Integer[]> faces1 = IntStream.range(0, faces0.length/6)
                                .mapToObj(i -> new Integer[]{faces0[6*i], faces0[6*i+1], 
                                    faces0[6*i+2], faces0[6*i+3], 
                                    faces0[6*i+4], faces0[6*i+5]})
                                .collect(Collectors.toList());

                        writer.write("# Faces (" + faces1.size() + ") for shape " + count + "" + newline);
                        writer.write("# Material for shape " + count + "" + newline);
                        writer.write("usemtl " + fileName + "-" + count + "" + newline);
                        sm0 = new int[mesh.getFaces().size()];
                        mesh.getFaceSmoothingGroups().toArray(sm0);
                        if (sm0[0] > 0) {
                            writer.write("s " + sm0[0] + "" + newline);
                        }

                        AtomicInteger c = new AtomicInteger();
                        final int inc = vCounter.get() + 1;
                        faces1.forEach(f->{
                            try {
                                writer.write("f " + (f[0] + inc) + "/" + (f[1] + inc) +
                                              " " + (f[2] + inc) + "/" + (f[3] + inc) +
                                              " " + (f[4] + inc) + "/" + (f[5] + inc) + "" + newline);
                                if (sm0[c.getAndIncrement()] != sm0[c.get()]) {
                                    writer.write("s " + (sm0[c.get()] > 0 ? sm0[c.get()] : "off" ) + "" + newline);
                                }
                            } catch (IOException ex) {
                                System.out.println("Error writting face "+ex);
                            }
                        });
                        vCounter.addAndGet(points1.size());
                        writer.write(newline);
                    } catch(IOException io){
                        System.out.println("Error creating writer obj " + io);
                    }
                });
        } catch(IOException io){
             System.out.println("Error creating writer obj "+io);
        } finally {
            try {
                if(writer!=null){
                    writer.close();
                }
            } catch (Exception e) {}
        }

        File mtlFile = new File(fileName+".mtl");

        try{
            writer = new BufferedWriter(new FileWriter(mtlFile));

            AtomicInteger counter = new AtomicInteger();
            group.getChildren().stream()
                .filter(MeshView.class::isInstance)
                .map(s -> ((PhongMaterial) ((MeshView) s).getMaterial()).getDiffuseColor())
                .forEach(color -> {
                    try{
                        diffuseColor=""+((float)(color.getRed()))+" "+((float)(color.getGreen()))+" "+((float)(color.getBlue()));

                        int count = counter.getAndIncrement();
                        writer.write("# Material " + fileName + "-" + count + "" + newline);
                        writer.write("newmtl " + fileName + "-" + count + "" + newline);
                        writer.write("illum 4" + newline); // Illumination [0-10]
                        writer.write("Kd " + diffuseColor + "" + newline); // diffuse color black
                        writer.write("Ka 0.10 0.10 0.10" + newline); // ambient color
                        writer.write("Tf 1.00 1.00 1.00" + newline); // Transmission filter
                        if (diffuseMap != null) {
                            writer.write("map_Kd " + diffuseMap + "" + newline);
                        }
                        writer.write("Ni 1.00" + newline); // optical density
                        writer.write("Ks 1.00 1.00 1.00" + newline); // specular reflectivity
                        writer.write("Ns 32.00" + newline); // specular exponent
                        writer.write(newline);
                    } catch(IOException io){
                        System.out.println("Error creating writer obj " + io);
                    }
                });
        } catch(IOException io){
             System.out.println("Error creating writer mtl "+io);
        } finally {
            try {
                if(writer!=null){
                    writer.close();
                }
            } catch (Exception e) {}
        }
    }
}

With SegmentedSphereMesh from F(X)yz you can create a sphere, so this will be a sample of how to build a group and export them:

@Override
public void start(Stage primaryStage) throws Exception {
    PerspectiveCamera camera = new PerspectiveCamera(true);
    camera.setTranslateZ(-20);
    Group sceneRoot = new Group();
    Scene scene = new Scene(sceneRoot, 800, 600, true, SceneAntialiasing.BALANCED);
    scene.setCamera(camera);
    Group group = new Group();

    for (int i = -5; i < 5; i++) {
        for (int j = -2; j < 2; j++) {
            for (int k = 0; k < 3; k++) {
                SegmentedSphereMesh sphere = new SegmentedSphereMesh(50, 0, 0, 0.75, new Point3D((float) i, (float) j, (float) k)); 

                sphere.setTextureModeNone(Color.rgb(255 / 10 * (6 + i), 255 / 5 * (j + 3), 255 / 3 * (k + 1)));
                group.getChildren().add(sphere);
            }
        }
    }

    group.getTransforms().addAll(new Rotate(40, Rotate.X_AXIS), new Rotate(10, Rotate.Y_AXIS));
    sceneRoot.getChildren().addAll(group);        

    primaryStage.setTitle("F(X)yz - Segmented Spheres Group");
    primaryStage.setScene(scene);
    primaryStage.show();   

    GroupOBJWriter writer=new GroupOBJWriter(group,"spheres");
    writer.exportMesh();
}

Group of spheres

If you now import the resulting spheres.obj with 3DViewer you'll see:

3DViewer

like image 151
José Pereda Avatar answered Oct 05 '22 02:10

José Pereda