|
|
|
@ -17,6 +17,8 @@ import java.nio.FloatBuffer; |
|
|
|
|
import org.critterai.nmgen.IntermediateData; |
|
|
|
|
import org.critterai.nmgen.NavmeshGenerator; |
|
|
|
|
import org.critterai.nmgen.TriangleMesh; |
|
|
|
|
import org.openide.DialogDisplayer; |
|
|
|
|
import org.openide.NotifyDescriptor; |
|
|
|
|
|
|
|
|
|
public class NavMeshGenerator implements Savable { |
|
|
|
|
|
|
|
|
@ -37,8 +39,8 @@ public class NavMeshGenerator implements Savable { |
|
|
|
|
private int maxVertsPerPoly = 6; |
|
|
|
|
private float contourSampleDistance = 25; |
|
|
|
|
private float contourMaxDeviation = 25; |
|
|
|
|
|
|
|
|
|
private IntermediateData intermediateData; |
|
|
|
|
private int timeout = 10000; |
|
|
|
|
|
|
|
|
|
public NavMeshGenerator() { |
|
|
|
|
} |
|
|
|
@ -61,7 +63,7 @@ public class NavMeshGenerator implements Savable { |
|
|
|
|
System.out.println("Contour Sample Dist: " + contourSampleDistance); |
|
|
|
|
System.out.println("Contour Max Dev.: " + contourMaxDeviation); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void setIntermediateData(IntermediateData data) { |
|
|
|
|
this.intermediateData = data; |
|
|
|
|
} |
|
|
|
@ -89,7 +91,8 @@ public class NavMeshGenerator implements Savable { |
|
|
|
|
indices[i] = ib.get(i); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TriangleMesh triMesh = nmgen.build(positions, indices, intermediateData); |
|
|
|
|
|
|
|
|
|
TriangleMesh triMesh = buildNavMesh(positions, indices, intermediateData); |
|
|
|
|
if (triMesh == null) { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
@ -106,51 +109,79 @@ public class NavMeshGenerator implements Savable { |
|
|
|
|
return mesh2; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private TriangleMesh buildNavMesh(float[] positions, int[] indices, IntermediateData intermediateData) { |
|
|
|
|
MeshBuildRunnable runnable = new MeshBuildRunnable(positions, indices, intermediateData); |
|
|
|
|
try { |
|
|
|
|
execute(runnable, timeout); |
|
|
|
|
} catch (TimeoutException ex) { |
|
|
|
|
DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message("NavMesh Generation timed out.")); |
|
|
|
|
} |
|
|
|
|
return runnable.getTriMesh(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static void execute(Thread task, long timeout) throws TimeoutException { |
|
|
|
|
task.start(); |
|
|
|
|
try { |
|
|
|
|
task.join(timeout); |
|
|
|
|
} catch (InterruptedException e) { |
|
|
|
|
} |
|
|
|
|
if (task.isAlive()) { |
|
|
|
|
task.interrupt(); |
|
|
|
|
throw new TimeoutException(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static void execute(Runnable task, long timeout) throws TimeoutException { |
|
|
|
|
Thread t = new Thread(task, "Timeout guard"); |
|
|
|
|
t.setDaemon(true); |
|
|
|
|
execute(t, timeout); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public Mesh terrain2mesh(Terrain terr) { |
|
|
|
|
float[] heights = terr.getHeightMap(); |
|
|
|
|
int length = heights.length; |
|
|
|
|
int side = (int) FastMath.sqrt(heights.length); |
|
|
|
|
float[] vertices = new float[length * 3]; |
|
|
|
|
int[] indices = new int[(side-1)*(side-1)*6]; |
|
|
|
|
|
|
|
|
|
Vector3f scale = ((Node)terr).getWorldScale().clone(); |
|
|
|
|
Vector3f trans = ((Node)terr).getWorldTranslation().clone(); |
|
|
|
|
trans.x -= terr.getTerrainSize()/2f; |
|
|
|
|
trans.z -= terr.getTerrainSize()/2f; |
|
|
|
|
int[] indices = new int[(side - 1) * (side - 1) * 6]; |
|
|
|
|
|
|
|
|
|
Vector3f scale = ((Node) terr).getWorldScale().clone(); |
|
|
|
|
Vector3f trans = ((Node) terr).getWorldTranslation().clone(); |
|
|
|
|
trans.x -= terr.getTerrainSize() / 2f; |
|
|
|
|
trans.z -= terr.getTerrainSize() / 2f; |
|
|
|
|
float offsetX = trans.x * scale.x; |
|
|
|
|
float offsetZ = trans.z * scale.z; |
|
|
|
|
|
|
|
|
|
// do vertices
|
|
|
|
|
int i=0; |
|
|
|
|
for (int z=0; z<side; z++) { |
|
|
|
|
for (int x=0; x<side; x++) { |
|
|
|
|
vertices[i++] = x+offsetX; |
|
|
|
|
vertices[i++] = heights[z*side+x]*scale.y; |
|
|
|
|
vertices[i++] = z+offsetZ; |
|
|
|
|
int i = 0; |
|
|
|
|
for (int z = 0; z < side; z++) { |
|
|
|
|
for (int x = 0; x < side; x++) { |
|
|
|
|
vertices[i++] = x + offsetX; |
|
|
|
|
vertices[i++] = heights[z * side + x] * scale.y; |
|
|
|
|
vertices[i++] = z + offsetZ; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// do indexes
|
|
|
|
|
i=0; |
|
|
|
|
for (int z=0; z<side-1; z++) { |
|
|
|
|
for (int x=0; x<side-1; x++) { |
|
|
|
|
i = 0; |
|
|
|
|
for (int z = 0; z < side - 1; z++) { |
|
|
|
|
for (int x = 0; x < side - 1; x++) { |
|
|
|
|
// triangle 1
|
|
|
|
|
indices[i++] = z*side+x; |
|
|
|
|
indices[i++] = (z+1)*side+x; |
|
|
|
|
indices[i++] = (z+1)*side+x+1; |
|
|
|
|
indices[i++] = z * side + x; |
|
|
|
|
indices[i++] = (z + 1) * side + x; |
|
|
|
|
indices[i++] = (z + 1) * side + x + 1; |
|
|
|
|
// triangle 2
|
|
|
|
|
indices[i++] = z*side+x; |
|
|
|
|
indices[i++] = (z+1)*side+x+1; |
|
|
|
|
indices[i++] = z*side+x+1; |
|
|
|
|
indices[i++] = z * side + x; |
|
|
|
|
indices[i++] = (z + 1) * side + x + 1; |
|
|
|
|
indices[i++] = z * side + x + 1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Mesh mesh2 = new Mesh(); |
|
|
|
|
mesh2.setBuffer(Type.Position, 3, vertices); |
|
|
|
|
mesh2.setBuffer(Type.Index, 3, indices); |
|
|
|
|
mesh2.updateBound(); |
|
|
|
|
mesh2.updateCounts(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return mesh2; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -302,6 +333,14 @@ public class NavMeshGenerator implements Savable { |
|
|
|
|
this.useConservativeExpansion = useConservativeExpansion; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public int getTimeout() { |
|
|
|
|
return timeout; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public void setTimeout(int timeout) { |
|
|
|
|
this.timeout = timeout; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public void write(JmeExporter ex) throws IOException { |
|
|
|
|
OutputCapsule oc = ex.getCapsule(this); |
|
|
|
|
oc.write(cellSize, "cellSize", 1f); |
|
|
|
@ -331,14 +370,44 @@ public class NavMeshGenerator implements Savable { |
|
|
|
|
maxTraversableSlope = ic.readFloat("maxTraversableSlope", 48f); |
|
|
|
|
clipLedges = ic.readBoolean("clipLedges", false); |
|
|
|
|
traversableAreaBorderSize = ic.readFloat("traversableAreaBorderSize", 1.2f); |
|
|
|
|
smoothingThreshold = (int)ic.readFloat("smoothingThreshold", 2); |
|
|
|
|
smoothingThreshold = (int) ic.readFloat("smoothingThreshold", 2); |
|
|
|
|
useConservativeExpansion = ic.readBoolean("useConservativeExpansion", true); |
|
|
|
|
minUnconnectedRegionSize = (int)ic.readFloat("minUnconnectedRegionSize", 3); |
|
|
|
|
mergeRegionSize = (int)ic.readFloat("mergeRegionSize", 10); |
|
|
|
|
minUnconnectedRegionSize = (int) ic.readFloat("minUnconnectedRegionSize", 3); |
|
|
|
|
mergeRegionSize = (int) ic.readFloat("mergeRegionSize", 10); |
|
|
|
|
maxEdgeLength = ic.readFloat("maxEdgeLength", 0); |
|
|
|
|
edgeMaxDeviation = ic.readFloat("edgeMaxDeviation", 2.4f); |
|
|
|
|
maxVertsPerPoly = (int)ic.readFloat("maxVertsPerPoly", 6); |
|
|
|
|
maxVertsPerPoly = (int) ic.readFloat("maxVertsPerPoly", 6); |
|
|
|
|
contourSampleDistance = ic.readFloat("contourSampleDistance", 25); |
|
|
|
|
contourMaxDeviation = ic.readFloat("contourMaxDeviation", 25); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private class MeshBuildRunnable implements Runnable { |
|
|
|
|
|
|
|
|
|
private float[] positions; |
|
|
|
|
private int[] indices; |
|
|
|
|
private IntermediateData intermediateData; |
|
|
|
|
private TriangleMesh triMesh; |
|
|
|
|
|
|
|
|
|
public MeshBuildRunnable(float[] positions, int[] indices, IntermediateData intermediateData) { |
|
|
|
|
this.positions = positions; |
|
|
|
|
this.indices = indices; |
|
|
|
|
this.intermediateData = intermediateData; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public void run() { |
|
|
|
|
triMesh = nmgen.build(positions, indices, intermediateData); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public TriangleMesh getTriMesh() { |
|
|
|
|
return triMesh; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static class TimeoutException extends Exception { |
|
|
|
|
|
|
|
|
|
/** Create an instance */ |
|
|
|
|
public TimeoutException() { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|