From d4c94bc7c0f0b7a43d986cb834db459fed34f958 Mon Sep 17 00:00:00 2001 From: Paul Speed Date: Sat, 30 Jan 2016 04:32:19 -0500 Subject: [PATCH 01/12] Added a vector4f based populate method. --- .../main/java/com/jme3/util/BufferUtils.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/jme3-core/src/main/java/com/jme3/util/BufferUtils.java b/jme3-core/src/main/java/com/jme3/util/BufferUtils.java index 6fc0c50f0..24af81bf6 100644 --- a/jme3-core/src/main/java/com/jme3/util/BufferUtils.java +++ b/jme3-core/src/main/java/com/jme3/util/BufferUtils.java @@ -401,6 +401,25 @@ public final class BufferUtils { vector.z = buf.get(index * 3 + 2); } + /** + * Updates the values of the given vector from the specified buffer at the + * index provided. + * + * @param vector + * the vector to set data on + * @param buf + * the buffer to read from + * @param index + * the position (in terms of vectors, not floats) to read from + * the buf + */ + public static void populateFromBuffer(Vector4f vector, FloatBuffer buf, int index) { + vector.x = buf.get(index * 4); + vector.y = buf.get(index * 4 + 1); + vector.z = buf.get(index * 4 + 2); + vector.w = buf.get(index * 4 + 3); + } + /** * Generates a Vector3f array from the given FloatBuffer. * From a0261e78fb5a8f3ac406b3af63c5344a32b790b6 Mon Sep 17 00:00:00 2001 From: Paul Speed Date: Sat, 30 Jan 2016 07:15:04 -0500 Subject: [PATCH 02/12] Added meaningful messages to the exceptions. --- .../src/main/java/com/jme3/collision/bih/BIHTree.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/collision/bih/BIHTree.java b/jme3-core/src/main/java/com/jme3/collision/bih/BIHTree.java index 5db94b409..2d7a4dc24 100644 --- a/jme3-core/src/main/java/com/jme3/collision/bih/BIHTree.java +++ b/jme3-core/src/main/java/com/jme3/collision/bih/BIHTree.java @@ -109,8 +109,11 @@ public class BIHTree implements CollisionData { this.mesh = mesh; this.maxTrisPerNode = maxTrisPerNode; - if (maxTrisPerNode < 1 || mesh == null) { - throw new IllegalArgumentException(); + if (maxTrisPerNode < 1) { + throw new IllegalArgumentException("maxTrisPerNode cannot be less than 1"); + } + if (mesh == null) { + throw new IllegalArgumentException("Mesh cannot be null"); } bihSwapTmp = new float[9]; @@ -451,7 +454,7 @@ public class BIHTree implements CollisionData { } else if (bv instanceof BoundingBox) { bbox = new BoundingBox((BoundingBox) bv); } else { - throw new UnsupportedCollisionException(); + throw new UnsupportedCollisionException("BoundingVolume:" + bv); } bbox.transform(worldMatrix.invert(), bbox); @@ -470,7 +473,7 @@ public class BIHTree implements CollisionData { BoundingVolume bv = (BoundingVolume) other; return collideWithBoundingVolume(bv, worldMatrix, results); } else { - throw new UnsupportedCollisionException(); + throw new UnsupportedCollisionException("Collidable:" + other); } } From c7dc73e85ffd0f24dec0ee47c0cea31446b4656d Mon Sep 17 00:00:00 2001 From: jmekaelthas Date: Sun, 7 Feb 2016 14:58:30 +0100 Subject: [PATCH 03/12] Bugfix: fixed incorrect faces' triangulation. --- .../scene/plugins/blender/meshes/Edge.java | 102 +++++++++++--- .../scene/plugins/blender/meshes/Face.java | 131 +++++++++++------- 2 files changed, 164 insertions(+), 69 deletions(-) diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Edge.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Edge.java index 2291b5e56..1d76fc02f 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Edge.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Edge.java @@ -10,6 +10,7 @@ import com.jme3.math.Vector3f; import com.jme3.scene.plugins.blender.file.BlenderFileException; import com.jme3.scene.plugins.blender.file.Pointer; import com.jme3.scene.plugins.blender.file.Structure; +import com.jme3.scene.plugins.blender.math.Vector3d; import com.jme3.scene.plugins.blender.meshes.IndexesLoop.IndexPredicate; /** @@ -24,6 +25,8 @@ public class Edge { /** The vertices indexes. */ private int index1, index2; + /** The vertices that can be set if we need and abstract edge outside the mesh (for computations). */ + private Vector3f v1, v2; /** The weight of the edge. */ private float crease; /** A variable that indicates if this edge belongs to any face or not. */ @@ -31,6 +34,13 @@ public class Edge { /** The mesh that owns the edge. */ private TemporalMesh temporalMesh; + public Edge(Vector3f v1, Vector3f v2) { + this.v1 = v1 == null ? new Vector3f() : v1; + this.v2 = v2 == null ? new Vector3f() : v2; + index1 = 0; + index2 = 1; + } + /** * This constructor only stores the indexes of the vertices. The position vertices should be stored * outside this class. @@ -74,14 +84,14 @@ public class Edge { * @return the first vertex of the edge */ public Vector3f getFirstVertex() { - return temporalMesh.getVertices().get(index1); + return temporalMesh == null ? v1 : temporalMesh.getVertices().get(index1); } /** * @return the second vertex of the edge */ public Vector3f getSecondVertex() { - return temporalMesh.getVertices().get(index2); + return temporalMesh == null ? v2 : temporalMesh.getVertices().get(index2); } /** @@ -188,28 +198,82 @@ public class Edge { * @return true if the edges cross and false otherwise */ public boolean cross(Edge edge) { - Vector3f P1 = this.getFirstVertex(); - Vector3f P2 = edge.getFirstVertex(); - Vector3f u = this.getSecondVertex().subtract(P1); - Vector3f v = edge.getSecondVertex().subtract(P2); - float t2 = (u.x * (P2.y - P1.y) - u.y * (P2.x - P1.x)) / (u.y * v.x - u.x * v.y); - float t1 = (P2.x - P1.x + v.x * t2) / u.x; - Vector3f p1 = P1.add(u.mult(t1)); - Vector3f p2 = P2.add(v.mult(t2)); + return this.getCrossPoint(edge) != null; + } + + /** + * The method computes the crossing pint of this edge and another edge. If + * there is no crossing then null is returned. + * + * @param edge + * the edge to compute corss point with + * @return cross point on null if none exist + */ + public Vector3f getCrossPoint(Edge edge) { + return this.getCrossPoint(edge, false, false); + } + + /** + * The method computes the crossing pint of this edge and another edge. If + * there is no crossing then null is returned. This method also allows to + * get the crossing point of the straight lines that contain these edges if + * you set the 'extend' parameter to true. + * + * @param edge + * the edge to compute corss point with + * @param extendThisEdge + * set to true to find a crossing point along the whole + * straight that contains the current edge + * @param extendSecondEdge + * set to true to find a crossing point along the whole + * straight that contains the given edge + * @return cross point on null if none exist + */ + public Vector3f getCrossPoint(Edge edge, boolean extendThisEdge, boolean extendSecondEdge) { + Vector3d P1 = new Vector3d(this.getFirstVertex()); + Vector3d P2 = new Vector3d(edge.getFirstVertex()); + Vector3d u = new Vector3d(this.getSecondVertex()).subtract(P1).normalizeLocal(); + Vector3d v = new Vector3d(edge.getSecondVertex()).subtract(P2).normalizeLocal(); + + double t1 = 0, t2 = 0; + if(u.x == 0 && v.x == 0) { + t2 = (u.z * (P2.y - P1.y) - u.y * (P2.z - P1.z)) / (u.y * v.z - u.z * v.y); + t1 = (P2.z - P1.z + v.z * t2) / u.z; + } else if(u.y == 0 && v.y == 0) { + t2 = (u.x * (P2.z - P1.z) - u.z * (P2.x - P1.x)) / (u.z * v.x - u.x * v.z); + t1 = (P2.x - P1.x + v.x * t2) / u.x; + } else if(u.z == 0 && v.z == 0) { + t2 = (u.x * (P2.y - P1.y) - u.y * (P2.x - P1.x)) / (u.y * v.x - u.x * v.y); + t1 = (P2.x - P1.x + v.x * t2) / u.x; + } else { + t2 = (P1.y * u.x - P1.x * u.y + P2.x * u.y - P2.y * u.x) / (v.y * u.x - u.y * v.x); + t1 = (P2.x - P1.x + v.x * t2) / u.x; + if(Math.abs(P1.z - P2.z + u.z * t1 - v.z * t2) > FastMath.FLT_EPSILON) { + return null; + } + } + Vector3d p1 = P1.add(u.mult(t1)); + Vector3d p2 = P2.add(v.mult(t2)); - if (p1.distance(p2) <= FastMath.FLT_EPSILON) { - // the lines cross, check if p1 and p2 are within the edges - Vector3f p = p1.subtract(P1); - float cos = p.dot(u) / (p.length() * u.length()); - if (cos > 0 && p.length() <= u.length()) { + if (p1.distance(p2) <= FastMath.FLT_EPSILON) { + if(extendThisEdge && extendSecondEdge) { + return p1.toVector3f(); + } + // the lines cross, check if p1 and p2 are within the edges + Vector3d p = p1.subtract(P1); + double cos = p.dot(u) / p.length(); + if (extendThisEdge || p.length()<= FastMath.FLT_EPSILON || cos >= 1 - FastMath.FLT_EPSILON && p.length() <= this.getLength()) { // p1 is inside the first edge, lets check the other edge now p = p2.subtract(P2); - cos = p.dot(v) / (p.length() * v.length()); - return cos > 0 && p.length() <= u.length(); + cos = p.dot(v) / p.length(); + if(extendSecondEdge || p.length()<= FastMath.FLT_EPSILON || cos >= 1 - FastMath.FLT_EPSILON && p.length() <= edge.getLength()) { + return p1.toVector3f(); + } } } - return false; - } + + return null; + } @Override public String toString() { diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Face.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Face.java index 34db4598d..a41d58ff0 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Face.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/meshes/Face.java @@ -276,30 +276,45 @@ public class Face implements Comparator { List facesToTriangulate = new ArrayList(Arrays.asList(this.clone())); while (facesToTriangulate.size() > 0) { Face face = facesToTriangulate.remove(0); - int previousIndex1 = -1, previousIndex2 = -1, previousIndex3 = -1; - while (face.vertexCount() > 0) { - indexes[0] = face.getIndex(0); - indexes[1] = face.findClosestVertex(indexes[0], -1); - indexes[2] = face.findClosestVertex(indexes[0], indexes[1]); + // two special cases will improve the computations speed + if(face.getIndexes().size() == 3) { + triangulatedFaces.add(face.getIndexes().clone()); + } else if(face.getIndexes().size() == 4) { + // in case face has 4 verts we use the plain triangulation + indexes[0] = face.getIndex(0); + indexes[1] = face.getIndex(1); + indexes[2] = face.getIndex(2); + triangulatedFaces.add(new IndexesLoop(indexes)); + + indexes[1] = face.getIndex(2); + indexes[2] = face.getIndex(3); + triangulatedFaces.add(new IndexesLoop(indexes)); + } else { + int previousIndex1 = -1, previousIndex2 = -1, previousIndex3 = -1; + while (face.vertexCount() > 0) { + indexes[0] = face.getIndex(0); + indexes[1] = face.findClosestVertex(indexes[0], -1); + indexes[2] = face.findClosestVertex(indexes[0], indexes[1]); - LOGGER.finer("Veryfying improper triangulation of the temporal mesh."); - if (indexes[0] < 0 || indexes[1] < 0 || indexes[2] < 0) { - throw new BlenderFileException("Unable to find two closest vertices while triangulating face in mesh: " + temporalMesh + "Please apply triangulation modifier in blender as a workaround and load again!"); - } - if (previousIndex1 == indexes[0] && previousIndex2 == indexes[1] && previousIndex3 == indexes[2]) { - throw new BlenderFileException("Infinite loop detected during triangulation of mesh: " + temporalMesh + "Please apply triangulation modifier in blender as a workaround and load again!"); - } - previousIndex1 = indexes[0]; - previousIndex2 = indexes[1]; - previousIndex3 = indexes[2]; + LOGGER.finer("Veryfying improper triangulation of the temporal mesh."); + if (indexes[0] < 0 || indexes[1] < 0 || indexes[2] < 0) { + throw new BlenderFileException("Unable to find two closest vertices while triangulating face in mesh: " + temporalMesh + "Please apply triangulation modifier in blender as a workaround and load again!"); + } + if (previousIndex1 == indexes[0] && previousIndex2 == indexes[1] && previousIndex3 == indexes[2]) { + throw new BlenderFileException("Infinite loop detected during triangulation of mesh: " + temporalMesh + "Please apply triangulation modifier in blender as a workaround and load again!"); + } + previousIndex1 = indexes[0]; + previousIndex2 = indexes[1]; + previousIndex3 = indexes[2]; - Arrays.sort(indexes, this); - facesToTriangulate.addAll(face.detachTriangle(indexes)); - triangulatedFaces.add(new IndexesLoop(indexes)); + Arrays.sort(indexes, this); + facesToTriangulate.addAll(face.detachTriangle(indexes)); + triangulatedFaces.add(new IndexesLoop(indexes)); + } } } } catch (BlenderFileException e) { - LOGGER.log(Level.WARNING, "Errors occured during face triangulation: {0}. The face will be triangulated with the most direct algorithm, " + "but the results might not be identical to blender.", e.getLocalizedMessage()); + LOGGER.log(Level.WARNING, "Errors occured during face triangulation: {0}. The face will be triangulated with the most direct algorithm, but the results might not be identical to blender.", e.getLocalizedMessage()); indexes[0] = this.getIndex(0); for (int i = 1; i < this.vertexCount() - 1; ++i) { indexes[1] = this.getIndex(i); @@ -308,7 +323,7 @@ public class Face implements Comparator { } } } - + /** * @return true if the face is smooth and false otherwise */ @@ -382,11 +397,9 @@ public class Face implements Comparator { int index2 = edge.getSecondIndex(); // check if the line between the vertices is not a border edge of the face if (!indexes.areNeighbours(index1, index2)) { - List vertices = temporalMesh.getVertices(); - for (int i = 0; i < indexes.size(); ++i) { - int i1 = this.getIndex(i); - int i2 = this.getIndex(i + 1); + int i1 = this.getIndex(i - 1); + int i2 = this.getIndex(i); // check if the edges have no common verts (because if they do, they cannot cross) if (i1 != index1 && i1 != index2 && i2 != index1 && i2 != index2) { if (edge.cross(new Edge(i1, i2, 0, false, temporalMesh))) { @@ -395,35 +408,53 @@ public class Face implements Comparator { } } - // the edge does NOT cross any of other edges, so now we need to verify if it is inside the face or outside - // we check it by comparing the angle that is created by vertices: [index1 - 1, index1, index1 + 1] - // with the one creaded by vertices: [index1 - 1, index1, index2] - // if the latter is greater than it means that the edge is outside the face - // IMPORTANT: we assume that all vertices are in one plane (this should be ensured before creating the Face) - int indexOfIndex1 = indexes.indexOf(index1); - int indexMinus1 = this.getIndex(indexOfIndex1 - 1);// indexOfIndex1 == 0 ? indexes.get(indexes.size() - 1) : indexes.get(indexOfIndex1 - 1); - int indexPlus1 = this.getIndex(indexOfIndex1 + 1);// indexOfIndex1 == indexes.size() - 1 ? 0 : indexes.get(indexOfIndex1 + 1); - - Vector3f edge1 = vertices.get(indexMinus1).subtract(vertices.get(index1)).normalizeLocal(); - Vector3f edge2 = vertices.get(indexPlus1).subtract(vertices.get(index1)).normalizeLocal(); - Vector3f newEdge = vertices.get(index2).subtract(vertices.get(index1)).normalizeLocal(); - - // verify f the later computed angle is inside or outside the face - Vector3f direction1 = edge1.cross(edge2).normalizeLocal(); - Vector3f direction2 = edge1.cross(newEdge).normalizeLocal(); - Vector3f normal = temporalMesh.getNormals().get(index1); - - boolean isAngle1Interior = normal.dot(direction1) < 0; - boolean isAngle2Interior = normal.dot(direction2) < 0; - - float angle1 = isAngle1Interior ? edge1.angleBetween(edge2) : FastMath.TWO_PI - edge1.angleBetween(edge2); - float angle2 = isAngle2Interior ? edge1.angleBetween(newEdge) : FastMath.TWO_PI - edge1.angleBetween(newEdge); - - return angle1 >= angle2; + // computing the edge's middle point + Vector3f edgeMiddlePoint = edge.computeCentroid(); + // computing the edge that is perpendicular to the given edge and has a length of 1 (length actually does not matter) + Vector3f edgeVector = edge.getSecondVertex().subtract(edge.getFirstVertex()); + Vector3f edgeNormal = temporalMesh.getNormals().get(index1).cross(edgeVector).normalizeLocal(); + Edge e = new Edge(edgeMiddlePoint, edgeNormal.add(edgeMiddlePoint)); + // compute the vectors from the middle point to the crossing between the extended edge 'e' and other edges of the face + List crossingVectors = new ArrayList(); + for (int i = 0; i < indexes.size(); ++i) { + int i1 = this.getIndex(i); + int i2 = this.getIndex(i + 1); + Vector3f crossPoint = e.getCrossPoint(new Edge(i1, i2, 0, false, temporalMesh), true, false); + if(crossPoint != null) { + crossingVectors.add(crossPoint.subtractLocal(edgeMiddlePoint)); + } + } + if(crossingVectors.size() == 0) { + return false;// edges do not cross + } + + // use only distinct vertices (doubles may appear if the crossing point is a vertex) + List distinctCrossingVectors = new ArrayList(); + for(Vector3f cv : crossingVectors) { + double minDistance = Double.MAX_VALUE; + for(Vector3f dcv : distinctCrossingVectors) { + minDistance = Math.min(minDistance, dcv.distance(cv)); + } + if(minDistance > FastMath.FLT_EPSILON) { + distinctCrossingVectors.add(cv); + } + } + + if(distinctCrossingVectors.size() == 0) { + throw new IllegalStateException("There MUST be at least 2 crossing vertices!"); + } + // checking if all crossing vectors point to the same direction (if yes then the edge is outside the face) + float direction = Math.signum(distinctCrossingVectors.get(0).dot(edgeNormal));// if at least one vector has different direction that this - it means that the edge is inside the face + for(int i=1;i Date: Mon, 8 Feb 2016 13:29:02 +0100 Subject: [PATCH 04/12] Bugfix: fixed a bug that caused ClassCastException while loading linked meshes. --- .../jme3/scene/plugins/blender/BlenderContext.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java index 58114cf28..c96807ed0 100644 --- a/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java +++ b/jme3-blender/src/main/java/com/jme3/scene/plugins/blender/BlenderContext.java @@ -59,6 +59,7 @@ import com.jme3.scene.plugins.blender.file.FileBlockHeader; import com.jme3.scene.plugins.blender.file.FileBlockHeader.BlockCode; import com.jme3.scene.plugins.blender.file.Structure; import com.jme3.scene.plugins.blender.materials.MaterialContext; +import com.jme3.scene.plugins.blender.meshes.TemporalMesh; import com.jme3.texture.Texture; /** @@ -389,11 +390,11 @@ public class BlenderContext { } } } else if("ME".equals(namePrefix)) { - List features = (List) linkedFeatures.get("meshes"); - if(features != null) { - for(Node feature : features) { - if(featureName.equals(feature.getName())) { - return feature; + List temporalMeshes = (List) linkedFeatures.get("meshes"); + if(temporalMeshes != null) { + for(TemporalMesh temporalMesh : temporalMeshes) { + if(featureName.equals(temporalMesh.getName())) { + return temporalMesh; } } } From 75360a2f92d5d7686a7dddde36f51ee47ee69b1c Mon Sep 17 00:00:00 2001 From: MeFisto94 Date: Wed, 10 Feb 2016 20:51:15 +0100 Subject: [PATCH 05/12] Just a small javadoc issue that was catching my eye --- .../src/main/java/com/jme3/bullet/objects/PhysicsVehicle.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jme3-jbullet/src/main/java/com/jme3/bullet/objects/PhysicsVehicle.java b/jme3-jbullet/src/main/java/com/jme3/bullet/objects/PhysicsVehicle.java index 5cebd5edb..3d5463421 100644 --- a/jme3-jbullet/src/main/java/com/jme3/bullet/objects/PhysicsVehicle.java +++ b/jme3-jbullet/src/main/java/com/jme3/bullet/objects/PhysicsVehicle.java @@ -456,7 +456,7 @@ public class PhysicsVehicle extends PhysicsRigidBody { /** * Get the current forward vector of the vehicle in world coordinates * @param vector The object to write the forward vector values to. - * Passing null will cause a new {@link Vector3f) to be created. + * Passing null will cause a new {@link Vector3f} to be created. * @return The forward vector */ public Vector3f getForwardVector(Vector3f vector) { From f9a9839228ca11e063a2a9a0c9d404d8b509b8f0 Mon Sep 17 00:00:00 2001 From: john01dav Date: Sun, 14 Feb 2016 13:54:01 -0600 Subject: [PATCH 06/12] Added enqueue runnable --- .../main/java/com/jme3/app/Application.java | 31 ++++++++ .../jme3test/app/TestEnqueueRunnable.java | 72 +++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 jme3-examples/src/main/java/jme3test/app/TestEnqueueRunnable.java diff --git a/jme3-core/src/main/java/com/jme3/app/Application.java b/jme3-core/src/main/java/com/jme3/app/Application.java index 81fdb6d3b..9688a5d1e 100644 --- a/jme3-core/src/main/java/com/jme3/app/Application.java +++ b/jme3-core/src/main/java/com/jme3/app/Application.java @@ -650,12 +650,28 @@ public class Application implements SystemListener { * Callables are executed right at the beginning of the main loop. * They are executed even if the application is currently paused * or out of focus. + * + * @param callable The callable to run in the main jME3 thread */ public Future enqueue(Callable callable) { AppTask task = new AppTask(callable); taskQueue.add(task); return task; } + + /** + * Enqueues a runnable object to execute in the jME3 + * rendering thread. + *

+ * Runnables are executed right at the beginning of the main loop. + * They are executed even if the application is currently paused + * or out of focus. + * + * @param runnable The runnable to run in the main jME3 thread + */ + public void enqueue(Runnable runnable){ + enqueue(new RunnableWrapper(runnable)); + } /** * Runs tasks enqueued via {@link #enqueue(Callable)} @@ -740,4 +756,19 @@ public class Application implements SystemListener { return viewPort; } + private class RunnableWrapper implements Callable{ + private final Runnable runnable; + + public RunnableWrapper(Runnable runnable){ + this.runnable = runnable; + } + + @Override + public Object call(){ + runnable.run(); + return null; + } + + } + } diff --git a/jme3-examples/src/main/java/jme3test/app/TestEnqueueRunnable.java b/jme3-examples/src/main/java/jme3test/app/TestEnqueueRunnable.java new file mode 100644 index 000000000..88ce27732 --- /dev/null +++ b/jme3-examples/src/main/java/jme3test/app/TestEnqueueRunnable.java @@ -0,0 +1,72 @@ +package jme3test.app; + +import com.jme3.app.SimpleApplication; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.scene.Geometry; +import com.jme3.scene.shape.Box; + +/** + * @author john01dav + */ +public class TestEnqueueRunnable extends SimpleApplication{ + private ExampleAsyncTask exampleAsyncTask; + + public static void main(String[] args){ + new TestEnqueueRunnable().start(); + } + + @Override + public void simpleInitApp(){ + Geometry geom = new Geometry("Box", new Box(1, 1, 1)); + Material material = new Material(getAssetManager(), "/Common/MatDefs/Misc/Unshaded.j3md"); + material.setColor("Color", ColorRGBA.Blue); //a color is needed to start with + geom.setMaterial(material); + getRootNode().attachChild(geom); + + exampleAsyncTask = new ExampleAsyncTask(material); + exampleAsyncTask.getThread().start(); + } + + @Override + public void destroy(){ + exampleAsyncTask.endTask(); + super.destroy(); + } + + private class ExampleAsyncTask implements Runnable{ + private final Thread thread; + private final Material material; + private volatile boolean running = true; + + public ExampleAsyncTask(Material material){ + this.thread = new Thread(this); + this.material = material; + } + + public Thread getThread(){ + return thread; + } + + public void run(){ + while(running){ + enqueue(new Runnable(){ //primary usage of this in real applications would use lambda expressions which are unavailable at java 6 + public void run(){ + material.setColor("Color", ColorRGBA.randomColor()); + } + }); + + try{ + Thread.sleep(1000); + }catch(InterruptedException e){} + } + } + + public void endTask(){ + running = false; + thread.interrupt(); + } + + } + +} From 04566d8c6ac14297ab03693470bd7eb67aff7e2f Mon Sep 17 00:00:00 2001 From: MeFisto94 Date: Wed, 17 Feb 2016 12:21:17 +0100 Subject: [PATCH 07/12] Fixes #360 : Material now serializes name --- jme3-core/src/main/java/com/jme3/material/Material.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jme3-core/src/main/java/com/jme3/material/Material.java b/jme3-core/src/main/java/com/jme3/material/Material.java index 44bfb73f5..2aabbd667 100644 --- a/jme3-core/src/main/java/com/jme3/material/Material.java +++ b/jme3-core/src/main/java/com/jme3/material/Material.java @@ -1236,12 +1236,14 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { oc.write(def.getAssetName(), "material_def", null); oc.write(additionalState, "render_state", null); oc.write(transparent, "is_transparent", false); + oc.write(name, "name", null); oc.writeStringSavableMap(paramValues, "parameters", null); } public void read(JmeImporter im) throws IOException { InputCapsule ic = im.getCapsule(this); + name = ic.readString("name", null); additionalState = (RenderState) ic.readSavable("render_state", null); transparent = ic.readBoolean("is_transparent", false); From 6c4e8010f252a2fb59b63b05dbd2b636255ffd34 Mon Sep 17 00:00:00 2001 From: MeFisto94 Date: Wed, 17 Feb 2016 23:45:27 +0100 Subject: [PATCH 08/12] Fixes the bug of shallow copies when cloning lights (See http://hub.jmonkeyengine.org/t/what-is-the-expected-meaning-of-light-clone/35100 ) --- .../src/main/java/com/jme3/light/DirectionalLight.java | 6 ++++++ jme3-core/src/main/java/com/jme3/light/Light.java | 4 +++- jme3-core/src/main/java/com/jme3/light/PointLight.java | 7 +++++++ jme3-core/src/main/java/com/jme3/light/SpotLight.java | 8 ++++++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/jme3-core/src/main/java/com/jme3/light/DirectionalLight.java b/jme3-core/src/main/java/com/jme3/light/DirectionalLight.java index b1abb65cf..018bd127e 100644 --- a/jme3-core/src/main/java/com/jme3/light/DirectionalLight.java +++ b/jme3-core/src/main/java/com/jme3/light/DirectionalLight.java @@ -146,4 +146,10 @@ public class DirectionalLight extends Light { direction = (Vector3f) ic.readSavable("direction", null); } + @Override + public DirectionalLight clone() { + DirectionalLight l = (DirectionalLight)super.clone(); + l.direction = direction.clone(); + return l; + } } diff --git a/jme3-core/src/main/java/com/jme3/light/Light.java b/jme3-core/src/main/java/com/jme3/light/Light.java index afef33b1d..6181037e9 100644 --- a/jme3-core/src/main/java/com/jme3/light/Light.java +++ b/jme3-core/src/main/java/com/jme3/light/Light.java @@ -228,7 +228,9 @@ public abstract class Light implements Savable, Cloneable { @Override public Light clone(){ try { - return (Light) super.clone(); + Light l = (Light) super.clone(); + l.color = color.clone(); + return l; } catch (CloneNotSupportedException ex) { throw new AssertionError(); } diff --git a/jme3-core/src/main/java/com/jme3/light/PointLight.java b/jme3-core/src/main/java/com/jme3/light/PointLight.java index 4b5224c30..23901e52f 100644 --- a/jme3-core/src/main/java/com/jme3/light/PointLight.java +++ b/jme3-core/src/main/java/com/jme3/light/PointLight.java @@ -241,4 +241,11 @@ public class PointLight extends Light { this.invRadius = 0; } } + + @Override + public PointLight clone() { + PointLight p = (PointLight)super.clone(); + p.position = position.clone(); + return p; + } } diff --git a/jme3-core/src/main/java/com/jme3/light/SpotLight.java b/jme3-core/src/main/java/com/jme3/light/SpotLight.java index bc1335b5b..f9750cdac 100644 --- a/jme3-core/src/main/java/com/jme3/light/SpotLight.java +++ b/jme3-core/src/main/java/com/jme3/light/SpotLight.java @@ -448,5 +448,13 @@ public class SpotLight extends Light { this.invSpotRange = 0; } } + + @Override + public SpotLight clone() { + SpotLight s = (SpotLight)super.clone(); + s.direction = direction.clone(); + s.position = position.clone(); + return s; + } } From f233565031fd47e26d358328a922fa0edcf106ef Mon Sep 17 00:00:00 2001 From: MeFisto94 Date: Thu, 18 Feb 2016 00:10:39 +0100 Subject: [PATCH 09/12] Changed the copyright notice in the comments just like it was done on 25ca07d. --- jme3-core/src/main/java/com/jme3/light/DirectionalLight.java | 2 +- jme3-core/src/main/java/com/jme3/light/Light.java | 2 +- jme3-core/src/main/java/com/jme3/light/PointLight.java | 2 +- jme3-core/src/main/java/com/jme3/light/SpotLight.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/light/DirectionalLight.java b/jme3-core/src/main/java/com/jme3/light/DirectionalLight.java index 018bd127e..6613b8428 100644 --- a/jme3-core/src/main/java/com/jme3/light/DirectionalLight.java +++ b/jme3-core/src/main/java/com/jme3/light/DirectionalLight.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012, 2015 jMonkeyEngine + * Copyright (c) 2009-2012, 2015-2016 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/jme3-core/src/main/java/com/jme3/light/Light.java b/jme3-core/src/main/java/com/jme3/light/Light.java index 6181037e9..f6ea68045 100644 --- a/jme3-core/src/main/java/com/jme3/light/Light.java +++ b/jme3-core/src/main/java/com/jme3/light/Light.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012, 2015 jMonkeyEngine + * Copyright (c) 2009-2012, 2015-2016 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/jme3-core/src/main/java/com/jme3/light/PointLight.java b/jme3-core/src/main/java/com/jme3/light/PointLight.java index 23901e52f..2e8a57882 100644 --- a/jme3-core/src/main/java/com/jme3/light/PointLight.java +++ b/jme3-core/src/main/java/com/jme3/light/PointLight.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012, 2015 jMonkeyEngine + * Copyright (c) 2009-2012, 2015-2016 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/jme3-core/src/main/java/com/jme3/light/SpotLight.java b/jme3-core/src/main/java/com/jme3/light/SpotLight.java index f9750cdac..982488687 100644 --- a/jme3-core/src/main/java/com/jme3/light/SpotLight.java +++ b/jme3-core/src/main/java/com/jme3/light/SpotLight.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012, 2015 jMonkeyEngine + * Copyright (c) 2009-2012, 2015-2016 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without From 2a959e17ab6a8fd0263708ad14170c4c8d65ab35 Mon Sep 17 00:00:00 2001 From: Kirill Vainer Date: Fri, 19 Feb 2016 14:20:25 -0500 Subject: [PATCH 10/12] particle: fix fading not working correctly --- .../src/main/resources/Common/MatDefs/Misc/Particle.vert | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/jme3-core/src/main/resources/Common/MatDefs/Misc/Particle.vert b/jme3-core/src/main/resources/Common/MatDefs/Misc/Particle.vert index 9c2733615..878c8e6da 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Misc/Particle.vert +++ b/jme3-core/src/main/resources/Common/MatDefs/Misc/Particle.vert @@ -32,11 +32,12 @@ void main(){ #ifdef POINT_SPRITE vec4 worldPos = g_WorldMatrix * pos; float d = distance(g_CameraPosition.xyz, worldPos.xyz); - gl_PointSize = max(1.0, (inSize * SIZE_MULTIPLIER * m_Quadratic) / d); + float size = (inSize * SIZE_MULTIPLIER * m_Quadratic) / d); + gl_PointSize = max(1.0, size); //vec4 worldViewPos = g_WorldViewMatrix * pos; //gl_PointSize = (inSize * SIZE_MULTIPLIER * m_Quadratic)*100.0 / worldViewPos.z; - color.a *= min(gl_PointSize, 1.0); + color.a *= min(size, 1.0); #endif -} \ No newline at end of file +} From 1c12b68bb0623b66b68531662ec81f51f12dc47e Mon Sep 17 00:00:00 2001 From: MeFisto94 Date: Sat, 20 Feb 2016 11:19:07 +0100 Subject: [PATCH 11/12] Validate SkyboxWizard's Input on multiple ways to inform the user rather than throwing an exception (#81). --- .../terraineditor/sky/SkyboxWizardPanel2.java | 85 ++++++++++++++----- 1 file changed, 65 insertions(+), 20 deletions(-) diff --git a/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/sky/SkyboxWizardPanel2.java b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/sky/SkyboxWizardPanel2.java index 267751ab4..270b313e9 100644 --- a/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/sky/SkyboxWizardPanel2.java +++ b/sdk/jme3-terrain-editor/src/com/jme3/gde/terraineditor/sky/SkyboxWizardPanel2.java @@ -32,14 +32,16 @@ package com.jme3.gde.terraineditor.sky; import com.jme3.math.Vector3f; +import com.jme3.texture.Image; import com.jme3.texture.Texture; import java.awt.Component; import javax.swing.event.ChangeListener; import org.openide.WizardDescriptor; +import org.openide.WizardValidationException; import org.openide.util.HelpCtx; @SuppressWarnings({"unchecked", "rawtypes"}) -public class SkyboxWizardPanel2 implements WizardDescriptor.Panel { +public class SkyboxWizardPanel2 implements WizardDescriptor.ValidatingPanel { /** * The visual component that displays this panel. If you need to access the @@ -76,10 +78,12 @@ public class SkyboxWizardPanel2 implements WizardDescriptor.Panel { // fireChangeEvent(); // and uncomment the complicated stuff below. } - + + @Override public final void addChangeListener(ChangeListener l) { } - + + @Override public final void removeChangeListener(ChangeListener l) { } /* @@ -105,14 +109,56 @@ public class SkyboxWizardPanel2 implements WizardDescriptor.Panel { } } */ - + + @Override + public void validate() throws WizardValidationException { + SkyboxVisualPanel2 sky = (SkyboxVisualPanel2)component; + + /* Check if there are empty textures */ + if (multipleTextures) { + if (sky.getEditorNorth().getAsText() == null) { throw new WizardValidationException(null, " Texture North: Missing texture!", null); } + if (sky.getEditorSouth().getAsText() == null) { throw new WizardValidationException(null, " Texture South: Missing texture!", null); } + if (sky.getEditorWest().getAsText() == null) { throw new WizardValidationException(null, " Texture West: Missing texture!", null); } + if (sky.getEditorEast().getAsText() == null) { throw new WizardValidationException(null, " Texture East: Missing texture!", null); } + if (sky.getEditorTop().getAsText() == null) { throw new WizardValidationException(null, " Texture Top: Missing texture!", null); } + if (sky.getEditorBottom().getAsText() == null) { throw new WizardValidationException(null, " Texture Bottom: Missing texture!", null); } + + /* Prevent Null-Pointer Exception. If this is triggered, the Texture has no Image or the AssetKey is invalid (which should never happen) */ + if (sky.getEditorNorth().getValue() == null || ((Texture)sky.getEditorNorth().getValue()).getImage() == null) { throw new WizardValidationException(null, " Texture North: Cannot load texture!", null); } + if (sky.getEditorSouth().getValue() == null || ((Texture)sky.getEditorSouth().getValue()).getImage() == null) { throw new WizardValidationException(null, " Texture South: Cannot load texture!", null); } + if (sky.getEditorWest().getValue() == null || ((Texture)sky.getEditorWest().getValue()).getImage() == null) { throw new WizardValidationException(null, " Texture West: Cannot load texture!", null); } + if (sky.getEditorEast().getValue() == null || ((Texture)sky.getEditorEast().getValue()).getImage() == null) { throw new WizardValidationException(null, " Texture East: Cannot load texture!", null); } + if (sky.getEditorTop().getValue() == null || ((Texture)sky.getEditorTop().getValue()).getImage() == null) { throw new WizardValidationException(null, " Texture Top: Cannot load texture!", null); } + if (sky.getEditorBottom().getValue() == null || ((Texture)sky.getEditorBottom().getValue()).getImage() == null) { throw new WizardValidationException(null, " Texture Bottom: Cannot load texture!", null); } + + /* Check for squares */ + Image I = ((Texture)sky.getEditorNorth().getValue()).getImage(); + if (I.getWidth() != I.getHeight()) { throw new WizardValidationException(null, " Texture North: Image has to be a square (width == height)!", null); } + I = ((Texture)sky.getEditorSouth().getValue()).getImage(); + if (I.getWidth() != I.getHeight()) { throw new WizardValidationException(null, " Texture South: Image has to be a square (width == height)!", null); } + I = ((Texture)sky.getEditorWest().getValue()).getImage(); + if (I.getWidth() != I.getHeight()) { throw new WizardValidationException(null, " Texture West: Image has to be a square (width == height)!", null); } + I = ((Texture)sky.getEditorEast().getValue()).getImage(); + if (I.getWidth() != I.getHeight()) { throw new WizardValidationException(null, " Texture East: Image has to be a square (width == height)!", null); } + I = ((Texture)sky.getEditorTop().getValue()).getImage(); + if (I.getWidth() != I.getHeight()) { throw new WizardValidationException(null, " Texture Top: Image has to be a square (width == height)!", null); } + I = ((Texture)sky.getEditorBottom().getValue()).getImage(); + if (I.getWidth() != I.getHeight()) { throw new WizardValidationException(null, " Texture Bottom: Image has to be a square (width == height)!", null); } + } else { + if (sky.getEditorSingle().getAsText() == null){ throw new WizardValidationException(null, " Single Texture: Missing texture!", null); } + if (sky.getEditorSingle().getValue() == null || ((Texture)sky.getEditorSingle().getValue()).getImage() == null){ throw new WizardValidationException(null, " Single Texture: Cannot load texture!", null); } + Image I = ((Texture)sky.getEditorSingle().getValue()).getImage(); + if (I.getWidth() != I.getHeight()) { throw new WizardValidationException(null, " Single Texture: Image has to be a square (width == height)!", null); } + } + } + // You can use a settings object to keep track of state. Normally the // settings object will be the WizardDescriptor, so you can use // WizardDescriptor.getProperty & putProperty to store information entered // by the user. - public void readSettings(Object settings) { - WizardDescriptor wiz = (WizardDescriptor) settings; - multipleTextures = (Boolean)wiz.getProperty("multipleTextures"); + @Override + public void readSettings(WizardDescriptor settings) { + multipleTextures = (Boolean)settings.getProperty("multipleTextures"); SkyboxVisualPanel2 comp = (SkyboxVisualPanel2) getComponent(); if (multipleTextures) { comp.getMultipleTexturePanel().setVisible(true); @@ -124,28 +170,27 @@ public class SkyboxWizardPanel2 implements WizardDescriptor.Panel { } @Override - public void storeSettings(Object settings) { - WizardDescriptor wiz = (WizardDescriptor) settings; + public void storeSettings(WizardDescriptor settings) { SkyboxVisualPanel2 comp = (SkyboxVisualPanel2) getComponent(); if (multipleTextures) { - wiz.putProperty("textureSouth", (Texture)comp.getEditorSouth().getValue()); - wiz.putProperty("textureNorth", (Texture)comp.getEditorNorth().getValue()); - wiz.putProperty("textureEast", (Texture)comp.getEditorEast().getValue()); - wiz.putProperty("textureWest", (Texture)comp.getEditorWest().getValue()); - wiz.putProperty("textureTop", (Texture)comp.getEditorTop().getValue()); - wiz.putProperty("textureBottom", (Texture)comp.getEditorBottom().getValue()); + settings.putProperty("textureSouth", (Texture)comp.getEditorSouth().getValue()); + settings.putProperty("textureNorth", (Texture)comp.getEditorNorth().getValue()); + settings.putProperty("textureEast", (Texture)comp.getEditorEast().getValue()); + settings.putProperty("textureWest", (Texture)comp.getEditorWest().getValue()); + settings.putProperty("textureTop", (Texture)comp.getEditorTop().getValue()); + settings.putProperty("textureBottom", (Texture)comp.getEditorBottom().getValue()); float x = new Float(comp.getNormal1X().getText()); float y = new Float(comp.getNormal1Y().getText()); float z = new Float(comp.getNormal1Z().getText()); - wiz.putProperty("normalScale", new Vector3f(x,y,z) ); + settings.putProperty("normalScale", new Vector3f(x,y,z) ); } else { - wiz.putProperty("textureSingle", (Texture)comp.getEditorSingle().getValue()); + settings.putProperty("textureSingle", (Texture)comp.getEditorSingle().getValue()); float x = new Float(comp.getNormal2X().getText()); float y = new Float(comp.getNormal2Y().getText()); float z = new Float(comp.getNormal2Z().getText()); - wiz.putProperty("normalScale", new Vector3f(x,y,z) ); - wiz.putProperty("envMapType", comp.getEnvMapType()); - wiz.putProperty("flipY", comp.getFlipYCheckBox().isSelected()); + settings.putProperty("normalScale", new Vector3f(x,y,z) ); + settings.putProperty("envMapType", comp.getEnvMapType()); + settings.putProperty("flipY", comp.getFlipYCheckBox().isSelected()); } } } From e051936f9be015b338992b2b10a51f15d8cf6d36 Mon Sep 17 00:00:00 2001 From: MeFisto94 Date: Sat, 20 Feb 2016 13:30:12 +0100 Subject: [PATCH 12/12] Simplify Naming of SDK Windows (#135) --- .../src/com/jme3/gde/assetpack/browser/Bundle.properties | 4 ++-- .../jme3/gde/core/appstates/AppStateExplorerTopComponent.java | 4 ++-- sdk/jme3-core/src/com/jme3/gde/core/filters/Bundle.properties | 4 ++-- .../src/com/jme3/gde/core/sceneexplorer/Bundle.properties | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sdk/jme3-assetpack-support/src/com/jme3/gde/assetpack/browser/Bundle.properties b/sdk/jme3-assetpack-support/src/com/jme3/gde/assetpack/browser/Bundle.properties index 515f491de..769fbf08f 100644 --- a/sdk/jme3-assetpack-support/src/com/jme3/gde/assetpack/browser/Bundle.properties +++ b/sdk/jme3-assetpack-support/src/com/jme3/gde/assetpack/browser/Bundle.properties @@ -1,6 +1,6 @@ CTL_AssetPackBrowserAction=AssetPackBrowser -CTL_AssetPackBrowserTopComponent=AssetPackBrowser Window -HINT_AssetPackBrowserTopComponent=This is a AssetPackBrowser window +CTL_AssetPackBrowserTopComponent=AssetPackBrowser +HINT_AssetPackBrowserTopComponent=The AssetPackBrowser allows easy managing of your AssetPacks AssetPackBrowserTopComponent.jTextField1.text=search AssetPackBrowserTopComponent.jButton1.text=update AssetPackBrowserTopComponent.jButton2.text=online assetpacks diff --git a/sdk/jme3-core/src/com/jme3/gde/core/appstates/AppStateExplorerTopComponent.java b/sdk/jme3-core/src/com/jme3/gde/core/appstates/AppStateExplorerTopComponent.java index 9a8db451a..2af268d4f 100644 --- a/sdk/jme3-core/src/com/jme3/gde/core/appstates/AppStateExplorerTopComponent.java +++ b/sdk/jme3-core/src/com/jme3/gde/core/appstates/AppStateExplorerTopComponent.java @@ -67,8 +67,8 @@ persistenceType = TopComponent.PERSISTENCE_ALWAYS) preferredID = "AppStateExplorerTopComponent") @Messages({ "CTL_AppStateExplorerAction=AppStateExplorer", - "CTL_AppStateExplorerTopComponent=AppStateExplorer Window", - "HINT_AppStateExplorerTopComponent=This is a AppStateExplorer window" + "CTL_AppStateExplorerTopComponent=AppStateExplorer", + "HINT_AppStateExplorerTopComponent=The AppStateExplorer provides an Overview over your current AppState" }) public final class AppStateExplorerTopComponent extends TopComponent implements ExplorerManager.Provider { diff --git a/sdk/jme3-core/src/com/jme3/gde/core/filters/Bundle.properties b/sdk/jme3-core/src/com/jme3/gde/core/filters/Bundle.properties index 651fa1a1e..5598f0ce4 100644 --- a/sdk/jme3-core/src/com/jme3/gde/core/filters/Bundle.properties +++ b/sdk/jme3-core/src/com/jme3/gde/core/filters/Bundle.properties @@ -1,3 +1,3 @@ CTL_FilterExplorerAction=FilterExplorer -CTL_FilterExplorerTopComponent=FilterExplorer Window -HINT_FilterExplorerTopComponent=This is a FilterExplorer window +CTL_FilterExplorerTopComponent=FilterExplorer +HINT_FilterExplorerTopComponent=The FilterExplorer provides an Overview over your current Filter diff --git a/sdk/jme3-core/src/com/jme3/gde/core/sceneexplorer/Bundle.properties b/sdk/jme3-core/src/com/jme3/gde/core/sceneexplorer/Bundle.properties index 4bf848d6a..12fc5d7ab 100644 --- a/sdk/jme3-core/src/com/jme3/gde/core/sceneexplorer/Bundle.properties +++ b/sdk/jme3-core/src/com/jme3/gde/core/sceneexplorer/Bundle.properties @@ -1,4 +1,4 @@ CTL_SceneExplorerAction=SceneExplorer -CTL_SceneExplorerTopComponent=SceneExplorer Window -HINT_SceneExplorerTopComponent=This is a SceneExplorer window +CTL_SceneExplorerTopComponent=SceneExplorer +HINT_SceneExplorerTopComponent=The SceneExplorer provides an Overview over the SceneGraph of your Scene. SceneExplorerTopComponent.jButton1.text=update