/**
* Copyright (c) 2003-2009, Xith3D Project Group all rights reserved.
*
* Portions based on the Java3D interface, Copyright by Sun Microsystems.
* Many thanks to the developers of Java3D and Sun Microsystems for their
* innovation and design.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the 'Xith3D Project Group' nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) A
* RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE
*/
package org.xith3d.utility.geometry;
import org.openmali.spatial.TriangleContainer;
import org.openmali.spatial.polygons.Triangle;
import org.openmali.vecmath2.Point3f;
import org.openmali.vecmath2.TexCoord3f;
import org.openmali.vecmath2.Vector3f;
import org.xith3d.scenegraph.Geometry;
/**
* The TangentsFactory generates tanget data (tangents and bitangents)
* for arbitrary meshes.
*
* @author Marvin Froehlich (aka Qudus)
*/
public class TangentsFactory
{
public static enum TangentsStoreMode
{
TEXTURE_COORDINATES,
VERTEX_ATTRIBUTES;
}
public static final TangentsFactory INSTANCE = new TangentsFactory();
public static TangentsFactory getInstance()
{
return ( INSTANCE );
}
private static final void fixNegativeZero( Vector3f vec )
{
if ( vec.getX() == -0f )
vec.setX( 0f );
if ( vec.getY() == -0f )
vec.setY( 0f );
if ( vec.getZ() == -0f )
vec.setZ( 0f );
}
/**
* Calculates the tangents and bitangents for the given GeomContainer.
* The GeometryArray must have at least three texture units where the first one (0) must be filled.
* It also has to have normals.<br>
* If both of the two array parameters are <code>null</code>,
* the tanget data is directly applied to the 2nd and 3rd (1, 2) texture units.
*
* @param geom
* @param storeMode use null to only store in the arrays (index1 and index2 are ignored then)
* @param storeIndex1
* @param storeIndex2
* @param tangents
* @param bitangents
*/
public void calculateTangents( Geometry geom, TangentsStoreMode storeMode, int storeIndex1, int storeIndex2, Vector3f[] tangents, Vector3f[] bitangents )
{
if ( !geom.hasFeature( Geometry.NORMALS ) )
throw new IllegalArgumentException( "The GeometryArray must have normals." );
if ( ( tangents == null ) && ( bitangents == null ) && ( storeMode == null ) )
{
throw new IllegalArgumentException( "storeMode must not be null, if both arrays are null." );
}
final int vertexCount = geom.getVertexCount();
final TriangleContainer triangCont = (TriangleContainer)geom;
Vector3f[] tan1 = new Vector3f[ vertexCount * 2 ];
Vector3f[] tan2 = new Vector3f[ vertexCount * 2 ];
for ( int i = 0; i < tan1.length; i++ )
{
tan1[ i ] = new Vector3f();
tan2[ i ] = new Vector3f();
}
final int triangleCount = triangCont.getTriangleCount();
Triangle triangle = new Triangle();
final float[] w1 = new float[ 4 ];
final float[] w2 = new float[ 4 ];
final float[] w3 = new float[ 4 ];
for ( int i = 0; i < triangleCount; i++ )
{
triangCont.getTriangle( i, triangle );
int i1 = triangle.getVertexIndexA();
int i2 = triangle.getVertexIndexB();
int i3 = triangle.getVertexIndexC();
final Point3f v1 = triangle.getVertexCoordA();
final Point3f v2 = triangle.getVertexCoordB();
final Point3f v3 = triangle.getVertexCoordC();
geom.getTextureCoordinate( 0, i1, w1 );
geom.getTextureCoordinate( 0, i2, w2 );
geom.getTextureCoordinate( 0, i3, w3 );
float x1 = v2.getX() - v1.getX();
float x2 = v3.getX() - v1.getX();
float y1 = v2.getY() - v1.getY();
float y2 = v3.getY() - v1.getY();
float z1 = v2.getZ() - v1.getZ();
float z2 = v3.getZ() - v1.getZ();
float s1 = w2[ 0 ] - w1[ 0 ];
float s2 = w3[ 0 ] - w1[ 0 ];
float t1 = w2[ 1 ] - w1[ 1 ];
float t2 = w3[ 1 ] - w1[ 1 ];
final float r = 1.0f / ( s1 * t2 - s2 * t1 );
Vector3f sdir = new Vector3f( ( t2 * x1 - t1 * x2 ) * r, ( t2 * y1 - t1 * y2 ) * r, ( t2 * z1 - t1 * z2 ) * r );
Vector3f tdir = new Vector3f( ( s1 * x2 - s2 * x1 ) * r, ( s1 * y2 - s2 * y1 ) * r, ( s1 * z2 - s2 * z1 ) * r );
tan1[ i1 ].add( sdir );
tan1[ i2 ].add( sdir );
tan1[ i3 ].add( sdir );
tan2[ i1 ].add( tdir );
tan2[ i2 ].add( tdir );
tan2[ i3 ].add( tdir );
}
final Vector3f normal = new Vector3f();
Vector3f tmp = new Vector3f();
Vector3f tmp2 = new Vector3f();
Vector3f tangent = new Vector3f();
Vector3f bitangent = new Vector3f();
TexCoord3f tex = new TexCoord3f();
Vector3f attrib = new Vector3f();
for ( int i = 0; i < vertexCount; i++ )
{
geom.getNormal( i, normal );
final Vector3f t = tan1[ i ];
// Gram-Schmidt orthogonalize
final float d = normal.dot( t );
tmp.set( normal );
tmp.scale( d );
tmp2.set( t );
tmp2.sub( tmp );
tangent.set( tmp2 );
tangent.normalize();
// Calculate handedness
//tmp.cross( normal, t );
//tangent.w = (tmp.dot( tan2[ i ] ) < 0.0f) ? -1.0f : 1.0f;
fixNegativeZero( tangent );
// calculate bitangent
bitangent.cross( tangent, normal );
fixNegativeZero( bitangent );
//System.out.println( tangent );
//System.out.println( bitangent );
if ( storeMode == TangentsStoreMode.VERTEX_ATTRIBUTES )
{
attrib.set( tangent.getX(), tangent.getY(), tangent.getZ() );
geom.setVertexAttribute( storeIndex1, i, attrib );
attrib.set( bitangent.getX(), bitangent.getY(), bitangent.getZ() );
geom.setVertexAttribute( storeIndex2, i, attrib );
}
else if ( storeMode == TangentsStoreMode.TEXTURE_COORDINATES )
{
tex.set( tangent.getX(), tangent.getY(), tangent.getZ() );
geom.setTextureCoordinate( storeIndex1, i, tex );
tex.set( bitangent.getX(), bitangent.getY(), bitangent.getZ() );
geom.setTextureCoordinate( storeIndex2, i, tex );
}
if ( tangents != null )
{
if ( tangents[ i ] == null )
tangents[ i ] = new Vector3f( tangent );
else
tangents[ i ].set( tangent );
}
if ( bitangents != null )
{
if ( bitangents[ i ] == null )
bitangents[ i ] = new Vector3f( bitangent );
else
bitangents[ i ].set( bitangent );
}
}
}
/**
* Calculates the tangents and bitangents for the given GeomContainer.
* The GeometryArray must have at least three texture units where the first one (0) must be filled.
* It also has to have normals.<br>
*
* @param geom
* @param storeMode
* @param storeIndex1
* @param storeIndex2
*/
public void calculateTangents( Geometry geom, TangentsStoreMode storeMode, int storeIndex1, int storeIndex2 )
{
if ( storeMode == null )
throw new IllegalArgumentException( "storeMode must not be null." );
calculateTangents( geom, storeMode, storeIndex1, storeIndex2, null, null );
}
protected TangentsFactory()
{
}
}