@ -49,13 +49,13 @@ import java.nio.FloatBuffer;
import java.nio.IntBuffer ;
import java.nio.IntBuffer ;
import java.util.ArrayList ;
import java.util.ArrayList ;
import java.util.List ;
import java.util.logging.Logger ;
import java.util.logging.Logger ;
import static com.jme3.util.BufferUtils.* ;
import static com.jme3.util.BufferUtils.* ;
/ * *
/ * *
* @author Lex
*
* /
* @author Lex ( Aleksey Nikiforov )
* /
public class TangentBinormalGenerator {
public class TangentBinormalGenerator {
private static final float ZERO_TOLERANCE = 0 . 0000001f ;
private static final float ZERO_TOLERANCE = 0 . 0000001f ;
@ -68,36 +68,37 @@ public class TangentBinormalGenerator {
setToleranceAngle ( 45 ) ;
setToleranceAngle ( 45 ) ;
}
}
private static class VertexData {
private static class VertexInfo {
public final Vector3f tangent = new Vector3f ( ) ;
public final Vector3f position ;
public final Vector3f binormal = new Vector3f ( ) ;
public final Vector3f normal ;
public final List < TriangleData > triangles =
public final ArrayList < Integer > indices = new ArrayList < Integer > ( ) ;
new ArrayList < TriangleData > ( ) ;
public VertexData ( ) {
public VertexInfo ( Vector3f position , Vector3f normal ) {
this . position = position ;
this . normal = normal ;
}
}
}
}
public static class TriangleData {
/ * * Collects all the triangle data for one vertex .
* /
private static class VertexData {
public final ArrayList < TriangleData > triangles = new ArrayList < TriangleData > ( ) ;
public VertexData ( ) { }
}
/ * * Keeps track of tangent , binormal , and normal for one triangle .
* /
public static class TriangleData {
public final Vector3f tangent ;
public final Vector3f tangent ;
public final Vector3f binormal ;
public final Vector3f binormal ;
public final Vector3f normal ;
public final Vector3f normal ;
public int index0 ;
public int index1 ;
public int index2 ;
public TriangleData ( Vector3f tangent , Vector3f binormal ,
public TriangleData ( Vector3f tangent , Vector3f binormal , Vector3f normal ) {
Vector3f normal ,
int index0 , int index1 , int index2 ) {
this . tangent = tangent ;
this . tangent = tangent ;
this . binormal = binormal ;
this . binormal = binormal ;
this . normal = normal ;
this . normal = normal ;
this . index0 = index0 ;
this . index1 = index1 ;
this . index2 = index2 ;
}
}
}
}
@ -365,8 +366,7 @@ public class TangentBinormalGenerator {
return new TriangleData (
return new TriangleData (
tangent ,
tangent ,
binormal ,
binormal ,
normal ,
normal ) ;
index [ 0 ] , index [ 1 ] , index [ 2 ] ) ;
}
}
public static void setToleranceAngle ( float angle ) {
public static void setToleranceAngle ( float angle ) {
@ -378,8 +378,57 @@ public class TangentBinormalGenerator {
toleranceAngle = angle ;
toleranceAngle = angle ;
}
}
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 ) ;
}
private static ArrayList < VertexInfo > linkVertices ( Mesh mesh ) {
ArrayList < VertexInfo > vertexMap = new ArrayList < VertexInfo > ( ) ;
FloatBuffer vertexBuffer = ( FloatBuffer ) mesh . getBuffer ( Type . Position ) . getData ( ) ;
FloatBuffer normalBuffer = ( FloatBuffer ) mesh . getBuffer ( Type . Normal ) . getData ( ) ;
Vector3f position = new Vector3f ( ) ;
Vector3f normal = new Vector3f ( ) ;
final int size = vertexBuffer . capacity ( ) / 3 ;
for ( int i = 0 ; i < size ; i + + ) {
populateFromBuffer ( position , vertexBuffer , i ) ;
populateFromBuffer ( normal , normalBuffer , i ) ;
boolean found = false ;
for ( int j = 0 ; j < vertexMap . size ( ) ; j + + ) {
VertexInfo vertexInfo = vertexMap . get ( j ) ;
if ( approxEqual ( vertexInfo . position , position ) & &
approxEqual ( vertexInfo . normal , normal ) )
{
vertexInfo . indices . add ( i ) ;
found = true ;
break ;
}
}
if ( ! found ) {
VertexInfo vertexInfo = new VertexInfo ( position . clone ( ) , normal . clone ( ) ) ;
vertexInfo . indices . add ( i ) ;
vertexMap . add ( vertexInfo ) ;
}
}
return vertexMap ;
}
private static void processTriangleData ( Mesh mesh , VertexData [ ] vertices ,
private static void processTriangleData ( Mesh mesh , VertexData [ ] vertices ,
boolean approxTangent ) {
boolean approxTangent )
{
ArrayList < VertexInfo > vertexMap = linkVertices ( mesh ) ;
FloatBuffer normalBuffer = ( FloatBuffer ) mesh . getBuffer ( Type . Normal ) . getData ( ) ;
FloatBuffer normalBuffer = ( FloatBuffer ) mesh . getBuffer ( Type . Normal ) . getData ( ) ;
FloatBuffer tangents = BufferUtils . createFloatBuffer ( vertices . length * 4 ) ;
FloatBuffer tangents = BufferUtils . createFloatBuffer ( vertices . length * 4 ) ;
@ -393,139 +442,159 @@ public class TangentBinormalGenerator {
Vector3f tangentUnit = new Vector3f ( ) ;
Vector3f tangentUnit = new Vector3f ( ) ;
Vector3f binormalUnit = new Vector3f ( ) ;
Vector3f binormalUnit = new Vector3f ( ) ;
for ( int i = 0 ; i < vertices . length ; i + + ) {
for ( int k = 0 ; k < vertexMap . size ( ) ; k + + ) {
float wCoord = - 1 ;
float wCoord = - 1 ;
populateFromBuffer ( givenNormal , normalBuffer , i ) ;
VertexInfo vertexInfo = vertexMap . get ( k ) ;
givenNormal . set ( vertexInfo . normal ) ;
givenNormal . normalizeLocal ( ) ;
givenNormal . normalizeLocal ( ) ;
VertexData currentVertex = vertices [ i ] ;
TriangleData firstTriangle = vertices [ vertexInfo . indices . get ( 0 ) ] . triangles . get ( 0 ) ;
List < TriangleData > triangles = currentVertex . triangles ;
// check tangent and binormal consistency
// check tangent and binormal consistency
tangent . set ( triangles . get ( 0 ) . tangent ) ;
tangent . set ( firs tT riangle. tangent ) ;
tangent . normalizeLocal ( ) ;
tangent . normalizeLocal ( ) ;
binormal . set ( triangles . get ( 0 ) . binormal ) ;
binormal . set ( firs tT riangle. binormal ) ;
binormal . normalizeLocal ( ) ;
binormal . normalizeLocal ( ) ;
for ( int j = 1 ; j < triangles . size ( ) ; j + + ) {
for ( int i : vertexInfo . indices ) {
TriangleData triangleData = triangles . get ( j ) ;
ArrayList < TriangleData > triangles = vertices [ i ] . triangles ;
tangentUnit . set ( triangleData . tangent ) ;
for ( int j = 0 ; j < triangles . size ( ) ; j + + ) {
tangentUnit . normalizeLocal ( ) ;
TriangleData triangleData = triangles . get ( j ) ;
if ( tangent . dot ( tangentUnit ) < toleranceDot ) {
log . log ( Level . WARNING ,
tangentUnit . set ( triangleData . tangent ) ;
"Angle between tangents exceeds tolerance "
tangentUnit . normalizeLocal ( ) ;
+ "for vertex {0}." , i ) ;
if ( tangent . dot ( tangentUnit ) < toleranceDot ) {
break ;
}
if ( ! approxTangent ) {
binormalUnit . set ( triangleData . binormal ) ;
binormalUnit . normalizeLocal ( ) ;
if ( binormal . dot ( binormalUnit ) < toleranceDot ) {
log . log ( Level . WARNING ,
log . log ( Level . WARNING ,
"Angle between binormal s exceeds tolerance "
"Angle between tangents exceeds tolerance "
+ "for vertex {0}." , i ) ;
+ "for vertex {0}." , i ) ;
break ;
break ;
}
}
if ( ! approxTangent ) {
binormalUnit . set ( triangleData . binormal ) ;
binormalUnit . normalizeLocal ( ) ;
if ( binormal . dot ( binormalUnit ) < toleranceDot ) {
log . log ( Level . WARNING ,
"Angle between binormals exceeds tolerance "
+ "for vertex {0}." , i ) ;
break ;
}
}
}
}
}
}
// find average tangent
// find average tangent
tangent . set ( 0 , 0 , 0 ) ;
tangent . set ( 0 , 0 , 0 ) ;
binormal . set ( 0 , 0 , 0 ) ;
binormal . set ( 0 , 0 , 0 ) ;
boolean flippedNormal = false ;
int triangleCount = 0 ;
for ( int j = 0 ; j < triangles . size ( ) ; j + + ) {
for ( int i : vertexInfo . indices ) {
TriangleData triangleData = triangles . get ( j ) ;
ArrayList < TriangleData > triangles = vertices [ i ] . triangles ;
tangent . addLocal ( triangleData . tangent ) ;
triangleCount + = triangles . size ( ) ;
binormal . addLocal ( triangleData . binormal ) ;
if ( givenNormal . dot ( triangleData . normal ) < 0 ) {
boolean flippedNormal = false ;
flippedNormal = true ;
for ( int j = 0 ; j < triangles . size ( ) ; j + + ) {
TriangleData triangleData = triangles . get ( j ) ;
tangent . addLocal ( triangleData . tangent ) ;
binormal . addLocal ( triangleData . binormal ) ;
if ( givenNormal . dot ( triangleData . normal ) < 0 ) {
flippedNormal = true ;
}
}
}
}
if ( flippedNormal /*&& approxTangent*/ ) {
if ( flippedNormal /*&& approxTangent*/ ) {
// Generated normal is flipped for this vertex,
// Generated normal is flipped for this vertex,
// so binormal = normal.cross(tangent) will be flipped in the shader
// so binormal = normal.cross(tangent) will be flipped in the shader
// log.log(Level.WARNING,
// log.log(Level.WARNING,
// "Binormal is flipped for vertex {0}.", i);
// "Binormal is flipped for vertex {0}.", i);
wCoord = 1 ;
wCoord = 1 ;
}
}
}
int blameVertex = vertexInfo . indices . get ( 0 ) ;
if ( tangent . length ( ) < ZERO_TOLERANCE ) {
if ( tangent . length ( ) < ZERO_TOLERANCE ) {
log . log ( Level . WARNING ,
log . log ( Level . WARNING ,
"Shared tangent is zero for vertex {0}." , i ) ;
"Shared tangent is zero for vertex {0}." , blameVertex ) ;
// attempt to fix from binormal
// attempt to fix from binormal
if ( binormal . length ( ) > = ZERO_TOLERANCE ) {
if ( binormal . length ( ) > = ZERO_TOLERANCE ) {
binormal . cross ( givenNormal , tangent ) ;
binormal . cross ( givenNormal , tangent ) ;
tangent . normalizeLocal ( ) ;
tangent . normalizeLocal ( ) ;
} // if all fails use the tangent from the first triangle
} // if all fails use the tangent from the first triangle
else {
else {
tangent . set ( triangles . get ( 0 ) . tangent ) ;
tangent . set ( firs tT riangle. tangent ) ;
}
}
} else {
} else {
tangent . divideLocal ( triangles . size ( ) ) ;
tangent . divideLocal ( triangleCount ) ;
}
}
tangentUnit . set ( tangent ) ;
tangentUnit . set ( tangent ) ;
tangentUnit . normalizeLocal ( ) ;
tangentUnit . normalizeLocal ( ) ;
if ( Math . abs ( Math . abs ( tangentUnit . dot ( givenNormal ) ) - 1 )
if ( Math . abs ( Math . abs ( tangentUnit . dot ( givenNormal ) ) - 1 )
< ZERO_TOLERANCE ) {
< ZERO_TOLERANCE ) {
log . log ( Level . WARNING ,
log . log ( Level . WARNING ,
"Normal and tangent are parallel for vertex {0}." , i ) ;
"Normal and tangent are parallel for vertex {0}." , blameVertex ) ;
}
}
if ( ! approxTangent ) {
if ( ! approxTangent ) {
if ( binormal . length ( ) < ZERO_TOLERANCE ) {
if ( binormal . length ( ) < ZERO_TOLERANCE ) {
log . log ( Level . WARNING ,
log . log ( Level . WARNING ,
"Shared binormal is zero for vertex {0}." , i ) ;
"Shared binormal is zero for vertex {0}." , blameVertex ) ;
// attempt to fix from tangent
// attempt to fix from tangent
if ( tangent . length ( ) > = ZERO_TOLERANCE ) {
if ( tangent . length ( ) > = ZERO_TOLERANCE ) {
givenNormal . cross ( tangent , binormal ) ;
givenNormal . cross ( tangent , binormal ) ;
binormal . normalizeLocal ( ) ;
binormal . normalizeLocal ( ) ;
} // if all fails use the binormal from the first triangle
} // if all fails use the binormal from the first triangle
else {
else {
binormal . set ( triangles . get ( 0 ) . binormal ) ;
binormal . set ( firs tT riangle. binormal ) ;
}
}
} else {
} else {
binormal . divideLocal ( triangles . size ( ) ) ;
binormal . divideLocal ( triangleCount ) ;
}
}
binormalUnit . set ( binormal ) ;
binormalUnit . set ( binormal ) ;
binormalUnit . normalizeLocal ( ) ;
binormalUnit . normalizeLocal ( ) ;
if ( Math . abs ( Math . abs ( binormalUnit . dot ( givenNormal ) ) - 1 )
if ( Math . abs ( Math . abs ( binormalUnit . dot ( givenNormal ) ) - 1 )
< ZERO_TOLERANCE ) {
< ZERO_TOLERANCE ) {
log . log ( Level . WARNING ,
log . log ( Level . WARNING ,
"Normal and binormal are parallel for vertex {0}." , i ) ;
"Normal and binormal are parallel for vertex {0}." , blameVertex ) ;
}
}
if ( Math . abs ( Math . abs ( binormalUnit . dot ( tangentUnit ) ) - 1 )
if ( Math . abs ( Math . abs ( binormalUnit . dot ( tangentUnit ) ) - 1 )
< ZERO_TOLERANCE ) {
< ZERO_TOLERANCE ) {
log . log ( Level . WARNING ,
log . log ( Level . WARNING ,
"Tangent and binormal are parallel for vertex {0}." , i ) ;
"Tangent and binormal are parallel for vertex {0}." , blameVertex ) ;
}
}
}
}
if ( approxTangent ) {
for ( int i : vertexInfo . indices ) {
// givenNormal.cross(tangent, binormal);
if ( approxTangent ) {
// binormal.cross(givenNormal, tangent);
// This calculation ensures that normal and tagent have a 90 degree angle.
tangent . normalizeLocal ( ) ;
// Removing this will lead to visual artifacts.
givenNormal . cross ( tangent , binormal ) ;
tangents . put ( ( i * 4 ) , tangent . x ) ;
binormal . cross ( givenNormal , tangent ) ;
tangents . put ( ( i * 4 ) + 1 , tangent . y ) ;
tangents . put ( ( i * 4 ) + 2 , tangent . z ) ;
tangent . normalizeLocal ( ) ;
tangents . put ( ( i * 4 ) + 3 , wCoord ) ;
} else {
tangents . put ( ( i * 4 ) , tangent . x ) ;
tangents . put ( ( i * 4 ) , tangent . x ) ;
tangents . put ( ( i * 4 ) + 1 , tangent . y ) ;
tangents . put ( ( i * 4 ) + 1 , tangent . y ) ;
tangents . put ( ( i * 4 ) + 2 , tangent . z ) ;
tangents . put ( ( i * 4 ) + 2 , tangent . z ) ;
tangents . put ( ( i * 4 ) + 3 , wCoord ) ;
tangents . put ( ( i * 4 ) + 3 , wCoord ) ;
} else {
tangents . put ( ( i * 4 ) , tangent . x ) ;
tangents . put ( ( i * 4 ) + 1 , tangent . y ) ;
tangents . put ( ( i * 4 ) + 2 , tangent . z ) ;
tangents . put ( ( i * 4 ) + 3 , wCoord ) ;
// setInBuffer(binormal, binormals, i);
//setInBuffer(binormal, binormals, i);
}
}
}
}
}