Merge remote-tracking branch 'upstream/master' into exp_bintray2
This commit is contained in:
commit
f29b77ea36
@ -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<Node> features = (List<Node>) linkedFeatures.get("meshes");
|
||||
if(features != null) {
|
||||
for(Node feature : features) {
|
||||
if(featureName.equals(feature.getName())) {
|
||||
return feature;
|
||||
List<TemporalMesh> temporalMeshes = (List<TemporalMesh>) linkedFeatures.get("meshes");
|
||||
if(temporalMeshes != null) {
|
||||
for(TemporalMesh temporalMesh : temporalMeshes) {
|
||||
if(featureName.equals(temporalMesh.getName())) {
|
||||
return temporalMesh;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 <b>true</b> 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 <b>true</b> to find a crossing point along the whole
|
||||
* straight that contains the current edge
|
||||
* @param extendSecondEdge
|
||||
* set to <b>true</b> 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() {
|
||||
|
@ -276,30 +276,45 @@ public class Face implements Comparator<Integer> {
|
||||
List<Face> facesToTriangulate = new ArrayList<Face>(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<Integer> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return <b>true</b> if the face is smooth and <b>false</b> otherwise
|
||||
*/
|
||||
@ -382,11 +397,9 @@ public class Face implements Comparator<Integer> {
|
||||
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<Vector3f> 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<Integer> {
|
||||
}
|
||||
}
|
||||
|
||||
// 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<Vector3f> crossingVectors = new ArrayList<Vector3f>();
|
||||
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<Vector3f> distinctCrossingVectors = new ArrayList<Vector3f>();
|
||||
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<distinctCrossingVectors.size();++i) {
|
||||
if(direction != Math.signum(distinctCrossingVectors.get(i).dot(edgeNormal))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
|
@ -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 <V> Future<V> enqueue(Callable<V> callable) {
|
||||
AppTask<V> task = new AppTask<V>(callable);
|
||||
taskQueue.add(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues a runnable object to execute in the jME3
|
||||
* rendering thread.
|
||||
* <p>
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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<WizardDescriptor> {
|
||||
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user