|
@@ -59,64 +59,66 @@ import java.util.logging.Level;
|
|
|
import java.util.logging.Logger;
|
|
|
|
|
|
/**
|
|
|
- *
|
|
|
+ *
|
|
|
* @author Lex (Aleksey Nikiforov)
|
|
|
- */
|
|
|
+ */
|
|
|
public class TangentBinormalGenerator {
|
|
|
-
|
|
|
+
|
|
|
+ private static final Logger log = Logger.getLogger(TangentBinormalGenerator.class.getName());
|
|
|
private static final float ZERO_TOLERANCE = 0.0000001f;
|
|
|
- private static final Logger log = Logger.getLogger(
|
|
|
- TangentBinormalGenerator.class.getName());
|
|
|
private static float toleranceDot;
|
|
|
public static boolean debug = false;
|
|
|
-
|
|
|
+
|
|
|
static {
|
|
|
setToleranceAngle(45);
|
|
|
}
|
|
|
|
|
|
-
|
|
|
private static class VertexInfo {
|
|
|
+
|
|
|
public final Vector3f position;
|
|
|
public final Vector3f normal;
|
|
|
public final Vector2f texCoord;
|
|
|
public final ArrayList<Integer> indices = new ArrayList<>();
|
|
|
-
|
|
|
+
|
|
|
public VertexInfo(Vector3f position, Vector3f normal, Vector2f texCoord) {
|
|
|
this.position = position;
|
|
|
this.normal = normal;
|
|
|
this.texCoord = texCoord;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- /** Collects all the triangle data for one vertex.
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Collects all the triangle data for one vertex.
|
|
|
*/
|
|
|
private static class VertexData {
|
|
|
+
|
|
|
public final ArrayList<TriangleData> triangles = new ArrayList<>();
|
|
|
-
|
|
|
- public VertexData() { }
|
|
|
}
|
|
|
-
|
|
|
- /** Keeps track of tangent, binormal, and normal for one triangle.
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Keeps track of tangent, binormal, and normal for one triangle.
|
|
|
*/
|
|
|
public static class TriangleData {
|
|
|
+
|
|
|
public final Vector3f tangent;
|
|
|
public final Vector3f binormal;
|
|
|
- public final Vector3f normal;
|
|
|
+ public final Vector3f normal;
|
|
|
public int[] index = new int[3];
|
|
|
public int triangleOffset;
|
|
|
-
|
|
|
+
|
|
|
public TriangleData(Vector3f tangent, Vector3f binormal, Vector3f normal) {
|
|
|
this.tangent = tangent;
|
|
|
this.binormal = binormal;
|
|
|
this.normal = normal;
|
|
|
}
|
|
|
+
|
|
|
public void setIndex(int[] index) {
|
|
|
for (int i = 0; i < index.length; i++) {
|
|
|
this.index[i] = index[i];
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* A private constructor to inhibit instantiation of this class.
|
|
|
*/
|
|
@@ -124,17 +126,17 @@ public class TangentBinormalGenerator {
|
|
|
}
|
|
|
|
|
|
private static List<VertexData> initVertexData(int size) {
|
|
|
- List<VertexData> vertices = new ArrayList<>(size);
|
|
|
+ List<VertexData> vertices = new ArrayList<>(size);
|
|
|
for (int i = 0; i < size; i++) {
|
|
|
vertices.add(new VertexData());
|
|
|
}
|
|
|
return vertices;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
public static void generate(Mesh mesh) {
|
|
|
generate(mesh, true, false);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
public static void generate(Spatial scene, boolean splitMirrored) {
|
|
|
if (scene instanceof Node) {
|
|
|
Node node = (Node) scene;
|
|
@@ -144,19 +146,19 @@ public class TangentBinormalGenerator {
|
|
|
} else {
|
|
|
Geometry geom = (Geometry) scene;
|
|
|
Mesh mesh = geom.getMesh();
|
|
|
-
|
|
|
+
|
|
|
// Check to ensure mesh has texcoords and normals before generating
|
|
|
- if (mesh.getBuffer(Type.TexCoord) != null
|
|
|
- && mesh.getBuffer(Type.Normal) != null){
|
|
|
- generate(geom.getMesh(),true, splitMirrored);
|
|
|
+ if (mesh.getBuffer(Type.TexCoord) != null
|
|
|
+ && mesh.getBuffer(Type.Normal) != null) {
|
|
|
+ generate(geom.getMesh(), true, splitMirrored);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
public static void generate(Spatial scene) {
|
|
|
generate(scene, false);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
public static void generateParallel(Spatial scene, ExecutorService executor) {
|
|
|
final Set<Mesh> meshes = new HashSet<>();
|
|
|
scene.breadthFirstTraversal(new SceneGraphVisitor() {
|
|
@@ -190,12 +192,9 @@ public class TangentBinormalGenerator {
|
|
|
log.log(Level.WARNING, "Error while computing tangents", exc);
|
|
|
}
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- public static void generate(Mesh mesh, boolean approxTangents, boolean splitMirrored) {
|
|
|
+ public static void generate(Mesh mesh, boolean approxTangents, boolean splitMirrored) {
|
|
|
int[] index = new int[3];
|
|
|
Vector3f[] v = new Vector3f[3];
|
|
|
Vector2f[] t = new Vector2f[3];
|
|
@@ -203,16 +202,16 @@ public class TangentBinormalGenerator {
|
|
|
v[i] = new Vector3f();
|
|
|
t[i] = new Vector2f();
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
if (mesh.getBuffer(Type.Normal) == null) {
|
|
|
throw new IllegalArgumentException("The given mesh has no normal data!");
|
|
|
}
|
|
|
-
|
|
|
- List<VertexData> vertices;
|
|
|
+
|
|
|
+ List<VertexData> vertices;
|
|
|
switch (mesh.getMode()) {
|
|
|
case Triangles:
|
|
|
vertices = processTriangles(mesh, index, v, t, splitMirrored);
|
|
|
- if(splitMirrored){
|
|
|
+ if (splitMirrored) {
|
|
|
splitVertices(mesh, vertices, splitMirrored);
|
|
|
}
|
|
|
break;
|
|
@@ -223,21 +222,20 @@ public class TangentBinormalGenerator {
|
|
|
vertices = processTriangleFan(mesh, index, v, t);
|
|
|
break;
|
|
|
default:
|
|
|
- throw new UnsupportedOperationException(
|
|
|
- mesh.getMode() + " is not supported.");
|
|
|
+ throw new UnsupportedOperationException(mesh.getMode() + " is not supported.");
|
|
|
}
|
|
|
-
|
|
|
- processTriangleData(mesh, vertices, approxTangents,splitMirrored);
|
|
|
+
|
|
|
+ processTriangleData(mesh, vertices, approxTangents, splitMirrored);
|
|
|
|
|
|
//if the mesh has a bind pose, we need to generate the bind pose for the tangent buffer
|
|
|
TangentUtils.generateBindPoseTangentsIfNecessary(mesh);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
public static void generate(Mesh mesh, boolean approxTangents) {
|
|
|
generate(mesh, approxTangents, false);
|
|
|
}
|
|
|
|
|
|
- private static List<VertexData> processTriangles(Mesh mesh,
|
|
|
+ private static List<VertexData> processTriangles(Mesh mesh,
|
|
|
int[] index, Vector3f[] v, Vector2f[] t, boolean splitMirrored) {
|
|
|
IndexBuffer indexBuffer = mesh.getIndexBuffer();
|
|
|
FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData();
|
|
@@ -245,36 +243,37 @@ public class TangentBinormalGenerator {
|
|
|
throw new IllegalArgumentException("Can only generate tangents for "
|
|
|
+ "meshes with texture coordinates");
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
FloatBuffer textureBuffer = (FloatBuffer) mesh.getBuffer(Type.TexCoord).getData();
|
|
|
-
|
|
|
+
|
|
|
List<VertexData> vertices = initVertexData(vertexBuffer.limit() / 3);
|
|
|
-
|
|
|
+
|
|
|
for (int i = 0; i < indexBuffer.size() / 3; i++) {
|
|
|
for (int j = 0; j < 3; j++) {
|
|
|
index[j] = indexBuffer.get(i * 3 + j);
|
|
|
populateFromBuffer(v[j], vertexBuffer, index[j]);
|
|
|
populateFromBuffer(t[j], textureBuffer, index[j]);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
TriangleData triData = processTriangle(index, v, t);
|
|
|
- if(splitMirrored){
|
|
|
+ if (splitMirrored) {
|
|
|
triData.setIndex(index);
|
|
|
- triData.triangleOffset = i * 3 ;
|
|
|
+ triData.triangleOffset = i * 3;
|
|
|
}
|
|
|
vertices.get(index[0]).triangles.add(triData);
|
|
|
vertices.get(index[1]).triangles.add(triData);
|
|
|
vertices.get(index[2]).triangles.add(triData);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return vertices;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
//Don't remove split mirrored boolean. It's not used right now, but I intend to
|
|
|
//make this method also split vertices with rotated tangent space and I'll
|
|
|
//add another splitRotated boolean
|
|
|
private static List<VertexData> splitVertices(Mesh mesh, List<VertexData> vertexData, boolean splitMirorred) {
|
|
|
- int nbVertices = mesh.getBuffer(Type.Position).getNumElements();
|
|
|
+
|
|
|
+ int nbVertices = mesh.getBuffer(Type.Position).getNumElements();
|
|
|
List<VertexData> newVertices = new ArrayList<>();
|
|
|
Map<Integer, Integer> indexMap = new HashMap<>();
|
|
|
FloatBuffer normalBuffer = mesh.getFloatBuffer(Type.Normal);
|
|
@@ -283,63 +282,64 @@ public class TangentBinormalGenerator {
|
|
|
ArrayList<TriangleData> triangles = vertexData.get(i).triangles;
|
|
|
Vector3f givenNormal = new Vector3f();
|
|
|
populateFromBuffer(givenNormal, normalBuffer, i);
|
|
|
-
|
|
|
+
|
|
|
ArrayList<TriangleData> trianglesUp = new ArrayList<>();
|
|
|
- ArrayList<TriangleData> trianglesDown = new ArrayList<>();
|
|
|
+ ArrayList<TriangleData> trianglesDown = new ArrayList<>();
|
|
|
for (int j = 0; j < triangles.size(); j++) {
|
|
|
TriangleData triangleData = triangles.get(j);
|
|
|
- if(parity(givenNormal, triangleData.normal) > 0){
|
|
|
+ if (parity(givenNormal, triangleData.normal) > 0) {
|
|
|
trianglesUp.add(triangleData);
|
|
|
- }else{
|
|
|
+ } else {
|
|
|
trianglesDown.add(triangleData);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
//if the vertex has triangles with opposite parity it has to be split
|
|
|
- if(!trianglesUp.isEmpty() && !trianglesDown.isEmpty()){
|
|
|
+ if (!trianglesUp.isEmpty() && !trianglesDown.isEmpty()) {
|
|
|
log.log(Level.FINE, "Splitting vertex {0}", i);
|
|
|
//assigning triangle with the same parity to the original vertex
|
|
|
vertexData.get(i).triangles.clear();
|
|
|
vertexData.get(i).triangles.addAll(trianglesUp);
|
|
|
-
|
|
|
+
|
|
|
//creating a new vertex
|
|
|
VertexData newVert = new VertexData();
|
|
|
//assigning triangles with opposite parity to it
|
|
|
newVert.triangles.addAll(trianglesDown);
|
|
|
-
|
|
|
+
|
|
|
newVertices.add(newVert);
|
|
|
//keep vertex index to fix the index buffers later
|
|
|
indexMap.put(nbVertices, i);
|
|
|
for (TriangleData tri : newVert.triangles) {
|
|
|
for (int j = 0; j < tri.index.length; j++) {
|
|
|
- if(tri.index[j] == i){
|
|
|
- tri.index[j] = nbVertices;
|
|
|
+ if (tri.index[j] == i) {
|
|
|
+ tri.index[j] = nbVertices;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
nbVertices++;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- if(!newVertices.isEmpty()){
|
|
|
-
|
|
|
+ if (!newVertices.isEmpty()) {
|
|
|
+
|
|
|
//we have new vertices, we need to update the mesh's buffers.
|
|
|
for (Type type : VertexBuffer.Type.values()) {
|
|
|
//skip tangent buffer as we're gonna overwrite it later
|
|
|
- if(type == Type.Tangent || type == Type.BindPoseTangent) continue;
|
|
|
+ if (type == Type.Tangent || type == Type.BindPoseTangent) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
VertexBuffer vb = mesh.getBuffer(type);
|
|
|
//Some buffer (hardware skinning ones) can be there but not
|
|
|
//initialized, they must be skipped.
|
|
|
//They'll be initialized when Hardware Skinning is engaged
|
|
|
- if(vb==null || vb.getNumComponents() == 0) continue;
|
|
|
-
|
|
|
- Buffer buffer = vb.getData();
|
|
|
+ if (vb == null || vb.getNumComponents() == 0) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ Buffer buffer = vb.getData();
|
|
|
//IndexBuffer has special treatment, only swapping the vertex indices is needed
|
|
|
- if(type == Type.Index){
|
|
|
- boolean isShortBuffer = vb.getFormat() == VertexBuffer.Format.UnsignedShort;
|
|
|
+ if (type == Type.Index) {
|
|
|
+ boolean isShortBuffer = vb.getFormat() == VertexBuffer.Format.UnsignedShort;
|
|
|
for (VertexData vertex : newVertices) {
|
|
|
for (TriangleData tri : vertex.triangles) {
|
|
|
for (int i = 0; i < tri.index.length; i++) {
|
|
@@ -352,36 +352,36 @@ public class TangentBinormalGenerator {
|
|
|
}
|
|
|
}
|
|
|
vb.setUpdateNeeded();
|
|
|
- }else{
|
|
|
+ } else {
|
|
|
//copy the buffer in a bigger one and append nex vertices to the end
|
|
|
- Buffer newVerts = VertexBuffer.createBuffer(vb.getFormat(), vb.getNumComponents(), nbVertices);
|
|
|
+ Buffer newVerts = VertexBuffer.createBuffer(vb.getFormat(), vb.getNumComponents(), nbVertices);
|
|
|
if (buffer != null) {
|
|
|
buffer.rewind();
|
|
|
- bulkPut(vb.getFormat(), newVerts,buffer);
|
|
|
-
|
|
|
- int index = vertexData.size();
|
|
|
+ bulkPut(vb.getFormat(), newVerts, buffer);
|
|
|
+
|
|
|
+ int index = vertexData.size();
|
|
|
newVerts.position(vertexData.size() * vb.getNumComponents());
|
|
|
for (int j = 0; j < newVertices.size(); j++) {
|
|
|
- int oldInd = indexMap.get(index) ;
|
|
|
- for (int i = 0; i < vb.getNumComponents(); i++) {
|
|
|
- putValue(vb.getFormat(), newVerts, buffer, oldInd* vb.getNumComponents() + i);
|
|
|
- }
|
|
|
+ int oldInd = indexMap.get(index);
|
|
|
+ for (int i = 0; i < vb.getNumComponents(); i++) {
|
|
|
+ putValue(vb.getFormat(), newVerts, buffer, oldInd * vb.getNumComponents() + i);
|
|
|
+ }
|
|
|
index++;
|
|
|
- }
|
|
|
- vb.updateData(newVerts);
|
|
|
+ }
|
|
|
+ vb.updateData(newVerts);
|
|
|
//destroy previous buffer as it's no longer needed
|
|
|
destroyDirectBuffer(buffer);
|
|
|
- }
|
|
|
- }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
vertexData.addAll(newVertices);
|
|
|
-
|
|
|
+
|
|
|
mesh.updateCounts();
|
|
|
}
|
|
|
|
|
|
return vertexData;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
private static void bulkPut(VertexBuffer.Format format, Buffer buf1, Buffer buf2) {
|
|
|
switch (format) {
|
|
|
case Byte:
|
|
@@ -411,8 +411,8 @@ public class TangentBinormalGenerator {
|
|
|
throw new UnsupportedOperationException("Unrecognized buffer format: " + format);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- private static void putValue(VertexBuffer.Format format, Buffer buf1, Buffer buf2,int index) {
|
|
|
+
|
|
|
+ private static void putValue(VertexBuffer.Format format, Buffer buf1, Buffer buf2, int index) {
|
|
|
switch (format) {
|
|
|
case Byte:
|
|
|
case Half:
|
|
@@ -423,113 +423,115 @@ public class TangentBinormalGenerator {
|
|
|
case Short:
|
|
|
case UnsignedShort:
|
|
|
short s = ((ShortBuffer) buf2).get(index);
|
|
|
- ((ShortBuffer) buf1).put(s);
|
|
|
+ ((ShortBuffer) buf1).put(s);
|
|
|
break;
|
|
|
|
|
|
case Int:
|
|
|
case UnsignedInt:
|
|
|
int i = ((IntBuffer) buf2).get(index);
|
|
|
- ((IntBuffer) buf1).put(i);
|
|
|
- break;
|
|
|
+ ((IntBuffer) buf1).put(i);
|
|
|
+ break;
|
|
|
case Float:
|
|
|
float f = ((FloatBuffer) buf2).get(index);
|
|
|
- ((FloatBuffer) buf1).put(f);
|
|
|
- break;
|
|
|
+ ((FloatBuffer) buf1).put(f);
|
|
|
+ break;
|
|
|
case Double:
|
|
|
double d = ((DoubleBuffer) buf2).get(index);
|
|
|
- ((DoubleBuffer) buf1).put(d);
|
|
|
- break;
|
|
|
+ ((DoubleBuffer) buf1).put(d);
|
|
|
+ break;
|
|
|
default:
|
|
|
throw new UnsupportedOperationException("Unrecognized buffer format: " + format);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
private static List<VertexData> processTriangleStrip(Mesh mesh,
|
|
|
int[] index, Vector3f[] v, Vector2f[] t) {
|
|
|
+
|
|
|
IndexBuffer indexBuffer = mesh.getIndexBuffer();
|
|
|
FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData();
|
|
|
FloatBuffer textureBuffer = (FloatBuffer) mesh.getBuffer(Type.TexCoord).getData();
|
|
|
-
|
|
|
+
|
|
|
List<VertexData> vertices = initVertexData(vertexBuffer.limit() / 3);
|
|
|
-
|
|
|
+
|
|
|
index[0] = indexBuffer.get(0);
|
|
|
index[1] = indexBuffer.get(1);
|
|
|
-
|
|
|
+
|
|
|
populateFromBuffer(v[0], vertexBuffer, index[0]);
|
|
|
populateFromBuffer(v[1], vertexBuffer, index[1]);
|
|
|
-
|
|
|
+
|
|
|
populateFromBuffer(t[0], textureBuffer, index[0]);
|
|
|
populateFromBuffer(t[1], textureBuffer, index[1]);
|
|
|
-
|
|
|
+
|
|
|
for (int i = 2; i < indexBuffer.size(); i++) {
|
|
|
index[2] = indexBuffer.get(i);
|
|
|
BufferUtils.populateFromBuffer(v[2], vertexBuffer, index[2]);
|
|
|
BufferUtils.populateFromBuffer(t[2], textureBuffer, index[2]);
|
|
|
-
|
|
|
+
|
|
|
boolean isDegenerate = isDegenerateTriangle(v[0], v[1], v[2]);
|
|
|
TriangleData triData = processTriangle(index, v, t);
|
|
|
-
|
|
|
+
|
|
|
if (!isDegenerate) {
|
|
|
vertices.get(index[0]).triangles.add(triData);
|
|
|
vertices.get(index[1]).triangles.add(triData);
|
|
|
vertices.get(index[2]).triangles.add(triData);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
Vector3f vTemp = v[0];
|
|
|
v[0] = v[1];
|
|
|
v[1] = v[2];
|
|
|
v[2] = vTemp;
|
|
|
-
|
|
|
+
|
|
|
Vector2f tTemp = t[0];
|
|
|
t[0] = t[1];
|
|
|
t[1] = t[2];
|
|
|
t[2] = tTemp;
|
|
|
-
|
|
|
+
|
|
|
index[0] = index[1];
|
|
|
index[1] = index[2];
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return vertices;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
private static List<VertexData> processTriangleFan(Mesh mesh,
|
|
|
int[] index, Vector3f[] v, Vector2f[] t) {
|
|
|
+
|
|
|
IndexBuffer indexBuffer = mesh.getIndexBuffer();
|
|
|
FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData();
|
|
|
FloatBuffer textureBuffer = (FloatBuffer) mesh.getBuffer(Type.TexCoord).getData();
|
|
|
-
|
|
|
+
|
|
|
List<VertexData> vertices = initVertexData(vertexBuffer.limit() / 3);
|
|
|
-
|
|
|
+
|
|
|
index[0] = indexBuffer.get(0);
|
|
|
index[1] = indexBuffer.get(1);
|
|
|
-
|
|
|
+
|
|
|
populateFromBuffer(v[0], vertexBuffer, index[0]);
|
|
|
populateFromBuffer(v[1], vertexBuffer, index[1]);
|
|
|
-
|
|
|
+
|
|
|
populateFromBuffer(t[0], textureBuffer, index[0]);
|
|
|
populateFromBuffer(t[1], textureBuffer, index[1]);
|
|
|
-
|
|
|
+
|
|
|
for (int i = 2; i < vertexBuffer.limit() / 3; i++) {
|
|
|
index[2] = indexBuffer.get(i);
|
|
|
populateFromBuffer(v[2], vertexBuffer, index[2]);
|
|
|
populateFromBuffer(t[2], textureBuffer, index[2]);
|
|
|
-
|
|
|
+
|
|
|
TriangleData triData = processTriangle(index, v, t);
|
|
|
vertices.get(index[0]).triangles.add(triData);
|
|
|
vertices.get(index[1]).triangles.add(triData);
|
|
|
vertices.get(index[2]).triangles.add(triData);
|
|
|
-
|
|
|
+
|
|
|
Vector3f vTemp = v[1];
|
|
|
v[1] = v[2];
|
|
|
v[2] = vTemp;
|
|
|
-
|
|
|
+
|
|
|
Vector2f tTemp = t[1];
|
|
|
t[1] = t[2];
|
|
|
t[2] = tTemp;
|
|
|
-
|
|
|
+
|
|
|
index[1] = index[2];
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return vertices;
|
|
|
}
|
|
|
|
|
@@ -537,24 +539,23 @@ public class TangentBinormalGenerator {
|
|
|
private static boolean isDegenerateTriangle(Vector3f a, Vector3f b, Vector3f c) {
|
|
|
return (a.subtract(b).cross(c.subtract(b))).lengthSquared() == 0;
|
|
|
}
|
|
|
-
|
|
|
- public static TriangleData processTriangle(int[] index,
|
|
|
- Vector3f[] v, Vector2f[] t) {
|
|
|
+
|
|
|
+ public static TriangleData processTriangle(int[] index, Vector3f[] v, Vector2f[] t) {
|
|
|
TempVars tmp = TempVars.get();
|
|
|
try {
|
|
|
Vector3f edge1 = tmp.vect1;
|
|
|
Vector3f edge2 = tmp.vect2;
|
|
|
Vector2f edge1uv = tmp.vect2d;
|
|
|
Vector2f edge2uv = tmp.vect2d2;
|
|
|
-
|
|
|
+
|
|
|
Vector3f tangent = tmp.vect3;
|
|
|
Vector3f binormal = tmp.vect4;
|
|
|
Vector3f normal = tmp.vect5;
|
|
|
-
|
|
|
+
|
|
|
t[1].subtract(t[0], edge1uv);
|
|
|
t[2].subtract(t[0], edge2uv);
|
|
|
float det = edge1uv.x * edge2uv.y - edge1uv.y * edge2uv.x;
|
|
|
-
|
|
|
+
|
|
|
boolean normalize = false;
|
|
|
if (Math.abs(det) < ZERO_TOLERANCE) {
|
|
|
log.log(Level.WARNING, "Collinear uv coordinates for triangle "
|
|
@@ -565,22 +566,20 @@ public class TangentBinormalGenerator {
|
|
|
det = 1;
|
|
|
normalize = true;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
v[1].subtract(v[0], edge1);
|
|
|
v[2].subtract(v[0], edge2);
|
|
|
-
|
|
|
+
|
|
|
tangent.set(edge1);
|
|
|
tangent.normalizeLocal();
|
|
|
binormal.set(edge2);
|
|
|
binormal.normalizeLocal();
|
|
|
-
|
|
|
- if (Math.abs(Math.abs(tangent.dot(binormal)) - 1)
|
|
|
- < ZERO_TOLERANCE) {
|
|
|
- log.log(Level.WARNING, "Vertices are on the same line "
|
|
|
- + "for triangle [{0}, {1}, {2}].",
|
|
|
+
|
|
|
+ if (Math.abs(Math.abs(tangent.dot(binormal)) - 1) < ZERO_TOLERANCE) {
|
|
|
+ log.log(Level.WARNING, "Vertices are on the same line for triangle [{0}, {1}, {2}].",
|
|
|
new Object[]{index[0], index[1], index[2]});
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
float factor = 1 / det;
|
|
|
tangent.x = (edge2uv.y * edge1.x - edge1uv.y * edge2.x) * factor;
|
|
|
tangent.y = (edge2uv.y * edge1.y - edge1uv.y * edge2.y) * factor;
|
|
@@ -588,96 +587,92 @@ public class TangentBinormalGenerator {
|
|
|
if (normalize) {
|
|
|
tangent.normalizeLocal();
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
binormal.x = (edge1uv.x * edge2.x - edge2uv.x * edge1.x) * factor;
|
|
|
binormal.y = (edge1uv.x * edge2.y - edge2uv.x * edge1.y) * factor;
|
|
|
binormal.z = (edge1uv.x * edge2.z - edge2uv.x * edge1.z) * factor;
|
|
|
if (normalize) {
|
|
|
binormal.normalizeLocal();
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
tangent.cross(binormal, normal);
|
|
|
normal.normalizeLocal();
|
|
|
+
|
|
|
+ return new TriangleData(tangent.clone(), binormal.clone(), normal.clone());
|
|
|
|
|
|
- return new TriangleData(
|
|
|
- tangent.clone(),
|
|
|
- binormal.clone(),
|
|
|
- normal.clone());
|
|
|
} finally {
|
|
|
tmp.release();
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
public static void setToleranceAngle(float angle) {
|
|
|
if (angle < 0 || angle > 179) {
|
|
|
- throw new IllegalArgumentException(
|
|
|
- "The angle must be between 0 and 179 degrees.");
|
|
|
+ throw new IllegalArgumentException("The angle must be between 0 and 179 degrees.");
|
|
|
}
|
|
|
toleranceDot = FastMath.cos(angle * FastMath.DEG_TO_RAD);
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
private static boolean approxEqual(Vector3f u, Vector3f v) {
|
|
|
float tolerance = 1E-4f;
|
|
|
- return (FastMath.abs(u.x - v.x) < tolerance) &&
|
|
|
- (FastMath.abs(u.y - v.y) < tolerance) &&
|
|
|
- (FastMath.abs(u.z - v.z) < tolerance);
|
|
|
+ return (FastMath.abs(u.x - v.x) < tolerance)
|
|
|
+ && (FastMath.abs(u.y - v.y) < tolerance)
|
|
|
+ && (FastMath.abs(u.z - v.z) < tolerance);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
private static boolean approxEqual(Vector2f u, Vector2f v) {
|
|
|
float tolerance = 1E-4f;
|
|
|
- return (FastMath.abs(u.x - v.x) < tolerance) &&
|
|
|
- (FastMath.abs(u.y - v.y) < tolerance);
|
|
|
+ return (FastMath.abs(u.x - v.x) < tolerance)
|
|
|
+ && (FastMath.abs(u.y - v.y) < tolerance);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
private static ArrayList<VertexInfo> linkVertices(Mesh mesh, boolean splitMirrored) {
|
|
|
ArrayList<VertexInfo> vertexMap = new ArrayList<>();
|
|
|
-
|
|
|
+
|
|
|
FloatBuffer vertexBuffer = mesh.getFloatBuffer(Type.Position);
|
|
|
FloatBuffer normalBuffer = mesh.getFloatBuffer(Type.Normal);
|
|
|
FloatBuffer texcoordBuffer = mesh.getFloatBuffer(Type.TexCoord);
|
|
|
-
|
|
|
+
|
|
|
Vector3f position = new Vector3f();
|
|
|
Vector3f normal = new Vector3f();
|
|
|
Vector2f texCoord = new Vector2f();
|
|
|
-
|
|
|
+
|
|
|
final int size = vertexBuffer.limit() / 3;
|
|
|
for (int i = 0; i < size; i++) {
|
|
|
-
|
|
|
+
|
|
|
populateFromBuffer(position, vertexBuffer, i);
|
|
|
populateFromBuffer(normal, normalBuffer, i);
|
|
|
populateFromBuffer(texCoord, texcoordBuffer, i);
|
|
|
-
|
|
|
+
|
|
|
boolean found = false;
|
|
|
//Nehon 07/07/2013
|
|
|
//Removed this part, joining split vertices to compute tangent space makes no sense to me
|
|
|
//separate vertices should have separate tangent space
|
|
|
- if(!splitMirrored){
|
|
|
+ if (!splitMirrored) {
|
|
|
for (int j = 0; j < vertexMap.size(); j++) {
|
|
|
VertexInfo vertexInfo = vertexMap.get(j);
|
|
|
- if (approxEqual(vertexInfo.position, position) &&
|
|
|
- approxEqual(vertexInfo.normal, normal) &&
|
|
|
- approxEqual(vertexInfo.texCoord, texCoord))
|
|
|
- {
|
|
|
+ if (approxEqual(vertexInfo.position, position)
|
|
|
+ && approxEqual(vertexInfo.normal, normal)
|
|
|
+ && approxEqual(vertexInfo.texCoord, texCoord)) {
|
|
|
vertexInfo.indices.add(i);
|
|
|
found = true;
|
|
|
- break;
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
if (!found) {
|
|
|
VertexInfo vertexInfo = new VertexInfo(position.clone(), normal.clone(), texCoord.clone());
|
|
|
vertexInfo.indices.add(i);
|
|
|
vertexMap.add(vertexInfo);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return vertexMap;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
private static void processTriangleData(Mesh mesh, List<VertexData> vertices,
|
|
|
boolean approxTangent, boolean splitMirrored) {
|
|
|
- ArrayList<VertexInfo> vertexMap = linkVertices(mesh,splitMirrored);
|
|
|
+ ArrayList<VertexInfo> vertexMap = linkVertices(mesh, splitMirrored);
|
|
|
|
|
|
FloatBuffer tangents = BufferUtils.createFloatBuffer(vertices.size() * 4);
|
|
|
|
|
@@ -719,9 +714,7 @@ public class TangentBinormalGenerator {
|
|
|
tangentUnit.set(triangleData.tangent);
|
|
|
tangentUnit.normalizeLocal();
|
|
|
if (tangent.dot(tangentUnit) < toleranceDot) {
|
|
|
- log.log(Level.WARNING,
|
|
|
- "Angle between tangents exceeds tolerance "
|
|
|
- + "for vertex {0}.", i);
|
|
|
+ log.log(Level.WARNING, "Angle between tangents exceeds tolerance for vertex {0}.", i);
|
|
|
break;
|
|
|
}
|
|
|
|
|
@@ -729,16 +722,13 @@ public class TangentBinormalGenerator {
|
|
|
binormalUnit.set(triangleData.binormal);
|
|
|
binormalUnit.normalizeLocal();
|
|
|
if (binormal.dot(binormalUnit) < toleranceDot) {
|
|
|
- log.log(Level.WARNING,
|
|
|
- "Angle between binormals exceeds tolerance "
|
|
|
- + "for vertex {0}.", i);
|
|
|
+ log.log(Level.WARNING, "Angle between binormals exceeds tolerance for vertex {0}.", i);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-
|
|
|
// find average tangent
|
|
|
tangent.set(0, 0, 0);
|
|
|
binormal.set(0, 0, 0);
|
|
@@ -755,16 +745,13 @@ public class TangentBinormalGenerator {
|
|
|
TriangleData triangleData = triangles.get(j);
|
|
|
tangent.addLocal(triangleData.tangent);
|
|
|
binormal.addLocal(triangleData.binormal);
|
|
|
-
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-
|
|
|
int blameVertex = vertexInfo.indices.get(0);
|
|
|
|
|
|
if (tangent.length() < ZERO_TOLERANCE) {
|
|
|
- log.log(Level.WARNING,
|
|
|
- "Shared tangent is zero for vertex {0}.", blameVertex);
|
|
|
+ log.log(Level.WARNING, "Shared tangent is zero for vertex {0}.", blameVertex);
|
|
|
// attempt to fix from binormal
|
|
|
if (binormal.length() >= ZERO_TOLERANCE) {
|
|
|
binormal.cross(givenNormal, tangent);
|
|
@@ -779,17 +766,13 @@ public class TangentBinormalGenerator {
|
|
|
|
|
|
tangentUnit.set(tangent);
|
|
|
tangentUnit.normalizeLocal();
|
|
|
- if (Math.abs(Math.abs(tangentUnit.dot(givenNormal)) - 1)
|
|
|
- < ZERO_TOLERANCE) {
|
|
|
- log.log(Level.WARNING,
|
|
|
- "Normal and tangent are parallel for vertex {0}.", blameVertex);
|
|
|
+ if (Math.abs(Math.abs(tangentUnit.dot(givenNormal)) - 1) < ZERO_TOLERANCE) {
|
|
|
+ log.log(Level.WARNING, "Normal and tangent are parallel for vertex {0}.", blameVertex);
|
|
|
}
|
|
|
|
|
|
-
|
|
|
if (!approxTangent) {
|
|
|
if (binormal.length() < ZERO_TOLERANCE) {
|
|
|
- log.log(Level.WARNING,
|
|
|
- "Shared binormal is zero for vertex {0}.", blameVertex);
|
|
|
+ log.log(Level.WARNING, "Shared binormal is zero for vertex {0}.", blameVertex);
|
|
|
// attempt to fix from tangent
|
|
|
if (tangent.length() >= ZERO_TOLERANCE) {
|
|
|
givenNormal.cross(tangent, binormal);
|
|
@@ -804,16 +787,12 @@ public class TangentBinormalGenerator {
|
|
|
|
|
|
binormalUnit.set(binormal);
|
|
|
binormalUnit.normalizeLocal();
|
|
|
- if (Math.abs(Math.abs(binormalUnit.dot(givenNormal)) - 1)
|
|
|
- < ZERO_TOLERANCE) {
|
|
|
- log.log(Level.WARNING,
|
|
|
- "Normal and binormal are parallel for vertex {0}.", blameVertex);
|
|
|
+ if (Math.abs(Math.abs(binormalUnit.dot(givenNormal)) - 1) < ZERO_TOLERANCE) {
|
|
|
+ log.log(Level.WARNING, "Normal and binormal are parallel for vertex {0}.", blameVertex);
|
|
|
}
|
|
|
|
|
|
- if (Math.abs(Math.abs(binormalUnit.dot(tangentUnit)) - 1)
|
|
|
- < ZERO_TOLERANCE) {
|
|
|
- log.log(Level.WARNING,
|
|
|
- "Tangent and binormal are parallel for vertex {0}.", blameVertex);
|
|
|
+ if (Math.abs(Math.abs(binormalUnit.dot(tangentUnit)) - 1) < ZERO_TOLERANCE) {
|
|
|
+ log.log(Level.WARNING, "Tangent and binormal are parallel for vertex {0}.", blameVertex);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -845,10 +824,8 @@ public class TangentBinormalGenerator {
|
|
|
// If the model already had a tangent buffer, replace it with the regenerated one
|
|
|
mesh.clearBuffer(Type.Tangent);
|
|
|
mesh.setBuffer(Type.Tangent, 4, tangents);
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- if(mesh.isAnimated()){
|
|
|
+
|
|
|
+ if (mesh.isAnimated()) {
|
|
|
mesh.clearBuffer(Type.BindPoseNormal);
|
|
|
mesh.clearBuffer(Type.BindPosePosition);
|
|
|
mesh.clearBuffer(Type.BindPoseTangent);
|
|
@@ -856,12 +833,12 @@ public class TangentBinormalGenerator {
|
|
|
}
|
|
|
|
|
|
if (debug) {
|
|
|
- writeColorBuffer( vertices, cols, mesh);
|
|
|
+ writeColorBuffer(vertices, cols, mesh);
|
|
|
}
|
|
|
mesh.updateBound();
|
|
|
mesh.updateCounts();
|
|
|
- }
|
|
|
-
|
|
|
+ }
|
|
|
+
|
|
|
private static void writeColorBuffer(List<VertexData> vertices, ColorRGBA[] cols, Mesh mesh) {
|
|
|
FloatBuffer colors = BufferUtils.createFloatBuffer(vertices.size() * 4);
|
|
|
colors.rewind();
|
|
@@ -881,7 +858,6 @@ public class TangentBinormalGenerator {
|
|
|
} else {
|
|
|
return 1;
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
|
|
|
public static Mesh genTbnLines(Mesh mesh, float scale) {
|
|
@@ -891,80 +867,80 @@ public class TangentBinormalGenerator {
|
|
|
return genTangentLines(mesh, scale);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
public static Mesh genNormalLines(Mesh mesh, float scale) {
|
|
|
FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData();
|
|
|
FloatBuffer normalBuffer = (FloatBuffer) mesh.getBuffer(Type.Normal).getData();
|
|
|
-
|
|
|
+
|
|
|
ColorRGBA originColor = ColorRGBA.White;
|
|
|
ColorRGBA normalColor = ColorRGBA.Blue;
|
|
|
-
|
|
|
+
|
|
|
Mesh lineMesh = new Mesh();
|
|
|
lineMesh.setMode(Mesh.Mode.Lines);
|
|
|
-
|
|
|
+
|
|
|
Vector3f origin = new Vector3f();
|
|
|
Vector3f point = new Vector3f();
|
|
|
-
|
|
|
+
|
|
|
FloatBuffer lineVertex = BufferUtils.createFloatBuffer(vertexBuffer.limit() * 2);
|
|
|
FloatBuffer lineColor = BufferUtils.createFloatBuffer(vertexBuffer.limit() / 3 * 4 * 2);
|
|
|
-
|
|
|
+
|
|
|
for (int i = 0; i < vertexBuffer.limit() / 3; i++) {
|
|
|
populateFromBuffer(origin, vertexBuffer, i);
|
|
|
populateFromBuffer(point, normalBuffer, i);
|
|
|
-
|
|
|
+
|
|
|
int index = i * 2;
|
|
|
-
|
|
|
+
|
|
|
setInBuffer(origin, lineVertex, index);
|
|
|
setInBuffer(originColor, lineColor, index);
|
|
|
-
|
|
|
+
|
|
|
point.multLocal(scale);
|
|
|
point.addLocal(origin);
|
|
|
setInBuffer(point, lineVertex, index + 1);
|
|
|
setInBuffer(normalColor, lineColor, index + 1);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
lineMesh.setBuffer(Type.Position, 3, lineVertex);
|
|
|
lineMesh.setBuffer(Type.Color, 4, lineColor);
|
|
|
-
|
|
|
+
|
|
|
lineMesh.setStatic();
|
|
|
//lineMesh.setInterleaved();
|
|
|
return lineMesh;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
private static Mesh genTangentLines(Mesh mesh, float scale) {
|
|
|
FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData();
|
|
|
FloatBuffer normalBuffer = (FloatBuffer) mesh.getBuffer(Type.Normal).getData();
|
|
|
FloatBuffer tangentBuffer = (FloatBuffer) mesh.getBuffer(Type.Tangent).getData();
|
|
|
-
|
|
|
+
|
|
|
FloatBuffer binormalBuffer = null;
|
|
|
if (mesh.getBuffer(Type.Binormal) != null) {
|
|
|
binormalBuffer = (FloatBuffer) mesh.getBuffer(Type.Binormal).getData();
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
ColorRGBA originColor = ColorRGBA.White;
|
|
|
ColorRGBA tangentColor = ColorRGBA.Red;
|
|
|
ColorRGBA binormalColor = ColorRGBA.Green;
|
|
|
ColorRGBA normalColor = ColorRGBA.Blue;
|
|
|
-
|
|
|
+
|
|
|
Mesh lineMesh = new Mesh();
|
|
|
lineMesh.setMode(Mesh.Mode.Lines);
|
|
|
-
|
|
|
+
|
|
|
Vector3f origin = new Vector3f();
|
|
|
Vector3f point = new Vector3f();
|
|
|
Vector3f tangent = new Vector3f();
|
|
|
Vector3f normal = new Vector3f();
|
|
|
-
|
|
|
+
|
|
|
IntBuffer lineIndex = BufferUtils.createIntBuffer(vertexBuffer.limit() / 3 * 6);
|
|
|
FloatBuffer lineVertex = BufferUtils.createFloatBuffer(vertexBuffer.limit() * 4);
|
|
|
FloatBuffer lineColor = BufferUtils.createFloatBuffer(vertexBuffer.limit() / 3 * 4 * 4);
|
|
|
-
|
|
|
+
|
|
|
boolean hasParity = mesh.getBuffer(Type.Tangent).getNumComponents() == 4;
|
|
|
float tangentW = 1;
|
|
|
-
|
|
|
+
|
|
|
for (int i = 0; i < vertexBuffer.limit() / 3; i++) {
|
|
|
populateFromBuffer(origin, vertexBuffer, i);
|
|
|
populateFromBuffer(normal, normalBuffer, i);
|
|
|
-
|
|
|
+
|
|
|
if (hasParity) {
|
|
|
tangent.x = tangentBuffer.get(i * 4);
|
|
|
tangent.y = tangentBuffer.get(i * 4 + 1);
|
|
@@ -973,9 +949,9 @@ public class TangentBinormalGenerator {
|
|
|
} else {
|
|
|
populateFromBuffer(tangent, tangentBuffer, i);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
int index = i * 4;
|
|
|
-
|
|
|
+
|
|
|
int id = i * 6;
|
|
|
lineIndex.put(id, index);
|
|
|
lineIndex.put(id + 1, index + 1);
|
|
@@ -983,10 +959,10 @@ public class TangentBinormalGenerator {
|
|
|
lineIndex.put(id + 3, index + 2);
|
|
|
lineIndex.put(id + 4, index);
|
|
|
lineIndex.put(id + 5, index + 3);
|
|
|
-
|
|
|
+
|
|
|
setInBuffer(origin, lineVertex, index);
|
|
|
setInBuffer(originColor, lineColor, index);
|
|
|
-
|
|
|
+
|
|
|
point.set(tangent);
|
|
|
point.multLocal(scale);
|
|
|
point.addLocal(origin);
|
|
@@ -994,7 +970,6 @@ public class TangentBinormalGenerator {
|
|
|
setInBuffer(tangentColor, lineColor, index + 1);
|
|
|
|
|
|
// wvBinormal = cross(wvNormal, wvTangent) * -inTangent.w
|
|
|
-
|
|
|
if (binormalBuffer == null) {
|
|
|
normal.cross(tangent, point);
|
|
|
point.multLocal(-tangentW);
|
|
@@ -1002,23 +977,23 @@ public class TangentBinormalGenerator {
|
|
|
} else {
|
|
|
populateFromBuffer(point, binormalBuffer, i);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
point.multLocal(scale);
|
|
|
point.addLocal(origin);
|
|
|
setInBuffer(point, lineVertex, index + 2);
|
|
|
setInBuffer(binormalColor, lineColor, index + 2);
|
|
|
-
|
|
|
+
|
|
|
point.set(normal);
|
|
|
point.multLocal(scale);
|
|
|
point.addLocal(origin);
|
|
|
setInBuffer(point, lineVertex, index + 3);
|
|
|
setInBuffer(normalColor, lineColor, index + 3);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
lineMesh.setBuffer(Type.Index, 1, lineIndex);
|
|
|
lineMesh.setBuffer(Type.Position, 3, lineVertex);
|
|
|
lineMesh.setBuffer(Type.Color, 4, lineColor);
|
|
|
-
|
|
|
+
|
|
|
lineMesh.setStatic();
|
|
|
//lineMesh.setInterleaved();
|
|
|
return lineMesh;
|