From: luukkainen Date: Fri, 5 Jul 2013 11:10:43 +0000 (+0000) Subject: Alpha-version of jME-bindings for g3d. X-Git-Tag: simantics-1.19.0~5 X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=commitdiff_plain;h=5b4454ce3dac8d0cf04caaaa696f300c16be99a0;p=simantics%2F3d.git Alpha-version of jME-bindings for g3d. refs #4378 git-svn-id: https://www.simantics.org/svn/simantics/3d/trunk@27693 ac1ea38d-2e2b-0410-8846-a27921b304fc --- diff --git a/org.simantics.g3d.jme/.classpath b/org.simantics.g3d.jme/.classpath new file mode 100644 index 00000000..8a8f1668 --- /dev/null +++ b/org.simantics.g3d.jme/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/org.simantics.g3d.jme/.project b/org.simantics.g3d.jme/.project new file mode 100644 index 00000000..424c0e23 --- /dev/null +++ b/org.simantics.g3d.jme/.project @@ -0,0 +1,28 @@ + + + org.simantics.g3d.jme + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/org.simantics.g3d.jme/.settings/org.eclipse.jdt.core.prefs b/org.simantics.g3d.jme/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..c107b6a5 --- /dev/null +++ b/org.simantics.g3d.jme/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,8 @@ +#Wed Aug 08 16:41:22 EEST 2012 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/org.simantics.g3d.jme/META-INF/MANIFEST.MF b/org.simantics.g3d.jme/META-INF/MANIFEST.MF new file mode 100644 index 00000000..59de283d --- /dev/null +++ b/org.simantics.g3d.jme/META-INF/MANIFEST.MF @@ -0,0 +1,22 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Jme +Bundle-SymbolicName: org.simantics.g3d.jme;singleton:=true +Bundle-Version: 1.0.0.qualifier +Bundle-Activator: org.simantics.g3d.jme.Activator +Require-Bundle: org.eclipse.ui, + org.eclipse.core.runtime, + org.simantics.g3d;bundle-version="1.0.0", + org.simantics.g3d.ontology;bundle-version="1.0.0", + com.jme;bundle-version="1.0.0", + javax.vecmath;bundle-version="1.5.2", + org.simantics.db;bundle-version="1.1.0", + org.simantics.db.common;bundle-version="1.1.0", + org.simantics.objmap2;bundle-version="1.0.0", + org.eclipse.ui.views;bundle-version="3.6.0" +Bundle-ActivationPolicy: lazy +Bundle-RequiredExecutionEnvironment: JavaSE-1.6 +Export-Package: org.simantics.g3d.jme.common, + org.simantics.g3d.jme.shape, + org.simantics.g3d.jme.system, + org.simantics.g3d.jme.utils diff --git a/org.simantics.g3d.jme/build.properties b/org.simantics.g3d.jme/build.properties new file mode 100644 index 00000000..6f20375d --- /dev/null +++ b/org.simantics.g3d.jme/build.properties @@ -0,0 +1,5 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.xml diff --git a/org.simantics.g3d.jme/plugin.xml b/org.simantics.g3d.jme/plugin.xml new file mode 100644 index 00000000..d175276b --- /dev/null +++ b/org.simantics.g3d.jme/plugin.xml @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/org.simantics.g3d.jme/src/org/simantics/g3d/jme/Activator.java b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/Activator.java new file mode 100644 index 00000000..49e768fe --- /dev/null +++ b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/Activator.java @@ -0,0 +1,50 @@ +package org.simantics.g3d.jme; + +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class Activator extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "org.simantics.g3d.jme"; //$NON-NLS-1$ + + // The shared instance + private static Activator plugin; + + /** + * The constructor + */ + public Activator() { + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static Activator getDefault() { + return plugin; + } + +} diff --git a/org.simantics.g3d.jme/src/org/simantics/g3d/jme/common/AbstractJMENodeMap.java b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/common/AbstractJMENodeMap.java new file mode 100644 index 00000000..162fbb3b --- /dev/null +++ b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/common/AbstractJMENodeMap.java @@ -0,0 +1,415 @@ +package org.simantics.g3d.jme.common; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; + +import org.simantics.db.ReadGraph; +import org.simantics.db.Session; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.request.ReadRequest; +import org.simantics.db.common.request.WriteRequest; +import org.simantics.db.exception.DatabaseException; +import org.simantics.g3d.ontology.G3D; +import org.simantics.g3d.scenegraph.IG3DNode; +import org.simantics.g3d.scenegraph.RenderListener; +import org.simantics.g3d.scenegraph.base.INode; +import org.simantics.g3d.scenegraph.base.NodeListener; +import org.simantics.g3d.scenegraph.base.ParentNode; +import org.simantics.objmap.graph.IMapping; +import org.simantics.objmap.graph.IMappingListener; +import org.simantics.utils.datastructures.MapList; +import org.simantics.utils.datastructures.MapSet; +import org.simantics.utils.datastructures.Pair; + +import com.jme3.app.Application; +import com.jme3.scene.Spatial; + +public abstract class AbstractJMENodeMap implements JMENodeMap, IMappingListener, NodeListener, RenderListener { + + protected Session session; + protected IMapping mapping; + protected Application app; +// protected InteractiveVtkPanel panel; + + protected MapList nodeToActor = new MapList(); + protected Map actorToNode = new HashMap(); + + protected ParentNode rootNode; + + public AbstractJMENodeMap(Session session, IMapping mapping, Application app, ParentNode rootNode) { + this.session = session; + this.mapping = mapping; + this.rootNode = rootNode; + this.app = app; +// this.panel = panel; +// panel.addListener(this); + mapping.addMappingListener(this); + rootNode.addListener(this); + } + + protected abstract void addActor(E node); + protected abstract void removeActor(E node); + protected abstract void updateActor(E node,Set ids); + + + public void repaint() { + + } + + public void populate() { + for (E node : rootNode.getNodes()) { + receiveAdd(node, node.getParentRel(),true); + } + repaint(); + } + + @Override + public IG3DNode getNode(Spatial prop) { + return actorToNode.get(prop); + } + + @SuppressWarnings("unchecked") + @Override + public Collection getRenderObjects(IG3DNode node) { + return nodeToActor.getValues((E)node); + } + + @SuppressWarnings("unchecked") + @Override + public ParentNode getRootNode() { + return (ParentNode)rootNode; + } + + @Override + public void commit() { + session.asyncRequest(new WriteRequest() { + + @Override + public void perform(WriteGraph graph) throws DatabaseException { + synchronized(syncMutex) { + graphUpdates = true; + mapping.updateDomain(graph); + graphUpdates = false; + } + } + }); + } + + @Override + public boolean isChangeTracking() { + return changeTracking; + } + + @Override + public void setChangeTracking(boolean enabled) { + changeTracking = enabled; + } + + private boolean changeTracking = true; + + private Object syncMutex = new Object(); + + + private List> added = new ArrayList>(); + private List> removed = new ArrayList>(); + //private List> updated = new ArrayList>(); + private MapSet updated = new MapSet();//new MapSet.Hash(); + + + @SuppressWarnings("unchecked") + @Override + public void updateRenderObjectsFor(IG3DNode node) { + List toDelete = new ArrayList(); + for (Spatial prop : nodeToActor.getValues((E)node)) { +// if (prop.GetVTKId() != 0) { +// panel.GetRenderer().RemoveActor(prop); +// //prop.Delete(); +// toDelete.add(prop); +// } + prop.removeFromParent(); + actorToNode.remove(prop); + } + nodeToActor.remove((E)node); + Collection coll = getActors((E)node); + if (coll == null) + return; + for (Spatial prop : coll) { + nodeToActor.add((E)node,prop); + actorToNode.put(prop, (E)node); + toDelete.remove(prop); + } + for (Spatial p : toDelete) { + //p.Delete(); + } + } + + protected abstract Collection getActors(E node); + + @SuppressWarnings("unchecked") + private void receiveAdd(E node, String id, boolean db) { + synchronized (syncMutex) { + for (Pair n : added) { + if (n.first.equals(node)) + return; + } + if (changeTracking) { + mapping.rangeModified(node.getParent()); + } + added.add(new Pair(node, id)); + } + repaint(); + } + + @SuppressWarnings("unchecked") + private void receiveRemove(E node, String id, boolean db) { + synchronized (syncMutex) { + for (Pair n : removed) { + if (n.first.equals(node)) + return; + } + if (changeTracking && !db) + mapping.rangeModified(node.getParent()); + removed.add(new Pair(node, id)); + } + repaint(); + } + + @SuppressWarnings("unchecked") + private void receiveUpdate(E node, String id, boolean db) { + synchronized (syncMutex) { +// for (Pair n : updated) { +// if (n.first.equals(node)) +// return; +// } + if (changeTracking && !db) + mapping.rangeModified(node); + //updated.add(new Pair(node, id)); + updated.add(node, id); + } + repaint(); + } + + private boolean graphUpdates = false; + private Set graphModified = new HashSet(); + + @Override + public void domainModified() { + if (graphUpdates) + return; + //System.out.println("domainModified"); + session.asyncRequest(new ReadRequest() { + + @SuppressWarnings("unchecked") + @Override + public void run(ReadGraph graph) throws DatabaseException { + graphUpdates = true; + for (Object domainObject : mapping.getDomainModified()) { + Object rangeObject = mapping.get(domainObject); + if (rangeObject != null) + graphModified.add(rangeObject); + } + mapping.updateRange(graph); + graphModified.clear(); + graphUpdates = false; + if (mapping.isRangeModified()) + commit(); + } + }); + + } + + @Override + public void rangeModified() { + //System.out.println("rangeModified"); + + } + + @Override + public void postRender() { + + } + + List> rem = new ArrayList>(); + List> add = new ArrayList>(); + MapSet mod = new MapSet();//new MapSet.Hash(); + Set propagation = new HashSet(); + Stack stack = new Stack(); + + @SuppressWarnings("unchecked") + @Override + public synchronized void preRender() { + rem.clear(); + add.clear(); + mod.clear(); + propagation.clear(); + + synchronized (syncMutex) { + rem.addAll(removed); + add.addAll(added); + //mod.addAll(updated); + for (E e : updated.getKeys()) { + for (String s : updated.getValues(e)) + mod.add(e, s); + } + + removed.clear(); + added.clear(); + updated.clear(); + } + + for (Pair n : rem) { + stopListening(n.first); + removeActor(n.first); + + } + + for (Pair n : add) { + addActor(n.first); + listen(n.first); + } + + for (E e : mod.getKeys()) { + Set ids = mod.getValues(e); + if (ids.contains(G3D.URIs.hasPosition) || ids.contains(G3D.URIs.hasOrientation)) { + if (!propagation.contains(e)) + propagation.add(e); + } + } + + if (propagation.size() > 0) { + stack.clear(); + stack.addAll(propagation); + propagation.clear(); + while (!stack.isEmpty()) { + E node = stack.pop(); + if (propagation.contains(node)) + continue; + propagation.add(node); + for (NodeListener l : node.getListeners()) { + if (l == this) { + //changeTracking = false; + //l.propertyChanged(node, G3D.URIs.hasPosition); + //changeTracking = true; + } else { + l.propertyChanged(node, G3D.URIs.hasPosition); + } + } + if (node instanceof ParentNode) { + stack.addAll(((ParentNode)node).getNodes()); + } + } + } + + for (E e : mod.getKeys()) { + Set ids = mod.getValues(e); + updateActor(e,ids); + } + + for (Pair n : rem) { + for (NodeListener l : nodeListeners) + l.nodeRemoved(null, n.first, n.second); + } + for (Pair n : add) { + for (NodeListener l : nodeListeners) + l.nodeAdded(n.first.getParent(), n.first, n.second); + } +// for (Pair n : mod) { +// for (NodeListener l : nodeListeners) +// l.propertyChanged(n.first, n.second); +// } + for (E e : mod.getKeys()) { + for (NodeListener l : nodeListeners) + for (String s : mod.getValues(e)) + l.propertyChanged(e, s); + } + } + + @SuppressWarnings("unchecked") + private void listen(INode node) { + node.addListener(this); + if (node instanceof ParentNode) { + ParentNode parentNode = (ParentNode)node; + for (INode n : parentNode.getNodes()) + listen(n); + } + } + + private void stopListening(INode node) { + node.removeListener(this); + if (node instanceof ParentNode) { + @SuppressWarnings("unchecked") + ParentNode parentNode = (ParentNode)node; + for (INode n : parentNode.getNodes()) + stopListening(n); + } + } + + @SuppressWarnings("unchecked") + @Override + public void propertyChanged(INode node, String id) { + //receiveUpdate((E)node, id, graphUpdates); + receiveUpdate((E)node, id, graphModified.contains(node)); + + } + + @SuppressWarnings("unchecked") + @Override + public void nodeAdded(ParentNode node, INode child, + String rel) { + //receiveAdd((E)child, rel ,graphUpdates); + receiveAdd((E)child, rel ,graphModified.contains(node)); + + } + + @SuppressWarnings("unchecked") + @Override + public void nodeRemoved(ParentNode node, INode child, + String rel) { + //receiveRemove((E)child, rel, graphUpdates); + receiveRemove((E)child, rel, graphModified.contains(node)); + } + + @Override + public void delete() { + changeTracking = false; + //panel.removeListener(this); + mapping.removeMappingListener(this); + + List nodes = new ArrayList(nodeToActor.getKeySize()); + nodes.addAll(nodeToActor.getKeys()); + for (E node : nodes) { + node.removeListener(this); + removeActor(node); + node.cleanup(); + } + for (Spatial prop : actorToNode.keySet()) { + prop.removeFromParent(); + //if (prop.GetVTKId() != 0) + // prop.Delete(); + } + actorToNode.clear(); + nodeToActor.clear(); + + } + + + private List nodeListeners = new ArrayList(); + @Override + public void addListener(NodeListener listener) { + nodeListeners.add(listener); + + } + + @Override + public void removeListener(NodeListener listener) { + nodeListeners.remove(listener); + + } + +} diff --git a/org.simantics.g3d.jme/src/org/simantics/g3d/jme/common/JMENodeMap.java b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/common/JMENodeMap.java new file mode 100644 index 00000000..bc7f7401 --- /dev/null +++ b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/common/JMENodeMap.java @@ -0,0 +1,9 @@ +package org.simantics.g3d.jme.common; + +import org.simantics.g3d.scenegraph.NodeMap; + +import com.jme3.scene.Spatial; + +public interface JMENodeMap extends NodeMap{ + +} diff --git a/org.simantics.g3d.jme/src/org/simantics/g3d/jme/shape/AxesActor.java b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/shape/AxesActor.java new file mode 100644 index 00000000..05f15ee1 --- /dev/null +++ b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/shape/AxesActor.java @@ -0,0 +1,39 @@ +package org.simantics.g3d.jme.shape; + +import com.jme3.app.Application; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector3f; +import com.jme3.scene.Geometry; +import com.jme3.scene.Mesh; +import com.jme3.scene.Node; +import com.jme3.scene.debug.Arrow; + +public class AxesActor extends Node { + + Application app; + + public AxesActor(Application app, double size) { + this.app = app; + putArrow(Vector3f.ZERO, Vector3f.UNIT_X.mult((float)size), ColorRGBA.Red); + putArrow(Vector3f.ZERO, Vector3f.UNIT_Y.mult((float)size), ColorRGBA.Green); + putArrow(Vector3f.ZERO, Vector3f.UNIT_Z.mult((float)size), ColorRGBA.Blue); + } + + public Geometry putShape(Mesh shape, ColorRGBA color){ + Geometry g = new Geometry("shape", shape); + Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); + mat.getAdditionalRenderState().setWireframe(true); + mat.setColor("Color", color); + g.setMaterial(mat); + this.attachChild(g); + return g; + } + + public void putArrow(Vector3f pos, Vector3f dir, ColorRGBA color){ + Arrow arrow = new Arrow(dir); + arrow.setLineWidth(4); // make arrow thicker + putShape(arrow, color).setLocalTranslation(pos); + } + +} diff --git a/org.simantics.g3d.jme/src/org/simantics/g3d/jme/shape/AxesActor2.java b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/shape/AxesActor2.java new file mode 100644 index 00000000..2c106c91 --- /dev/null +++ b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/shape/AxesActor2.java @@ -0,0 +1,60 @@ +package org.simantics.g3d.jme.shape; + +import javax.vecmath.AxisAngle4d; +import javax.vecmath.Vector3d; + +import org.simantics.g3d.math.MathTools; +import org.simantics.g3d.shape.Color4d; +import org.simantics.g3d.shape.Cone; +import org.simantics.g3d.shape.Cylinder; +import org.simantics.g3d.shape.Sphere; + +import com.jme3.app.Application; + +public class AxesActor2 extends MeshNode { + + public AxesActor2(Application app, double size) { + super(app); + + int res = 16; + + org.simantics.g3d.shape.Mesh cone_x = Cone.create(size*0.3, res); + org.simantics.g3d.shape.Mesh cone_y = Cone.create(size*0.3, res); + org.simantics.g3d.shape.Mesh cone_z = Cone.create(size*0.3, res); + cone_x.rotate(MathTools.getQuat(new AxisAngle4d(0,0,-1,Math.PI*0.5))); + cone_z.rotate(MathTools.getQuat(new AxisAngle4d(1,0,0,Math.PI*0.5))); + cone_x.translate(new Vector3d(size,0,0)); + cone_y.translate(new Vector3d(0,size,0)); + cone_z.translate(new Vector3d(0,0,size)); + + org.simantics.g3d.shape.Mesh tube_x = Cylinder.create(MathTools.ORIGIN, new Vector3d(size,0,0), size*0.1, res); + org.simantics.g3d.shape.Mesh tube_y = Cylinder.create(MathTools.ORIGIN, new Vector3d(0,size,0), size*0.1, res); + org.simantics.g3d.shape.Mesh tube_z = Cylinder.create(MathTools.ORIGIN, new Vector3d(0,0,size), size*0.1, res); + + org.simantics.g3d.shape.Mesh sphere = Sphere.create(size*0.3, res, res*2/3); + + Color4d x_col = new Color4d(1,0,0,1); + Color4d y_col = new Color4d(1,1,0,1); + Color4d z_col = new Color4d(0,1,0,1); + Color4d o_col = new Color4d(0,0,1,1); + + cone_x.setColor(x_col); + tube_x.setColor(x_col); + cone_y.setColor(y_col); + tube_y.setColor(y_col); + cone_z.setColor(z_col); + tube_z.setColor(z_col); + sphere.setColor(o_col); + + sphere.add(cone_x); + sphere.add(tube_x); + sphere.add(cone_y); + sphere.add(tube_y); + sphere.add(cone_z); + sphere.add(tube_z); + + setMesh(sphere); + } + + +} diff --git a/org.simantics.g3d.jme/src/org/simantics/g3d/jme/shape/AxisActor.java b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/shape/AxisActor.java new file mode 100644 index 00000000..c6ddd585 --- /dev/null +++ b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/shape/AxisActor.java @@ -0,0 +1,41 @@ +package org.simantics.g3d.jme.shape; + +import javax.vecmath.Vector3d; + +import com.jme3.app.Application; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector3f; +import com.jme3.scene.Geometry; +import com.jme3.scene.Mesh; +import com.jme3.scene.Node; +import com.jme3.scene.debug.Arrow; + +public class AxisActor extends Node { + + Application app; + + public AxisActor(Application app, String name, Vector3d v) { + this.app = app; + Vector3f vec = new Vector3f((float)v.x, (float)v.y, (float)v.z); + putArrow(Vector3f.ZERO, vec, ColorRGBA.Red); + + } + + public Geometry putShape(Mesh shape, ColorRGBA color){ + Geometry g = new Geometry("shape", shape); + Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); + mat.getAdditionalRenderState().setWireframe(true); + mat.setColor("Color", color); + g.setMaterial(mat); + this.attachChild(g); + return g; + } + + public void putArrow(Vector3f pos, Vector3f dir, ColorRGBA color){ + Arrow arrow = new Arrow(dir); + arrow.setLineWidth(4); // make arrow thicker + putShape(arrow, color).setLocalTranslation(pos); + } + +} diff --git a/org.simantics.g3d.jme/src/org/simantics/g3d/jme/shape/DualHeadArrowActor.java b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/shape/DualHeadArrowActor.java new file mode 100644 index 00000000..b81abeea --- /dev/null +++ b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/shape/DualHeadArrowActor.java @@ -0,0 +1,44 @@ +package org.simantics.g3d.jme.shape; + +import javax.vecmath.Vector3d; + +import com.jme3.app.Application; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector3f; +import com.jme3.scene.Geometry; +import com.jme3.scene.Mesh; +import com.jme3.scene.Node; +import com.jme3.scene.debug.Arrow; + +public class DualHeadArrowActor extends Node { + + Application app; + + public DualHeadArrowActor(Application app, String name, Vector3d v) { + this.app = app; + Vector3f vec = new Vector3f((float)(v.x*0.5), (float)(v.y*0.5), (float)(v.z*0.5)); + Vector3f vecN = new Vector3f(vec); + vecN.negate(); + putArrow(vec, vec, ColorRGBA.Red); + putArrow(vec, vecN, ColorRGBA.Red); + + } + + public Geometry putShape(Mesh shape, ColorRGBA color){ + Geometry g = new Geometry("shape", shape); + Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); + mat.getAdditionalRenderState().setWireframe(true); + mat.setColor("Color", color); + g.setMaterial(mat); + this.attachChild(g); + return g; + } + + public void putArrow(Vector3f pos, Vector3f dir, ColorRGBA color){ + Arrow arrow = new Arrow(dir); + arrow.setLineWidth(4); // make arrow thicker + putShape(arrow, color).setLocalTranslation(pos); + } + +} diff --git a/org.simantics.g3d.jme/src/org/simantics/g3d/jme/shape/MeshNode.java b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/shape/MeshNode.java new file mode 100644 index 00000000..b6baa798 --- /dev/null +++ b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/shape/MeshNode.java @@ -0,0 +1,84 @@ +package org.simantics.g3d.jme.shape; + +import javax.vecmath.Vector3d; + +import org.simantics.g3d.shape.Color4d; + +import com.jme3.app.Application; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.scene.Geometry; +import com.jme3.scene.Mesh; +import com.jme3.scene.VertexBuffer.Type; + +public class MeshNode extends Geometry { + Application app; + + + public MeshNode(Application app) { + this.app = app; + } + + public void setMesh(org.simantics.g3d.shape.Mesh mesh) { + + Mesh shape = new Mesh(); + + float points[] = new float[mesh.getVertices().size()*3]; + for (int i = 0; i < mesh.getVertices().size(); i++) { + Vector3d v = mesh.getVertices().get(i); + points[i * 3] = (float)v.x; + points[i * 3+1] = (float)v.y; + points[i * 3+2] = (float)v.z; + } + shape.setBuffer(Type.Position, 3, points); + + if (mesh.getNormals() == null) + mesh.createNormals(); + float normals[] = new float[mesh.getNormals().size()*3]; + for (int i = 0; i < mesh.getNormals().size(); i++) { + Vector3d v = mesh.getNormals().get(i); + normals[i * 3] = (float)v.x; + normals[i * 3+1] = (float)v.y; + normals[i * 3+2] = (float)v.z; + } + shape.setBuffer(Type.Normal, 3, normals); + + int index[] = new int[mesh.getIndices().size()]; + for (int i = 0; i < index.length; i++) { + index[i] = mesh.getIndices().get(i); + } + shape.setBuffer(Type.Index, 3, index); + + if (mesh.getColors() != null) { + float colors[] = new float[mesh.getColors().size()*4]; + for (int i = 0; i < mesh.getColors().size(); i++) { + Color4d v = mesh.getColors().get(i); + colors[i * 4] = (float)v.x; + colors[i * 4+1] = (float)v.y; + colors[i * 4+2] = (float)v.z; + colors[i * 4+3] = (float)v.w; + } + shape.setBuffer(Type.Color, 4, colors); + } + + setMesh(shape); + updateModelBound(); + + //Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); + Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md"); + + + if (true) { + ColorRGBA color = new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f); + mat.setColor("Diffuse", color); + mat.setColor("Ambient", color); + mat.setColor("Specular", new ColorRGBA(1, 1, 1, 1)); + mat.setBoolean("UseMaterialColors", true); + } + if (mesh.getColors() != null) + mat.setBoolean("UseVertexColor", true); + setMaterial(mat); + + } + +} diff --git a/org.simantics.g3d.jme/src/org/simantics/g3d/jme/shape/TubeActor.java b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/shape/TubeActor.java new file mode 100644 index 00000000..fbdef417 --- /dev/null +++ b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/shape/TubeActor.java @@ -0,0 +1,205 @@ +package org.simantics.g3d.jme.shape; + +import java.util.List; + +import javax.vecmath.AxisAngle4d; +import javax.vecmath.Vector3d; + +import org.simantics.g3d.math.MathTools; + +import com.jme3.app.Application; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.scene.Geometry; +import com.jme3.scene.Mesh; +import com.jme3.scene.VertexBuffer.Type; + +public class TubeActor extends Geometry { + + List vertices; + List colors; + List radiis; + Double radius = 1.0; + int resolution = 8; + + public TubeActor() { + super(); + } + + public void setResolution(int resolution) { + if (resolution > 2) + this.resolution = resolution; + } + + public void setVertices(List vertices) { + this.vertices = vertices; + } + + public void setColors(List colors) { + this.colors = colors; + } + + public void setRadiis(List radiis) { + this.radiis = radiis; + } + + public void setRadius(Double radius) { + this.radius = radius; + } + + + + public void createTube(Application app) { + if (vertices.size() < 2 ) + throw new IllegalArgumentException("Tube must have at least two vertices"); + + Vector3d t = new Vector3d(); + + for (int i = 0; i < vertices.size() - 1; i++) { + t.set(vertices.get(i+1)); + t.sub(vertices.get(i)); + if (t.lengthSquared() < 0.0001) + throw new IllegalArgumentException("vertices at index " + i + " are too close to each other"); + } + + float points[] = new float[vertices.size()*resolution*3]; + float normals[] = new float[vertices.size()*resolution*3]; + float clrs[] = null; + if (this.colors != null) + clrs = new float[vertices.size()*resolution*4]; + + for (int i = 0; i < vertices.size(); i++) { + createCircle(i,points,normals, clrs); + } + + int index[] = new int[(vertices.size()-1)*resolution*6]; + + createIndices(index); + + Mesh mesh = new Mesh(); + mesh.setBuffer(Type.Index, 3, index); + mesh.setBuffer(Type.Position, 3, points); + mesh.setBuffer(Type.Normal, 3, normals); + if (clrs != null) + mesh.setBuffer(Type.Color, 4, clrs); + setMesh(mesh); + updateModelBound(); + + ColorRGBA color = new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f); + Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md"); + mat.setColor("Diffuse", color); + mat.setColor("Ambient", color); + mat.setColor("Specular", new ColorRGBA(1, 1, 1, 1)); + mat.setBoolean("UseMaterialColors", true); + if (clrs != null) + mat.setBoolean("UseVertexColor", true); + setMaterial(mat); + + vertices.clear(); + if (colors != null) + colors.clear(); + if (radiis != null) + radiis.clear(); + + } + + private void createCircle(int i, float points[],float normals[], float clrs[]) { + final Vector3d up = new Vector3d(0,1,0); + final Vector3d up2 = new Vector3d(0,0,1); + ColorRGBA c = null; + if (clrs != null) + c = this.colors.get(i); + Vector3d p = vertices.get(i); + Vector3d t = getTangent(i); + Vector3d n = new Vector3d(); + if (up.dot(t) < 0.99) { + n.cross(up, t); + } else { + n.cross(up2, t); + } + n.normalize(); + if (radiis != null) { + n.scale(radiis.get(i)); + } else { + n.scale(radius); + } + + for (int index = 0; index < resolution; index ++) { + Vector3d v; + if (index == 0) { + v = new Vector3d(n); + + } else { + AxisAngle4d aa = new AxisAngle4d(t, (Math.PI * 2 * (double)index)/(double)resolution); + v = new Vector3d(); + MathTools.rotate(MathTools.getQuat(aa), n, v); + } + int vIndex = (i*resolution + index)*3; + points[vIndex+0] = (float)(p.x+v.x); + points[vIndex+1] = (float)(p.y + v.y); + points[vIndex+2] = (float)(p.z + v.z); + v.normalize(); + normals[vIndex+0] = (float)(v.x); + normals[vIndex+1] = (float)(v.y); + normals[vIndex+2] = (float)(v.z); + if (colors != null) { + int cIndex = (i*resolution + index)*4; + clrs[cIndex] = c.r; + clrs[cIndex+1] = c.g; + clrs[cIndex+2] = c.b; + clrs[cIndex+3] = c.a; + } + } + } + + private Vector3d getTangent(int i) { + Vector3d p,n; + if (i == 0) { + p = vertices.get(0); + n = vertices.get(1); + } else if (i == vertices.size() - 1) { + p = vertices.get(i-1); + n = vertices.get(i); + } else { + p = vertices.get(i-1); + n = vertices.get(i+1); + } + n = new Vector3d(n); + n.sub(p); + n.normalize(); + return n; + } + + private void createIndices(int index[]) { + for (int c = 0; c < vertices.size() - 1; c++) { + for (int s = 0; s < resolution; s++) { + int ii = (c * resolution + s) * 6; + int iv = c*resolution + s; + + /* + iv+1 ---- iv + resolution + 1 + | /| + |/ | + iv ---- iv + resolution + */ + if (s < resolution - 1) { + index[ii+2] = iv; + index[ii+1] = iv+resolution; + index[ii+0] = iv+resolution+1; + + index[ii+5] = iv; + index[ii+4] = iv+resolution+1; + index[ii+3] = iv+1; + } else { + index[ii+2] = iv; + index[ii+1] = iv+resolution; + index[ii+0] = iv+1; + + index[ii+5] = iv; + index[ii+4] = iv+1; + index[ii+3] = iv+1-resolution; + } + } + } + } +} diff --git a/org.simantics.g3d.jme/src/org/simantics/g3d/jme/system/SWTApplication.java b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/system/SWTApplication.java new file mode 100644 index 00000000..d7a61a86 --- /dev/null +++ b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/system/SWTApplication.java @@ -0,0 +1,44 @@ +package org.simantics.g3d.jme.system; + +import java.util.logging.Level; + +import com.jme3.app.Application; +import com.jme3.system.AppSettings; +import com.jme3.system.JmeContext; +import com.jme3.system.JmeSystem; + + +public class SWTApplication extends Application { + + SWTCanvas canvas; + + public SWTApplication(SWTCanvas canvas) { + this.canvas = canvas; + } + + public void createCanvas(){ + + if (settings == null){ + settings = new AppSettings(true); + } + + context.setSystemListener(this); + } + + + public void start(JmeContext.Type contextType){ + if (context != null && context.isCreated()){ + //logger.warning("start() called when application already created!"); + return; + } + + if (settings == null){ + settings = new AppSettings(true); + } + + //logger.log(Level.FINE, "Starting application: {0}", getClass().getName()); + context = canvas;//JmeSystem.newContext(settings, contextType); + context.setSystemListener(this); + context.create(false); + } +} diff --git a/org.simantics.g3d.jme/src/org/simantics/g3d/jme/system/SWTCanvas.java b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/system/SWTCanvas.java new file mode 100644 index 00000000..637d53e9 --- /dev/null +++ b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/system/SWTCanvas.java @@ -0,0 +1,1099 @@ +package org.simantics.g3d.jme.system; + + + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.MouseMoveListener; +import org.eclipse.swt.events.MouseTrackListener; +import org.eclipse.swt.events.MouseWheelListener; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.opengl.GLCanvas; +import org.eclipse.swt.opengl.GLData; +import org.eclipse.swt.widgets.Composite; +import org.lwjgl.LWJGLException; +import org.lwjgl.Sys; +import org.lwjgl.input.Keyboard; +import org.lwjgl.input.Mouse; +import org.lwjgl.opengl.GLContext; +import org.lwjgl.opengl.Pbuffer; +import org.lwjgl.opengl.PixelFormat; + +import com.jme3.cursors.plugins.JmeCursor; +import com.jme3.input.JoyInput; +import com.jme3.input.KeyInput; +import com.jme3.input.MouseInput; +import com.jme3.input.RawInputListener; +import com.jme3.input.event.InputEvent; +import com.jme3.input.event.KeyInputEvent; +import com.jme3.input.event.MouseButtonEvent; +import com.jme3.input.event.MouseMotionEvent; +import com.jme3.system.AppSettings; +import com.jme3.system.JmeSystem; +import com.jme3.system.Platform; +import com.jme3.system.lwjgl.LwjglAbstractDisplay; +import com.jme3.system.lwjgl.LwjglDisplay; +import com.jme3.system.lwjgl.LwjglTimer; + +/** + * SWT OpenGL canvas for jME + * + * Note: this class is experimental, and contains questionable code. + * + * + * @author Marko Luukkainen + * + */ +public class SWTCanvas extends LwjglAbstractDisplay { + + protected static final boolean USE_SHARED_CONTEXT = false; + + + private static final Logger logger = Logger.getLogger(LwjglDisplay.class.getName()); + + GLCanvas canvas; + + private int width; + private int height; + + private boolean runningFirstTime = true; + private boolean mouseWasGrabbed = false; + + private boolean mouseWasCreated = false; + private boolean keyboardWasCreated = false; + + private Pbuffer pbuffer; + private PixelFormat pbufferFormat; + private PixelFormat canvasFormat; + + private boolean disposed = false; + + public SWTCanvas(Composite parent) { + parent.setLayout(new FillLayout()); + + GLData data = new GLData(); + data.doubleBuffer = true; + data.blueSize = 8; + data.redSize = 8; + data.greenSize = 8; + data.depthSize = 32; + data.alphaSize = 8; + data.stencilSize = 8; + data.stereo = false; + data.accumAlphaSize = 0; + data.accumBlueSize = 0; + data.accumGreenSize = 0; + data.accumRedSize = 0; + canvas = new GLCanvas(parent, SWT.EMBEDDED|SWT.NO_REDRAW_RESIZE, data); + //canvas = new GLCanvas(parent, SWT.NONE, data); + + + } + + public void dispose() { + if (disposed) + return; + + setCurrent(); + deinitInThread(); +// pauseCanvas(); + canvas.dispose(); + disposed = true; + } + + public boolean isDisposed() { + return disposed; + } + + @Override + public Type getType() { + return Type.Canvas; // TODO : is Canvas Correct? + } + + @Override + public void setTitle(String title) { + // do nothing + } + + @Override + public void restart() { + frameRate = settings.getFrameRate(); + } + + + private boolean c = false; + + public void create(boolean waitFor){ + if (c) + return; + c = true; + + setCurrent(); + initInThread(); + renderable.set(true); + //restoreCanvas(); + +// canvas.addPaintListener(new PaintListener() { +// +// @Override +// public void paintControl(PaintEvent arg0) { +// repaint(); +// } +// }); + + org.eclipse.swt.widgets.Display.getCurrent().asyncExec(new Runnable() { + + @Override + public void run() { + if (canvas.isDisposed()) + return; + repaint(); + org.eclipse.swt.widgets.Display.getCurrent().asyncExec(this); + + } + }); + + } + + @Override + protected void runLoop() { + + if (renderable.get()){ + Point size = canvas.getSize(); + //System.out.println("Render " + size); + int newWidth = Math.max(size.x, 1); + int newHeight = Math.max(size.y, 1); + if (width != newWidth || height != newHeight){ + System.out.println("Render resize " + size); + width = newWidth; + height = newHeight; + if (listener != null){ + listener.reshape(width, height); + } + } + } + if (!created.get()) + throw new IllegalStateException(); + listener.update(); + if (renderable.get()){ + assert checkGLError(); + + // calls swap buffers, etc. + try { + canvas.swapBuffers(); +// Display.processMessages(); +// if (autoFlush){ +// Display.update(false); +// }else{ +// Display.processMessages(); +// Thread.sleep(50); +// // add a small wait +// // to reduce CPU usage +// } + } catch (Throwable ex){ + listener.handleError("Error while swapping buffers", ex); + } + } +// if (frameRate > 0) +// Display.sync(frameRate); + +// if (renderable.get()){ +// if (autoFlush){ +// // check input after we synchronize with framerate. +// // this reduces input lag. +// Display.processMessages(); +// } +// } + } + + public void repaint() { + setCurrent(); +// if (renderable.get()){ +// if (Display.isCloseRequested()) +// listener.requestClose(false); + +// if (wasActive != Display.isActive()) { +// if (!wasActive) { +// listener.gainFocus(); +// timer.reset(); +// wasActive = true; +// } else { +// listener.loseFocus(); +// wasActive = false; +// } +// } +// } + + runLoop(); + } + + @Override + public void run() { + // do nothing + //super.run(); + } + + @Override + protected void destroyContext() { + try { + // invalidate the state so renderer can resume operation + if (!USE_SHARED_CONTEXT){ + renderer.cleanup(); + } + + //if (Display.isCreated()){ + if (!canvas.isDisposed()) { + /* FIXES: + * org.lwjgl.LWJGLException: X Error + * BadWindow (invalid Window parameter) request_code: 2 minor_code: 0 + * + * Destroying keyboard early prevents the error above, triggered + * by destroying keyboard in by Display.destroy() or Display.setParent(null). + * Therefore Keyboard.destroy() should precede any of these calls. + */ + if (Keyboard.isCreated()){ + // Should only happen if called in + // LwjglAbstractDisplay.deinitInThread(). + Keyboard.destroy(); + } + + //try { + // NOTE: On Windows XP, not calling setParent(null) + // freezes the application. + // On Mac it freezes the application. + // On Linux it fixes a crash with X Window System. + if (JmeSystem.getPlatform() == Platform.Windows32 + || JmeSystem.getPlatform() == Platform.Windows64){ + //Display.setParent(null); + } + //} catch (LWJGLException ex) { + // logger.log(Level.SEVERE, "Encountered exception when setting parent to null", ex); + //} + +// Display.destroy(); + } + + // The canvas is no longer visible, + // but the context thread is still running. + if (!needClose.get()){ + // MUST make sure there's still a context current here .. + // Display is dead, make pbuffer available to the system + makePbufferAvailable(); + + renderer.invalidateState(); + }else{ + // The context thread is no longer running. + // Destroy pbuffer. + destroyPbuffer(); + } + } catch (LWJGLException ex) { + listener.handleError("Failed make pbuffer available", ex); + } + + } + + + + public void setFocus() { + canvas.setFocus(); + } + + public void setCurrent(GLCanvas canvas) { + // delegate to the GLContext + canvas.setCurrent(); + try { + GLContext.useContext(canvas); + } catch (LWJGLException e) { + e.printStackTrace(); + } + } + + public void setCurrent() { + setCurrent(canvas); + } + + + + private void pauseCanvas(){ + if (Mouse.isCreated()){ + if (Mouse.isGrabbed()){ + Mouse.setGrabbed(false); + mouseWasGrabbed = true; + } + mouseWasCreated = true; + Mouse.destroy(); + } + if (Keyboard.isCreated()){ + keyboardWasCreated = true; + Keyboard.destroy(); + } + + renderable.set(false); + destroyContext(); + } + + /** + * Called to restore the canvas. + */ + private void restoreCanvas(){ + setCurrent(); + logger.log(Level.INFO, "OGL: Waiting for canvas to become displayable.."); + while (!canvas.getVisible()){ + try { + Thread.sleep(10); + } catch (InterruptedException ex) { + logger.log(Level.SEVERE, "OGL: Interrupted! ", ex); + } + } + + logger.log(Level.INFO, "OGL: Creating display context .."); + + // Set renderable to true, since canvas is now displayable. + renderable.set(true); + createContext(settings); + + logger.log(Level.INFO, "OGL: Display is active!"); + + try { + if (mouseWasCreated){ + Mouse.create(); + if (mouseWasGrabbed){ + Mouse.setGrabbed(true); + mouseWasGrabbed = false; + } + } + if (keyboardWasCreated){ + Keyboard.create(); + keyboardWasCreated = false; + } + } catch (LWJGLException ex){ + logger.log(Level.SEVERE, "Encountered exception when restoring input", ex); + } + + + } + /** + * It seems it is best to use one pixel format for all shared contexts. + * @see http://developer.apple.com/library/mac/#qa/qa1248/_index.html + */ + protected PixelFormat acquirePixelFormat(boolean forPbuffer){ + if (forPbuffer){ + // Use 0 samples for pbuffer format, prevents + // crashes on bad drivers + if (pbufferFormat == null){ + pbufferFormat = new PixelFormat(settings.getBitsPerPixel(), + 0, + settings.getDepthBits(), + settings.getStencilBits(), + 0); + } + return pbufferFormat; + }else{ + if (canvasFormat == null){ + int samples = getNumSamplesToUse(); + canvasFormat = new PixelFormat(settings.getBitsPerPixel(), + 0, + settings.getDepthBits(), + settings.getStencilBits(), + samples); + } + return canvasFormat; + } + } + + /** + * Makes sure the pbuffer is available and ready for use + */ + protected void makePbufferAvailable() throws LWJGLException{ + if (pbuffer != null && pbuffer.isBufferLost()){ + logger.log(Level.WARNING, "PBuffer was lost!"); + pbuffer.destroy(); + pbuffer = null; + } + + if (pbuffer == null) { + pbuffer = new Pbuffer(1, 1, acquirePixelFormat(true), null); + pbuffer.makeCurrent(); + logger.log(Level.INFO, "OGL: Pbuffer has been created"); + + // Any created objects are no longer valid + if (!runningFirstTime){ + renderer.resetGLObjects(); + } + } + + pbuffer.makeCurrent(); + if (!pbuffer.isCurrent()){ + throw new LWJGLException("Pbuffer cannot be made current"); + } + } + + protected void destroyPbuffer(){ + if (pbuffer != null){ + if (!pbuffer.isBufferLost()){ + pbuffer.destroy(); + } + pbuffer = null; + } + } + + @Override + protected void createContext(AppSettings settings) { + setCurrent(); + + // In case canvas is not visible, we still take framerate + // from settings to prevent "100% CPU usage" + frameRate = settings.getFrameRate(); + + try { + if (renderable.get()){ + if (!runningFirstTime){ + // because the display is a different opengl context + // must reset the context state. + if (!USE_SHARED_CONTEXT){ + renderer.cleanup(); + } + } + + // if the pbuffer is currently active, + // make sure to deactivate it + destroyPbuffer(); + + if (Keyboard.isCreated()){ + Keyboard.destroy(); + } + + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + } + +// Display.setVSyncEnabled(settings.isVSync()); + //Display.setParent(canvas); + +// if (USE_SHARED_CONTEXT){ +// Display.create(acquirePixelFormat(false), pbuffer); +// }else{ +// Display.create(acquirePixelFormat(false)); +// } + + renderer.invalidateState(); + }else{ + // First create the pbuffer, if it is needed. + makePbufferAvailable(); + } + + // At this point, the OpenGL context is active. + if (runningFirstTime){ + // THIS is the part that creates the renderer. + // It must always be called, now that we have the pbuffer workaround. + initContextFirstTime(); + runningFirstTime = false; + } + } catch (LWJGLException ex) { + listener.handleError("Failed to initialize OpenGL context", ex); + // TODO: Fix deadlock that happens after the error (throw runtime exception?) + } + } + + + public JoyInput getJoyInput() { + return null; + } + + + SWTMouseInput mouseInput; + + public MouseInput getMouseInput() { + if (mouseInput == null){ + mouseInput = new SWTMouseInput(this); + } + return mouseInput; + } + + + SWTKeyInput keyInput; + + public KeyInput getKeyInput() { + if (keyInput == null){ + keyInput = new SWTKeyInput(this); + } + return keyInput; + } + + + + public class SWTMouseInput implements MouseInput, MouseListener, MouseMoveListener, MouseWheelListener, MouseTrackListener { + SWTCanvas canvas; + boolean init = false; + RawInputListener listener; + + public SWTMouseInput(SWTCanvas canvas) { + this.canvas = canvas; + + } + + @Override + public void destroy() { + this.canvas.canvas.removeMouseListener(this); + this.canvas.canvas.removeMouseMoveListener(this); + this.canvas.canvas.removeMouseWheelListener(this); + this.canvas.canvas.removeMouseTrackListener(this); + } + + @Override + public void initialize() { + this.canvas.canvas.addMouseListener(this); + this.canvas.canvas.addMouseMoveListener(this); + this.canvas.canvas.addMouseWheelListener(this); + this.canvas.canvas.addMouseTrackListener(this); + init = true; + } + + @Override + public int getButtonCount() { + return 3; + } + + @Override + public boolean isInitialized() { + return init; + } + + @Override + public void setInputListener(RawInputListener listener) { + this.listener = listener; + + } + + public long getInputTimeNanos() { + return Sys.getTime() * LwjglTimer.LWJGL_TIME_TO_NANOS; + } + + @Override + public void setCursorVisible(boolean visible) { + + } + + @Override + public void setNativeCursor(JmeCursor cursor) { + + } + + @Override + public void update() { + if (listener == null || events.size() == 0) + return; + listener.beginInput(); + for (InputEvent e : events) { + if (e instanceof MouseButtonEvent) + listener.onMouseButtonEvent((MouseButtonEvent)e); + else + listener.onMouseMotionEvent((MouseMotionEvent)e); + } + events.clear(); + listener.endInput(); + } + + private List events = new ArrayList(); + + @Override + public void mouseDoubleClick(MouseEvent e) { + //events.add(new MouseButtonEvent(e.button,true, e.x, e.y)); + + } + + @Override + public void mouseDown(MouseEvent e) { + events.add(new MouseButtonEvent(e.button,true, e.x, e.y)); + + } + + @Override + public void mouseUp(MouseEvent e) { + events.add(new MouseButtonEvent(e.button,false, e.x, e.y)); + + } + + int px = 0; + int py = 0; + + @Override + public void mouseMove(MouseEvent e) { + events.add(new MouseMotionEvent(e.x, e.y, e.x - px, e.y - py, 0, 0)); + px = e.x; + py = e.y; + + } + + @Override + public void mouseScrolled(MouseEvent e) { + events.add(new MouseMotionEvent(e.x, e.y, 0, 0, e.button, e.count)); + } + + @Override + public void mouseEnter(MouseEvent e) { + px = e.x; + py = e.y; + } + + @Override + public void mouseExit(MouseEvent e) { + + } + + @Override + public void mouseHover(MouseEvent e) { + + } + } + + public class SWTKeyInput implements KeyInput, KeyListener { + SWTCanvas canvas; + boolean init = false; + RawInputListener listener; + + public SWTKeyInput(SWTCanvas canvas) { + this.canvas = canvas; + } + + @Override + public void destroy() { + this.canvas.canvas.removeKeyListener(this); + } + + @Override + public void initialize() { + this.canvas.canvas.addKeyListener(this); + init = true; + } + + @Override + public boolean isInitialized() { + return init; + } + + @Override + public void setInputListener(RawInputListener listener) { + this.listener = listener; + } + + public long getInputTimeNanos() { + return Sys.getTime() * LwjglTimer.LWJGL_TIME_TO_NANOS; + } + + private List events = new ArrayList(); + + KeyInputEvent prevEvent = new KeyInputEvent(0, (char)0, false, false); + + @Override + public void update() { + if (listener == null || events.size() == 0) + return; + listener.beginInput(); + + for (KeyInputEvent e : events) { + //System.out.println("Key " + e.getKeyCode() + " " + e.getKeyChar() + " " + e.isPressed() + " " + e.isRepeating()); + + // SWT reports wrong key code in released event for a key, if multiple keys are down at the same time + // example: + // press 'a' -> event press a + // press 'b' -> event press b + // release 'a' -> event release b + // keep 'b' pressed -> event press b (with slight pause before events starts coming by) + // release 'b' -> event release b + + // press 'a' -> event press a + // press 'b' -> event press b + // release 'b' -> event release b + // keep 'a' pressed -> nothing happens. + // release 'a' -> nothing happens + + if (e.isPressed()) { + if (e.getKeyCode() != prevEvent.getKeyCode() && prevEvent.isPressed()) { + listener.onKeyEvent(new KeyInputEvent(prevEvent.getKeyCode(), prevEvent.getKeyChar(), false, false)); + } + } + + listener.onKeyEvent(e); + prevEvent = e; + } + events.clear(); + listener.endInput(); + + } + + @Override + public void keyPressed(KeyEvent e) { + events.add(new KeyInputEvent(getLWJGLKeyCode(e.keyCode), e.character, true, false)); + + } + + @Override + public void keyReleased(KeyEvent e) { + + events.add(new KeyInputEvent(getLWJGLKeyCode(e.keyCode), e.character, false, false)); + } + + private int getLWJGLKeyCode(int swtKeyCode) { + // TODO : LWJGL uses mapping/layout of US keyboard, these have to be checked + if(swtKeyCode > 1024) { + int keyCode = 0; + switch (swtKeyCode) { + case SWT.CTRL: + keyCode = Keyboard.KEY_LCONTROL; + break; + case SWT.ALT: + keyCode = Keyboard.KEY_LMETA; + break; + + case SWT.SHIFT: + keyCode = Keyboard.KEY_LSHIFT; + break; + case SWT.ARROW_LEFT: + keyCode = Keyboard.KEY_LEFT; + break; + case SWT.ARROW_RIGHT: + keyCode = Keyboard.KEY_RIGHT; + break; + case SWT.ARROW_UP: + keyCode = Keyboard.KEY_UP; + break; + case SWT.ARROW_DOWN: + keyCode = Keyboard.KEY_DOWN; + break; + case SWT.KEYPAD_0: + keyCode = Keyboard.KEY_NUMPAD0; + break; + case SWT.KEYPAD_1: + keyCode = Keyboard.KEY_NUMPAD1; + break; + case SWT.KEYPAD_2: + keyCode = Keyboard.KEY_NUMPAD2; + break; + case SWT.KEYPAD_3: + keyCode = Keyboard.KEY_NUMPAD3; + break; + case SWT.KEYPAD_4: + keyCode = Keyboard.KEY_NUMPAD4; + break; + case SWT.KEYPAD_5: + keyCode = Keyboard.KEY_NUMPAD5; + break; + case SWT.KEYPAD_6: + keyCode = Keyboard.KEY_NUMPAD6; + break; + case SWT.KEYPAD_7: + keyCode = Keyboard.KEY_NUMPAD7; + break; + case SWT.KEYPAD_8: + keyCode = Keyboard.KEY_NUMPAD8; + break; + case SWT.KEYPAD_9: + keyCode = Keyboard.KEY_NUMPAD9; + break; + case SWT.KEYPAD_CR: + keyCode = Keyboard.KEY_NUMPADENTER; + break; + case SWT.NUM_LOCK: + keyCode = Keyboard.KEY_NUMLOCK; + break; + case SWT.SCROLL_LOCK: + keyCode = Keyboard.KEY_SCROLL; + break; + case SWT.CAPS_LOCK: + keyCode = Keyboard.KEY_CAPITAL; + break; + case SWT.INSERT: + keyCode = Keyboard.KEY_INSERT; + break; + case SWT.HOME: + keyCode = Keyboard.KEY_HOME; + break; + case SWT.END: + keyCode = Keyboard.KEY_END; + break; + case SWT.PAGE_UP: + keyCode = Keyboard.KEY_NEXT; + break; + case SWT.PAGE_DOWN: + keyCode = Keyboard.KEY_PRIOR; + break; + case SWT.PAUSE: + keyCode = Keyboard.KEY_PAUSE; + break; + case SWT.BREAK: + keyCode = Keyboard.KEY_PAUSE; + break; + case SWT.PRINT_SCREEN: + keyCode = Keyboard.KEY_SYSRQ; + break; + case SWT.HELP: + keyCode = 0; + break; + case SWT.KEYPAD_MULTIPLY: + keyCode = Keyboard.KEY_MULTIPLY; + break; + case SWT.KEYPAD_DIVIDE: + keyCode = Keyboard.KEY_DIVIDE; + break; + case SWT.KEYPAD_DECIMAL: + keyCode = Keyboard.KEY_DECIMAL; + break; + case SWT.F1: + keyCode = Keyboard.KEY_F1; + break; + case SWT.F2: + keyCode = Keyboard.KEY_F2; + break; + case SWT.F3: + keyCode = Keyboard.KEY_F3; + break; + case SWT.F4: + keyCode = Keyboard.KEY_F4; + break; + case SWT.F5: + keyCode = Keyboard.KEY_F5; + break; + case SWT.F6: + keyCode = Keyboard.KEY_F6; + break; + case SWT.F7: + keyCode = Keyboard.KEY_F7; + break; + case SWT.F8: + keyCode = Keyboard.KEY_F8; + break; + case SWT.F9: + keyCode = Keyboard.KEY_F9; + break; + case SWT.F10: + keyCode = Keyboard.KEY_F10; + break; + case SWT.F11: + keyCode = Keyboard.KEY_F11; + break; + case SWT.F12: + keyCode = Keyboard.KEY_F12; + break; + default : + keyCode = Keyboard.KEY_NONE; + break; + } + + return keyCode; + } else if (swtKeyCode == 8) { + return Keyboard.KEY_BACK; + } else if (swtKeyCode == SWT.ESC) { + return Keyboard.KEY_ESCAPE; + } else if (swtKeyCode == SWT.SPACE) { + return Keyboard.KEY_SPACE; + } else if (swtKeyCode >= 49 && swtKeyCode < 58) { // 1 - 9 + return swtKeyCode - 47; + } else if (swtKeyCode == 48) { + return Keyboard.KEY_0; + } else if (swtKeyCode == SWT.TAB) { + return Keyboard.KEY_TAB; + } else if (swtKeyCode == 46) { + return Keyboard.KEY_PERIOD; + } else if (swtKeyCode == 44) { + return Keyboard.KEY_COMMA; + } else if (swtKeyCode == 39) { // '/* on SWE/FI keyboard + return Keyboard.KEY_SLASH; + } else if (swtKeyCode == 45) { + return Keyboard.KEY_MINUS; + } else if (swtKeyCode == 43) { + return Keyboard.KEY_EQUALS; // +/? on SWE/FI keyboard + } else if (swtKeyCode == SWT.DEL) { + return Keyboard.KEY_DELETE; + } else if (swtKeyCode == SWT.CR) { + return Keyboard.KEY_RETURN; + } else if (swtKeyCode == 167) { // ยง on SWE/FI keyboard + return Keyboard.KEY_BACKSLASH; + } + else if (swtKeyCode >= 97 ) + swtKeyCode -= 32; + if (swtKeyCode >= 65 && swtKeyCode <= 90) { + switch (swtKeyCode) { + case 65: + return Keyboard.KEY_A; + case 66: + return Keyboard.KEY_B; + case 67: + return Keyboard.KEY_C; + case 68: + return Keyboard.KEY_D; + case 69: + return Keyboard.KEY_E; + case 70: + return Keyboard.KEY_F; + case 71: + return Keyboard.KEY_G; + case 72: + return Keyboard.KEY_H; + case 73: + return Keyboard.KEY_I; + case 74: + return Keyboard.KEY_J; + case 75: + return Keyboard.KEY_K; + case 76: + return Keyboard.KEY_L; + case 77: + return Keyboard.KEY_M; + case 78: + return Keyboard.KEY_N; + case 79: + return Keyboard.KEY_O; + case 80: + return Keyboard.KEY_P; + case 81: + return Keyboard.KEY_Q; + case 82: + return Keyboard.KEY_R; + case 83: + return Keyboard.KEY_S; + case 84: + return Keyboard.KEY_T; + case 85: + return Keyboard.KEY_U; + case 86: + return Keyboard.KEY_V; + case 87: + return Keyboard.KEY_W; + case 88: + return Keyboard.KEY_X; + case 89: + return Keyboard.KEY_Y; + case 90: + return Keyboard.KEY_Z; + + } + } + return Keyboard.KEY_UNLABELED; + } + + + private int getAWTKeyCode(int swtKeyCode) { + if(swtKeyCode > 1024) { + int keyCode = 0; + switch (swtKeyCode) { + case SWT.CTRL: + keyCode = java.awt.event.KeyEvent.VK_CONTROL; + break; + case SWT.ALT: + keyCode = java.awt.event.KeyEvent.VK_ALT; + break; + + case SWT.SHIFT: + keyCode = java.awt.event.KeyEvent.VK_SHIFT; + break; + case SWT.ARROW_LEFT: + keyCode = java.awt.event.KeyEvent.VK_LEFT; + break; + case SWT.ARROW_RIGHT: + keyCode = java.awt.event.KeyEvent.VK_RIGHT; + break; + case SWT.ARROW_UP: + keyCode = java.awt.event.KeyEvent.VK_UP; + break; + case SWT.ARROW_DOWN: + keyCode = java.awt.event.KeyEvent.VK_DOWN; + break; + case SWT.KEYPAD_0: + keyCode = java.awt.event.KeyEvent.VK_NUMPAD0; + break; + case SWT.KEYPAD_1: + keyCode = java.awt.event.KeyEvent.VK_NUMPAD1; + break; + case SWT.KEYPAD_2: + keyCode = java.awt.event.KeyEvent.VK_NUMPAD2; + break; + case SWT.KEYPAD_3: + keyCode = java.awt.event.KeyEvent.VK_NUMPAD3; + break; + case SWT.KEYPAD_4: + keyCode = java.awt.event.KeyEvent.VK_NUMPAD4; + break; + case SWT.KEYPAD_5: + keyCode = java.awt.event.KeyEvent.VK_NUMPAD5; + break; + case SWT.KEYPAD_6: + keyCode = java.awt.event.KeyEvent.VK_NUMPAD6; + break; + case SWT.KEYPAD_7: + keyCode = java.awt.event.KeyEvent.VK_NUMPAD7; + break; + case SWT.KEYPAD_8: + keyCode = java.awt.event.KeyEvent.VK_NUMPAD8; + break; + case SWT.KEYPAD_9: + keyCode = java.awt.event.KeyEvent.VK_NUMPAD9; + break; + case SWT.KEYPAD_CR: + keyCode = java.awt.event.KeyEvent.VK_ENTER; + break; + case SWT.NUM_LOCK: + keyCode = java.awt.event.KeyEvent.VK_NUM_LOCK; + break; + case SWT.SCROLL_LOCK: + keyCode = java.awt.event.KeyEvent.VK_SCROLL_LOCK; + break; + case SWT.CAPS_LOCK: + keyCode = java.awt.event.KeyEvent.VK_CAPS_LOCK; + break; + case SWT.INSERT: + keyCode = java.awt.event.KeyEvent.VK_INSERT; + break; + case SWT.HOME: + keyCode = java.awt.event.KeyEvent.VK_HOME; + break; + case SWT.END: + keyCode = java.awt.event.KeyEvent.VK_END; + break; + case SWT.PAGE_UP: + keyCode = java.awt.event.KeyEvent.VK_PAGE_UP; + break; + case SWT.PAGE_DOWN: + keyCode = java.awt.event.KeyEvent.VK_PAGE_DOWN; + break; + case SWT.PAUSE: + keyCode = java.awt.event.KeyEvent.VK_PAUSE; + break; + case SWT.BREAK: + keyCode = java.awt.event.KeyEvent.VK_PAUSE; + break; + case SWT.PRINT_SCREEN: + keyCode = java.awt.event.KeyEvent.VK_PRINTSCREEN; + break; + case SWT.HELP: + keyCode = java.awt.event.KeyEvent.VK_HELP; + break; + default : + keyCode = 0; + break; + } + + return keyCode; + } else if (swtKeyCode == 8) { + return java.awt.event.KeyEvent.VK_BACK_SPACE; + } + else if (swtKeyCode >= 97 ) + return swtKeyCode - 32; + else + return swtKeyCode; + } + } + +} diff --git a/org.simantics.g3d.jme/src/org/simantics/g3d/jme/system/SimpleSWTApplication.java b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/system/SimpleSWTApplication.java new file mode 100644 index 00000000..3bb991e5 --- /dev/null +++ b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/system/SimpleSWTApplication.java @@ -0,0 +1,46 @@ +package org.simantics.g3d.jme.system; + +import com.jme3.app.SimpleApplication; +import com.jme3.system.AppSettings; +import com.jme3.system.JmeContext; + +public abstract class SimpleSWTApplication extends SimpleApplication { + + SWTCanvas canvas; + + public SimpleSWTApplication(SWTCanvas canvas) { + this.canvas = canvas; + showSettings = false; + } + + @Override + public void setShowSettings(boolean showSettings) { + + } + + public void createCanvas(){ + + if (settings == null){ + settings = new AppSettings(true); + } + + context.setSystemListener(this); + } + + public void start(JmeContext.Type contextType){ + if (context != null && context.isCreated()){ + //logger.warning("start() called when application already created!"); + return; + } + + if (settings == null){ + settings = new AppSettings(true); + } + + //logger.log(Level.FINE, "Starting application: {0}", getClass().getName()); + context = canvas;//JmeSystem.newContext(settings, contextType); + context.setSystemListener(this); + context.create(false); + } + +} diff --git a/org.simantics.g3d.jme/src/org/simantics/g3d/jme/test/JMETestViewPart.java b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/test/JMETestViewPart.java new file mode 100644 index 00000000..7576c849 --- /dev/null +++ b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/test/JMETestViewPart.java @@ -0,0 +1,403 @@ +package org.simantics.g3d.jme.test; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.part.ViewPart; +import org.simantics.g3d.jme.system.SWTCanvas; +import org.simantics.g3d.jme.system.SimpleSWTApplication; + +import com.jme3.audio.AudioNode; +import com.jme3.audio.LowPassFilter; +import com.jme3.effect.ParticleEmitter; +import com.jme3.effect.ParticleMesh; +import com.jme3.input.controls.ActionListener; +import com.jme3.input.controls.KeyTrigger; +import com.jme3.light.DirectionalLight; +import com.jme3.light.PointLight; +import com.jme3.material.Material; +import com.jme3.material.RenderState.BlendMode; +import com.jme3.math.ColorRGBA; +import com.jme3.math.FastMath; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector3f; +import com.jme3.post.FilterPostProcessor; +import com.jme3.post.filters.BloomFilter; +import com.jme3.post.filters.DepthOfFieldFilter; +import com.jme3.post.filters.LightScatteringFilter; +import com.jme3.renderer.Camera; +import com.jme3.renderer.queue.RenderQueue.Bucket; +import com.jme3.renderer.queue.RenderQueue.ShadowMode; +import com.jme3.scene.Geometry; +import com.jme3.scene.Node; +import com.jme3.scene.Spatial; +import com.jme3.scene.shape.Box; +import com.jme3.scene.shape.Sphere; +import com.jme3.terrain.geomipmap.TerrainQuad; +import com.jme3.terrain.heightmap.AbstractHeightMap; +import com.jme3.terrain.heightmap.ImageBasedHeightMap; +import com.jme3.texture.Texture; +import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.Texture2D; +import com.jme3.util.SkyFactory; +import com.jme3.water.WaterFilter; + +public class JMETestViewPart extends ViewPart { + + + protected SWTCanvas canvas; + + + + @Override + public void createPartControl(Composite parent) { + + canvas = new SWTCanvas(parent); + + + parent.addDisposeListener(new DisposeListener() { + + @Override + public void widgetDisposed(DisposeEvent arg0) { + canvas.dispose(); + } + }); + + TestApp app = new TestApp(canvas); + //TestPostWater app = new TestPostWater(canvas); + //app.createCanvas(); + app.start(); + + + } + + + + + @Override + public void setFocus() { + canvas.setFocus(); + + } + + public class TestApp extends SimpleSWTApplication { + + public TestApp(SWTCanvas canvas) { + super(canvas); + + } + + float angle; + PointLight pl; + Spatial lightMdl; + + @Override + public void simpleInitApp() { + viewPort.setBackgroundColor(ColorRGBA.DarkGray); + + Spatial bumpy = (Spatial) assetManager.loadModel("Models/MonkeyHead/MonkeyHead.mesh.xml"); + rootNode.attachChild(bumpy); + + lightMdl = new Geometry("Light", new Sphere(10, 10, 0.1f)); + lightMdl.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m")); + rootNode.attachChild(lightMdl); + + // flourescent main light + pl = new PointLight(); + pl.setColor(new ColorRGBA(0.88f, 0.92f, 0.95f, 1.0f)); + rootNode.addLight(pl); + + // sunset light + DirectionalLight dl = new DirectionalLight(); + dl.setDirection(new Vector3f(-0.1f,-0.7f,1).normalizeLocal()); + dl.setColor(new ColorRGBA(0.44f, 0.30f, 0.20f, 1.0f)); + rootNode.addLight(dl); + + // skylight + dl = new DirectionalLight(); + dl.setDirection(new Vector3f(-0.6f,-1,-0.6f).normalizeLocal()); + dl.setColor(new ColorRGBA(0.10f, 0.22f, 0.44f, 1.0f)); + rootNode.addLight(dl); + + // white ambient light + dl = new DirectionalLight(); + dl.setDirection(new Vector3f(1, -0.5f,-0.1f).normalizeLocal()); + dl.setColor(new ColorRGBA(0.50f, 0.40f, 0.50f, 1.0f)); + rootNode.addLight(dl); + } + + @Override + public void simpleUpdate(float tpf){ + angle += tpf * 0.25f; + angle %= FastMath.TWO_PI; + + pl.setPosition(new Vector3f(FastMath.cos(angle) * 6f, 3f, FastMath.sin(angle) * 6f)); + lightMdl.setLocalTranslation(pl.getPosition()); + } + } + + + public class TestPostWater extends SimpleSWTApplication { + + public TestPostWater(SWTCanvas canvas) { + super(canvas); + } + + private Vector3f lightDir = new Vector3f(-4.9236743f, -1.27054665f, 5.896916f); + private WaterFilter water; + TerrainQuad terrain; + Material matRock; + AudioNode waves; + LowPassFilter underWaterAudioFilter = new LowPassFilter(0.5f, 0.1f); + LowPassFilter underWaterReverbFilter = new LowPassFilter(0.5f, 0.1f); + LowPassFilter aboveWaterAudioFilter = new LowPassFilter(1, 1); + + + + @Override + public void simpleInitApp() { + + setDisplayFps(false); + setDisplayStatView(false); + + Node mainScene = new Node("Main Scene"); + rootNode.attachChild(mainScene); + + createTerrain(mainScene); + DirectionalLight sun = new DirectionalLight(); + sun.setDirection(lightDir); + sun.setColor(ColorRGBA.White.clone().multLocal(1.7f)); + mainScene.addLight(sun); + + DirectionalLight l = new DirectionalLight(); + l.setDirection(Vector3f.UNIT_Y.mult(-1)); + l.setColor(ColorRGBA.White.clone().multLocal(0.3f)); +// mainScene.addLight(l); + + flyCam.setMoveSpeed(50); + + //cam.setLocation(new Vector3f(-700, 100, 300)); + //cam.setRotation(new Quaternion().fromAngleAxis(0.5f, Vector3f.UNIT_Z)); + cam.setLocation(new Vector3f(-327.21957f, 61.6459f, 126.884346f)); + cam.setRotation(new Quaternion(0.052168474f, 0.9443102f, -0.18395276f, 0.2678024f)); + + + cam.setRotation(new Quaternion().fromAngles(new float[]{FastMath.PI * 0.06f, FastMath.PI * 0.65f, 0})); + + + Spatial sky = SkyFactory.createSky(assetManager, "Scenes/Beach/FullskiesSunset0068.dds", false); + sky.setLocalScale(350); + + mainScene.attachChild(sky); + cam.setFrustumFar(4000); + //cam.setFrustumNear(100); + + + + //private FilterPostProcessor fpp; + + + water = new WaterFilter(rootNode, lightDir); + + FilterPostProcessor fpp = new FilterPostProcessor(assetManager); + + fpp.addFilter(water); + BloomFilter bloom=new BloomFilter(); + //bloom.getE + bloom.setExposurePower(55); + bloom.setBloomIntensity(1.0f); + fpp.addFilter(bloom); + LightScatteringFilter lsf = new LightScatteringFilter(lightDir.mult(-300)); + lsf.setLightDensity(1.0f); + fpp.addFilter(lsf); + DepthOfFieldFilter dof=new DepthOfFieldFilter(); + dof.setFocusDistance(0); + dof.setFocusRange(100); + fpp.addFilter(dof); +// + + // fpp.addFilter(new TranslucentBucketFilter()); + // + + // fpp.setNumSamples(4); + + + water.setWaveScale(0.003f); + water.setMaxAmplitude(2f); + water.setFoamExistence(new Vector3f(1f, 4, 0.5f)); + water.setFoamTexture((Texture2D) assetManager.loadTexture("Common/MatDefs/Water/Textures/foam2.jpg")); + //water.setNormalScale(0.5f); + + //water.setRefractionConstant(0.25f); + water.setRefractionStrength(0.2f); + //water.setFoamHardness(0.6f); + + water.setWaterHeight(initialWaterHeight); + uw=cam.getLocation().y cameras = new ArrayList(); + cameras.add(getCamera()); + terrain.setMaterial(matRock); + terrain.setLocalScale(new Vector3f(5, 5, 5)); + terrain.setLocalTranslation(new Vector3f(0, -30, 0)); + terrain.setLocked(false); // unlock it so we can edit the height + + terrain.setShadowMode(ShadowMode.Receive); + rootNode.attachChild(terrain); + + } + //This part is to emulate tides, slightly varrying the height of the water plane + private float time = 0.0f; + private float waterHeight = 0.0f; + private float initialWaterHeight = 0.8f; + private boolean uw=false; + @Override + public void simpleUpdate(float tpf) { + super.simpleUpdate(tpf); + // box.updateGeometricState(); + time += tpf; + waterHeight = (float) Math.cos(((time * 0.6f) % FastMath.TWO_PI)) * 1.5f; + water.setWaterHeight(initialWaterHeight + waterHeight); + if(water.isUnderWater() && !uw){ + + waves.setDryFilter(new LowPassFilter(0.5f, 0.1f)); + uw=true; + } + if(!water.isUnderWater() && uw){ + uw=false; + //waves.setReverbEnabled(false); + waves.setDryFilter(new LowPassFilter(1, 1f)); + //waves.setDryFilter(new LowPassFilter(1,1f)); + + } + } + } + + + +} diff --git a/org.simantics.g3d.jme/src/org/simantics/g3d/jme/utils/JmeUtil.java b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/utils/JmeUtil.java new file mode 100644 index 00000000..e680ffc7 --- /dev/null +++ b/org.simantics.g3d.jme/src/org/simantics/g3d/jme/utils/JmeUtil.java @@ -0,0 +1,89 @@ +package org.simantics.g3d.jme.utils; + +import java.util.Collection; + +import javax.vecmath.AxisAngle4d; +import javax.vecmath.Quat4d; +import javax.vecmath.Vector3d; + +import org.simantics.g3d.math.MathTools; + +import com.jme3.app.Application; +import com.jme3.math.Quaternion; +import com.jme3.renderer.RenderManager; +import com.jme3.scene.Node; +import com.jme3.scene.Spatial; + +public class JmeUtil { + + public static Node getRoot(Application app) { + return getRoot(app.getRenderManager()); + } + + public static Node getRoot(RenderManager manager) { + return (Node)(manager.getMainView("Default").getScenes().get(0)); + } + + public static void updateTransform(Collection props, Vector3d pos, Quat4d q) { + double position[] = new double[]{pos.x,pos.y,pos.z}; + updateTransform(props, position, q); + } + + public static void updateTransform(Spatial actor, double pos[], Quat4d quat) { + Quaternion q = new Quaternion((float)quat.x, (float)quat.y,(float)quat.z, (float)quat.w); + actor.setLocalRotation(q); + actor.setLocalTranslation((float)pos[0], (float)pos[1], (float)pos[2]); + + } + + public static void updateTransform(Collection props, double pos[], AxisAngle4d aa) { + Quat4d q = new Quat4d(); + MathTools.getQuat(aa, q); + updateTransform(props, pos, q); + } + + public static void updateTransform(Collection props, double pos[], Quat4d quat) { + Quaternion q = new Quaternion((float)quat.x, (float)quat.y,(float)quat.z, (float)quat.w); + for (Spatial actor : props) { + actor.setLocalRotation(q); + actor.setLocalTranslation((float)pos[0], (float)pos[1], (float)pos[2]); + } + } + + public static void updateTransform(Collection props, Vector3d pos, Quat4d quat, double scale) { + Quaternion q = new Quaternion((float)quat.x, (float)quat.y,(float)quat.z, (float)quat.w); + for (Spatial actor : props) { + actor.setLocalRotation(q); + actor.setLocalTranslation((float)pos.x, (float)pos.y, (float)pos.z); + actor.setLocalScale((float)scale); + } + } + + public static void updateTransform(Collection props, double pos[], Quat4d quat, double scale) { + Quaternion q = new Quaternion((float)quat.x, (float)quat.y,(float)quat.z, (float)quat.w); + for (Spatial actor : props) { + actor.setLocalRotation(q); + actor.setLocalTranslation((float)pos[0], (float)pos[1], (float)pos[2]); + actor.setLocalScale((float)scale); + } + } + + public static void updateTransform(Spatial actor, Vector3d pos, Quat4d quat, double scalex, double scaley,double scalez) { + Quaternion q = new Quaternion((float)quat.x, (float)quat.y,(float)quat.z, (float)quat.w); + + actor.setLocalRotation(q); + actor.setLocalTranslation((float)pos.x, (float)pos.y, (float)pos.z); + actor.setLocalScale((float)scalex,(float)scaley,(float)scalez); + + } + + public static void updateTransform(Collection props, Vector3d pos, Quat4d quat, double scalex, double scaley,double scalez) { + Quaternion q = new Quaternion((float)quat.x, (float)quat.y,(float)quat.z, (float)quat.w); + for (Spatial actor : props) { + actor.setLocalRotation(q); + actor.setLocalTranslation((float)pos.x, (float)pos.y, (float)pos.z); + actor.setLocalScale((float)scalex,(float)scaley,(float)scalez); + } + } + +} diff --git a/org.simantics.opencascade.jme/.classpath b/org.simantics.opencascade.jme/.classpath new file mode 100644 index 00000000..8a8f1668 --- /dev/null +++ b/org.simantics.opencascade.jme/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/org.simantics.opencascade.jme/.project b/org.simantics.opencascade.jme/.project new file mode 100644 index 00000000..246f0461 --- /dev/null +++ b/org.simantics.opencascade.jme/.project @@ -0,0 +1,28 @@ + + + org.simantics.opencascade.jme + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/org.simantics.opencascade.jme/.settings/org.eclipse.jdt.core.prefs b/org.simantics.opencascade.jme/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..27353b81 --- /dev/null +++ b/org.simantics.opencascade.jme/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,8 @@ +#Mon Aug 13 10:36:56 EEST 2012 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/org.simantics.opencascade.jme/META-INF/MANIFEST.MF b/org.simantics.opencascade.jme/META-INF/MANIFEST.MF new file mode 100644 index 00000000..37567820 --- /dev/null +++ b/org.simantics.opencascade.jme/META-INF/MANIFEST.MF @@ -0,0 +1,17 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Jme +Bundle-SymbolicName: org.simantics.opencascade.jme +Bundle-Version: 1.0.0.qualifier +Bundle-Activator: org.simantics.opencascade.jme.Activator +Bundle-Vendor: VTT +Require-Bundle: org.eclipse.ui, + org.eclipse.core.runtime, + org.jcae.opencascade;bundle-version="6.5.2", + org.simantics.opencascade;bundle-version="1.0.0", + com.jme;bundle-version="1.0.0", + javax.vecmath;bundle-version="1.5.2", + org.simantics.utils.datastructures;bundle-version="1.1.0" +Bundle-RequiredExecutionEnvironment: JavaSE-1.6 +Bundle-ActivationPolicy: lazy +Export-Package: org.simantics.opencascade.jme diff --git a/org.simantics.opencascade.jme/build.properties b/org.simantics.opencascade.jme/build.properties new file mode 100644 index 00000000..41eb6ade --- /dev/null +++ b/org.simantics.opencascade.jme/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/org.simantics.opencascade.jme/src/org/simantics/opencascade/jme/Activator.java b/org.simantics.opencascade.jme/src/org/simantics/opencascade/jme/Activator.java new file mode 100644 index 00000000..aa3471ee --- /dev/null +++ b/org.simantics.opencascade.jme/src/org/simantics/opencascade/jme/Activator.java @@ -0,0 +1,50 @@ +package org.simantics.opencascade.jme; + +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class Activator extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "org.simantics.opencascade.jme"; //$NON-NLS-1$ + + // The shared instance + private static Activator plugin; + + /** + * The constructor + */ + public Activator() { + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static Activator getDefault() { + return plugin; + } + +} diff --git a/org.simantics.opencascade.jme/src/org/simantics/opencascade/jme/JmeOCCTTool.java b/org.simantics.opencascade.jme/src/org/simantics/opencascade/jme/JmeOCCTTool.java new file mode 100644 index 00000000..0184477e --- /dev/null +++ b/org.simantics.opencascade.jme/src/org/simantics/opencascade/jme/JmeOCCTTool.java @@ -0,0 +1,441 @@ +package org.simantics.opencascade.jme; + +import java.nio.FloatBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.vecmath.Matrix4d; +import javax.vecmath.Point3d; +import javax.vecmath.Vector3d; +import javax.vecmath.Vector3f; + +import org.jcae.opencascade.jni.BRepMesh_IncrementalMesh; +import org.jcae.opencascade.jni.BRep_Tool; +import org.jcae.opencascade.jni.GP_Trsf; +import org.jcae.opencascade.jni.Poly_Triangulation; +import org.jcae.opencascade.jni.TopAbs_Orientation; +import org.jcae.opencascade.jni.TopAbs_ShapeEnum; +import org.jcae.opencascade.jni.TopExp_Explorer; +import org.jcae.opencascade.jni.TopLoc_Location; +import org.jcae.opencascade.jni.TopoDS_Face; +import org.jcae.opencascade.jni.TopoDS_Shape; +import org.simantics.opencascade.OCCTTool; +import org.simantics.utils.datastructures.MapList; + +import com.jme3.scene.Geometry; +import com.jme3.scene.Mesh; +import com.jme3.scene.Mesh.Mode; +import com.jme3.scene.Node; +import com.jme3.scene.Spatial; +import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.mesh.IndexBuffer; + + + +public class JmeOCCTTool { + + + public static Spatial vtkOCCShapeToAssembly(TopoDS_Shape shape) { + double deflection = 0.001; + + if (deflection <= 0.0) { + deflection = 0.0005; + System.out.println("Bad value for deflection. Using: " + deflection); + } + + // FIXME : leaks memory! + //BRepTools.clean(shape); + + double mass = OCCTTool.getMass(shape); + + if (mass < 1.0e-12) { + System.out.println("Non 3D-shape detected"); + System.out.println("The cad import features are currently limited to 3D models."); + } + + double length = OCCTTool.getBoundingBoxDiagonal(shape); + deflection *= length; // use relative units + + BRepMesh_IncrementalMesh mesh = new BRepMesh_IncrementalMesh(shape,deflection); + + int faceNumber = 0; + TopExp_Explorer expFace = new TopExp_Explorer(); + + Node node = new Node(); + for (expFace.init(shape, TopAbs_ShapeEnum.FACE); expFace.more(); expFace.next()) { + TopoDS_Face face = (TopoDS_Face) expFace.current(); + Mesh partGrid = createPartGrid(face); + face.delete(); + if (partGrid == null) + continue; + faceNumber++; + //gridToAssembly(assemblies, partGrid, stlSurfaceData, stlEdgeData); + gridToAssembly(node, partGrid); + + } + expFace.delete(); + mesh.delete(); + + if (faceNumber == 0) { + System.out + .println("Cad import: error: no surface triangulation was generated."); + return null; + } + + return node; + } + + + + public static void gridToAssembly(Node assemblies, Mesh partGrid) { + Geometry geom = new Geometry(); + geom.setMesh(partGrid); + assemblies.attachChild(geom); + + } + + + + public static Mesh createPartGrid ( TopoDS_Face face) + { + TopLoc_Location Location = new TopLoc_Location(); + + Poly_Triangulation triangulation = BRep_Tool.triangulation(face, Location); + + if(triangulation == null) { + Location.delete(); + System.out.println("Encountered empty triangulation after face"); + return null; + } + + boolean reverse = face.orientation()==TopAbs_Orientation.REVERSED; + + int[]triangles = triangulation.triangles(); + double[]nodes = triangulation.nodes(); + + int nofTriangles = triangulation.nbTriangles(); + int nofNodes = triangulation.nbNodes(); + + triangulation.delete(); + + if(nofTriangles < 1) { + System.out.println("No triangles for mesh on face"); + Location.delete(); + return null; + } + + if(nofNodes < 1) { + System.out.println("No nodes for mesh on face:"); + Location.delete(); + return null; + } + + Mesh mesh = new Mesh(); + + int index[] = new int[nofTriangles*3]; + + for(int i = 0; i < nofTriangles; i++) + { + int n0, n1, n2; + if (!reverse) { + n0 = triangles[3 * i]; + n1 = triangles[3 * i + 1]; + n2 = triangles[3 * i + 2]; + } else { + n0 = triangles[3 * i + 2]; + n1 = triangles[3 * i + 1]; + n2 = triangles[3 * i]; + } + + + index[i*3] = n0; + index[i*3+1] = n1; + index[i*3+2] = n2; + + } + + + GP_Trsf transformation = Location.transformation(); + Location.delete(); + + double d_mat[] = new double[16]; + transformation.getValues(d_mat); + + Matrix4d mat = new Matrix4d(d_mat); + + float vertex[] = new float[nofNodes*3]; + + for(int i = 0; i < nofNodes; i++) { + // FIXME: GP_Trsf.transform(double[]) leaks memory + + //double xyz[] = new double[]{nodes[3 * i], nodes[3 * i + 1], nodes[3 * i + 2]}; + //transformation.transforms(xyz); + //partPoints.InsertPoint(i, xyz); + Point3d p = new Point3d(nodes[3 * i], nodes[3 * i + 1], nodes[3 * i + 2]); + mat.transform(p); + vertex[3 * i] = (float)p.x; + vertex[3 * i+1] = (float)p.y; + vertex[3 * i+2] = (float)p.z; + } + + transformation.delete(); + + mesh.setBuffer(Type.Position, 3, vertex); + mesh.setBuffer(Type.Index, 3, index); + return mesh; + + } + + + + public static Mesh createPartGrid ( List meshPoints, List meshTriangles) + { + + + + + + int nofTriangles = meshTriangles.size() / 3; + int nofNodes = meshPoints.size() /3; + + + if(nofTriangles < 1) { + System.out.println("No triangles for mesh on face"); + return null; + } + + if(nofNodes < 1) { + System.out.println("No nodes for mesh on face:"); + return null; + } + Mesh mesh = new Mesh(); + //System.out.println("v " + nofNodes + " t " +nofTriangles); + int index[] = new int[nofTriangles*3]; + + for(int i = 0; i < nofTriangles; i++) + { + int n0, n1, n2; + n0 = meshTriangles.get(3 * i); + n1 = meshTriangles.get(3 * i + 1); + n2 = meshTriangles.get(3 * i + 2); + + index[i * 3 ] = n0; + index[i * 3 + 1] = n1; + index[i * 3 + 2] = n2; + + } + + float vertex[] = new float[nofNodes*3]; + + for(int i = 0; i < nofNodes; i++) { + + + vertex[3 * i] = meshPoints.get(3*i).floatValue(); + vertex[3 * i+1] = meshPoints.get(3*i+1).floatValue(); + vertex[3 * i+2] = meshPoints.get(3*i+2).floatValue(); + + } + float normal[] = calcNormals(vertex, index); + + + mesh.setBuffer(Type.Position, 3, vertex); + mesh.setBuffer(Type.Normal, 3, normal); + mesh.setBuffer(Type.Index, 3, index); + + return mesh; + } + + private static float[] calcNormals(float[] fnodes, int[] trias) { + float nnodes[] = new float[fnodes.length]; + for (int i = 0; i < nnodes.length; i++) nnodes[i] = 0.f; + Vector3f v1 = new Vector3f(); + Vector3f v2 = new Vector3f(); + Vector3f v3 = new Vector3f(); + Vector3f t1 = new Vector3f(); + Vector3f t2 = new Vector3f(); + Vector3f n = new Vector3f(); + for (int i = 0; i < trias.length; i+=3) { + v1.x = fnodes[trias[i]*3]; + v1.y = fnodes[trias[i]*3+1]; + v1.z = fnodes[trias[i]*3+2]; + v2.x = fnodes[trias[i+1]*3]; + v2.y = fnodes[trias[i+1]*3+1]; + v2.z = fnodes[trias[i+1]*3+2]; + v3.x = fnodes[trias[i+2]*3]; + v3.y = fnodes[trias[i+2]*3+1]; + v3.z = fnodes[trias[i+2]*3+2]; + t1.sub(v3,v1); + t2.sub(v2,v1); + n.cross(t2, t1); + //n.normalize(); + nnodes[trias[i]*3] += n.x; + nnodes[trias[i]*3+1] += n.y; + nnodes[trias[i]*3+2] += n.z; + nnodes[trias[i+1]*3] += n.x; + nnodes[trias[i+1]*3+1] += n.y; + nnodes[trias[i+1]*3+2] += n.z; + nnodes[trias[i+2]*3] += n.x; + nnodes[trias[i+2]*3+1] += n.y; + nnodes[trias[i+2]*3+2] += n.z; + } + for (int i = 0; i < nnodes.length; i+=3) { + n.x = nnodes[i]; + n.y = nnodes[i+1]; + n.z = nnodes[i+2]; + n.normalize(); + nnodes[i] = n.x; + nnodes[i+1] = n.y; + nnodes[i+2] = n.z; + } + return nnodes; + } + + public static Mesh createEdgeMesh(Mesh triMesh) { + + // list all edges in the mesh + MapList triEdgeIndices = new MapList(); + for (int i = 0; i < triMesh.getTriangleCount(); i++) { + int[] tri = new int[3]; + triMesh.getTriangle(i, tri); + + if (!triEdgeIndices.contains(tri[0], tri[1])) { + triEdgeIndices.add(tri[0], tri[1]); + } + if (!triEdgeIndices.contains(tri[1], tri[2])) { + triEdgeIndices.add(tri[1], tri[2]); + } + if (!triEdgeIndices.contains(tri[2], tri[0])) { + triEdgeIndices.add(tri[2], tri[0]); + } + } + // find, which edges are listed only once; those are boundaries of the mesh. + MapList edgeIndices = new MapList(); + for (int s : triEdgeIndices.getKeys()) { + for (int e : triEdgeIndices.getValues(s)) { + if (!triEdgeIndices.contains(e, s)) { + edgeIndices.add(s,e); + edgeIndices.add(e,s); + } + } + } + + // create a new mesh, containing boundary vertices and edges of the original mesh. + + // create list of edge vertices in the original mesh + List vertices = new ArrayList(); + FloatBuffer data = triMesh.getFloatBuffer(Type.Position); + for (Integer i : edgeIndices.getKeys()) { + List edges = edgeIndices.getValues(i); + if (!vertices.contains(i)) + vertices.add(i); + if (!vertices.contains(edges.get(0))) + vertices.add(edges.get(0)); + if (!vertices.contains(edges.get(1))) + vertices.add(edges.get(1)); + + } + // create vertices for edge mesh, and map new vertices to orignals. + float vertex[] = new float[vertices.size() * 3]; + int i2 = 0; + Map indexMap = new HashMap(); + for (int i : vertices) { + vertex[i2*3] = data.get(i*3); + vertex[i2*3+1] = data.get(i*3+1); + vertex[i2*3+2] = data.get(i*3+2); + + indexMap.put(i, i2); + i2++; + + } + + // create line indices for the edge mesh. + List indices = new ArrayList(); + + for (int i = 0; i < vertices.size(); i++) { + int s = vertices.get(i); + List edges = edgeIndices.getValues(s); + for (int e : edges) { + if (edgeIndices.contains(s, e) && indexMap.containsKey(s) && indexMap.containsKey(e)) { + indices.add(indexMap.get(s)); + indices.add(indexMap.get(e)); + } + } + + } + + int index[] = new int[indices.size()]; + for (int i = 0; i < indices.size(); i++) { + index[i] = indices.get(i); + } + + Mesh mesh = new Mesh(); + mesh.setMode(Mode.Lines); + mesh.setBuffer(Type.Position, 3, vertex); + mesh.setBuffer(Type.Index, 2, index); + + return mesh; + } + + public static Mesh createVertexMesh(Mesh edgeMesh) { + + MapList edgeIndices = new MapList(); + IndexBuffer indices = edgeMesh.getIndexBuffer(); + for (int i = 0; i < indices.size(); i+=2) { + int s = indices.get(i); + int e = indices.get(i+1); + + if (!edgeIndices.contains(s, e)) { + edgeIndices.add(s, e); + edgeIndices.add(e, s); + } + + } + + List vertices = new ArrayList(); + float data[] = edgeMesh.getFloatBuffer(Type.Position).array(); + for (Integer i : edgeIndices.getKeys()) { + List edges = edgeIndices.getValues(i); + if (edges.size() != 2) + vertices.add(i); + else { + Point3d t = new Point3d(data[i*3],data[i*3+1],data[i*3+2]); + Vector3d v1 = new Vector3d(data[edges.get(0)*3],data[edges.get(0)*3+1],data[edges.get(0)*3+2]); + Vector3d v2 = new Vector3d(data[edges.get(1)*3],data[edges.get(1)*3+1],data[edges.get(1)*3+2]); + + v1.sub(t); + v2.sub(t); + double angle = Math.PI - v1.angle(v2); + if (angle > Math.PI/6) + vertices.add(i); + } + } + + float vertex[] = new float[vertices.size() * 3]; + int i2 = 0; + Map indexMap = new HashMap(); + for (int i : vertices) { + vertex[i2*3] = data[i*3]; + vertex[i2*3+1] = data[i*3+1]; + vertex[i2*3+2] = data[i*3+2]; + + indexMap.put(i, i2); + i2++; + + } + + int index[] = new int[vertices.size()]; + for (int i = 0; i < vertices.size(); i++) { + index[i] = i; + } + + Mesh mesh = new Mesh(); + mesh.setMode(Mode.Points); + mesh.setBuffer(Type.Position, 3, vertex); + mesh.setBuffer(Type.Index, 1, index); + + return mesh; + } + +} diff --git a/org.simantics.opencascade.jme/src/org/simantics/opencascade/jme/JmeSolidObject.java b/org.simantics.opencascade.jme/src/org/simantics/opencascade/jme/JmeSolidObject.java new file mode 100644 index 00000000..620cddec --- /dev/null +++ b/org.simantics.opencascade.jme/src/org/simantics/opencascade/jme/JmeSolidObject.java @@ -0,0 +1,291 @@ +package org.simantics.opencascade.jme; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.eclipse.swt.widgets.Display; +import org.jcae.opencascade.jni.BRepMesh_IncrementalMesh; +import org.jcae.opencascade.jni.TopAbs_ShapeEnum; +import org.jcae.opencascade.jni.TopExp_Explorer; +import org.jcae.opencascade.jni.TopoDS_Face; +import org.jcae.opencascade.jni.TopoDS_Shape; +import org.simantics.opencascade.OCCTTool; + +import com.jme3.app.Application; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.renderer.RenderManager; +import com.jme3.scene.Geometry; +import com.jme3.scene.Mesh; +import com.jme3.scene.Node; +import com.jme3.scene.Spatial; + +public class JmeSolidObject { + + + public static double deflection = 0.001; + + public static double featureAngle = 30; + public static boolean computeNormals = true; + public static boolean cleanPart = false; + public static boolean mergePoints = false; + + private Application app; + private TopoDS_Shape shape; + + private List actors = new ArrayList(2); + + private List solid = new ArrayList(1); + private List edges = new ArrayList(1); + + public JmeSolidObject(Application app,TopoDS_Shape shape) { + this.shape = shape; + this.app = app; + } + + public void visualizeSolid(boolean showEdges, boolean showVertices) { + visualizeSolid(true, showEdges, showVertices); + } + + + public void visualizeSolid(boolean showFaces, boolean showEdges, boolean showVertices) { + clearActorsSWT(); + Mesh data = createSolidMesh(shape); + if (data == null) + return; + if (showFaces) { + solid.add(createActor(data)); + } + if (showEdges) { + Spatial edge = createEdgesActor(data); + edges.add(edge); + if (showVertices) { + actors.add(createVerticesActor(edge)); + } + } + + actors.addAll(solid); + actors.addAll(edges); + + showActorsSWT(); + } + + public void visualizeFaces(boolean showEdges, boolean showVertices) { + clearActorsSWT(); + Collection datas = createFaceMeshes(shape); + for (Mesh data : datas) { + solid.add(createActor(data)); + + if (showEdges) { + Spatial edgesActor = createEdgesActor(data); + edges.add(edgesActor); + if (showVertices) { + actors.add(createVerticesActor(edgesActor)); + } + } + } + actors.addAll(solid); + actors.addAll(edges); + + showActorsSWT(); + } + + public List getActors() { + return actors; + } + + public List getSolid() { + return solid; + } + + public List getEdges() { + return edges; + } + + + + public void showActorsSWT() { + assert (Thread.currentThread() == Display.getDefault().getThread()); + Node root = (Node)(app.getRenderManager().getMainView("Default").getScenes().get(0)); + for (Spatial act : actors) { + root.attachChild(act); + } + } + + public void showActors() { + Display.getDefault().asyncExec(new Runnable() { + + @Override + public void run() { + showActorsSWT(); + } + }); + } + + public void clearActorsSWT() { + assert (Thread.currentThread() == Display.getDefault().getThread()); + if (actors.size() == 0) + return; + for (Spatial act : actors) { + act.removeFromParent(); + } + actors.clear(); + solid.clear(); + edges.clear(); + } + + private void clearActorsSWT(List actors) { + assert (Thread.currentThread() == Display.getDefault().getThread()); + if (actors.size() == 0) + return; + for (Spatial act : actors) { + act.removeFromParent(); + } + } + + public void clearActors() { + if (actors.size() == 0) + return; + final List temp = new ArrayList(actors.size()); + temp.addAll(actors); + actors.clear(); + solid.clear(); + edges.clear(); + Display.getDefault().asyncExec(new Runnable() { + + @Override + public void run() { + clearActorsSWT(temp); + } + }); + } + + public void dispose() { + if (shape != null) { + shape.delete(); + shape = null; + } + clearActors(); + } + + public void delete() { + if (shape != null) { + shape.delete(); + shape = null; + } + clearActorsSWT(); + } + + private static double TOLERANCE = 0.01; + + public static Mesh createSolidMesh(TopoDS_Shape shape) { + + double volume = OCCTTool.getBoundingBoxDiagonal(shape); + if (volume < TOLERANCE) + return null; + + BRepMesh_IncrementalMesh mesh = new BRepMesh_IncrementalMesh(shape,deflection*volume); + + TopExp_Explorer expFace = new TopExp_Explorer(); + + List meshPoints = new ArrayList(); + List meshTriangles = new ArrayList(); + for (expFace.init(shape, TopAbs_ShapeEnum.FACE); expFace.more(); expFace.next()) { + TopoDS_Face face = (TopoDS_Face) expFace.current(); + OCCTTool.appendToMesh(face, meshPoints, meshTriangles); + face.delete(); + } + if (meshPoints.size() == 0 || meshTriangles.size() == 0) + return null; + + Mesh data = JmeOCCTTool.createPartGrid(meshPoints, meshTriangles); + + expFace.delete(); + mesh.delete(); + + return data; + } + + public static Collection createFaceMeshes(TopoDS_Shape shape) { + + double volume = OCCTTool.getBoundingBoxDiagonal(shape); + Collection faces = new ArrayList(); + + if (volume > TOLERANCE) { + + BRepMesh_IncrementalMesh mesh = new BRepMesh_IncrementalMesh(shape,deflection*volume); + + TopExp_Explorer expFace = new TopExp_Explorer(); + + + for (expFace.init(shape, TopAbs_ShapeEnum.FACE); expFace.more(); expFace.next()) { + TopoDS_Face face = (TopoDS_Face) expFace.current(); + Mesh data = JmeOCCTTool.createPartGrid(face); + face.delete(); + faces.add(data); + } + + expFace.delete(); + mesh.delete(); + } + + return faces; + } + + + + public Spatial createActor(Mesh partGrid) { + + + Geometry geom = new Geometry(); + + geom.setMesh(partGrid); + + ColorRGBA color = new ColorRGBA(1.0f, 1.0f, 0.0f, 1.0f); + //Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); + //mat.setColor("Color", color); + Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md"); + mat.setColor("Diffuse", color); + mat.setColor("Ambient", color); + mat.setColor("Specular", new ColorRGBA(1, 1, 1, 1)); + mat.setBoolean("UseMaterialColors", true); + geom.setMaterial(mat); + geom.updateModelBound(); + return geom; + + } + + public Spatial createEdgesActor(Mesh partGrid) { + Mesh edgeMesh = JmeOCCTTool.createEdgeMesh(partGrid); + edgeMesh.setLineWidth(2.f); + Geometry geom = new Geometry(); + geom.setMesh(edgeMesh); + + Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); + ColorRGBA color = new ColorRGBA(0.0f, 0.0f, 0.0f, 1.0f); + mat.setColor("Color", color); + geom.setMaterial(mat); + geom.updateModelBound(); + return geom; + } + + public Spatial createVerticesActor(Spatial partEdgesActor) { + Geometry edgeGeom = (Geometry)partEdgesActor; + Mesh vertexMesh = JmeOCCTTool.createVertexMesh(edgeGeom.getMesh()); + + Geometry geom = new Geometry(); + geom.setMesh(vertexMesh); + + Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); + ColorRGBA color = new ColorRGBA(0.0f, 0.0f, 0.0f, 1.0f); + mat.setColor("Color", color); + geom.setMaterial(mat); + geom.updateModelBound(); + return geom; + } + + + + +}