/** * 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 java.util.ArrayList; import java.util.List; import org.jagatoo.util.arrays.ArrayUtils; import org.openmali.spatial.TriangleContainer; import org.openmali.spatial.VertexContainer; import org.openmali.spatial.polygons.Triangle; import org.openmali.vecmath2.Colorf; import org.openmali.vecmath2.Point3f; import org.openmali.vecmath2.TexCoord2f; import org.openmali.vecmath2.Tuple3f; import org.openmali.vecmath2.Vector3f; import org.xith3d.scenegraph.Geometry; import org.xith3d.scenegraph.IndexedGeometryArray; import org.xith3d.scenegraph.IndexedTriangleArray; import org.xith3d.scenegraph.Shape3D; import org.xith3d.scenegraph.TriangleArray; /** * This class provides static utility methods for Geometries. * * @author Marvin Froehlich (aka Qudus) * @author Amos Wenger (aka BlueSky) */ public class GeometryUtils { /** * Retrieves the vertex coordinates from a VertexContainer. * * @param vc the source Geometry * @param coords the coords array (must be of correct length) */ public static final void getVertexCoords( VertexContainer vc, Tuple3f[] coords ) { final int n = vc.getVertexCount(); for ( int i = 0; i < n; i++ ) { if ( coords[ i ] == null ) coords[ i ] = new Point3f(); vc.getVertex( i, coords[ i ] ); } } /** * Retrieves the vertex coordinates from a VertexContainer. * * @param vc the source Geometry * * @return the Point3f array of vertex coords */ public static final Point3f[] getVertexCoords( VertexContainer vc ) { final Point3f[] coords = new Point3f[ vc.getVertexCount() ]; getVertexCoords( vc, coords ); return ( coords ); } /** * Retrieves the vertices from a Shape3D. * * @param shape the source Shape3D * * @return the Point3f array of vertex coords */ public static final Point3f[] getVertexCoords( Shape3D shape ) { if ( shape.getGeometry() == null ) return ( null ); return ( getVertexCoords( shape.getGeometry() ) ); } /** * Retrieves the normals from a Geometry. * * @param geom the source Geometry * @param normals the Vector3f array to store normals in */ public static final void getNormals( Geometry geom, Vector3f[] normals ) { final int n = geom.getVertexCount(); for ( int i = 0; i < n; i++ ) { if ( normals[ i ] == null ) normals[ i ] = new Vector3f(); geom.getNormal( i, normals[ i ] ); } } /** * Retrieves the normals from a Geometry. * * @param geom the source Geometry * @return the Vector3f array of normals */ public static final Vector3f[] getNormals( Geometry geom ) { final Vector3f[] normals = new Vector3f[ geom.getVertexCount() ]; getNormals( geom, normals ); return ( normals ); } /** * Retrieves the normals from a Shape3D. * * @param shape the source Shape3D * * @return the Vector3f array of normals */ public static final Vector3f[] getNormals( Shape3D shape ) { if ( shape.getGeometry() == null ) return ( null ); return ( getNormals( shape.getGeometry() ) ); } /** * Merges multiple Geometries into one big TriangleArray. * * @param geoms * * @return the new TriangleArray. */ public static TriangleArray mergeGeometriesTA( Geometry... geoms ) { if ( geoms.length == 0 ) return ( null ); int numTriangles = 0; int features = ~0; for ( int i = 0; i < geoms.length; i++ ) { TriangleContainer tc = (TriangleContainer)geoms[i]; numTriangles += tc.getTriangleCount(); features &= geoms[i].getVertexFormat(); } int numVertices = numTriangles * 3; TriangleArray targetTA = new TriangleArray( numVertices ); Triangle triangle = new Triangle( features, 2 ); int offset = 0; for ( int i = 0; i < geoms.length; i++ ) { TriangleContainer tc = (TriangleContainer)geoms[i]; numTriangles = tc.getTriangleCount(); for ( int t = 0; t < numTriangles; t++ ) { tc.getTriangle( t, triangle ); targetTA.setTriangle( offset + t, triangle ); } offset += numTriangles; } return ( targetTA ); } private static int findVertex( Point3f[] coords, Vector3f[] normals, Colorf[] colors, TexCoord2f[] texCoords, int searchStart, int searchEnd, int sampleIndex ) { int index = ArrayUtils.indexOf( coords, searchStart, searchEnd, coords[ sampleIndex ], false ); if ( index < 0 ) return ( index ); if ( normals != null ) { if ( !normals[index].equals( normals[sampleIndex] ) ) { if ( index < searchEnd ) return ( findVertex( coords, normals, colors, texCoords, index + 1, searchEnd, sampleIndex ) ); return ( -1 ); } } if ( colors != null ) { if ( !colors[index].equals( colors[sampleIndex] ) ) { if ( index < searchEnd ) return ( findVertex( coords, normals, colors, texCoords, index + 1, searchEnd, sampleIndex ) ); return ( -1 ); } } if ( texCoords != null ) { if ( !texCoords[index].equals( texCoords[sampleIndex] ) ) { if ( index < searchEnd ) return ( findVertex( coords, normals, colors, texCoords, index + 1, searchEnd, sampleIndex ) ); return ( -1 ); } } return ( index ); } /** * Merges multiple Geometries into one big IndexedTriangleArray. * * @param geoms * * @return the new IndexedTriangleArray. */ public static IndexedTriangleArray mergeGeometriesITA( Geometry... geoms ) { if ( geoms.length == 0 ) return ( null ); int numTriangles = 0; int features = ~0; for ( int i = 0; i < geoms.length; i++ ) { TriangleContainer tc = (TriangleContainer)geoms[i]; numTriangles += tc.getTriangleCount(); features &= geoms[i].getVertexFormat(); } int numVertices = numTriangles * 3; Point3f[] coords = ( ( features & Geometry.COORDINATES ) != 0 ) ? new Point3f[ numVertices ] : null; Vector3f[] normals = ( ( features & Geometry.NORMALS ) != 0 ) ? new Vector3f[ numVertices ] : null; Colorf[] colors = ( ( features & Geometry.COLORS ) != 0 ) ? new Colorf[ numVertices ] : null; TexCoord2f[] texCoords = ( ( features & Geometry.TEXTURE_COORDINATES ) != 0 ) ? new TexCoord2f[ numVertices ] : null; Triangle triangle = new Triangle( features, 2 ); int offset = 0; for ( int i = 0; i < geoms.length; i++ ) { TriangleContainer tc = (TriangleContainer)geoms[i]; numTriangles = tc.getTriangleCount(); for ( int t = 0; t < numTriangles; t++ ) { tc.getTriangle( t, triangle ); coords[offset + t * 3 + 0] = new Point3f( triangle.getVertexCoordA() ); coords[offset + t * 3 + 1] = new Point3f( triangle.getVertexCoordB() ); coords[offset + t * 3 + 2] = new Point3f( triangle.getVertexCoordC() ); if ( normals != null ) { normals[offset + t * 3 + 0] = new Vector3f( triangle.getVertexNormalA() ); normals[offset + t * 3 + 1] = new Vector3f( triangle.getVertexNormalB() ); normals[offset + t * 3 + 2] = new Vector3f( triangle.getVertexNormalC() ); } if ( colors != null ) { colors[offset + t * 3 + 0] = new Colorf( triangle.getVertexColorA() ); colors[offset + t * 3 + 1] = new Colorf( triangle.getVertexColorB() ); colors[offset + t * 3 + 2] = new Colorf( triangle.getVertexColorC() ); } if ( texCoords != null ) { texCoords[offset + t * 3 + 0] = new TexCoord2f( triangle.getVertexTexCoordA() ); texCoords[offset + t * 3 + 1] = new TexCoord2f( triangle.getVertexTexCoordB() ); texCoords[offset + t * 3 + 2] = new TexCoord2f( triangle.getVertexTexCoordC() ); } } offset += geoms[i].getVertexCount(); } int[] index = new int[ numVertices ]; index[0] = 0; index[1] = 1; index[2] = 2; int numUniqueVertices = 3; for ( int i = 3; i < numVertices; i++ ) { int j = findVertex( coords, normals, colors, texCoords, 0, i - 1, i ); if ( j < 0 ) { index[i] = i; numUniqueVertices++; } else { index[i] = index[j]; } } Point3f[] uniqueCoords = ( ( features & Geometry.COORDINATES ) != 0 ) ? new Point3f[ numUniqueVertices ] : null; Vector3f[] uniqueNormals = ( ( features & Geometry.NORMALS ) != 0 ) ? new Vector3f[ numUniqueVertices ] : null; Colorf[] uniqueColors = ( ( features & Geometry.COLORS ) != 0 ) ? new Colorf[ numUniqueVertices ] : null; TexCoord2f[] uniqueTexCoords = ( ( features & Geometry.TEXTURE_COORDINATES ) != 0 ) ? new TexCoord2f[ numUniqueVertices ] : null; int j = 0; for ( int i = 0; i < numVertices; i++ ) { if ( index[i] == i ) { for ( int k = 0; k < numVertices; k++ ) { if ( index[k] == i ) index[k] = j; } uniqueCoords[j] = coords[i]; if ( normals != null ) uniqueNormals[j] = normals[i]; if ( colors != null ) uniqueColors[j] = colors[i]; if ( normals != null ) uniqueTexCoords[j] = texCoords[i]; j++; } } IndexedTriangleArray targetITA = new IndexedTriangleArray( numUniqueVertices, numVertices ); targetITA.setCoordinates( 0, uniqueCoords ); if ( uniqueNormals != null ) targetITA.setNormals( 0, uniqueNormals ); if ( uniqueColors != null ) targetITA.setColors( 0, uniqueColors ); if ( uniqueTexCoords != null ) targetITA.setTextureCoordinates( 0, 0, uniqueTexCoords ); targetITA.setIndex( index ); return ( targetITA ); } /** * Splits a shape into several parts. * * @param shape * @param maxFacesPerPart * * @return the List of resulting Shape3Ds */ public static List< Shape3D > split( Shape3D shape, int maxFacesPerPart/*, int max*/) { Geometry src = shape.getGeometry(); if ( src == null ) return ( null ); int vertexCount = src.getVertexCount(); float[] origCoords = ( (Geometry)src ).getCoordRefFloat(); float[] origNormals = ( (Geometry)src ).getNormalRefFloat(); //float[] origUVs = ( (Geometry)src ).getTexCoordRefFloat( 0 ); ArrayList< Shape3D > parts = new ArrayList< Shape3D >(); /*divide : */for ( int start = 0; start < vertexCount; start += ( maxFacesPerPart * 3 ) ) { /*if (start > max) { break divide; }*/ if ( start + ( maxFacesPerPart * 3 ) > vertexCount ) { maxFacesPerPart = ( start + ( maxFacesPerPart * 3 ) ) - vertexCount; } float[] coords = new float[ maxFacesPerPart * 3 ]; System.arraycopy( origCoords, start, coords, 0, coords.length ); float[] normals = new float[ maxFacesPerPart * 3 ]; System.arraycopy( origNormals, start, normals, 0, normals.length ); /*float[] UVs = new float[ maxFacesPerPart * 2 ]; System.arraycopy( origUVs, start, UVs, 0, UVs.length );*/ TriangleArray ta = new TriangleArray( coords.length ); ta.setOptimization( shape.getGeometry().getOptimization() ); ta.setCoordinates( 0, coords ); ta.setNormals( 0, normals ); //ta.setTextureCoordinates( 0, 0, UVs ); parts.add( new Shape3D( ta, shape.getAppearance() ) ); } return ( parts ); } /** * Subdivides a Shape's Geometry into n pieces. * Total number of faces after subdivision will be :<br> * initial number of faces * n * number of vertices per face * * @param shape the shape to split * @param n The number of times to subdivide the shape */ public static void subdivide( Shape3D shape, int n ) { // While we have still work, subdivide if ( n > 1 ) { subdivide( shape, --n ); } Geometry geom = shape.getGeometry(); // Here we have the real work if ( geom instanceof TriangleArray ) { TriangleArray src = (TriangleArray)geom; TriangleArray dst = new TriangleArray( src.getVertexCount() * 3 ); float x1 = 0; float x2 = 0; float x3 = 0; float xc = 0; float y1 = 0; float y2 = 0; float y3 = 0; float yc = 0; float z1 = 0; float z2 = 0; float z3 = 0; float zc = 0; // 1st step : vertices float[] srcVerts = src.getCoordRefFloat(); float[] dstVerts = new float[ srcVerts.length * 3 ]; int j = 0; for ( int i = 0; i < srcVerts.length; i += 3 * 3 ) { x1 = srcVerts[ i ]; x2 = srcVerts[ i + 3 ]; x3 = srcVerts[ i + 6 ]; xc = ( x1 + x2 + x3 ) * 0.333f; y1 = srcVerts[ i + 1 ]; y2 = srcVerts[ i + 4 ]; y3 = srcVerts[ i + 7 ]; yc = ( y1 + y2 + y3 ) * 0.333f; z1 = srcVerts[ i + 2 ]; z2 = srcVerts[ i + 5 ]; z3 = srcVerts[ i + 8 ]; zc = ( z1 + z2 + z3 ) * .333f; dstVerts[ j++ ] = x1; dstVerts[ j++ ] = y1; dstVerts[ j++ ] = z1; dstVerts[ j++ ] = x2; dstVerts[ j++ ] = y2; dstVerts[ j++ ] = z2; dstVerts[ j++ ] = xc; dstVerts[ j++ ] = yc; dstVerts[ j++ ] = zc; dstVerts[ j++ ] = x2; dstVerts[ j++ ] = y2; dstVerts[ j++ ] = z2; dstVerts[ j++ ] = x3; dstVerts[ j++ ] = y3; dstVerts[ j++ ] = z3; dstVerts[ j++ ] = xc; dstVerts[ j++ ] = yc; dstVerts[ j++ ] = zc; dstVerts[ j++ ] = x3; dstVerts[ j++ ] = y3; dstVerts[ j++ ] = z3; dstVerts[ j++ ] = x1; dstVerts[ j++ ] = y1; dstVerts[ j++ ] = z1; dstVerts[ j++ ] = xc; dstVerts[ j++ ] = yc; dstVerts[ j++ ] = zc; } dst.setCoordinates( 0, dstVerts ); // 2nd step : normals if ( ( src.getVertexFormat() & Geometry.NORMALS ) != 0 ) { float[] srcNormals = src.getNormalRefFloat(); float[] dstNormals = new float[ srcNormals.length * 3 ]; j = 0; for ( int i = 0; i < srcNormals.length; i += 3 * 3 ) { x1 = srcNormals[ i ]; x2 = srcNormals[ i + 3 ]; x3 = srcNormals[ i + 6 ]; xc = ( x1 + x2 + x3 ) * 0.333f; y1 = srcNormals[ i + 1 ]; y2 = srcNormals[ i + 4 ]; y3 = srcNormals[ i + 7 ]; yc = ( y1 + y2 + y3 ) * 0.333f; z1 = srcNormals[ i + 2 ]; z2 = srcNormals[ i + 5 ]; z3 = srcNormals[ i + 8 ]; zc = ( z1 + z2 + z3 ) * 0.333f; dstNormals[ j++ ] = x1; dstNormals[ j++ ] = y1; dstNormals[ j++ ] = z1; dstNormals[ j++ ] = x2; dstNormals[ j++ ] = y2; dstNormals[ j++ ] = z2; dstNormals[ j++ ] = xc; dstNormals[ j++ ] = yc; dstNormals[ j++ ] = zc; dstNormals[ j++ ] = x2; dstNormals[ j++ ] = y2; dstNormals[ j++ ] = z2; dstNormals[ j++ ] = x3; dstNormals[ j++ ] = y3; dstNormals[ j++ ] = z3; dstNormals[ j++ ] = xc; dstNormals[ j++ ] = yc; dstNormals[ j++ ] = zc; dstNormals[ j++ ] = x3; dstNormals[ j++ ] = y3; dstNormals[ j++ ] = z3; dstNormals[ j++ ] = x1; dstNormals[ j++ ] = y1; dstNormals[ j++ ] = z1; dstNormals[ j++ ] = xc; dstNormals[ j++ ] = yc; dstNormals[ j++ ] = zc; } dst.setNormals( 0, dstNormals ); } // 3rd step : UVs if ( ( ( src.getVertexFormat() & Geometry.TEXTURE_COORDINATES ) != 0 ) && ( src.getTexCoordSize( 0 ) == 2 ) ) { for ( int tu = 0; tu < src.getNumTextureUnits(); tu++ ) { float[] srcTexCoords = src.getTexCoordRefFloat( src.getTexCoordSetMap()[ tu ] ); float[] dstTexCoords = new float[ srcTexCoords.length * 3 ]; j = 0; for ( int i = 0; i < srcTexCoords.length; i += 3 * 2 ) { x1 = srcTexCoords[ i ]; x2 = srcTexCoords[ i + 2 ]; x3 = srcTexCoords[ i + 4 ]; xc = ( x1 + x2 + x3 ) * 0.333f; y1 = srcTexCoords[ i + 1 ]; y2 = srcTexCoords[ i + 3 ]; y3 = srcTexCoords[ i + 5 ]; yc = ( y1 + y2 + y3 ) * 0.333f; dstTexCoords[ j++ ] = x1; dstTexCoords[ j++ ] = y1; dstTexCoords[ j++ ] = x2; dstTexCoords[ j++ ] = y2; dstTexCoords[ j++ ] = xc; dstTexCoords[ j++ ] = yc; dstTexCoords[ j++ ] = x2; dstTexCoords[ j++ ] = y2; dstTexCoords[ j++ ] = x3; dstTexCoords[ j++ ] = y3; dstTexCoords[ j++ ] = xc; dstTexCoords[ j++ ] = yc; dstTexCoords[ j++ ] = x3; dstTexCoords[ j++ ] = y3; dstTexCoords[ j++ ] = x1; dstTexCoords[ j++ ] = y1; dstTexCoords[ j++ ] = xc; dstTexCoords[ j++ ] = yc; } dst.setTextureCoordinates( src.getTexCoordSetMap()[ tu ], 0, 2, dstTexCoords ); } } // 4th step : Color if ( ( ( src.getVertexFormat() & Geometry.COLORS ) != 0 ) && ( !src.hasColorAlpha() ) ) { float[] srcColors = src.getColorRefFloat(); float[] dstColors = new float[ srcColors.length * 3 ]; j = 0; for ( int i = 0; i < srcColors.length; i += 3 * 3 ) { x1 = srcColors[ i ]; x2 = srcColors[ i + 3 ]; x3 = srcColors[ i + 6 ]; xc = ( x1 + x2 + x3 ) * 0.333f; y1 = srcColors[ i + 1 ]; y2 = srcColors[ i + 4 ]; y3 = srcColors[ i + 7 ]; yc = ( y1 + y2 + y3 ) * 0.333f; z1 = srcColors[ i + 2 ]; z2 = srcColors[ i + 5 ]; z3 = srcColors[ i + 8 ]; zc = ( z1 + z2 + z3 ) * 0.333f; dstColors[ j++ ] = x1; dstColors[ j++ ] = y1; dstColors[ j++ ] = z1; dstColors[ j++ ] = x2; dstColors[ j++ ] = y2; dstColors[ j++ ] = z2; dstColors[ j++ ] = xc; dstColors[ j++ ] = yc; dstColors[ j++ ] = zc; dstColors[ j++ ] = x2; dstColors[ j++ ] = y2; dstColors[ j++ ] = z2; dstColors[ j++ ] = x3; dstColors[ j++ ] = y3; dstColors[ j++ ] = z3; dstColors[ j++ ] = xc; dstColors[ j++ ] = yc; dstColors[ j++ ] = zc; dstColors[ j++ ] = x3; dstColors[ j++ ] = y3; dstColors[ j++ ] = z3; dstColors[ j++ ] = x1; dstColors[ j++ ] = y1; dstColors[ j++ ] = z1; dstColors[ j++ ] = xc; dstColors[ j++ ] = yc; dstColors[ j++ ] = zc; } dst.setColors( 0, 3, dstColors ); } // Final step : swap the geoms shape.setGeometry( dst ); } else { throw new Error( "Error! Cannot subdivide a Geometry of type : " + geom.getClass().getName() + "." + " Sorry :( (it's just not implemented yet)" ); } } /** * Generates (flat) TextureCoordinates from the x/y coordinates of the Geometry's vertex-coords. * The TextureCoordinates are directly applied the the Geometry. * * @param geom the Geometry to get the vertex-coords from and to store the tex-coords to * @param texCoords the reference array for the texcoords. Can be <code>null</code>. */ public static final void generateTexCoordsXY( Geometry geom, TexCoord2f[] texCoords ) { if ( ( !geom.hasTextureCoordinates() ) || ( geom.getTexCoordSize( 0 ) != 2 ) ) { throw new UnsupportedOperationException( "The Geometry cannot receive 2D-texture-coords" ); } Point3f coord = Point3f.fromPool(); // Find the range of z values float xmin = +Float.MAX_VALUE; float xmax = -Float.MAX_VALUE; float ymin = +Float.MAX_VALUE; float ymax = -Float.MAX_VALUE; final float n = geom.getVertexCount(); for ( int i = 0; i < n; i++ ) { geom.getVertex( i, coord ); xmin = Math.min( xmin, coord.getX() ); xmax = Math.max( xmax, coord.getX() ); ymin = Math.min( ymin, coord.getY() ); ymax = Math.max( ymax, coord.getY() ); } final float xscale = 1.0f / ( xmax - xmin ); final float yscale = 1.0f / ( ymax - ymin ); TexCoord2f texCoord = new TexCoord2f(); // Generate the coordinates for ( int i = 0; i < n; i++ ) { geom.getVertex( i, coord ); texCoord.setS( ( coord.getX() - xmin ) * xscale ); texCoord.setT( ( coord.getY() - ymin ) * yscale ); if ( texCoords != null ) { if ( texCoords[ i ] == null ) texCoords[ i ] = new TexCoord2f(); texCoords[ i ].set( texCoord ); } geom.setTextureCoordinate( 0, i, texCoord ); } Point3f.toPool( coord ); } /** * Generates (flat) TextureCoordinates from the x/z coordinates of the Geometry's vertex-coords. * The TextureCoordinates are directly applied the the Geometry. * * @param geom the Geometry to get the vertex-coords from and to store the tex-coords to * @param texCoords the reference array for the texcoords. Can be <code>null</code>. */ public static final void generateTexCoordsXZ( Geometry geom, TexCoord2f[] texCoords ) { if ( ( !geom.hasTextureCoordinates() ) || ( geom.getTexCoordSize( 0 ) != 2 ) ) { throw new UnsupportedOperationException( "The Geometry cannot receive texture-coords" ); } Point3f coord = Point3f.fromPool(); // Find the range of z values float xmin = +Float.MAX_VALUE; float xmax = -Float.MAX_VALUE; float zmin = +Float.MAX_VALUE; float zmax = -Float.MAX_VALUE; final float n = geom.getVertexCount(); for ( int i = 0; i < n; i++ ) { geom.getVertex( i, coord ); xmin = Math.min( xmin, coord.getX() ); xmax = Math.max( xmax, coord.getX() ); zmin = Math.min( zmin, coord.getZ() ); zmax = Math.max( zmax, coord.getZ() ); } final float xscale = 1.0f / ( xmax - xmin ); final float zscale = 1.0f / ( zmax - zmin ); TexCoord2f texCoord = new TexCoord2f(); // Generate the coordinates for ( int i = 0; i < n; i++ ) { geom.getVertex( i, coord ); texCoord.setS( ( coord.getX() - xmin ) * xscale ); texCoord.setT( ( coord.getY() - zmin ) * zscale ); if ( texCoords != null ) { if ( texCoords[ i ] == null ) texCoords[ i ] = new TexCoord2f(); texCoords[ i ].set( texCoord ); } geom.setTextureCoordinate( 0, i, texCoord ); } Point3f.toPool( coord ); } /** * Generates (flat) TextureCoordinates from the x/y coordinates of the Geometry's vertex-coords. * The TextureCoordinates are directly applied the the Geometry. * * @param geom the Geometry to get the vertex-coords from and to store the tex-coords to * @param texCoords the reference array for the texcoords. Can be <code>null</code>. */ public static final void generateTexCoordsZY( Geometry geom, TexCoord2f[] texCoords ) { if ( ( !geom.hasTextureCoordinates() ) || ( geom.getTexCoordSize( 0 ) != 2 ) ) { throw new UnsupportedOperationException( "The Geometry cannot receive texture-coords" ); } Point3f coord = Point3f.fromPool(); // Find the range of z values float zmin = +Float.MAX_VALUE; float zmax = -Float.MAX_VALUE; float ymin = +Float.MAX_VALUE; float ymax = -Float.MAX_VALUE; final float n = geom.getVertexCount(); for ( int i = 0; i < n; i++ ) { geom.getVertex( i, coord ); zmin = Math.min( zmin, coord.getZ() ); zmax = Math.max( zmax, coord.getZ() ); ymin = Math.min( ymin, coord.getY() ); ymax = Math.max( ymax, coord.getY() ); } final float zscale = 1.0f / ( zmax - zmin ); final float yscale = 1.0f / ( ymax - ymin ); TexCoord2f texCoord = new TexCoord2f(); // Generate the coordinates for ( int i = 0; i < n; i++ ) { geom.getVertex( i, coord ); texCoord.setS( ( coord.getZ() - zmin ) * zscale ); texCoord.setT( ( coord.getY() - ymin ) * yscale ); if ( texCoords != null ) { if ( texCoords[ i ] == null ) texCoords[ i ] = new TexCoord2f(); texCoords[ i ].set( texCoord ); } geom.setTextureCoordinate( 0, i, texCoord ); } Point3f.toPool( coord ); } /** * Computes smooth vertex-indices and writes them into the vertex-coord-data. * * TODO: optimize! * * @param geom * @param initialIndex * @param indexCount * @param flipNormals */ public static void computeVertexNormals( Geometry geom, int initialIndex, int indexCount, boolean flipNormals ) { final float EPSILON = 0.001f; Point3f[] coords = new Point3f[ geom.getValidVertexCount() ]; for ( int i = 0; i < coords.length; i++ ) { coords[ geom.getInitialVertexIndex() + i ] = new Point3f(); } geom.getCoordinates( 0, coords ); final int[] indices; if ( geom instanceof IndexedGeometryArray ) { final IndexedGeometryArray idxGeom = (IndexedGeometryArray)geom; indices = idxGeom.getIndex(); if ( initialIndex < 0 ) initialIndex = idxGeom.getInitialIndexIndex(); if ( indexCount < 0 ) indexCount = idxGeom.getValidIndexCount(); } else { indices = new int[ coords.length ]; if ( initialIndex < 0 ) initialIndex = 0; if ( indexCount < 0 ) indexCount = indices.length; for ( int i = 0; i < indices.length; i++ ) { indices[ i ] = i; } /* for ( int i = 0; i < coords.length; i++ ) { if ( indices[ i ] == i ) { for ( int j = i + 1; j < coords.length; j++ ) { if ( indices[ j ] == j ) { if ( coords[ i ].distance( coords[ j ] ) <= EPSILON ) { indices[ j ] = indices[ i ]; } } } } } */ } Vector3f[] normals = new Vector3f[ coords.length ]; for ( int i = 0; i < normals.length; i++ ) { normals[ i ] = new Vector3f(); } geom.getNormals( 0, normals ); for ( int i = 0; i < normals.length; i++ ) { normals[ i ].normalize(); } Vector3f[] newNormals = new Vector3f[ coords.length ]; for ( int i = 0; i < newNormals.length; i++ ) { newNormals[ i ] = new Vector3f( 0f, 0f, 0f ); } TriangleContainer trianCont = (TriangleContainer)geom; Triangle[] triangles = new Triangle[ trianCont.getTriangleCount() ]; for ( int i = 0; i < triangles.length; i++ ) { triangles[ i ] = new Triangle(); trianCont.getTriangle( i, triangles[ i ] ); } Vector3f normal = new Vector3f(); for ( int i = initialIndex; i < initialIndex + indexCount; i++ ) { int idx = indices[ i ]; float angleSum = 0f; for ( int j = 0; j < triangles.length; j++ ) { if ( coords[ idx ].distance( triangles[ j ].getVertexCoordA() ) <= EPSILON ) { angleSum += triangles[ j ].getAngleA(); } else if ( coords[ idx ].distance( triangles[ j ].getVertexCoordB() ) <= EPSILON ) { angleSum += triangles[ j ].getAngleB(); } else if ( coords[ idx ].distance( triangles[ j ].getVertexCoordC() ) <= EPSILON ) { angleSum += triangles[ j ].getAngleC(); } } for ( int j = 0; j < triangles.length; j++ ) { if ( coords[ idx ].distance( triangles[ j ].getVertexCoordA() ) <= EPSILON ) { triangles[ j ].getFaceNormal( normal ); final float angle = triangles[ j ].getAngleA(); normal.scale( angle / angleSum ); newNormals[ idx ].add( normal ); } else if ( coords[ idx ].distance( triangles[ j ].getVertexCoordB() ) <= EPSILON ) { triangles[ j ].getFaceNormal( normal ); final float angle = triangles[ j ].getAngleB(); normal.scale( angle / angleSum ); newNormals[ idx ].add( normal ); } else if ( coords[ idx ].distance( triangles[ j ].getVertexCoordC() ) <= EPSILON ) { triangles[ j ].getFaceNormal( normal ); final float angle = triangles[ j ].getAngleC(); normal.scale( angle / angleSum ); newNormals[ idx ].add( normal ); } } } if ( flipNormals ) { for ( int i = 0; i < newNormals.length; i++ ) { newNormals[ i ].normalize(); newNormals[ i ].negate(); } } else { for ( int i = 0; i < newNormals.length; i++ ) { //System.out.println( newNormals[ i ] ); newNormals[ i ].normalize(); } } geom.setNormals( 0, newNormals ); } /** * Computes smooth vertex-indices and writes them into the vertex-coord-data. * * @param geom * @param initialIndex * @param indexCount */ public static void computeVertexNormals( Geometry geom, int initialIndex, int indexCount ) { computeVertexNormals( geom, initialIndex, indexCount, false ); } /** * Computes smooth vertex normals and writes them into the vertex-coord-data. * * @param geom * @param initialIndex * @param indexCount * @param flipNormals */ @SuppressWarnings( "unused" ) private static void _computeVertexNormals( Geometry geom, int initialIndex, int indexCount, boolean flipNormals ) { final float EPSILON = 0.001f; Point3f[] coords = new Point3f[ geom.getValidVertexCount() ]; for ( int i = 0; i < coords.length; i++ ) { coords[ geom.getInitialVertexIndex() + i ] = new Point3f(); } geom.getCoordinates( 0, coords ); final int[] indices; if ( geom instanceof IndexedGeometryArray ) { final IndexedGeometryArray idxGeom = (IndexedGeometryArray)geom; indices = idxGeom.getIndex(); if ( initialIndex < 0 ) initialIndex = idxGeom.getInitialIndexIndex(); if ( indexCount < 0 ) indexCount = idxGeom.getValidIndexCount(); } else { indices = new int[ coords.length ]; if ( initialIndex < 0 ) initialIndex = 0; if ( indexCount < 0 ) indexCount = indices.length; for ( int i = 0; i < indices.length; i++ ) { indices[ i ] = i; } for ( int i = 0; i < coords.length; i++ ) { if ( indices[ i ] == i ) { for ( int j = i + 1; j < coords.length; j++ ) { if ( indices[ j ] == j ) { if ( coords[ i ].distance( coords[ j ] ) <= EPSILON ) { indices[ j ] = indices[ i ]; } } } } } } Vector3f[] normals = new Vector3f[ coords.length ]; for ( int i = 0; i < normals.length; i++ ) { normals[ i ] = new Vector3f(); } geom.getNormals( 0, normals ); for ( int i = 0; i < normals.length; i++ ) { normals[ i ].normalize(); } Vector3f[] newNormals = new Vector3f[ coords.length ]; for ( int i = 0; i < newNormals.length; i++ ) { newNormals[ i ] = new Vector3f( 0f, 0f, 0f ); } TriangleContainer trianCont = (TriangleContainer)geom; Triangle[] triangles = new Triangle[ trianCont.getTriangleCount() ]; for ( int i = 0; i < triangles.length; i++ ) { triangles[ i ] = new Triangle(); trianCont.getTriangle( i, triangles[ i ] ); } for ( int i = initialIndex; i < initialIndex + indexCount; i++ ) { int idx= indices[ i ]; for ( int j = 0; j < triangles.length; j++ ) { if ( coords[ idx ].distance( triangles[ j ].getVertexCoordA() ) <= EPSILON ) { triangles[ j ].getFaceNormalACAB( newNormals[ idx ] ); } else if ( coords[ idx ].distance( triangles[ j ].getVertexCoordB() ) <= EPSILON ) { triangles[ j ].getFaceNormalBABC( newNormals[ idx ] ); } else if ( coords[ idx ].distance( triangles[ j ].getVertexCoordC() ) <= EPSILON ) { triangles[ j ].getFaceNormalCBCA( newNormals[ idx ] ); } } } if ( flipNormals ) { for ( int i = 0; i < newNormals.length; i++ ) { newNormals[ i ].negate(); } } geom.setNormals( 0, newNormals ); } /** * Computes smooth vertex-indices and writes them into the vertex-coord-data. * * @param geom * @param flipNormals */ public static void computeVertexNormals( Geometry geom, boolean flipNormals ) { computeVertexNormals( geom, -1, -1, flipNormals ); } /** * Computes smooth vertex-indices and writes them into the vertex-coord-data. * * @param geom */ public static void computeVertexNormals( Geometry geom ) { computeVertexNormals( geom, -1, -1 ); } }