]> gerrit.simantics Code Review - simantics/3d.git/blobdiff - org.simantics.g3d/src/org/simantics/proconf/g3d/scenegraph/ShapeNode.java
git-svn-id: https://www.simantics.org/svn/simantics/3d/trunk@22280 ac1ea38d-2e2b...
[simantics/3d.git] / org.simantics.g3d / src / org / simantics / proconf / g3d / scenegraph / ShapeNode.java
diff --git a/org.simantics.g3d/src/org/simantics/proconf/g3d/scenegraph/ShapeNode.java b/org.simantics.g3d/src/org/simantics/proconf/g3d/scenegraph/ShapeNode.java
new file mode 100644 (file)
index 0000000..52afcbc
--- /dev/null
@@ -0,0 +1,721 @@
+/*******************************************************************************\r
+ * Copyright (c) 2007- VTT Technical Research Centre of Finland.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ *     VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.proconf.g3d.scenegraph;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.List;\r
+\r
+import javax.vecmath.AxisAngle4d;\r
+import javax.vecmath.Quat4d;\r
+\r
+import org.simantics.animation.stubs.Interpolator;\r
+import org.simantics.animation.curve.SlerpCurve;\r
+import org.simantics.animation.curve.TCBCurve;\r
+import org.simantics.proconf.g3d.Resources;\r
+import org.simantics.proconf.g3d.animation.Animatable;\r
+import org.simantics.proconf.g3d.animation.Animation;\r
+import org.simantics.proconf.g3d.animation.ChanneledColorInterpolator;\r
+import org.simantics.proconf.g3d.animation.ChanneledPositionInterpolator;\r
+import org.simantics.proconf.g3d.animation.ConstantInterpolator;\r
+import org.simantics.proconf.g3d.animation.ScalarInterpolator;\r
+import org.simantics.proconf.g3d.animation.SlerpInterpolator;\r
+import org.simantics.proconf.g3d.animation.TCBInterpolator;\r
+import org.simantics.proconf.g3d.base.AppearanceProvider;\r
+import org.simantics.proconf.g3d.base.AppearanceProviderRegistry;\r
+import org.simantics.proconf.g3d.base.AppearanceTools;\r
+import org.simantics.proconf.g3d.base.G3DTools;\r
+import org.simantics.proconf.g3d.base.GeometryProvider;\r
+import org.simantics.proconf.g3d.base.GeometryProviderRegistry;\r
+import org.simantics.proconf.g3d.base.ThreeDimensionalEditorBase;\r
+import org.simantics.proconf.g3d.stubs.Appearance;\r
+import org.simantics.proconf.g3d.stubs.Color;\r
+import org.simantics.proconf.g3d.stubs.G3DModel;\r
+import org.simantics.proconf.g3d.stubs.G3DNode;\r
+import org.simantics.proconf.g3d.stubs.Orientation;\r
+import org.simantics.utils.ui.ErrorLogger;\r
+\r
+import com.jme.bounding.BoundingBox;\r
+import com.jme.bounding.CollisionTreeManager;\r
+import com.jme.intersection.PickResults;\r
+import com.jme.math.Ray;\r
+import com.jme.renderer.ColorRGBA;\r
+import com.jme.renderer.Renderer;\r
+import com.jme.scene.Geometry;\r
+import com.jme.scene.Node;\r
+import com.jme.scene.SharedMesh;\r
+import com.jme.scene.TriMesh;\r
+import com.jme.scene.state.AlphaState;\r
+import com.jme.scene.state.MaterialState;\r
+import com.jme.scene.state.RenderState;\r
+import com.jme.scene.state.WireframeState;\r
+import com.jme.scene.state.ZBufferState;\r
+\r
+import org.simantics.db.Builtins;\r
+import org.simantics.db.ContextGraph;\r
+import org.simantics.db.Graph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.layer0.utils.IEntity;\r
+\r
+public class ShapeNode extends AbstractGraphicsNode implements Animatable, IGeometryNode{\r
+\r
+    public static final int NORMAL = 0;\r
+    public static final int TRANSPARENT = 1;\r
+    public static final int SELECTED_EDGE = 2;\r
+    public static final int HIGHLIGHTED_EDGE = 3;\r
+    \r
+    private boolean highlighted = false;\r
+\r
+    protected Geometry mesh = null;\r
+    protected Geometry lines = null;\r
+    protected Geometry[] geometry = null;\r
+\r
+    private boolean visible[] = new boolean[4];\r
+    \r
+    private Node body;\r
+    private Node transparent;\r
+    private Node edge;\r
+\r
+    private MaterialState selectedEdgeState;\r
+    private MaterialState highlightedEdgeState;\r
+    \r
+    private Collection<RenderState> renderStates;\r
+    private boolean isTransparent;\r
+    \r
+\r
+    public ShapeNode(ThreeDimensionalEditorBase editor,IGraphicsNode parent, Graph graph, Resource shapeResource) {\r
+        super(editor,parent, graph, shapeResource);\r
+        for (int i = 0; i < visible.length; i++)\r
+            visible[i] = false;\r
+        \r
+        body = new Node();\r
+\r
+        body.setName(id); \r
+        \r
+        \r
+        transparent = new Node() {\r
+               private static final long serialVersionUID = 1L;\r
+               @Override\r
+               public void calculatePick(Ray ray, PickResults results) {\r
+               \r
+               }\r
+        };\r
+        \r
+       // transparent.setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);\r
+        \r
+        edge = new Node(){\r
+               private static final long serialVersionUID = 1L;\r
+               @Override\r
+               public void calculatePick(Ray ray, PickResults results) {\r
+               \r
+               }\r
+        };\r
+        transparent.setIsCollidable(false);\r
+        edge.setIsCollidable(false);\r
+        \r
+        MaterialState ms = editor.getRenderingComponent().getDisplaySystem().getRenderer().createMaterialState();\r
+        ms.setDiffuse(new ColorRGBA(0.0f, 0.75f, 0.0f,0.3f));\r
+        ms.setEmissive(new ColorRGBA(0f, 0f, 0f,0.3f));\r
+        ms.setSpecular(new ColorRGBA(0.5f, 0.5f, 0.5f,0.3f));\r
+        ms.setAmbient(new ColorRGBA(0.0f, 0.75f, 0.0f,0.3f));\r
+        ms.setShininess(128.f);\r
+        ms.setMaterialFace(MaterialState.MF_FRONT_AND_BACK);\r
+        transparent.setRenderState(ms);\r
+        \r
+        AlphaState as = editor.getRenderingComponent().getDisplaySystem().getRenderer().createAlphaState();\r
+        as.setBlendEnabled(true);\r
+        as.setDstFunction(AlphaState.DB_ONE_MINUS_SRC_ALPHA);\r
+        as.setSrcFunction(AlphaState.DB_SRC_ALPHA);\r
+        transparent.setRenderState(as);\r
+        \r
+        ms = editor.getRenderingComponent().getDisplaySystem().getRenderer().createMaterialState();\r
+        ms.setDiffuse(new ColorRGBA(1.f, 1.f, 1.f, 1.f));\r
+        ms.setEmissive(new ColorRGBA(1.f, 1.f, 1.f, 1.f));\r
+        ms.setSpecular(new ColorRGBA(1.f, 1.f, 1.f, 1.f));\r
+        ms.setAmbient(new ColorRGBA(1.f, 1.f, 1.f, 1.f));\r
+        ms.setShininess(128.f);\r
+        selectedEdgeState = ms;\r
+        \r
+        ms = editor.getRenderingComponent().getDisplaySystem().getRenderer().createMaterialState();\r
+        ms.setDiffuse(new ColorRGBA(1.f, 0.f, 1.f, 1.f));\r
+        ms.setEmissive(new ColorRGBA(1.f, 0.f, 1.f, 1.f));\r
+        ms.setSpecular(new ColorRGBA(1.f, 0.f, 1.f, 1.f));\r
+        ms.setAmbient(new ColorRGBA(1.f, 0.f, 1.f, 1.f));\r
+        ms.setShininess(128.f);\r
+        \r
+        highlightedEdgeState = ms;\r
+\r
+        \r
+    }\r
+\r
+\r
+    /**\r
+     * This method is used to get implementation specific geometry.\r
+     * Arrays first element is a mesh, second contains edges.\r
+     * @return\r
+     */\r
+    public Geometry[] getGeometry(Graph graph, boolean update) {\r
+       G3DNode shape = getG3DNode(graph);\r
+       final GeometryProvider provider = GeometryProviderRegistry.getGeometryProvider(shape);\r
+       if (!update) {\r
+               return provider.getGeometryFromResource(shape, false);\r
+       } else {\r
+               if (geometry == null) {\r
+                               geometry = provider.getGeometryFromResource(shape, false);\r
+                       } else {\r
+                               provider.reconstructGeometry(shape, false, geometry);\r
+                       }\r
+               return geometry;\r
+       }\r
+    }\r
+\r
+    /**\r
+     * Updates shapes and it's ancestors geometry\r
+     */\r
+    public void updateGeometry(Graph graph) {\r
+        updateTransform(graph);\r
+       // cleanAnimation();\r
+        //System.out.println("ShapeNode.updateGeometry() " + name);\r
+        if (geometry == null) {\r
+               Geometry g[] = getGeometry(graph,true);\r
+               if (g != null) {\r
+                               mesh = g[0];\r
+                               //TODO : uid\r
+                               mesh.setName(id); \r
+                               mesh.setModelBound(new BoundingBox());\r
+                               if (g.length > 1) {\r
+                                       lines = g[1];\r
+                               } else {\r
+                                       lines = null;\r
+                               }\r
+                               body.attachChild(mesh);\r
+                               transparent.detachAllChildren();\r
+                               SharedMesh m = new SharedMesh("",(TriMesh)mesh);\r
+                               m.setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);\r
+                               m.getBatch(0).setCastsShadows(false);\r
+                               transparent.attachChild(m);\r
+                               \r
+                               \r
+                               if (lines == null) {\r
+                                       WireframeState ws = editor.getRenderingComponent().getDisplaySystem().getRenderer().createWireframeState();\r
+                                       edge.attachChild(new SharedMesh("",(TriMesh)mesh));\r
+                                       edge.setRenderState(ws);\r
+                               } else {\r
+                                       ZBufferState zs = editor.getRenderingComponent().getDisplaySystem().getRenderer().createZBufferState();\r
+                           zs.setFunction(ZBufferState.CF_ALWAYS);\r
+                           AlphaState as = editor.getRenderingComponent().getDisplaySystem().getRenderer().createAlphaState();\r
+                           as.setBlendEnabled(true);\r
+                           as.setDstFunction(AlphaState.DB_ONE_MINUS_SRC_ALPHA);\r
+                           as.setSrcFunction(AlphaState.DB_SRC_ALPHA);\r
+                           lines.setRenderState(zs);\r
+                           lines.setRenderState(as);\r
+                           lines.setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);\r
+                           edge.attachChild(lines);\r
+                               }\r
+\r
+               }\r
+        }\r
+        if (geometry != null) {\r
+               getGeometry(graph,true);\r
+\r
+            \r
+            G3DNode shape = getG3DNode(graph);\r
+            //if (renderStates == null)\r
+               updateAppearance(shape);\r
+\r
+            if (isVisible()) {\r
+               getGroup().attachChild(body);\r
+            } else {\r
+               body.removeFromParent();\r
+            }\r
+            if (isTransparentVisible()) {\r
+               getGroup().attachChild(transparent);\r
+               //setVisible(TRANSPARENT, true);\r
+            } else {\r
+               transparent.removeFromParent();\r
+            }\r
+\r
+            if (isSelectedVisible() || isHighlightedVisible()) {\r
+               getGroup().attachChild(edge);\r
+               //setVisible(SELECTED_EDGE, true);\r
+            } else {\r
+               edge.removeFromParent();\r
+               //setVisible(SELECTED_EDGE,false);\r
+            }\r
+            \r
+            \r
+            mesh.updateModelBound();\r
+            CollisionTreeManager.getInstance().updateCollisionTree(mesh);\r
+            //mesh.updateCollisionTree();\r
+  \r
+        }\r
+    }\r
+    \r
+    protected void updateAppearance(IEntity shape) {\r
+       AppearanceProvider provider = AppearanceProviderRegistry.getAppearanceProvider(shape);\r
+        if (provider != null) {\r
+            renderStates = provider.getAppearanceFromResource(shape, editor.getRenderingComponent().getDisplaySystem().getRenderer());\r
+        } else {\r
+               renderStates = getMaterial();\r
+        }\r
+        \r
+        isTransparent = false;\r
+        for (RenderState s : renderStates) {\r
+               if (s instanceof AlphaState)\r
+                       isTransparent = true;\r
+        }\r
+        setAppearance();\r
+    }\r
+    \r
+    protected void setAppearance() {\r
+       if (mesh ==  null || renderStates == null) {\r
+               return;\r
+       }\r
+               for (RenderState s : renderStates)\r
+                       mesh.setRenderState(s);\r
+               if (isTransparent)\r
+                       mesh.setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);\r
+               else\r
+                       mesh.setRenderQueueMode(Renderer.QUEUE_OPAQUE);\r
+    }\r
+\r
+    public void setSelected(boolean selected) {\r
+        if (this.selected == selected)\r
+            return;\r
+        this.selected = selected;\r
+        if (selected) {\r
+\r
+            setSelectedVisible(true);\r
+            setTransparentVisible(true);\r
+        } else {\r
+            setSelectedVisible(false);\r
+            setTransparentVisible(false);\r
+        }\r
+    }\r
+\r
+    public boolean isSelected() {\r
+        return selected;\r
+    }\r
+    \r
+    \r
+\r
+    public boolean isHighlighted() {\r
+        return highlighted;\r
+    }\r
+\r
+    public void setHighlighted(boolean highlighted) {\r
+        if (this.highlighted == highlighted)\r
+            return;\r
+        this.highlighted = highlighted;\r
+        if (highlighted) {\r
+            setHighlightedVisible(true);\r
+        } else {\r
+            setHighlightedVisible(false);\r
+        }\r
+    }\r
+    \r
+    public boolean isVisible(int shape) {\r
+        return visible[shape];\r
+    }\r
+    \r
+    public void setVisible(int shape, boolean visible) {\r
+        if (this.visible[shape] == visible)\r
+            return;   \r
+        this.visible[shape] = visible;\r
+        if (mesh == null) {\r
+               return;\r
+        }\r
+        if (this.visible[NORMAL]){\r
+               getGroup().attachChild(body);\r
+        } else {\r
+               body.removeFromParent();\r
+        }\r
+        if (this.visible[TRANSPARENT]) {\r
+               getGroup().attachChild(transparent);\r
+        } else {\r
+               transparent.removeFromParent();\r
+        }\r
+        if (this.visible[SELECTED_EDGE] || this.visible[HIGHLIGHTED_EDGE]) {\r
+               if (this.visible[HIGHLIGHTED_EDGE])\r
+                       edge.setRenderState(highlightedEdgeState);\r
+               else\r
+                       edge.setRenderState(selectedEdgeState);\r
+               getGroup().attachChild(edge);\r
+               edge.updateRenderState();\r
+        } else {\r
+               edge.removeFromParent();\r
+        } \r
+    }\r
+\r
+    public boolean isVisible() {\r
+        return isVisible(NORMAL);\r
+    }\r
+\r
+    public void setVisible(boolean visible) {\r
+        setVisible(NORMAL, visible);\r
+    }\r
+\r
+    public boolean isSelectedVisible() {\r
+        return isVisible(SELECTED_EDGE);\r
+    }\r
+\r
+    public void setSelectedVisible(boolean visible) {\r
+        setVisible(SELECTED_EDGE, visible);\r
+    }\r
+\r
+    public boolean isHighlightedVisible() {\r
+        return isVisible(HIGHLIGHTED_EDGE);\r
+    }\r
+\r
+    public void setHighlightedVisible(boolean visible) {\r
+        setVisible(HIGHLIGHTED_EDGE, visible);\r
+        \r
+    }\r
+\r
+    \r
+\r
+    public boolean isTransparentVisible() {\r
+        return isVisible(TRANSPARENT);\r
+    }\r
+\r
+    public void setTransparentVisible(boolean visible) {\r
+        setVisible(TRANSPARENT, visible);\r
+    }\r
+\r
+    public void setPickable(boolean pickable) {\r
+       body.setIsCollidable(pickable);\r
+    }\r
+    \r
+    public Collection<RenderState> getMaterial() {\r
+       List<RenderState> states = new ArrayList<RenderState>();\r
+       MaterialState ms = editor.getRenderingComponent().getDisplaySystem().getRenderer().createMaterialState();\r
+       ms.setEmissive(new ColorRGBA(0.f,0.f,0.f,0.f));\r
+       ms.setSpecular(new ColorRGBA(1.f,1.f,1.f,1.f));\r
+       ms.setDiffuse(new ColorRGBA(0.75f,0.f,0.f,0.f));\r
+       ms.setAmbient(new ColorRGBA(0.75f,0.f,0.f,0.f));\r
+       ms.setEnabled(true);\r
+       ms.setShininess(128.f);\r
+       states.add(ms);\r
+\r
+       return states;  \r
+    }\r
+    \r
+    private Animation animation;\r
+    private static int preCalcSteps = 9;\r
+    private Geometry[] preCalc = null;\r
+    private int currentPreCalc = 0;\r
+    \r
+    public void animate(double delta,double frameTime) {\r
+        if (animation != null)\r
+            animation.interpolate(delta);\r
+        if (preCalc != null) {\r
+            int newPreCalc = (int)Math.round(delta*(preCalc.length-1));\r
+            if (currentPreCalc != newPreCalc) {\r
+\r
+               preCalc[currentPreCalc].removeFromParent();\r
+                currentPreCalc = newPreCalc;\r
+                \r
+                body.attachChild(preCalc[currentPreCalc]);\r
+            }\r
+        }\r
+    }\r
+    \r
+    private void cleanAnimation() {\r
+       this.animation = null;\r
+       if (preCalc != null) {\r
+               for (Geometry g : preCalc) {\r
+                       if (g != null) {\r
+                               g.removeFromParent();\r
+                               g.clearBuffers();\r
+                       }\r
+               }\r
+               preCalc = null;\r
+       }\r
+    }\r
+\r
+    /**\r
+     * Sets shape's animation\r
+     * TODO : multiple animations at the same time! (must check common animatable properties)\r
+     * TODO : initial values (material, ..) (requires changes in the ontology)\r
+     * TODO : messy code, refactor!\r
+     * TODO : calculate number of required pre-calculated geometries \r
+     * @param animation\r
+     */\r
+    public boolean setAnimation(Graph g, Resource res) {\r
+       ContextGraph graph;\r
+       if (g instanceof ContextGraph) {\r
+               graph = (ContextGraph)g;\r
+       } else {\r
+               graph = new ContextGraph(g);\r
+               graph.setContext(shapeResource);\r
+       }\r
+       cleanAnimation();\r
+       if (res == null) {\r
+               if (isVisible())\r
+                       body.attachChild(mesh);\r
+            return false;\r
+        }\r
+       org.simantics.animation.stubs.Animation animation = new org.simantics.animation.stubs.Animation(graph,res);\r
+       G3DNode shape = getG3DNode(graph);\r
+        G3DNode modelResource = G3DTools.getModelFromResource(graph,shape.getResource());\r
+        assert (modelResource != null);\r
+        G3DModel model = new G3DModel(graph,modelResource.getResource());\r
+        Collection<org.simantics.animation.stubs.Animation> animations = model.getAnimation();\r
+        boolean found = false;\r
+        for (org.simantics.animation.stubs.Animation a : animations) {\r
+            if (a.getResource().equals(animation.getResource())) {\r
+                found = true;\r
+                break;\r
+            }\r
+        }\r
+        if (!found) {\r
+            ErrorLogger.getDefault().logWarning("Shape " + shape.getResource() + " cannot handle animation " + animation.getResource() + " because it isn't model's animation", null);\r
+            return false;\r
+        }\r
+        Collection<org.simantics.animation.stubs.Interpolator> interpolators = animation.getInterpolator();\r
+        List<org.simantics.animation.stubs.Interpolator> handled = new ArrayList<org.simantics.animation.stubs.Interpolator>();\r
+        List<org.simantics.animation.stubs.Interpolator> precalculated = new ArrayList<org.simantics.animation.stubs.Interpolator>();\r
+        for (org.simantics.animation.stubs.Interpolator i : interpolators) {\r
+            IEntity target = i.getTarget();\r
+            if (G3DTools.hasProperty(graph,shape.getResource(),target.getResource()))\r
+                handled.add(i);\r
+            else if (G3DTools.hasSubProperty(graph,shape.getResource(),target.getResource())) {\r
+                precalculated.add(i);\r
+            }\r
+        }\r
+        if (handled.size() == 0 && precalculated.size() == 0) {\r
+            ErrorLogger.getDefault().logWarning("Shape " + shape.getResource() + " cannot handle animation " + animation.getResource() + " since it doesn't change any of shape's properties", null);\r
+            return false;\r
+        }\r
+        \r
+        \r
+        this.animation = new Animation();\r
+        \r
+        org.simantics.animation.stubs.Interpolator[] pos = new org.simantics.animation.stubs.Interpolator[3];\r
+        org.simantics.animation.stubs.Interpolator[] ambient = new org.simantics.animation.stubs.Interpolator[3];\r
+        org.simantics.animation.stubs.Interpolator[] diffuse = new org.simantics.animation.stubs.Interpolator[3];\r
+        org.simantics.animation.stubs.Interpolator[] specular = new org.simantics.animation.stubs.Interpolator[3];\r
+        org.simantics.animation.stubs.Interpolator[] emissive = new org.simantics.animation.stubs.Interpolator[3];\r
+\r
+        Builtins builtins = graph.getBuiltins();\r
+\r
+        \r
+        for (org.simantics.animation.stubs.Interpolator i : handled) {\r
+            IEntity target = i.getTarget();\r
+            //if (target.isInstanceOf(Resources.g3dResource.LocalOrientation)) {\r
+            if (target.isInstanceOf(Resources.g3dResource.Orientation) && target.getRelatedObjects(Resources.g3dResource.LocalOrientationOf).size() == 1) {\r
+                SlerpInterpolator si = new SlerpInterpolator((SlerpCurve)Resources.curveBuilder.loadInterpolator(i));\r
+                si.setTarget(transform);\r
+                this.animation.addInterpolator(si);\r
+            } else if (target.isInstanceOf(builtins.Double)) {\r
+               Resource targetResource = target.getResource();\r
+               Collection<IEntity> p = target.getRelatedObjects(builtins.PropertyOf);\r
+                if (p.size() == 1) {\r
+                    IEntity parent = p.iterator().next();\r
+                    //if (parent.isInstanceOf(Resources.g3dResource.LocalPosition)) {\r
+                    if (parent.isInstanceOf(Resources.g3dResource.Position) && parent.getRelatedObjects(Resources.g3dResource.LocalPositionOf).size() == 1) {\r
+                        if (parent.getSingleRelatedObject(Resources.g3dResource.HasX).getResource().equals(targetResource)) {\r
+                             pos[0] = i;\r
+                        } else if (parent.getSingleRelatedObject(Resources.g3dResource.HasY).getResource().equals(targetResource)) {\r
+                            pos[1] = i;\r
+                        } else if (parent.getSingleRelatedObject(Resources.g3dResource.HasZ).getResource().equals(targetResource)) {\r
+                            pos[2] = i;\r
+                        } else {\r
+                            ErrorLogger.getDefault().logWarning("Cannot map animation interpolator " + i.getResource() + " to target (Position ?)" + target.getResource(),  null);\r
+                        }\r
+                    } else if (parent.isInstanceOf(Resources.g3dResource.Color)) {\r
+                        org.simantics.animation.stubs.Interpolator[] color = null;\r
+                        if (parent.isInstanceOf(Resources.g3dResource.Color) && parent.getRelatedObjects(Resources.g3dResource.AmbientColorOf).size() > 0) {\r
+                                color = ambient;\r
+                        } else if (parent.isInstanceOf(Resources.g3dResource.Color)&& parent.getRelatedObjects(Resources.g3dResource.DiffuseColorOf).size() > 0) {\r
+                                color = diffuse;\r
+                        } else if (parent.isInstanceOf(Resources.g3dResource.Color) && parent.getRelatedObjects(Resources.g3dResource.SpecularColorOf).size() > 0) {\r
+                                color = specular;\r
+                        } else if (parent.isInstanceOf(Resources.g3dResource.Color) && parent.getRelatedObjects(Resources.g3dResource.EmissiveColorOf).size() > 0) {\r
+                                color = emissive;\r
+                        } else {\r
+                            ErrorLogger.getDefault().logWarning("Cannot map animation interpolator " + i.getResource() + " to target (Color)" + target.getResource() + " unknown color type",  null);\r
+                        }\r
+                        if (color != null) {\r
+                            if (parent.getSingleRelatedObject(Resources.g3dResource.HasRed).getResource().equals(targetResource)) {\r
+                                color[0] = i;\r
+                            } else if (parent.getSingleRelatedObject(Resources.g3dResource.HasGreen).getResource().equals(targetResource)) {\r
+                                color[1] = i;\r
+                            } else if (parent.getSingleRelatedObject(Resources.g3dResource.HasBlue).getResource().equals(targetResource)) {\r
+                                color[2] = i;\r
+                            } else {\r
+                                ErrorLogger.getDefault().logWarning(\r
+                                        "Cannot map animation interpolator " + i.getResource()\r
+                                                + " to target (Color ?)" + target.getResource(), null);\r
+                            }\r
+                        }\r
+                    } else if (parent.isInstanceOf(Resources.g3dResource.Material)) {\r
+                       // TODO : transparency or shininess     \r
+                    } else {\r
+                        ErrorLogger.getDefault().logWarning("Cannot map animation interpolator " + i.getResource() + " to target" + target.getResource() + " adding it to precalculated interpolators",  null);\r
+                        precalculated.add(i);\r
+                    }\r
+                } else {\r
+                    if (p.size() == 0) {\r
+                        ErrorLogger.getDefault().logWarning("Cannot map animation interpolator " + i.getResource() + " to target (Double)" + target.getResource() + " since it is not a part of a property",  null);\r
+                    } else {\r
+                        ErrorLogger.getDefault().logWarning("Cannot map animation interpolator " + i.getResource() + " to target (Double)" + target.getResource() + " since it acts as a property to more than one entity",  null);  \r
+                    }\r
+                }\r
+            } else {\r
+                ErrorLogger.getDefault().logWarning("Cannot map animation interpolator " + i.getResource() + " to target" + target.getResource(),  null);\r
+            }\r
+        }\r
+        \r
+        if (pos[0] != null || pos[1] != null || pos[2] != null) {\r
+            ScalarInterpolator xIp;\r
+            ScalarInterpolator yIp;\r
+            ScalarInterpolator zIp;\r
+            if (pos[0] != null) {\r
+                xIp = new TCBInterpolator((TCBCurve)Resources.curveBuilder.loadInterpolator(pos[0]));\r
+            } else {\r
+                xIp = new ConstantInterpolator(shape.getLocalPosition().getX()[0]);\r
+            }\r
+            if (pos[1] != null) {\r
+                yIp = new TCBInterpolator((TCBCurve)Resources.curveBuilder.loadInterpolator(pos[1]));\r
+            } else {\r
+                yIp = new ConstantInterpolator(shape.getLocalPosition().getY()[0]);\r
+            }\r
+            if (pos[2] != null) {\r
+                zIp = new TCBInterpolator((TCBCurve)Resources.curveBuilder.loadInterpolator(pos[2]));\r
+            } else {\r
+                zIp = new ConstantInterpolator(shape.getLocalPosition().getZ()[0]);\r
+            }\r
+            ChanneledPositionInterpolator ip = new ChanneledPositionInterpolator(xIp,yIp,zIp);\r
+            ip.setTarget(transform);\r
+            this.animation.addInterpolator(ip);\r
+        \r
+        }  \r
+        addColorInterpolator(shape, ambient, ChanneledColorInterpolator.AMBIENT);\r
+        addColorInterpolator(shape, diffuse, ChanneledColorInterpolator.DIFFUSE);\r
+        addColorInterpolator(shape, emissive, ChanneledColorInterpolator.EMISSIVE);\r
+        addColorInterpolator(shape, specular, ChanneledColorInterpolator.SPECULAR);\r
+        \r
+        if (precalculated.size() == 0) {\r
+            preCalc = null;\r
+        } else {\r
+                preCalc = new Geometry[preCalcSteps+1];\r
+                for (int i = 0; i <= preCalcSteps; i++) {\r
+                    double delta = ((double)i / (double)preCalcSteps);\r
+                    // TODO : copy-paste from CSGAnimatorView\r
+                    // FIXME : does not update transformations (since ContextGraph does not support queries for context dependent values)\r
+                    for (Interpolator ip : precalculated) {\r
+                        if (ip.isInstanceOf(Resources.animationResource.ScalarInterpolator)) {\r
+                            // TODO : creating curve each time when time is set is slow.\r
+                            // Curve should be cached\r
+                            TCBCurve c = (TCBCurve)Resources.curveBuilder.loadInterpolator(ip);\r
+                            double out = c.evaluate(delta);\r
+                            //Double d = DoubleFactory.create(ip.getTarget());\r
+                            //d.setValue(new double[]{out});\r
+                            IEntity d = ip.getTarget();\r
+                            d.toProperty().setDoubleArray(new double[]{out});\r
+                        } else if (ip.isInstanceOf(Resources.animationResource.SlerpInterpolator)) {\r
+                            // TODO : creating curve each time when time is set is slow.\r
+                            // Curve should be cached\r
+                            SlerpCurve c = (SlerpCurve)Resources.curveBuilder.loadInterpolator(ip);\r
+                            Quat4d out = c.evaluate(delta);\r
+                            Orientation r = new Orientation(ip.getTarget());\r
+                            AxisAngle4d aa = new AxisAngle4d();\r
+                            aa.set(out);\r
+                            G3DTools.setOrientation(r, aa);\r
+                        }\r
+                    }\r
+                    preCalc[i] = getGeometry(graph,false)[0];\r
+                    preCalc[i].setIsCollidable(false);\r
+                    AppearanceTools.copyMaterial(mesh, preCalc[i]);\r
+                }\r
+                \r
+                // We'll have to remove original (non-animated) shape from the node\r
+                mesh.removeFromParent();\r
+                body.attachChild(preCalc[0]);\r
+           \r
+        }\r
+        return true;\r
+    }\r
+    \r
+    private void addColorInterpolator(G3DNode shape, org.simantics.animation.stubs.Interpolator[] color, int type) {\r
+        if (color[0] != null || color[1] != null || color[2] != null) {\r
+            ScalarInterpolator xIp;\r
+            ScalarInterpolator yIp;\r
+            ScalarInterpolator zIp;\r
+            Color col = null;\r
+            Collection<IEntity> appearanceResource = shape.getRelatedObjects(Resources.g3dResource.HasAppearance);\r
+            if (appearanceResource.size() == 0) {\r
+                ErrorLogger.getDefault().logWarning("Cannot create interpolator for color because shape " + shape.getResource() + " has no appearance", null);\r
+            }\r
+            Appearance a = new Appearance(shape.getGraph(),appearanceResource.iterator().next().getResource());\r
+            switch (type) {\r
+                case ChanneledColorInterpolator.AMBIENT:\r
+                    col = a.getMaterial().getAmbientColor();\r
+                    break;\r
+                case ChanneledColorInterpolator.DIFFUSE:\r
+                    col = a.getMaterial().getDiffuseColor();\r
+                    break;\r
+                case ChanneledColorInterpolator.EMISSIVE:\r
+                    col = a.getMaterial().getEmissiveColor();\r
+                    break;\r
+                case ChanneledColorInterpolator.SPECULAR:\r
+                    col = a.getMaterial().getSpecularColor();\r
+                    break;\r
+                default:\r
+                    ErrorLogger.defaultLogError("Unknown color type", null);\r
+                    return;\r
+            }\r
+\r
+            if (color[0] != null) {\r
+                xIp = new TCBInterpolator((TCBCurve)Resources.curveBuilder.loadInterpolator(color[0]));//CurveUtils.loadCurve(color[0].getResource()));\r
+            } else {\r
+                xIp = new ConstantInterpolator(col.getRed()[0]);\r
+            }\r
+            if (color[1] != null) {\r
+                yIp = new TCBInterpolator((TCBCurve)Resources.curveBuilder.loadInterpolator(color[1]));//CurveUtils.loadCurve(color[1].getResource()));\r
+            } else {\r
+                yIp = new ConstantInterpolator(col.getGreen()[0]);\r
+            }\r
+            if (color[1] != null) {\r
+                zIp = new TCBInterpolator((TCBCurve)Resources.curveBuilder.loadInterpolator(color[2]));//CurveUtils.loadCurve(color[2].getResource()));\r
+            } else {\r
+                zIp = new ConstantInterpolator(col.getBlue()[0]);\r
+            }\r
+            ChanneledColorInterpolator ip = new ChanneledColorInterpolator(xIp,yIp,zIp);\r
+            ip.setType(type);\r
+            ip.setTarget(mesh.getRenderState(RenderState.RS_MATERIAL));\r
+            this.animation.addInterpolator(ip);\r
+        } \r
+    }\r
+    \r
+    public boolean setRandomAnimation(Graph graph) {\r
+        return false;\r
+    }\r
+    \r
+    public void dispose() {\r
+//     mesh.clearBuffers();\r
+//     mesh.clearBatches();\r
+//     lines.clearBuffers();\r
+//     lines.clearBatches();\r
+       if (mesh != null) {\r
+               mesh.removeFromParent();\r
+               mesh.dispose();\r
+               mesh = null;\r
+       }\r
+       if (lines != null) {\r
+               lines.removeFromParent();\r
+               lines.dispose();\r
+               lines = null;\r
+       }\r
+       super.dispose();\r
+    }\r
+}
\ No newline at end of file