changeset 3796:e591db661d96

Added Java source code exporter to 3D Viewer
author "Jasper Potts"
date Mon, 03 Jun 2013 14:16:03 -0700
parents 2d119fc96ca6
children a5b41e87883e
files apps/experiments/3DViewer/src/main/java/com/javafx/experiments/exporters/javasource/JavaSourceExporter.java
diffstat 1 files changed, 400 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/apps/experiments/3DViewer/src/main/java/com/javafx/experiments/exporters/javasource/JavaSourceExporter.java	Mon Jun 03 14:16:03 2013 -0700
@@ -0,0 +1,400 @@
+/*
+ * Copyright (c) 2010, 2013 Oracle and/or its affiliates.
+ * All rights reserved. Use is subject to license terms.
+ *
+ * This file is available and licensed under the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  - Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  - Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the distribution.
+ *  - Neither the name of Oracle Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.javafx.experiments.exporters.javasource;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javafx.animation.Interpolator;
+import javafx.animation.KeyFrame;
+import javafx.animation.KeyValue;
+import javafx.animation.Timeline;
+import javafx.beans.value.WritableValue;
+import javafx.scene.Group;
+import javafx.scene.Node;
+import javafx.scene.paint.Color;
+import javafx.scene.paint.PhongMaterial;
+import javafx.scene.shape.MeshView;
+import javafx.scene.shape.TriangleMesh;
+import javafx.scene.transform.Rotate;
+import javafx.scene.transform.Scale;
+import javafx.scene.transform.Transform;
+import javafx.scene.transform.Translate;
+import javafx.util.Duration;
+import com.javafx.experiments.importers.Optimizer;
+import com.javafx.experiments.importers.maya.MayaImporter;
+import com.sun.javafx.animation.TickCalculation;
+import com.sun.scenario.animation.NumberTangentInterpolator;
+import com.sun.scenario.animation.SplineInterpolator;
+
+/**
+ * A exporter for 3D Models and animations that creates a Java Source file.
+ */
+public class JavaSourceExporter {
+    private int nodeCount = 0;
+    private int materialCount = 0;
+    private int meshCount = 0;
+    private int meshViewCount = 0;
+    private int methodCount = 1;
+    private int translateCount = 0;
+    private int rotateCount = 0;
+    private int scaleCount = 0;
+    private Map<WritableValue,String> writableVarMap = new HashMap<>();
+    private StringBuilder nodeCode = new StringBuilder();
+    private StringBuilder timelineCode = new StringBuilder();
+    private final boolean hasTimeline;
+
+    public JavaSourceExporter(Node rootNode, Timeline timeline) {
+        this.hasTimeline = timeline != null;
+        process("        ",rootNode);
+        if (hasTimeline) process("        ",timeline);
+    }
+
+    public void export(String packageName, File outputFile) {
+        final String className = outputFile.getName().substring(0,outputFile.getName().lastIndexOf('.'));
+
+        try {
+            BufferedWriter out = new BufferedWriter(new FileWriter(outputFile));
+//            BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
+            StringBuilder nodeVars = new StringBuilder();
+            nodeVars.append("    private static Node ");
+            for (int i=0; i<nodeCount; i++) {
+                if (i!=0) nodeVars.append(',');
+                nodeVars.append("NODE_"+i);
+            }
+            nodeVars.append(";\n");
+            nodeVars.append("    private static TriangleMesh ");
+            for (int i=0; i<meshCount; i++) {
+                if (i!=0) nodeVars.append(',');
+                nodeVars.append("MESH_"+i);
+            }
+            nodeVars.append(";\n");
+            if (translateCount > 0) {
+                nodeVars.append("    private static Translate ");
+                for (int i=0; i<translateCount; i++) {
+                    if (i!=0) nodeVars.append(',');
+                    nodeVars.append("TRANS_"+i);
+                }
+                nodeVars.append(";\n");
+            }
+            if (rotateCount > 0) {
+                nodeVars.append("    private static Rotate ");
+                for (int i=0; i<rotateCount; i++) {
+                    if (i!=0) nodeVars.append(',');
+                    nodeVars.append("ROT_"+i);
+                }
+                nodeVars.append(";\n");
+            }
+            if (scaleCount > 0) {
+                nodeVars.append("    private static Scale ");
+                for (int i=0; i<scaleCount; i++) {
+                    if (i!=0) nodeVars.append(',');
+                    nodeVars.append("SCALE_"+i);
+                }
+                nodeVars.append(";\n");
+            }
+
+            nodeVars.append("    public static final Node ROOT;\n");
+            if (hasTimeline) {
+                nodeVars.append("    public static final Timeline TIMELINE = new Timeline();\n");
+            }
+            StringBuilder methodCode = new StringBuilder();
+            for (int m=0; m< methodCount;m++) {
+                methodCode.append("        method"+m+"();\n");
+            }
+
+            out.write(
+                    "package "+packageName+";\n\n" +
+                    "import javafx.util.Duration;\n" +
+                    "import javafx.animation.*;\n" +
+                    "import javafx.scene.*;\n" +
+                    "import javafx.scene.paint.*;\n" +
+                    "import javafx.scene.shape.*;\n" +
+                    "import javafx.scene.transform.*;\n\n" +
+                    "public class "+className+" {\n" +
+                    nodeVars +
+                    "    // ======== NODE CODE ===============\n" +
+                    "    private static void method0(){\n" +
+                    nodeCode +
+                    "    }\n" +
+                    "    static {\n" +
+                    methodCode +
+                    "        // ======== TIMELINE CODE ===============\n" +
+                    timelineCode +
+                    "        // ======== SET PUBLIC VARS ===============\n" +
+                    "        ROOT = NODE_0;\n" +
+                    "    }\n" +
+                    "}\n");
+
+            out.flush();
+            out.close();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private int process(String indent, Node node) {
+        if (node instanceof MeshView) {
+            return process(indent,(MeshView)node);
+        } else if (node instanceof Group) {
+            return process(indent,(Group)node);
+        } else {
+            throw new UnsupportedOperationException("Found unknown node type: "+node.getClass().getName());
+        }
+    }
+
+    private int process(String indent, MeshView node) {
+        final int index = nodeCount ++;
+        final String varName = "NODE_"+index;
+        final int meshViewIndex = meshViewCount ++;
+        final String meshViewVarName = "MESHVIEW_"+meshViewIndex;
+        nodeCode.append(indent+"MeshView "+meshViewVarName+" = new MeshView();\n");
+        nodeCode.append(indent+varName+" = "+meshViewVarName+";\n");
+        processNodeTransforms(indent,meshViewVarName,node);
+        process(indent,meshViewVarName,(PhongMaterial)node.getMaterial());
+        process(indent,meshViewVarName,(TriangleMesh)node.getMesh());
+        return index;
+    }
+
+    private void processNodeTransforms(String indent, String varName, Node node) {
+        if (node.getTranslateX() != 0) nodeCode.append(indent+varName+".setTranslateX("+node.getTranslateX()+");\n");
+        if (node.getTranslateY() != 0) nodeCode.append(indent+varName+".setTranslateY("+node.getTranslateY()+");\n");
+        if (node.getTranslateZ() != 0) nodeCode.append(indent+varName+".setTranslateZ("+node.getTranslateZ()+");\n");
+        if (node.getRotate() != 0) nodeCode.append(indent+varName+".setRotate("+node.getRotate()+");\n");
+        if (!node.getTransforms().isEmpty()) {
+            nodeCode.append(indent+varName+".getTransforms().addAll(\n");
+            for (int i=0; i< node.getTransforms().size(); i++) {
+                if (i!=0) nodeCode.append(",\n");
+                Transform transform = node.getTransforms().get(i);
+                if (transform instanceof Translate) {
+                    Translate t = (Translate)transform;
+                    nodeCode.append(indent+"    "+storeTransform(t)+" = new Translate("+t.getX()+","+t.getY()+","+t.getZ()+")");
+                } else if (transform instanceof Scale) {
+                    Scale s = (Scale)transform;
+                    nodeCode.append(indent+"    "+storeTransform(s)+" = new Scale("+s.getX()+","+s.getY()+","+s.getZ()+","+s.getPivotX()+","+s.getPivotY()+","+s.getPivotZ()+")");
+                } else if (transform instanceof Rotate) {
+                    Rotate r = (Rotate)transform;
+                    nodeCode.append(indent+"    "+storeTransform(r)+" = new Rotate("+r.getAngle()+","+r.getPivotX()+","+r.getPivotY()+","+r.getPivotZ()+",");
+                    if (r.getAxis() == Rotate.X_AXIS) {
+                        nodeCode.append("Rotate.X_AXIS");
+                    } else if (r.getAxis() == Rotate.Y_AXIS) {
+                        nodeCode.append("Rotate.Y_AXIS");
+                    } else if (r.getAxis() == Rotate.Z_AXIS) {
+                        nodeCode.append("Rotate.Z_AXIS");
+                    }
+                    nodeCode.append(")");
+                } else {
+                    throw new UnsupportedOperationException("Unknown Transform Type: "+transform.getClass());
+                }
+
+            }
+            nodeCode.append("\n"+indent+");\n");
+        }
+    }
+
+    private String storeTransform(Transform transform) {
+        String varName;
+        if (transform instanceof Translate) {
+            final int index = translateCount ++;
+            varName = "TRANS_"+index;
+            Translate t = (Translate)transform;
+            writableVarMap.put(t.xProperty(),varName+".xProperty()");
+            writableVarMap.put(t.yProperty(),varName+".yProperty()");
+            writableVarMap.put(t.zProperty(),varName+".zProperty()");
+        } else if (transform instanceof Scale) {
+            final int index = scaleCount ++;
+            varName = "SCALE_"+index;
+            Scale s = (Scale)transform;
+            writableVarMap.put(s.xProperty(),varName+".xProperty()");
+            writableVarMap.put(s.yProperty(),varName+".yProperty()");
+            writableVarMap.put(s.zProperty(),varName+".zProperty()");
+            writableVarMap.put(s.pivotXProperty(),varName+".pivotXProperty()");
+            writableVarMap.put(s.pivotYProperty(),varName+".pivotYProperty()");
+            writableVarMap.put(s.pivotZProperty(),varName+".pivotZProperty()");
+        } else if (transform instanceof Rotate) {
+            final int index = rotateCount ++;
+            varName = "ROT_"+index;
+            Rotate r = (Rotate)transform;
+            writableVarMap.put(r.angleProperty(),varName+".angleProperty()");
+        } else {
+            throw new UnsupportedOperationException("Unknown Transform Type: "+transform.getClass());
+        }
+        return varName;
+    }
+
+    private int process(String indent, Group node) {
+        final int index = nodeCount ++;
+        final String varName = "NODE_"+index;
+        List<Integer> childIndex = new ArrayList<>();
+        for (int i = 0; i < node.getChildren().size(); i++) {
+            Node child = node.getChildren().get(i);
+            childIndex.add(
+                    process(indent,child));
+        }
+        nodeCode.append(indent+varName+" = new Group(");
+        for (int i = 0; i < childIndex.size(); i++) {
+            if (i!=0) nodeCode.append(',');
+            nodeCode.append("NODE_"+childIndex.get(i));
+        }
+        nodeCode.append(");\n");
+        processNodeTransforms(indent, varName, node);
+        nodeCode.append("    }\n    private static void method" + (methodCount++) + "(){\n");
+        return index;
+    }
+
+    private void process(String indent, String varName, TriangleMesh mesh) {
+        final int index = meshCount ++;
+        final String meshName = "MESH_"+index;
+
+        nodeCode.append(indent+meshName+" = new TriangleMesh();\n");
+        nodeCode.append(indent+varName+".setMesh("+meshName+");\n");
+
+        nodeCode.append(indent+meshName+".getPoints().ensureCapacity("+mesh.getPoints().size()+");\n");
+        nodeCode.append(indent + meshName + ".getPoints().addAll(");
+        for (int i = 0; i < mesh.getPoints().size(); i++) {
+            if (i!=0) nodeCode.append(',');
+            nodeCode.append(mesh.getPoints().get(i)+"f");
+        }
+        nodeCode.append(");\n");
+        nodeCode.append("    }\n    private static void method" + (methodCount++) + "(){\n");
+        nodeCode.append(indent+meshName+".getTexCoords().ensureCapacity("+mesh.getTexCoords().size()+");\n");
+        nodeCode.append(indent + meshName + ".getTexCoords().addAll(");
+        for (int i = 0; i < mesh.getTexCoords().size(); i++) {
+            if (i!=0) nodeCode.append(',');
+            nodeCode.append(mesh.getTexCoords().get(i)+"f");
+        }
+        nodeCode.append(");\n");
+        nodeCode.append("    }\n    private static void method" + (methodCount++) + "(){\n");
+        nodeCode.append(indent+meshName+".getFaces().ensureCapacity("+mesh.getFaces().size()+");\n");
+        nodeCode.append(indent + meshName + ".getFaces().addAll(");
+        for (int i = 0; i < mesh.getFaces().size(); i++) {
+            if ((i%5000) == 0 && i > 0) {
+                nodeCode.append(");\n");
+                nodeCode.append("    }\n    private static void method" + (methodCount++) + "(){\n");
+                nodeCode.append(indent+meshName+".getFaces().addAll(");
+            } else if (i!=0) {
+                nodeCode.append(',');
+            }
+            nodeCode.append(mesh.getFaces().get(i));
+        }
+        nodeCode.append(");\n");
+        nodeCode.append("    }\n    private static void method" + (methodCount++) + "(){\n");
+        nodeCode.append(indent+meshName+".getFaceSmoothingGroups().ensureCapacity("+mesh.getFaceSmoothingGroups().size()+");\n");
+        nodeCode.append(indent+meshName+".getFaceSmoothingGroups().addAll(");
+        for (int i = 0; i < mesh.getFaceSmoothingGroups().size(); i++) {
+            if (i!=0) nodeCode.append(',');
+            nodeCode.append(mesh.getFaceSmoothingGroups().get(i));
+        }
+        nodeCode.append(");\n");
+
+//        nodeCode.append(indent+varName+".setMesh("+process((TriangleMesh)node.getMesh())+");\n");
+    }
+
+    private void process(String indent, String varName, PhongMaterial material) {
+        final int index = materialCount ++;
+        final String materialName = "MATERIAL_"+index;
+
+        nodeCode.append(indent + "PhongMaterial " + materialName + " = new PhongMaterial();\n");
+        nodeCode.append(indent + materialName + ".setDiffuseColor(" + toCode(material.getDiffuseColor()) + ");\n");
+        nodeCode.append(indent + materialName + ".setSpecularColor(" + toCode(material.getSpecularColor()) + ");\n");
+        nodeCode.append(indent+materialName+".setSpecularPower("+material.getSpecularPower()+");\n");
+        nodeCode.append(indent+varName+".setMaterial("+materialName+");\n");
+    }
+
+    private void process(String indent, Timeline timeline) {
+        int count = 0;
+        for (KeyFrame keyFrame: timeline.getKeyFrames()) {
+            nodeCode.append(indent+"TIMELINE.getKeyFrames().add(new KeyFrame(Duration.millis("+keyFrame.getTime().toMillis()+"d),\n");
+            boolean firstKeyValue = true;
+            for (KeyValue keyValue: keyFrame.getValues()) {
+                if (firstKeyValue) {
+                    firstKeyValue = false;
+                } else {
+                    nodeCode.append(",\n");
+                }
+                String var = writableVarMap.get(keyValue.getTarget());
+                if (var == null) System.err.println("Failed to find writable value in map for : "+keyValue.getTarget());
+                nodeCode.append(indent+"    new KeyValue("+var+","+keyValue.getEndValue()+","+toString(keyValue.getInterpolator())+")");
+            }
+            nodeCode.append("\n"+indent+"));\n");
+
+            if (count > 0 && ((count % 10) == 0)) {
+                nodeCode.append("    }\n    private static void method" + (methodCount++) + "(){\n");
+            }
+            count ++;
+        }
+    }
+
+    private String toString(Interpolator interpolator) {
+        if (interpolator == Interpolator.DISCRETE) {
+            return "Interpolator.DISCRETE";
+        } else if (interpolator == Interpolator.EASE_BOTH) {
+            return "Interpolator.EASE_BOTH";
+        } else if (interpolator == Interpolator.EASE_IN) {
+            return "Interpolator.EASE_IN";
+        } else if (interpolator == Interpolator.EASE_OUT) {
+            return "Interpolator.EASE_OUT";
+        } else if (interpolator == Interpolator.LINEAR) {
+            return "Interpolator.LINEAR";
+        } else if (interpolator instanceof SplineInterpolator) {
+            SplineInterpolator si = (SplineInterpolator)interpolator;
+            return "Interpolator.SPLINE("+si.getX1()+"d,"+si.getY1()+"d,"+si.getX2()+"d,"+si.getY2()+"d)";
+        } else if (interpolator instanceof NumberTangentInterpolator) {
+            NumberTangentInterpolator ti = (NumberTangentInterpolator)interpolator;
+            return "Interpolator.TANGENT(Duration.millis("+ TickCalculation.toMillis((long)ti.getInTicks())+"d),"+ti.getInValue()
+                    +"d,Duration.millis("+TickCalculation.toMillis((long)ti.getInTicks())+"d),"+ti.getOutValue()+"d)";
+        } else {
+            throw new UnsupportedOperationException("Unknown Interpolator type: "+interpolator.getClass());
+        }
+    }
+
+    private String toCode(Color color) {
+        return "new Color("+color.getRed()+","+color.getGreen()+","+color.getBlue()+","+color.getOpacity()+")";
+    }
+
+    public static void main(String[] args) {
+        MayaImporter importer = new MayaImporter();
+        importer.load("file:///Users/jpotts/Downloads/dukeBot_w0193b_bakedAnim_euler.ma");
+        Optimizer optimizer = new Optimizer(importer.getTimeline(),importer.getRoot());
+        optimizer.optimize();
+
+        JavaSourceExporter javaSourceExporter = new JavaSourceExporter(importer.getRoot(), importer.getTimeline());
+        File out = new File("/Users/jpotts/Projects/jfx-graphics-8.0/rt/apps/experiments/3DViewer/src/main/java/com/javafx/experiments/jfx3dviewer/Duke.java");
+        javaSourceExporter.export("com.javafx.experiments.jfx3dviewer",out);
+    }
+}