Package org.mt4j.util

Source Code of org.mt4j.util.TriangleNormalGenerator

/***********************************************************************
* mt4j Copyright (c) 2008 - 2009 C.Ruff, Fraunhofer-Gesellschaft All rights reserved.
*   This program is free software: you can redistribute it and/or modify
*   it under the terms of the GNU General Public License as published by
*   the Free Software Foundation, either version 3 of the License, or
*   (at your option) any later version.
*
*   This program is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*   GNU General Public License for more details.
*
*   You should have received a copy of the GNU General Public License
*   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
***********************************************************************/
package org.mt4j.util;

import java.util.ArrayList;

import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.SimpleLayout;
import org.mt4j.components.visibleComponents.GeometryInfo;
import org.mt4j.util.math.ToolsGeometry;
import org.mt4j.util.math.ToolsMath;
import org.mt4j.util.math.Vector3D;
import org.mt4j.util.math.Vertex;

import processing.core.PApplet;

/**
* This class can be used to generate normals for indexed triangles geometry.
* <br><li>It duplicates a vertex that has more than 1 texture coordinate.
* So texture with indexed geometry can still be used.
* <br><li>It also duplicates equal vertices that get assigned different normals in the calculation process according
* to the face they belong to.
* <p>
*
* @author C.Ruff
*/
public class TriangleNormalGenerator {
 
  /** The Constant logger. */
  private static final Logger logger = Logger.getLogger(TriangleNormalGenerator.class.getName());
  static{
    logger.setLevel(Level.ERROR);
    SimpleLayout l = new SimpleLayout();
    ConsoleAppender ca = new ConsoleAppender(l);
    logger.addAppender(ca);
  }
 
  /** The null vect. */
  private Vertex nullVect;
 
  /** Use these for console debug info. */
//  private boolean debug;

  /** The use normals equal to face. */
  private boolean useNormalsEqualToFace;

  /** The use equal neighbor normals again. */
  private boolean useEqualNeighborNormalsAgain;
 
  /**
   * Constructor.
   */
  public TriangleNormalGenerator(){
    nullVect = new Vertex(0,0,0, -1, -1);
    useNormalsEqualToFace       = true;
    useEqualNeighborNormalsAgain   = true;
  }
 
 
  /**
   * Generates smooth normals for a triangle geometry array.
   *
   * @param pa the pa
   * @param geometryInfo the geometry info
   *
   * @return the geometry info
   *
   * the geometry array with normals
   */
  public GeometryInfo generateTriangleNormals(PApplet pa, GeometryInfo geometryInfo){
    return this.generateTriangleNormals(pa, geometryInfo, 180);
  }
 
 
  /**
   * Generates normals for the provided geometry info according to the crease angle.
   * <br>A crease angle of 180 will result in a all smoothed model, smoothing each vertex normal
   * across all its neighbor faces' face normals.
   * <br>A crease angle of zero (0) will result in a flat shaded geometry. Only face normals will
   * be used then.
   * <br>A crease angle of 89 will create hard edges at 90 degree angle faces and smooth faces with
   * less then 90 degree normals. This would be ideal to generate normals for a cube.
   * <br>The best crease angle for a model has to be found by testing different angles.
   *
   * <br><strong>Note:</strong>The geometry has to represent a TRIANGLES array!
   * <br><strong>Note:</strong>The stroke color information gets lost during the process and is reset
   * to the default color. Set it again if needed with <code>setStrokeColorAll</code>.
   *
   * @param pa the pa
   * @param geometryInfo the geometry info
   * @param creaseAngle the crease angle
   *
   * @return the geometry info
   *
   * the geometry array with normals
   */
  public GeometryInfo generateTriangleNormals(PApplet pa, GeometryInfo geometryInfo, float creaseAngle ){
    Vertex[] vertices = geometryInfo.getVertices();
   
    //Gen texcoord array
    float[][] texCoords = new float[vertices.length][2];
    for (int i = 0; i < vertices.length; i++) {
      Vertex v = vertices[i];
      texCoords[i][0] = v.getTexCoordU();
      texCoords[i][1] = v.getTexCoordV();
    }
   
    //Gen or get indices array
    int[] indices = null;
    if (!geometryInfo.isIndexed()){
      indices = new int[vertices.length];
      for (int i = 0; i < vertices.length; i++) {
        indices[i] = i;
      }
    }else{
      indices = geometryInfo.getIndices();
    }
   
    //Gen texcoord array as same as indices array
    int[] texIndices = new int[indices.length];
    for (int i = 0; i < indices.length; i++) {
      texIndices[i] = indices[i];
    }
   
    //Generate normals
    GeometryInfo geom = null;
    if (creaseAngle == 180){
      geom = this.generateSmoothNormals(pa, vertices, indices, texCoords, texIndices, creaseAngle, false, false);
    }else{
      geom = this.generateCreaseAngleNormals(pa, vertices, indices, texCoords, texIndices, creaseAngle, false, false);
    }
   
    //Reset indices if they werent set before,too
    //Reconstruct the passed in geometryinfo
    if (!geometryInfo.isIndexed()){
      geometryInfo.reconstruct(geom.getVertices(), geom.getNormals(), null, true, false, null);
    }else{
      geometryInfo.reconstruct(geom.getVertices(), geom.getNormals(), geom.getIndices(), true, false, null);
    }
    geom = null;
    return geometryInfo;
  }

 
 
 
  /**
   * Generates triangle normals, smoothed acroos all neighbor faces.
   * <br>Also dissolves multiple texturecoordinates belonging to the same vertex by
   * duplicating them.
   *
   * @param pa the pa
   * @param originalVertices the original vertices
   * @param originalIndices the original indices
   * @param originalTexCoords the original tex coords
   * @param originalTexIndices the original tex indices
   * @param creaseAngle the crease angle
   * @param flipTextureY the flip texture y
   * @param flipTextureX the flip texture x
   *
   * @return the geometry info
   */
  public GeometryInfo generateSmoothNormals(PApplet pa, Vertex[] originalVertices, int[] originalIndices, float[][] originalTexCoords, int[] originalTexIndices, float creaseAngle, boolean flipTextureY, boolean flipTextureX){
      int newDuplicatesWithDiffTexCoordsCreated   = 0;
      int newDuplicatesWithDiffNormalCreated     = 0;
     
      logger.debug("-> Loading all smoothed model.");
     
      ArrayList<VertexData>   vertexDatas = new ArrayList<VertexData>(originalVertices.length);
      ArrayList<MyFace>     faces     = new ArrayList<MyFace>(Math.round(originalIndices.length/3));
     
      //Init and fill vertexdata list with as many as vertices
      for (int i = 0; i < originalVertices.length; i++) {
        //Vertex v = vertices[i];
        VertexData vd = new VertexData();
        vd.setArrayIndex(i);
        vertexDatas.add(vd);
      }
     
     
      int pp0 = 0;
      int pp1 = 0;
      int pp2 = 0;
     
      int tIndexPP0 = 0;
      int tIndexPP1 = 0;
      int tIndexPP2 = 0;
      //GO through indices array and create a face for every three indices (must be triangle array!)
      for (int i = 0; i < originalIndices.length/3; i++) {
        //int currentIndex = indices[i];
       
        //Next 3 vertex indices as the new faces pointers
        pp0 = originalIndices[i*3];
        pp1 = originalIndices[i*3+1];
        pp2 = originalIndices[i*3+2];
       
        if (originalTexCoords.length > 0){
          //Next 3 texture texture indices //(vertex and texture indices indexed in the same order)
          tIndexPP0 = originalTexIndices[i*3];
          tIndexPP1 = originalTexIndices[i*3+1];
          tIndexPP2 = originalTexIndices[i*3+2];
        }
       
       
        MyFace myFace = new MyFace();
        myFace.p0 = pp0;
        myFace.p1 = pp1;
        myFace.p2 = pp2;
       
        int vertexPointer = pp0;
        int texturePointer = tIndexPP0;
        for (int j = 0; j < 3; j++) {
          if      (j == 0)  {
            vertexPointer   = pp0;
            texturePointer  = tIndexPP0;
          }
          else if(j == 1){
            vertexPointer   = pp1;
            texturePointer  = tIndexPP1;
          }
          else if(j == 2){
            vertexPointer   = pp2;
            texturePointer  = tIndexPP2;
          }
         
          //Get the vertexdata at the index, the face points to
          VertexData myVertexData   = vertexDatas.get(vertexPointer);
         
          Vertex newVertex = new Vertex(
              originalVertices[vertexPointer].x,
              originalVertices[vertexPointer].y,
              originalVertices[vertexPointer].z,
              originalVertices[vertexPointer].getR(),
              originalVertices[vertexPointer].getG(),
              originalVertices[vertexPointer].getB(),
              originalVertices[vertexPointer].getA()
          );
         
 
//          Create texcoords and add to vertex
          float[] tex = new float[2];
          if (originalTexCoords.length > 0){
            tex[0] = originalTexCoords[texturePointer][0];
            tex[1] = originalTexCoords[texturePointer][1];
           
            if (flipTextureY){
              tex[1] = 1.0f-tex[1];
            }
           
            if (flipTextureX){
              tex[0] = 1.0f-tex[0];
            }
           
            newVertex.setTexCoordU(tex[0]);
            newVertex.setTexCoordV(tex[1]);
          }

          //Check if the vertex data at the pointer is empty thats the case before first time a vertex is added to it here
          if (myVertexData.getVertex() == null){
            myVertexData.setVertex(newVertex);
            myVertexData.addNeighborFace(myFace);
            logger.debug("vdP" + j + " vertex in vertexData not initialzied -> set it");
//            logger.debug("vdP0 is empty -> adding first vertex, texture tuple");
          }else{//Vertex data at index already contains one or more vertices
           
            //Check if the vertex data at the index contains a vertex that is equal to the new vertex and its texture information
            //-> if true, just add the vertex to that vertexdata and the face it also is in
            //-> if false, check if a duplicate vertexdata is registered in the vertexdata the face points to
            //    -> if true, add the vertex to the duplicate, change face index to the duplicates index
            //    -> if false, create new vertexdata at end of list, add the vertex and texcoords of the vertex, and register
            //      it as a duplicate in the original vertexdata, change the face index to the new duplicates index
            if (myVertexData.equalsVertex(newVertex)){
              //Register das face mit dem vertex data
              myVertexData.addNeighborFace(myFace);
              logger.debug("vdP" + j + "already CONTAINS a vertex with same coords and texture information -> do nothing, just a the current face to its neighborlist");
            }else{
             
              //Check if already duplicates were created, maybe with the same vertex and texture information,
              //then we have to add the vertex to that vertexdata rather than creating a new one
              int duplicateIndex = myVertexData.getVertDuplicateSameVertDiffTextureCoordListIndex(newVertex);
             
              if (duplicateIndex != -1){ //Es gibt schon ein duplicate mit gleichen tex coords wie dieses hier, adde bei duplicateIndex
                //Change face pointer of p0 to the duplicates index
                if      (j == 0myFace.p0 = duplicateIndex;
                else if(j == 1) myFace.p1 = duplicateIndex;
                else if(j == 2myFace.p2 = duplicateIndex;
               
                //wenn wir orginal hinzuf�gen wird auch allen duplicates neighbor face zugef�gt.
                myVertexData.addNeighborFace(myFace);
               
                logger.debug("vdP" + j + "has different texture coordiantes but a already created duplicate has the same -> change this face pointer to the duplicate one");
              }else{//there was no duplicate created with the same texture coords yet -> create one!
               
                //Neuen Vertex machen, an original vertices list anh�ngen, neuen index in face auf diesen setzen, face adden
                VertexData newVertexData = new VertexData();
               
                //Add the vertex information to the newly created vertexdata
                newVertexData.setVertex(newVertex);
               
                //Get new index at end of vertex list
                int newIndexOfNewVertexData = vertexDatas.size();
               
                //Change the index of the face to the index the new Vdata is created at
                if      (j == 0myFace.p0 = newIndexOfNewVertexData;
                else if(j == 1) myFace.p1 = newIndexOfNewVertexData;
                else if(j == 2myFace.p2 = newIndexOfNewVertexData;
               
                //Tell the vertexdata the index it is in the overall data
                newVertexData.setArrayIndex(newIndexOfNewVertexData);
               
                //Add new vertex data at the end of the list of all vertexdatas
                vertexDatas.add(newVertexData);
               
                //Inform the original vertexdata, that a duplicate with diff tex coords was created and register it
                myVertexData.registerCreatedDuplicateDiffTexCoords(newVertexData);
               
                //Adde face zu orginal face -> damit wird auch duplicates und dem neuen hinzugef�gt,
                //wenn wir es vorher mit registerDuplicate am orginal registiert haben!
                myVertexData.addNeighborFace(myFace);
               
                //copy the face list the vertex is contained in of the original
                //to the duplicate (for normal generation later, so they are still neighbors)!
                newVertexData.addNeighborFaces(myVertexData.getFacesContainedIn());
                logger.debug("vdP" + j + "isnt empty but DOESENT CONTAIN (also no duplicate contains) a vertex with same coords and texture information -> creating new V.D. at: " + newIndexOfNewVertexData);
                newDuplicatesWithDiffTexCoordsCreated++;
              }//end if duplicate exists
            }//end if vertexdata already contains Vnew
          }//end if vertexdata is empty
        }//end for p0,p1,p2
       
        //Calculate the face's normal vector
        myFace.calcFaceNormal(vertexDatas);
       
        myFace.index = faces.size();
       
        //Add face to facelist
        faces.add(myFace);
      }
     
      logger.debug("-> Processed duplicate vertex/texture points.");
     
     
      //Create arrays
      Vertex[] newVertices   = new Vertex[vertexDatas.size()];
      Vector3D[] normals    = new Vector3D[vertexDatas.size()];
      int[] newIndices    = new int[faces.size()*3];
     
     
      /*
       * Go through the final faces list and fill vertex/newIndices/normal arrays
       */
      for (int j = 0; j < faces.size(); j++) {
        MyFace myFace = faces.get(j);
        //Get vertex pointers of face to newVertices in newVertices list
        int indexP0 = myFace.p0;
        int indexP1 = myFace.p1;
        int indexP2 = myFace.p2;
       
        //Use pointers as newIndices and fill newIndices array
        newIndices[j*3]    = indexP0;
        newIndices[j*3+1= indexP1;
        newIndices[j*3+2= indexP2;
       
        //Get the vertexdatas out of the list with the pointers
        VertexData vdP0 = vertexDatas.get(indexP0);
        VertexData vdP1 = vertexDatas.get(indexP1);
        VertexData vdP2 = vertexDatas.get(indexP2);
       
        //Get the vertex out of the vdata
        Vertex v0 = vdP0.getVertex();
        Vertex v1 = vdP1.getVertex();
        Vertex v2 = vdP2.getVertex();
       
        //Put newVertices from vertexdata list in vertex array for the geometry array
        newVertices[indexP0] = v0;
        newVertices[indexP1] = v1;
        newVertices[indexP2] = v2;
       
        //Get the faces normal
//        Vector3D faceNormal = Tools3D.getNormal(v0, v1, v2, true); //myFace.normal;//
//        Vector3D faceNormal = myFace.normal.getCopy();
//        faceNormal.normalizeLocal();
       
        Vector3D faceNormal = myFace.normalNormalized;
       
        Vector3D normalP0;
        if (vdP0.getNeighborFaces().size() > 1){
//          logger.debug("Calcing v normal of face: " + myFace.index + " P0");
          normalP0 = vdP0.calcVertexNormalAllNeighbors();
        }else{
          normalP0 = faceNormal;
        }
       
        Vector3D normalP1;
        if (vdP1.getNeighborFaces().size() > 1){
//          logger.debug("Calcing v normal of face: " + myFace.index + " P1");
          normalP1 = vdP1.calcVertexNormalAllNeighbors();
        }else{
          normalP1 = faceNormal;
        }
       
        Vector3D normalP2;
        if (vdP2.getNeighborFaces().size() > 1){
//          logger.debug("Calcing v normal of face: " + myFace.index + " P2");
          normalP2 = vdP2.calcVertexNormalAllNeighbors();
        }else{
          normalP2 = faceNormal;
        }
       
        normals[indexP0] = normalP0;
        normals[indexP1] = normalP1;
        normals[indexP2] = normalP2;
       
        ////Normalen debug linien sehen mit normals invertiert richtig
        //aus, aber model wird falsch geshaded!
        /*
        normals[indexP0].scale(-1);
        normals[indexP1].scale(-1);
        normals[indexP2].scale(-1);
        */
      }
     
      //Leider n�tig? da manche models newVertices enthalten, die in den faces garnicht referenziert werden, quasi tote verts
      for (int j = 0; j < newVertices.length; j++) {
        if (newVertices[j] == null){
          newVertices[j] = nullVect;
        }
        //Normals array should be same length as verts, check for nulls here
        if (normals[j] == null){
          normals[j] = nullVect;
        }
        //System.out.print(normals[j] + " - ");
      }
     
      logger.debug("----------------------------------------------------------------------------------");
      logger.debug("New duplicates of same vertices with different texture coordinates created: " + newDuplicatesWithDiffTexCoordsCreated);
      logger.debug("New duplicates of same vertices with different normal created: " + newDuplicatesWithDiffNormalCreated);
      logger.debug("Original number of vertices: " + originalVertices.length);
      logger.debug("Final number of vertices: " + vertexDatas.size());
      logger.debug("Original number of faces: " + originalIndices.length/3);
      logger.debug("Final number of faces: " + faces.size());
      logger.debug("Original number of indices: " + originalIndices.length);
      logger.debug("Final number of indices: " + newIndices.length);
      logger.debug("Final number of normals: " + normals.length);
      logger.debug("----------------------------------------------------------------------------------");
     
      if (newVertices.length > 2 && faces.size() > 0){
        // Create a geometryInfo with all vertices of the mesh
        GeometryInfo geometryInfo = new GeometryInfo(pa, newVertices, newIndices);
 
        //Set the normals for the geometry
        geometryInfo.setNormals(normals, true, false);
 
        //Clean up a bit
        vertexDatas = null;
        faces    = null;
       
        return geometryInfo;
      }
      return null;
    }



 
 
  /**
   * Generates normals for the provided geometry data according to the crease angle.
   * <br>A crease angle of 180 will result in a all smoothed model, smoothing each vertex normal
   * across all its neighbor faces' face normals.
   * <br>A crease angle of zero (0) will result in a flat shaded geometry. Only face normals will
   * be used then.
   * <br>A crease angle of 89 will create hard edges at 90 degree angle faces and smooth faces with
   * less then 90 degree normals. This would be ideal to generate normals for a cube.
   * <br>The best crease angle for a model has to be found by testing.
   *
   * @param pa the pa
   * @param originalVertices the original vertices
   * @param originalIndices the original indices
   * @param originalTexCoords the original tex coords
   * @param originalTexIndices the original tex indices
   * @param creaseAngle the crease angle
   * @param flipTextureY the flip texture y
   * @param flipTextureX the flip texture x
   *
   * @return the geometry info
   *
   * indexed, geometry info with normals
   */
  public GeometryInfo generateCreaseAngleNormals(PApplet pa, Vertex[] originalVertices, int[] originalIndices, float[][] originalTexCoords, int[] originalTexIndices, float creaseAngle, boolean flipTextureY, boolean flipTextureX){
      int newDuplicatesWithDiffTexCoordsCreated   = 0;
      int newDuplicatesWithDiffNormalCreated     = 0;
     
      boolean useNormailizedNormalsForAdding = true;

      logger.debug("-> Loading  model with a crease angle: " + creaseAngle);
     
      float creaseAngleRad = (float)Math.toRadians(creaseAngle);
//      float creaseAngleRad =
      float creaseCosinus = (float)Math.cos(creaseAngle);
     
      nullVect   = new Vertex(0,0,0, -1, -1);
      ArrayList<VertexData>   vertexDatas = new ArrayList<VertexData>();
      ArrayList<MyFace>     faces     = new ArrayList<MyFace>();
     
      //Init and fill vertexdata list with as many as vertices
      for (int i = 0; i < originalVertices.length; i++) {
        //Vertex v = vertices[i];
        VertexData vd = new VertexData();
        vd.setArrayIndex(i);
        vertexDatas.add(vd);
      }
     
  /////////////////////////////////////
     
      //GO through indices array and create a face for every three indices (must be triangle array!)
      int pp0 = 0;
      int pp1 = 0;
      int pp2 = 0;
     
      int tIndexPP0 = 0;
      int tIndexPP1 = 0;
      int tIndexPP2 = 0;
     
      for (int i = 0; i < originalIndices.length/3; i++) {
        //int currentIndex = indices[i];
       
        //Next 3 vertex indices as the new faces pointers for our face
        pp0 = originalIndices[i*3];
        pp1 = originalIndices[i*3+1];
        pp2 = originalIndices[i*3+2];
       
        if (originalTexCoords.length > 0){
          //Next 3 texture texture indices //(vertex and texture indices indexed in the same order)
          tIndexPP0 = originalTexIndices[i*3];
          tIndexPP1 = originalTexIndices[i*3+1];
          tIndexPP2 = originalTexIndices[i*3+2];
        }
       
       
        MyFace myFace = new MyFace();
        myFace.p0 = pp0;
        myFace.p1 = pp1;
        myFace.p2 = pp2;
       
        int vertexPointer = pp0;
        int texturePointer = tIndexPP0;
        for (int j = 0; j < 3; j++) {
          if      (j == 0)  {
            vertexPointer   = pp0;
            texturePointer  = tIndexPP0;
          }
          else if(j == 1){
            vertexPointer   = pp1;
            texturePointer  = tIndexPP1;
          }
          else if(j == 2){
            vertexPointer   = pp2;
            texturePointer  = tIndexPP2;
          }
         
          //Get the vertexdata at the index, the face points to
          VertexData myVertexData   = vertexDatas.get(vertexPointer);
         
          Vertex newVertex = new Vertex(
              originalVertices[vertexPointer].x,
              originalVertices[vertexPointer].y,
              originalVertices[vertexPointer].z,
              originalVertices[vertexPointer].getR(),
              originalVertices[vertexPointer].getG(),
              originalVertices[vertexPointer].getB(),
              originalVertices[vertexPointer].getA()
          );
 
          //Create texcoords and add to vertex
          float[] tex = new float[2];
          if (originalTexCoords.length > 0){
            tex[0] = originalTexCoords[texturePointer][0];
            tex[1] = originalTexCoords[texturePointer][1];
           
            if (flipTextureY){
              tex[1] = 1.0f-tex[1];
            }
           
            if (flipTextureX){
              tex[0] = 1.0f-tex[0];
            }
           
            newVertex.setTexCoordU(tex[0]);
            newVertex.setTexCoordV(tex[1]);
          }

          //Check if the vertex data at the pointer is empty thats the case before first time a vertex is added to it here
          if (myVertexData.getVertex() == null){
            myVertexData.setVertex(newVertex);
            myVertexData.addNeighborFace(myFace);
            logger.debug("vdP" + j + " vertex in vertexData not initialized -> set it: " + newVertex);
          }else{//Vertex data at index already contains one or more vertices
           
            //Check if the vertex data at the index contains a vertex that is equal to the new vertex and its texture information
            //-> if true, just add the vertex to that vertexdata and the face it also is in
            //-> if false, check if a duplicate vertexdata is registered in the vertexdata the face points to
            //    -> if true, add the vertex to the duplicate, change face index to the duplicates index
            //    -> if false, create new vertexdata at end of list, add the vertex and texcoords of the vertex, and register
            //      it as a duplicate in the original vertexdata, change the face index to the new duplicates index
            if (myVertexData.equalsVertex(newVertex)){
              //Register das face mit dem vertex data
              myVertexData.addNeighborFace(myFace);
              logger.debug("vdP" + j + "already CONTAINS a vertex with same coords and texture information -> do nothing, just add the current face to its neighborlist");
            }else{
             
              //Check if already duplicates were created, maybe with the same vertex and texture information,
              //then we have to add the vertex to that vertexdata rather than creating a new one
              int duplicateIndex = myVertexData.getVertDuplicateSameVertDiffTextureCoordListIndex(newVertex);
             
              if (duplicateIndex != -1){ //Es gibt schon ein duplicate mit gleichen tex coords wie dieses hier, adde bei duplicateIndex
                //Change face pointer of p0 to the duplicates index
                if      (j == 0myFace.p0 = duplicateIndex;
                else if(j == 1) myFace.p1 = duplicateIndex;
                else if(j == 2myFace.p2 = duplicateIndex;
               
                //wenn wir orginal hinzuf�gen wird auch allen duplicates neighbor face zugef�gt.
                myVertexData.addNeighborFace(myFace);
               
                logger.debug("vdP" + j + "has different texture coordiantes but a already created duplicate has the same -> change this face pointer to the duplicate one");
              }else{//there was no duplicate created with the same texture coords yet -> create one!
               
                //Neuen Vertex machen, an original vertices list anh�ngen, neuen index in face auf diesen setzen, face adden
                VertexData newVertexData = new VertexData();
               
                //Add the vertex information to the newly created vertexdata
                newVertexData.setVertex(newVertex);
               
                //Get new index at end of vertex list
                int newIndexOfNewVertexData = vertexDatas.size();
               
                //Change the index of the face to the index the new Vdata is created at
                if      (j == 0myFace.p0 = newIndexOfNewVertexData;
                else if(j == 1) myFace.p1 = newIndexOfNewVertexData;
                else if(j == 2myFace.p2 = newIndexOfNewVertexData;
               
                //Tell the vertexdata the index it is in the overall data
                newVertexData.setArrayIndex(newIndexOfNewVertexData);
               
                //Add new vertex data at the end of the list of all vertexdatas
                vertexDatas.add(newVertexData);
               
                //Inform the original vertexdata, that a duplicate with diff tex coords was created and register it
                myVertexData.registerCreatedDuplicateDiffTexCoords(newVertexData);
               
                //Adde face zu orginal face -> damit wird auch duplicates und dem neuen hinzugef�gt,
                //wenn wir es vorher mit registerDuplicate am orginal registiert haben!
                myVertexData.addNeighborFace(myFace);
               
                //copy the face list the vertex is contained in of the original
                //to the duplicate (for normal generation later, so they are still neighbors)!
                newVertexData.addNeighborFaces(myVertexData.getFacesContainedIn());
                logger.debug("vdP" + j + "isnt empty but DOESENT CONTAIN (also no duplicate contains) a vertex with same coords and texture information -> creating new V.D. at: " + newIndexOfNewVertexData);
                newDuplicatesWithDiffTexCoordsCreated++;
              }//end if duplicate exists
            }//end if vertexdata already contains Vnew
          }//end if vertexdata is empty
        }//end for p0,p1,p2
       
        //Calculate the face's normal vector
        myFace.calcFaceNormal(vertexDatas);
       
        myFace.index = faces.size();
       
        //Add face to facelist
        faces.add(myFace);
      }
     
      logger.debug("-> Processed duplicate vertex/texture points.");
  /////////////////////////////////////////
     
     
  //////////////////////////////////////   
 
      if (creaseAngleRad != 0.0) {
        //Durch alle selbst kreierten faces gehen, und schauen ob ein vertex des faces mit einem seiner
        //nachbar faces einen "sharp edge" hat oder smooth ist.
        //wenn smooth -> als smooth neighbor des face pointers hinzuf�gen
        for (int j = 0; j < faces.size(); j++) {
          MyFace currentFace = faces.get(j);
 
          //Get vertex pointers of face to vertices in vertices list
          int indexP0 = currentFace.p0;
          int indexP1 = currentFace.p1;
          int indexP2 = currentFace.p2;
 
          //Get the vertexdatas out of the list with the pointers
          VertexData vdP0 = vertexDatas.get(indexP0);
          VertexData vdP1 = vertexDatas.get(indexP1);
          VertexData vdP2 = vertexDatas.get(indexP2);
 
 
          int[] currentFaceVertIndices = currentFace.getVertexIndices();
          //Go through all 3 vertexdata entries in the current face and check for its smooth neighbors
          for (int faceVD = 0; faceVD < currentFaceVertIndices.length /*currentFace.getVertexIndices().length*/; faceVD++) {
 
            VertexData currentFaceVertexData = null;
            if      (faceVD == 0)  {currentFaceVertexData   = vdP0; /*logger.debug("Face: " + j + " - P0");*/}
            else if(faceVD == 1)   {currentFaceVertexData   = vdP1; /*logger.debug("Face: " + j + " - P1");*/}
            else if(faceVD == 2)  {currentFaceVertexData   = vdP2; /*logger.debug("Face: " + j + " - P2");*/}
 
            ArrayList<MyFace> facesVDIsIn = currentFaceVertexData.getFacesContainedIn();
 
            //Go through all neighbor faces that the vertex(data) is a part of
            for (int k = 0; k < facesVDIsIn.size(); k++) {
              MyFace anotherFaceVDisIn = facesVDIsIn.get(k);
 
              //Check that we are not considering the same face as the currentFace
              if (!anotherFaceVDisIn.equals(currentFace)){
 
                boolean onSameSurface = isOnSameSurfaceRadians(currentFace, anotherFaceVDisIn, creaseAngleRad);
//                boolean onSameSurface = isOnSameSurfaceCosAngle(currentFace, anotherFaceVDisIn, creaseCosinus);
               
                //Check if the current face and the other face VD are connected
                //by an angle < cos_angle degrees,
                //if so, add the faces to the vds smooth neighbor list (for later normal generation)
                //if NOT so, create new VertexData, as a copy of the current one at the end of
                //the vertexdata list, adjust the face pointers of the current face to the new one
                //(we need another vertex so we have two different normals for them if the two faces arent smoothly connected)
                if (onSameSurface){
                  if      (faceVD == 0)  {
                    logger.debug("Face: " + (currentFace.index) + " (P0:" + currentFace.p0 + " P1:" + currentFace.p1 + " P2:" + currentFace.p2 + ")" + " is smooth with face: " + (anotherFaceVDisIn.index) + " (P0:" + anotherFaceVDisIn.p0 + " P1:" + anotherFaceVDisIn.p1 + " P2:" + anotherFaceVDisIn.p2 + ") at currentFaces' pointer: " +  currentFace.p0 + " (" + vdP0.getVertex()+ " )");
                  }
                  else if(faceVD == 1)   {
                    logger.debug("Face: " + (currentFace.index) + " (P0:" + currentFace.p0 + " P1:" + currentFace.p1 + " P2:" + currentFace.p2 + ")" + " is smooth with face: " + (anotherFaceVDisIn.index) + " (P0:" + anotherFaceVDisIn.p0 + " P1:" + anotherFaceVDisIn.p1 + " P2:" + anotherFaceVDisIn.p2 + ") at currentFaces' pointer: " +  currentFace.p1+ " (" + vdP1.getVertex()+ " )");
                  }
                  else if(faceVD == 2)  {
                    logger.debug("Face: " + (currentFace.index) + " (P0:" + currentFace.p0 + " P1:" + currentFace.p1 + " P2:" + currentFace.p2 + ")" + " is smooth with face: " + (anotherFaceVDisIn.index) + " (P0:" + anotherFaceVDisIn.p0 + " P1:" + anotherFaceVDisIn.p1 + " P2:" + anotherFaceVDisIn.p2 + ") at currentFaces' pointer: " +  currentFace.p2+ " (" + vdP2.getVertex()+ " )");
                  }

                  if      (faceVD == 0)  {
                    currentFace.addSmoothNeighborP0(anotherFaceVDisIn);
                  }
                  else if(faceVD == 1)   {
                    currentFace.addSmoothNeighborP1(anotherFaceVDisIn);
                  }
                  else if(faceVD == 2)  {
                    currentFace.addSmoothNeighborP2(anotherFaceVDisIn);
                  }
                }//if smooth
              }//if not checking against this same face
            }//for loop through all faces the current vertex is in
          }//for loop through all 3 vertexdatas of the current face
        }//for loop through all previously created faces
 
      }//end if creaseAngle != 0.0
  ///////////////////////////////////////////////////////////////////////////   

     
     
  /////////////////  //moved to the next loop
//      //Vertex normalen berechnen
//      for (int j = 0; j < faces.size(); j++) {
//        MyFace currentFace = faces.get(j);
//        currentFace.calcVertexNormals(useNormailizedNormalsForAdding);
//      }
  //////////////////
     
      // /|\
      // combine?
      // \|/
     
  //////////////////////////////////////////////////////////////////////   
     
 
      //Die vertex normalen wurden pro face und pro pointer auf ein vertex in den faces berechnet
      //Jetzt f�gen wir den VertexDatas, die die vertices representieren diese vertex normalen hinzu.
      //Wenn in mehreren faces auf den gleichen vertex gezeigt wird, aber in dem face f�r diesen vertex eine
      //andere normale berechnet wurde (weil face mit anderen smooth ist) m�ssen wir evtl das Vertex(data) duplizieren
      //und diesem die andere normale hinzuf�gen
      for (int j = 0; j < faces.size(); j++) {
        MyFace currentFace = faces.get(j);
       
        //GENERATE THE FACES VERTEX NORMAL BASED ON ITS SMOOTH NEIGHBORS
        currentFace.calcVertexNormals(useNormailizedNormalsForAdding);
       
        int[] faceVertexPointer = currentFace.getVertexIndices();
       
        //Go trough all (3) vertexpointer p0..2 of the current face
        for (int i = 0; i < faceVertexPointer.length; i++) {
          int currentVertexPointer = faceVertexPointer[i];
         
          logger.debug("-> Processing face[" + j + "].P" + i + " Vertex: " + vertexDatas.get(currentVertexPointer).getVertex());
         
          //Hole vertexdata auf das das currentFace an dem aktuellen zeiger (p0,p1,p2) zeigt
          VertexData currentVertexDataP0OrP1OrP2 = vertexDatas.get(currentVertexPointer);
         
          //Get normal saved in the vertexdata at position Face.Px
          Vector3D currentFacesCurrentVDNormal = currentVertexDataP0OrP1OrP2.getUniqueVertexNormal();
         
          //Get vertex normal array calculated and saved in the face for each point Px
          Vector3D[] vertexNormalsCurrentFace = currentFace.getVertexNormals();
         
          //Check if the vertexnormal data at the pointer is null -> thats the case before first time a vertexnormal is set here
          if (currentFacesCurrentVDNormal == null){
            currentVertexDataP0OrP1OrP2.setUniqueVertexNormal(vertexNormalsCurrentFace[i]);
            logger.debug("Face " + j + ", vdP" + i + " (Vertex: " + vertexDatas.get(currentVertexPointer).getVertex() + ")" + " normal not yet set -> set it: " + vertexNormalsCurrentFace[i]);
          }else{//Vertexdata at index already contains a vertexnormal -> check if its the equal to this faces currentVD's
           
            if (currentFacesCurrentVDNormal.equalsVectorWithTolerance(vertexNormalsCurrentFace[i], ToolsMath.ZERO_TOLERANCE)){
              logger.debug("Face " + j + ", vdP" + i +  " (Vertex: " + vertexDatas.get(currentVertexPointer).getVertex() + ")" + " already CONTAINS a normal with the same values as the normal of this faces point ->  we can leave the index and normal at the same place: N:" + vertexNormalsCurrentFace[i]);
            }else{
              int duplicateIndexOfSameVertDiffNormal = currentVertexDataP0OrP1OrP2.getVertDuplicateSameVertDiffNormalListIndex(vertexNormalsCurrentFace[i]);
             
              if (duplicateIndexOfSameVertDiffNormal != -1){ //Es gibt schon ein duplicate mit gleichen tex coords wie dieses hier, adde bei duplicateIndex
                //Change face pointer of p0 to the duplicates index
                if      (i == 0currentFace.p0 = duplicateIndexOfSameVertDiffNormal;
                else if(i == 1) currentFace.p1 = duplicateIndexOfSameVertDiffNormal;
                else if(i == 2currentFace.p2 = duplicateIndexOfSameVertDiffNormal;
                logger.debug("Face " + j + " vdP" + i  + " (Vertex: " + vertexDatas.get(currentVertexPointer).getVertex() + ")" " vertexnormal is conform with a duplicate of the original vertex -> point to that duplicate: N:"  + vertexNormalsCurrentFace[i]);
              }else{//duplicate index == -1 -> neither the orignal faces point has the same vertexnormal nor a duplicate with different normal has that normal -> create new VD with the different normal and same vertex
               
                //Neuen Vertex machen, an original vertices list anh�ngen, neuen index in face auf diesen setzen
                VertexData newVertexData = new VertexData();
               
                //Add the vertex information to the newly created vertexdata
                newVertexData.setVertex(currentVertexDataP0OrP1OrP2.getVertex());
               
                //Set the vertex normal for the new vertexdata  as the vertex normal of the current face' calced normal for that vertex
                newVertexData.setUniqueVertexNormal(vertexNormalsCurrentFace[i]);
               
                //Get new index at end of vertex list
                int newIndexOfNewVertexData = vertexDatas.size();
               
                //Change the index of the face to the index the new Vdata is created at
                if      (i == 0currentFace.p0 = newIndexOfNewVertexData;
                else if(i == 1) currentFace.p1 = newIndexOfNewVertexData;
                else if(i == 2currentFace.p2 = newIndexOfNewVertexData;
               
                //Tell the vertexdata the index it is in the overall data
                newVertexData.setArrayIndex(newIndexOfNewVertexData);
               
                //Add new vertex data at the end of the list of all vertexdatas
                vertexDatas.add(newVertexData);
               
                //Inform the original vertexdata, that a duplicate with diff tex coords was created and register it
                currentVertexDataP0OrP1OrP2.registerCreatedDuplicateDiffNormal(newVertexData);
                logger.debug("Face " + j + ", vdP" + i  + " (Vertex: " + vertexDatas.get(currentVertexPointer).getVertex() + ")" " has a different vertexnormal and DOESENT CONTAIN a link to a duplicate vertex with same normal information -> creating new VD at: " + newIndexOfNewVertexData + " N:" + vertexNormalsCurrentFace[i]);
                newDuplicatesWithDiffNormalCreated++;
              }//end if duplicate exists
            }//end if vertexdata already contains Vnew
          }//end if vertexdata is empty
       
      }
  ////////////////////////////////////   
     
     
     
     
  //////////////////////////////////////////   
      //Create arrays
      Vertex[] newVertices   = new Vertex[vertexDatas.size()];
      Vector3D[] normals    = new Vector3D[vertexDatas.size()];
      int[] newIndices    = new int[faces.size()*3];
     
      /*
       * Go through the final faces list and fill vertex/newIndices/normal arrays
       */
      for (int j = 0; j < faces.size(); j++) {
        MyFace myFace = faces.get(j);
        //Get vertex pointers of face to newVertices in newVertices list
        int indexP0 = myFace.p0;
        int indexP1 = myFace.p1;
        int indexP2 = myFace.p2;
       
        //Use pointers as newIndices and fill newIndices array
        newIndices[j*3]    = indexP0;
        newIndices[j*3+1= indexP1;
        newIndices[j*3+2= indexP2;
       
        //Get the vertexdatas out of the list with the pointers
        VertexData vdP0 = vertexDatas.get(indexP0);
        VertexData vdP1 = vertexDatas.get(indexP1);
        VertexData vdP2 = vertexDatas.get(indexP2);
       
        //Get the vertex out of the vdata
        Vertex v0 = vdP0.getVertex();
        Vertex v1 = vdP1.getVertex();
        Vertex v2 = vdP2.getVertex();
       
        //Put newVertices from vertexdata list in vertex array for the geometry array
        newVertices[indexP0] = v0;
        newVertices[indexP1] = v1;
        newVertices[indexP2] = v2;
       
        //Get the faces normal
        normals[indexP0] = vdP0.uniqueVertexNormal;
        normals[indexP1] = vdP1.uniqueVertexNormal;
        normals[indexP2] = vdP2.uniqueVertexNormal;
       
        ////Normalen debug linien sehen mit normals invertiert richtig
        //aus, aber model wird falsch geshaded!
        /*
        normals[indexP0].scale(-1);
        normals[indexP1].scale(-1);
        normals[indexP2].scale(-1);
        */
      }
     
      //Leider n�tig? da manche models newVertices enthalten, die in den faces garnicht referenziert werden, quasi tote verts
      for (int j = 0; j < newVertices.length; j++) {
        if (newVertices[j] == null){
          newVertices[j] = nullVect;
        }
        //Normals array should be same length as verts, check for nulls here
        if (normals[j] == null){
          normals[j] = nullVect;
        }
  //      System.out.print(normals[j] + " - ");
      }
  //    logger.debug();
  ///////////////////////////////////////////////////////   
     
      logger.debug("----------------------------------------------------------------------------------");
      logger.debug("New duplicates of vertices with same vertex but different texture coordinates created: " + newDuplicatesWithDiffTexCoordsCreated);
      logger.debug("New duplicates of vertices with same vertex but different normal created: " + newDuplicatesWithDiffNormalCreated);
      logger.debug("Original number of vertices: " + originalVertices.length);
      logger.debug("Final number of vertices: " + vertexDatas.size());
      logger.debug("Final number of indices: " + newIndices.length);
      logger.debug("Final number of faces: " + faces.size());
      logger.debug("Final number of normals: " + normals.length);
      logger.debug("----------------------------------------------------------------------------------");
     
      if (newVertices.length > 2 && faces.size() > 0){
        // Create a geometryInfo with all vertices of the mesh
        GeometryInfo geometryInfo = new GeometryInfo(pa, newVertices, newIndices);
 
        //Set the normals for the geometry
        geometryInfo.setNormals(normals, true, false);
 
        //Clean up a bit
        vertexDatas = null;
        faces    = null;
       
        return geometryInfo;
      }
      return null;
    }


 
  /**
   * Sets the debug mode.
   *
   * @param debug the debug
   */
  public void setDebug(boolean debug) {
    if (debug)
      logger.setLevel(Level.DEBUG);
    else
      logger.setLevel(Level.ERROR);
  }
 
  /**
   * This influences the normal generation with crease angles.
   * If <code>useNormalsEqualToFace</code> is set to true, normals
   * of neighbor faces, that have the same normal as the face and vertex were checking
   * against will be used in the calculation.
   * </br>If <code>useNormalsEqualToFace</code> is set to false, these normals equal to
   * the test face normal will be discarded to avoid adding up redundant normals.
   * </br>The default is FALSE.
   *
   * @param useNormalsEqualToFace use other normals equal to face or
   */
  public void setUseNormalsEqualToFace(boolean useNormalsEqualToFace) {
    this.useNormalsEqualToFace = useNormalsEqualToFace;
  }

  /**
   * This influences the normal generation with crease angles.
   * <br<>If <code>useEqualNeighborNormalsAgain</code> is set to true, normals
   * of neighbor faces, that have the same normal as a neighbor face previously used in
   * the calculation for one same vertex normal will again be used and added in.
   * <br>If <code>useEqualNeighborNormalsAgain</code> is set to false, these normals will
   * not be added in the calculation again.
   * <br>The default is FALSE.
   *
   * @param useEqualNeighborNormalsAgain use equal neighbor normals again
   */
  public void setUseEqualNeighborNormalsAgain(boolean useEqualNeighborNormalsAgain) {
    this.useEqualNeighborNormalsAgain = useEqualNeighborNormalsAgain;
  }
 
 
  /**
   * Calculates ..
   *
   * @param face1 the face1
   * @param face2 the face2
   * @param cosAngle the cos angle
   *
   * @return true, if checks if is on same surface radians
   */
    private boolean isOnSameSurfaceRadians(MyFace face1, MyFace face2, float cosAngle) {
     
  //    float cosineBetweenNormals   = face1.normal.dot(face2.normal);
  //    logger.debug(Math.acos(cosineBetweenNormals));
  //    boolean smooth   = cosineBetweenNormals > cosAngle;
  //    //boolean smoothTriangles = (cosineBetweenNormals > cosAngle);
  //        //logger.debug("surface: compare dot=" +dot + " cos-angle=" + cos_angle + " return " + (dot > cos_angle));
  //    logger.debug(Vector3D.angleBetween(face1.normal, face2.normal));
  //    logger.debug();
  //   
  //        return smooth;
         
      boolean debugSmoothChecking = false;
          float angleBetweenNorms = Vector3D.angleBetween(face1.normal, face2.normal);
         
          if (debugSmoothChecking){
             float angleBetweenNormsDegrees = (float)Math.toDegrees(angleBetweenNorms);
            logger.debug("Angle between normals :" + angleBetweenNormsDegrees);
            logger.debug("Crease angle: " + Math.toDegrees(cosAngle));
          }
         
          boolean smooth = angleBetweenNorms < cosAngle;
         
          if (debugSmoothChecking)
            logger.debug("-> Smooth: " + smooth);
         
          if (Float.isNaN(angleBetweenNorms)){
            smooth = true;
            if (debugSmoothChecking)
              logger.debug("NAN!");
          }
         
          return smooth;
  //    float threshold = (float)Math.cos(creaseAngle);
  //    float cosine   = face1.normal.dot(face2.normal);
  //    boolean smooth   = cosine > threshold;
  //
  //    //boolean smoothTriangles = (cosine > threshold);
  //        //logger.debug("surface: compare dot=" +dot + " cos-angle=" + cos_angle + " return " + (dot > cos_angle));
  //        return smooth;
  //        return false;
  ////    return true;
      }



  /**
   * Calculates ..
   *
   * @param face1 the face1
   * @param face2 the face2
   * @param cosAngle the cos angle
   *
   * @return true, if checks if is on same surface cos angle
   */
  private boolean isOnSameSurfaceCosAngle(MyFace face1, MyFace face2, float cosAngle) { //FIXME really correct?
    float cosineBetweenNormals   = face1.normal.dot(face2.normal);
    boolean smooth   = cosineBetweenNormals > (float)Math.abs(cosAngle);
   
    if (Float.isNaN(cosineBetweenNormals))
      smooth = true;
   
      return smooth;
  }



  /**
   * Class representing a triangle face of a mesh.
   *
   * @author C.Ruff
   */
    private class MyFace{
     
      /** The p0. */
      int p0;
     
      /** The p1. */
      int p1;
     
      /** The p2. */
      int p2;
     
      /** The smooth neighbors p0. */
      private ArrayList<MyFace> smoothNeighborsP0;
     
      /** The smooth neighbors p1. */
      private ArrayList<MyFace> smoothNeighborsP1;
     
      /** The smooth neighbors p2. */
      private ArrayList<MyFace> smoothNeighborsP2;
     
      /** The normal. */
      Vector3D normal;
     
      /** The normal normalized. */
      Vector3D normalNormalized;
     
      /** The center. */
      private Vector3D center;
     
      /** The index. */
      int index;
     
      /** The vertex normal p0. */
      private Vector3D vertexNormalP0;
     
      /** The vertex normal p1. */
      private Vector3D vertexNormalP1;
     
      /** The vertex normal p2. */
      private Vector3D vertexNormalP2;
     
      /** The vertex normals. */
      private Vector3D[] vertexNormals;
     
      /**
       * Instantiates a new my face.
       */
      public MyFace(){
        p0 = -1;
        p1 = -1;
        p2 = -1;
        //normal = nullVect;
       
        smoothNeighborsP0 = new ArrayList<MyFace>();
        smoothNeighborsP1 = new ArrayList<MyFace>();
        smoothNeighborsP2 = new ArrayList<MyFace>();
      }
     
      /**
       * Gets the vertex indices.
       *
       * @return the vertex indices
       *
       * the pointers (indices) into the vertex array, this face holds (length=3 here)
       */
      public int[] getVertexIndices() {
        return new int[]{p0,p1,p2};
      }
     
      /**
       * Registers the face as a smooth neighbor of this faces vertex at
       * the index P0. This is only relevant if a crease angle is used.
       *
       * @param neighborFace the neighbor face
       */
      public void addSmoothNeighborP0(MyFace neighborFace){
        if (!smoothNeighborsP0.contains(neighborFace)){
          smoothNeighborsP0.add(neighborFace);
        }
      }
     
      /**
       * Registers the face as a smooth neighbor of this faces vertex at
       * the index P1. This is only relevant if a crease angle is used.
       *
       * @param neighborFace the neighbor face
       */
      public void addSmoothNeighborP1(MyFace neighborFace){
        if (!smoothNeighborsP1.contains(neighborFace)){
          smoothNeighborsP1.add(neighborFace);
        }
      }
     
      /**
       * Registers the face as a smooth neighbor of this faces vertex at
       * the index P2. This is only relevant if a crease angle is used.
       *
       * @param neighborFace the neighbor face
       */
      public void addSmoothNeighborP2(MyFace neighborFace){
        if (!smoothNeighborsP2.contains(neighborFace)){
          smoothNeighborsP2.add(neighborFace);
        }
      }
     
 
      /**
       * Calculates this face's face normal.
       *
       * @param vertexList the vertex list
       */
      public void calcFaceNormal(ArrayList<VertexData> vertexList){
        //We DONT NORMALIZE YET!
        this.normal = ToolsGeometry.getNormal(vertexList.get(p0).getVertex(), vertexList.get(p1).getVertex(), vertexList.get(p2).getVertex(), false);
        this.normalNormalized = normal.getCopy();
        this.normalNormalized.normalizeLocal();
      }
     
      /**
       * Gets the center point local.
       *
       * @param vertexDataList the vertex data list
       *
       * @return the center point local
       *
       * the center point of this face
       */
      public Vector3D getCenterPointLocal(ArrayList<VertexData> vertexDataList){
        center = vertexDataList.get(p0).getVertex().getCopy();
        center.addLocal(vertexDataList.get(p1).getVertex());
        center.addLocal(vertexDataList.get(p2).getVertex());
        center.scaleLocal(ToolsMath.ONE_THIRD);
        return this.center;
      }
 
     
      /**
       * Cals the vertex normals for the three points this face has.
       * 2 different ways are implemented from which can be chosen by
       * the boolean variable.
       * if "useNormailizedNormalsForAdding" is false, all unnormalized normals of all
       * smooth neighbor faces are added up and at the end normalized.
       * If "useNormailizedNormalsForAdding" is false, all neighbors face normals are normalized before adding up.
       * Also, normals that are equal to this faces normal arent added again.
       * Furthermore, only normals that arent equal to one already added from another
       * neighbor are added in. This is slower but should produce better results.
       *
       * @param useNormailizedNormalsForAdding the use normailized normals for adding
       */
      public void calcVertexNormals(boolean useNormailizedNormalsForAdding) {
        if (normal.equals(nullVect)){
          throw new RuntimeException("We have to calculate the face normal before calling calcVertexNormals!");
        }
       

        if (useNormailizedNormalsForAdding){
          //Use normalized normals for all calculations
          //Also checks if normals are attempted to be added that are equal to this faces normal
          //or normals that have already been added -> those are not added
         
//          /*
          Vector3D normalizedFaceNormal = this.normal.getCopy();
          normalizedFaceNormal.normalizeLocal();

          logger.debug("");
          logger.debug("Face " + index + " normal: " + this.normal + " Normalized: " + normalizedFaceNormal);
          logger.debug("P0:");

          //For each face point, calc vertex normal by adding up all
          //smooth connected neighbors + this faces' normal and normalize in the end
          ArrayList<Vector3D> alreadyAddedInP0 = new ArrayList<Vector3D>();
//          vertexNormalP0 = this.normal.getCopy(); //Init with own faces face normal
          vertexNormalP0 = normalizedFaceNormal.getCopy();
          for (int i = 0; i < smoothNeighborsP0.size(); i++) {
            MyFace neighborFaceP0 = smoothNeighborsP0.get(i);

            Vector3D nextSmoothNeighborNormal       = neighborFaceP0.normal;
            Vector3D nextSmoothNeighborNormalNormalized = nextSmoothNeighborNormal.getCopy();
            //TODO doch nochmal probieren mit vorher ausgerechneter normal? sonst performance loss!
            nextSmoothNeighborNormalNormalized.normalizeLocal();//neighborFaceP0.normalNormalized;
            boolean alreadyAddedSameNormalIn = false;
           
           
            //Dont add faces normals that are equal to this faces normals
            if (!useNormalsEqualToFace && nextSmoothNeighborNormalNormalized.equalsVectorWithTolerance(normalizedFaceNormal, ToolsMath.ZERO_TOLERANCE)){
              alreadyAddedSameNormalIn = true;
              logger.debug("Not using normal: " + nextSmoothNeighborNormalNormalized + " of face " + neighborFaceP0.index + " in vertex norm calc because its equal to this faces normal.");
            }
//            else //Dont add face normals that are equal to one already added
//            {
            if (!useEqualNeighborNormalsAgain){
              for(Vector3D neighBorNorm : alreadyAddedInP0){
                if(neighBorNorm.equalsVectorWithTolerance(nextSmoothNeighborNormalNormalized, ToolsMath.ZERO_TOLERANCE)){
                  alreadyAddedSameNormalIn = true;
                  logger.debug("Already added same normal -> dont add again N: " + neighBorNorm);
                }
              }
            }
//            }

            if (!alreadyAddedSameNormalIn){
              vertexNormalP0.addLocal(nextSmoothNeighborNormalNormalized);
              alreadyAddedInP0.add(nextSmoothNeighborNormalNormalized);
              logger.debug("Added normal: " + nextSmoothNeighborNormalNormalized + " of face: " + neighborFaceP0.index);
            }
          }
          vertexNormalP0.normalizeLocal();

          logger.debug("P1");
          //For each face point, calc vertex normalby adding up all
          //smooth connected neighbors + this faces' normal and normalize in the end
          ArrayList<Vector3D> alreadyAddedInP1 = new ArrayList<Vector3D>();
//          vertexNormalP1 = this.normal.getCopy(); //Init with own faces face normal
          vertexNormalP1 = normalizedFaceNormal.getCopy();
          for (int i = 0; i < smoothNeighborsP1.size(); i++) {
            MyFace neighborFaceP1 = smoothNeighborsP1.get(i);

            Vector3D nextSmoothNeighborNormal       = neighborFaceP1.normal;
            Vector3D nextSmoothNeighborNormalNormalized = nextSmoothNeighborNormal.getCopy();
            //TODO doch nochmal probieren mit vorher ausgerechneter normal? sonst performance loss!
            nextSmoothNeighborNormalNormalized.normalizeLocal();//neighborFaceP1.normalNormalized;
            boolean alreadyAddedSameNormalIn = false;
           
            //Dont add faces normals that are equal to this faces normals
            if (!useNormalsEqualToFace && nextSmoothNeighborNormalNormalized.equalsVectorWithTolerance(normalizedFaceNormal, ToolsMath.ZERO_TOLERANCE)){
              alreadyAddedSameNormalIn = true;
              logger.debug("Not using normal: " + nextSmoothNeighborNormalNormalized + " of face " + neighborFaceP1.index + " in vertex norm calc because its equal to this faces normal.");
            }
//            else //Dont add face normals that are equal to one already added
//            {
            if (!useEqualNeighborNormalsAgain){
              for(Vector3D neighBorNorm : alreadyAddedInP1){
                if(neighBorNorm.equalsVectorWithTolerance(nextSmoothNeighborNormalNormalized, ToolsMath.ZERO_TOLERANCE)){
                  alreadyAddedSameNormalIn = true;
                  logger.debug("Already added same normal -> dont add again N: " + neighBorNorm);
                }
              }
            }
//            }

            if (!alreadyAddedSameNormalIn){
              vertexNormalP1.addLocal(nextSmoothNeighborNormalNormalized);

              alreadyAddedInP1.add(nextSmoothNeighborNormalNormalized);
              logger.debug("Added normal: " + nextSmoothNeighborNormalNormalized + " of face: " + neighborFaceP1.index);
            }
          }
          vertexNormalP1.normalizeLocal();

         
          logger.debug("P2");
          //For each face point, calc vertex normalby adding up all
          //smooth connected neighbors + this faces' normal and normalize in the end
          ArrayList<Vector3D> alreadyAddedInP2 = new ArrayList<Vector3D>();
//          vertexNormalP2 = this.normal.getCopy(); //Init with own faces face normal
          vertexNormalP2 = normalizedFaceNormal.getCopy();
          for (int i = 0; i < smoothNeighborsP2.size(); i++) {
            MyFace neighborFaceP2 = smoothNeighborsP2.get(i);

            Vector3D nextSmoothNeighborNormal       = neighborFaceP2.normal;
            Vector3D nextSmoothNeighborNormalNormalized = nextSmoothNeighborNormal.getCopy();
            //TODO doch nochmal probieren mit vorher ausgerechneter normal? sonst performance loss!
            nextSmoothNeighborNormalNormalized.normalizeLocal();//neighborFaceP2.normalNormalized;
            boolean alreadyAddedSameNormalIn = false;
           
           
            //Dont add faces normals that are equal to this faces normals
            if (!useNormalsEqualToFace && nextSmoothNeighborNormalNormalized.equalsVectorWithTolerance(normalizedFaceNormal, ToolsMath.ZERO_TOLERANCE)){
              alreadyAddedSameNormalIn = true;
              logger.debug("Not using normal: " + nextSmoothNeighborNormalNormalized + " of face " + neighborFaceP2.index + " in vertex norm calc because its equal to this faces normal.");
            }
//            else //Dont add face normals that are equal to one already added
//            {
            if (!useEqualNeighborNormalsAgain){
              for(Vector3D neighBorNorm : alreadyAddedInP2){
                if(neighBorNorm.equalsVectorWithTolerance(nextSmoothNeighborNormalNormalized, ToolsMath.ZERO_TOLERANCE)){
                  alreadyAddedSameNormalIn = true;
                  logger.debug("Already added same normal -> dont add again N: " + neighBorNorm);
                }
              }
            }
//            }

            if (!alreadyAddedSameNormalIn){
              vertexNormalP2.addLocal(nextSmoothNeighborNormalNormalized);
              alreadyAddedInP2.add(nextSmoothNeighborNormalNormalized);
              logger.debug("Added normal: " + nextSmoothNeighborNormalNormalized + " of face: " + neighborFaceP2.index);
            }
          }
          vertexNormalP2.normalizeLocal();
        }else{
          //Just add up all smooth neighbors and normalize after
         
          //P0 Normal
          vertexNormalP0 = this.normal.getCopy();
          for (int i = 0; i < smoothNeighborsP0.size(); i++) {
            MyFace neighborFaceP0 = smoothNeighborsP0.get(i);
            vertexNormalP0.addLocal(neighborFaceP0.normal);
          }
          vertexNormalP0.normalizeLocal();
         
          //P1 Normal
          vertexNormalP1 = this.normal.getCopy();
          for (int i = 0; i < smoothNeighborsP1.size(); i++) {
            MyFace neighborFaceP1 = smoothNeighborsP1.get(i);
            vertexNormalP1.addLocal(neighborFaceP1.normal);
          }
          vertexNormalP1.normalizeLocal();
         
         
          vertexNormalP2 = this.normal.getCopy();
          for (int i = 0; i < smoothNeighborsP2.size(); i++) {
            MyFace neighborFaceP2 = smoothNeighborsP2.get(i);
            vertexNormalP2.addLocal(neighborFaceP2.normal);
          }
          vertexNormalP2.normalizeLocal();
        }

       
          logger.debug("Face: " + index + " -> P0 VertexNormal:-> " + vertexNormalP0);
          logger.debug("Face: " + index + " -> P1 VertexNormal:-> " + vertexNormalP1);
          logger.debug("Face: " + index + " -> P2 VertexNormal:-> " + vertexNormalP2);
          logger.debug("");
       
        vertexNormals = new Vector3D[]{vertexNormalP0,vertexNormalP1,vertexNormalP2};
      }
 
     
      /**
       * Gets the vertex normals.
       *
       * @return the vertex normals
       */
      public Vector3D[] getVertexNormals() {
        if (vertexNormals == null){
          throw new RuntimeException("We have to calculate the vertex normals first!");
        }
        return vertexNormals;
      }
    }//End MyFace class
   
   
    /**
     * Class to hold information about a mesh vertex.
     *
     * @author C.Ruff
     */
    private class VertexData{
     
      /** The array index. */
      private int arrayIndex;
     
      /** The vertex. */
      private Vertex vertex;
     
      /** The faces. */
      private ArrayList<MyFace> faces;
     
      /** The duplications with diff tex coords. */
      private ArrayList<VertexData> duplicationsWithDiffTexCoords;
     
//      private ArrayList<MyFace> smoothNeighbors;
//      private Vector3D vertexNormal;
//      private boolean vertexNormalDirty;
     
      /** The unique vertex normal. */
      private Vector3D uniqueVertexNormal;
     
      /** The duplications with diff normal. */
      private ArrayList<VertexData> duplicationsWithDiffNormal;
     
      //Normal calculated in the getSmoothnormals method
      /** The all neighbors normal. */
      private Vector3D allNeighborsNormal;
 
      /**
       * Constructor.
       */
      public VertexData() {
        super();
        faces               = new ArrayList<MyFace>();
        duplicationsWithDiffTexCoords   = new ArrayList<VertexData>();
       
       
        duplicationsWithDiffNormal     = new ArrayList<VertexData>();
       
//        smoothNeighbors         = new ArrayList<MyFace>();
       
        arrayIndex = -1;
       
        allNeighborsNormal = null;
//        vertexNormalDirty   = true;
//        vertexNormal     = new Vector3D(0,0,0);
      }
     
     
      /**
       * Adds and registers the vertex with this vertexdata. The Vertex
       * datas vertex is null at the start and has to be initilazied once here.
       *
       * @param vertex the vertex
       */
      public void setVertex(Vertex vertex){
        this.vertex = vertex;
      }
     
     
      /**
       * Gets the vertex.
       *
       * @return the vertex
       *
       * Returns the vertex this vertex data wraps around.
       */
      public Vertex getVertex() {
        return vertex;
      }
     
     
      //this is wrong here, the vertex normal when considering a crease angle
      //has to be calculated per face and per face vertex instead of per vertex here
      //alone. Because for one face the same vertex has to be smoothed with different
      //faces than for another face.
//      /**
//       * Tells the VertexData, that the specified face is a neighbor which should
//       * be used to calculate a smooth vertex normal later.
//       *
//       * @param myFace
//       */
//      public void addNewSmoothNeighbor(MyFace myFace){
//        if (! this.smoothNeighbors.contains(myFace)){
//          this.smoothNeighbors.add(myFace);
//          this.vertexNormalDirty = true;
//        }
//      }

//      /**
//       * Calculates and returns the vertexnormal for this vertexdata,
//       * using all the vertexes smooth neighbors and adding them up, then normalizing.
//       * This should be called only after all the smooth neighbors were added.
//       *
//       * @return
//       *   the normal
//       */
//      public Vector3D calcVertexNormalSmoothNeighbors() {
//        if (vertexNormalDirty){
//          vertexNormal = new Vector3D(0,0,0);
//         
//          //Add up face normals of smooth neighbors
//          for (int i = 0; i < smoothNeighbors.size(); i++) {
//            MyFace smoothNeighbor = smoothNeighbors.get(i);
//            vertexNormal.addLocal(smoothNeighbor.normal);
//          }
//          //Normalize in the end
//          vertexNormal.normalizeLocal();
//          vertexNormalDirty = false;
//          return vertexNormal;
//        }else{
//          return vertexNormal;
//        }
//      }
     
//      /**
//       * @return
//       *  whether the vertexdata has smooth neighbors
//       */
//      public boolean hasSmoothNeighborFaces(){
//        return !this.smoothNeighbors.isEmpty();
//      }
     
     
 
      /**
       * Add and register the specified neighbor for this vertexdata.
       * This also adds the face to the duplicates registered (added) with this vertexdata
       * with different tex coords but same vertex pointer.
       *
       * @param face the face
       */
      public void addNeighborFace(MyFace face){
        if (!faces.contains(face))
          faces.add(face);
        //Auch duplicates face hinzuf�gen, da sie ja den gleichen vertex sharen
        //und f�r die normalenberechnung alle neighborfaces brauchen
        for (VertexData vdDuplicate : this.getDuplicateVertexWithDiffTexCoordsList()){
          vdDuplicate.addNeighborFace(face);
        }
      }
     
      /**
       * Add and register the specified neighbors for this vertexdata and ints duplicates
       * with different tex coords but same vertex pointer.
       *
       * @param addFaces the add faces
       */
      public void addNeighborFaces(ArrayList<MyFace> addFaces){
        for (MyFace currFace: addFaces){
          if (!faces.contains(currFace)){
            faces.add(currFace);
          }
        }
        //Auch duplicates faces hinzuf�gen, da sie ja den gleichen vertex sharen
        //und f�r die normalenberechnung alle neighborfaces brauchen
        for (VertexData vdDuplicate : this.getDuplicateVertexWithDiffTexCoordsList()){
          vdDuplicate.addNeighborFaces(addFaces);
        }
      }
     
      /**
       * Gets the neighbor faces.
       *
       * @return the neighbor faces
       *
       * All neighbors of this vertex, so all faces this vertex is a part of
       */
      public ArrayList<MyFace> getNeighborFaces(){
        return this.faces;
      }
     
      /**
       * Calculates and returns the vertex normal considering
       * _all_ the vertexes neighbors (smooth or not!). Results
       * in an all smoothed model.
       *
       * @return the vector3 d
       *
       * the resulting vertex normal
       */
      public Vector3D calcVertexNormalAllNeighbors(){
        if (allNeighborsNormal == null ){
          //Add up face normals of smooth neighbors
          Vector3D allNeighborNormal = new Vector3D(0,0,0);
          for (int i = 0; i < faces.size(); i++) {
            MyFace neighbor = faces.get(i);
            allNeighborNormal.addLocal(neighbor.normal);
           
            logger.debug("Vertex index:" + this.getArrayIndex() + " calcing in neighbor normal of face: " + neighbor.index);
          }
          //Normalize in the end
          allNeighborNormal.normalizeLocal();
          allNeighborsNormal = allNeighborNormal;
          return allNeighborsNormal;
        }else{
          return allNeighborsNormal;
        }
      }
     
     
     
     
  ///////////// Handle vertices with same vertex but different texture coordinates
  /**
   * Returns the index of the already created duplicate with the same vertex and texture
   * information we are checking with, or "-1" if no duplicate with that information exists.
   * Then we have to create a new duplicate and register it to this Vertex Data's duplicate list.
   *
   * @param vertex the vertex
   *
   * @return the vert duplicate same vert diff texture coord list index
   *
   * the index of the dupicate with the same tex coords or -1 if there is none
   */
      public int getVertDuplicateSameVertDiffTextureCoordListIndex(Vertex vertex){
        //Go through list of all duplicates
        for (int i = 0; i < duplicationsWithDiffTexCoords.size(); i++) {
          VertexData possibleDuplicate = duplicationsWithDiffTexCoords.get(i);
          if (possibleDuplicate.equalsVertex(vertex)){
            return possibleDuplicate.getArrayIndex();
          }
        }
        return -1;
      }
     
      /**
       * Checks if the vertex with the same coordinates and texture coordinates was already added
       * to this vertexData.
       *
       * @param vertex the vertex
       *
       * @return true, if equals vertex
       */
      private boolean equalsVertex(Vertex vertex){
        return this.vertex.equalsVector(vertex);
      }//contains()
     
      /**
       * Tell the vertexdata obj, that we have created a duplicate and saved it.
       *
       * @param vd the vd
       */
      public void registerCreatedDuplicateDiffTexCoords(VertexData vd){
        this.duplicationsWithDiffTexCoords.add(vd);
      }
     
      /**
       * Gets the duplicate vertex with diff tex coords list.
       *
       * @return the duplicate vertex with diff tex coords list
       */
      private ArrayList<VertexData> getDuplicateVertexWithDiffTexCoordsList(){
        return this.duplicationsWithDiffTexCoords;
      }
  ///////////// Handle vertices with same vertex but different texture coordinates
     
 
  ///////////// Handle vertices with same vertex but different Vertex normal // This is only relevant if a crease angle is used!
      /**
   * Returns the vertex normal that has been calculated and set
   * with a crease angle for a group of faces for which this normal
   * is the same.
   *
   * @return the unique vertex normal
   */
      public Vector3D getUniqueVertexNormal(){
        return uniqueVertexNormal;
      }
     
     
      /**
       * Sets the vertex normal for this vertex(data). In the crease angle
       * calculation process. May only set it once. Different normals belonging
       * to the same vertex have to be put in another vertex data object!
       * The vertex normal is calculated in the faces and then set here.
       *
       * @param newVertexNormal the new vertex normal
       */
      public void setUniqueVertexNormal(Vector3D newVertexNormal) {
        this.uniqueVertexNormal = newVertexNormal;
      }
     
      /**
       * The crease angle cal process tell this vertex data that it created another vertex data
       * because in this one, the vertex is the same but the normal differs -> duplicate the vertex!.
       *
       * @param vdWithDiffNormal the vd with diff normal
       */
      public void registerCreatedDuplicateDiffNormal(VertexData vdWithDiffNormal) {
        this.duplicationsWithDiffNormal.add(vdWithDiffNormal);
      }
     
      /**
       * Asks the vertex data if a duplicate of itself with a different normal was created and registered as a duplicate.
       * <br>If there is a duplicate already registered with the same normal to be checked with, we get the index of that duplicate
       * in the overall vertex data list and can point the face to the duplicate so we dont have to create another vertexdata.
       * <br>It is assumed that the normal we are checking against belongs to a vertex with the same values as this Vertex datas vertex!
       *
       * @param normalToCheckWith the normal to check with
       *
       * @return the vert duplicate same vert diff normal list index
       *
       * the index of the duplicate or -1 if there is none
       */
      public int getVertDuplicateSameVertDiffNormalListIndex(Vector3D normalToCheckWith) {
  //      Go through list of all duplicates
        for (int i = 0; i < duplicationsWithDiffNormal.size(); i++) {
          VertexData possibleDuplicate = duplicationsWithDiffNormal.get(i);
          if (possibleDuplicate.getUniqueVertexNormal().equalsVectorWithTolerance(normalToCheckWith, ToolsMath.ZERO_TOLERANCE)){
            return possibleDuplicate.getArrayIndex();
          }
        }
        return -1;
      }
  ///////////// Handle vertices with same vertex but different Vertex normal
     
     
     
      /**
   * Tell the vertexdata which index its at in a list of vertexdatas for example.
   *
   * @param i the i
   */
      public void setArrayIndex(int i){
        this.arrayIndex = i;
      }
     
      /**
       * Gets the array index.
       *
       * @return the array index
       *
       * returns the index, the vertexData is at in the overall vertex data list.
       * ->This has to be set manually by calling <code>setArrayIndex</code> first
       */
      public int getArrayIndex(){
        return this.arrayIndex;
      }
     
      /**
       * Gets the faces contained in.
       *
       * @return the faces contained in
       *
       * a list of faces, this vertex(-data) is a part of (all its neighbors)
       * NOTE: not at all stages of the algorithm returns all real neighbors. Some may  be deleted already.
       */
      public ArrayList<MyFace> getFacesContainedIn(){
        return this.faces;
      }
    }//Vertexdata class

   
   

    /*----------------------------------------------------------------------------*/
   
   
 
}
 
TOP

Related Classes of org.mt4j.util.TriangleNormalGenerator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.