如何3D打印JavaFX 3D场景?

时间:2016-07-16 18:38:53

标签: java javafx 3d blender javafx-3d

我一直在使用JavaFX中的这个科学应用程序。该程序基本上从算法生成数据,并将它们可视化为相交的球体。 enter image description here

我想3D打印这个模型的一部分(彩色形状)。有没有办法导出JavaFX场景?即使这意味着将其导出到.stl或.obj等文件中并导入Blender以进行进一步细化或文件转换。

1 个答案:

答案 0 :(得分:3)

如果要将3D模型导出到.obj,则需要访问每个形状的TriangleMesh,以便导出顶点和面。

目前,使用内置的JavaFX 3D形状如Sphere,这是不可能的,因为它们的网格不会被曝光。

此外,没有内置的导出方法。

因此,该解决方案将从头开始创建您的3D形状并提供导出方法,或使用第三方解决方案。

如果您查看F(X)yz library,它可以解决部分问题,因为它已经提供了:

  • 可以完全访问其网格信息的多个3D形状
  • 导出为.obj和.mtl以获取任何给定的3D形状。

不幸的是,它现在不允许将多个形状导出到一个.obj文件中,但这可以轻松添加。

这是Group导出器的快速实现,它将组中的所有MeshView及其漫反射颜色导出到单个obj文件和单个mtl文件。

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) {}
        }
    }
}

使用来自F(X)yz的SegmentedSphereMesh,您可以创建一个球体,因此这将是如何构建组并导出它们的示例:

@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

如果现在导入生成的spheres.obj 3DViewer,您会看到:

3DViewer