Package org.osm2world.core.target.common

Source Code of org.osm2world.core.target.common.FaceTarget$IsolatedTriangle

package org.osm2world.core.target.common;

import static java.lang.Math.abs;
import static java.util.Collections.nCopies;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import org.osm2world.core.math.TriangleXYZ;
import org.osm2world.core.math.TriangleXYZWithNormals;
import org.osm2world.core.math.VectorXYZ;
import org.osm2world.core.math.VectorXZ;
import org.osm2world.core.target.Renderable;
import org.osm2world.core.target.common.material.Material;
import org.osm2world.core.world.data.WorldObject;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;

/**
* a target that relies on faces to represent geometry.
* The faces used by this target are polygons
* with three or more coplanar vertices.
*
* TODO: this currently produces faces that are not convex
*/
public abstract class FaceTarget<R extends Renderable>
  extends AbstractTarget<R> {
 
  abstract public void drawFace(Material material, List<VectorXYZ> vs,
      List<VectorXYZ> normals, List<List<VectorXZ>> texCoordLists);
 
  /**
   * decides whether faces should be reconstructed from triangulations
   * and other primitives.
   */
  abstract public boolean reconstructFaces();
 
  /**
   * prevents triangles from before the call to be connected with triangles
   * after this call when faces are reconstructed.
   *
   * This is automatically done at the beginning of each new object.
   * It only has any effect if {@link #reconstructFaces()} is enabled.
   *
   * Calling this method at appropriate times can also help to speed up
   * performance by lowering the number of candidates for merging.
   */
  public void flushReconstructedFaces() {
    drawAndClearCurrentFaces();
  }
 
  /**
   * mutable representation of a face
   */
  protected final static class Face {
   
    public final List<VectorXYZ> vs;
    public final List<List<VectorXZ>> texCoordLists;
    public final VectorXYZ normal;
   
    public Face(List<VectorXYZ> vs,
        List<List<VectorXZ>> texCoordLists, VectorXYZ normal) {
     
      this.vs = vs;
      this.texCoordLists = texCoordLists;
      this.normal = normal;
     
    }
   
    /**
     * @return  true if the triangle has been successfully inserted
     */
    public boolean tryInsert(IsolatedTriangle t) {
     
      for (int i = 0; i < vs.size(); i++) {
        int j = (i+1) % vs.size();
        int k = (i+2) % vs.size();
       
        if (vs.get(i).equals(t.triangle.v3)
            && vs.get(j).equals(t.triangle.v2)) { /* TODO tex coords equal */
         
          if (vs.get(k).equals(t.triangle.v1)) {
           
            removeVertex(j);
           
          } else {
           
            insertVertex(j, t.triangle.v1,
                texCoordLists, t.texCoordOffset + 0);
           
          }
         
          return true;
         
        }
       
        if (vs.get(i).equals(t.triangle.v1)
            && vs.get(j).equals(t.triangle.v3)) { /* TODO tex coords equal */
         
          if (vs.get(k).equals(t.triangle.v2)) {
           
            removeVertex(j);
           
          } else {
           
            insertVertex(j, t.triangle.v2,
                texCoordLists, t.texCoordOffset + 1);
           
          }
         
          return true;
         
        }
       
        if (vs.get(i).equals(t.triangle.v2)
            && vs.get(j).equals(t.triangle.v1)) { /* TODO tex coords equal */
         
          if (vs.get(k).equals(t.triangle.v3)) {
           
            removeVertex(j);
           
          } else {
           
            insertVertex(j, t.triangle.v3,
                texCoordLists, t.texCoordOffset + 2);
           
          }
         
          return true;
         
        }
       
      }
     
      return false;
     
    }
   
    public void removeDuplicateEdges() {
     
      boolean repeat = true;
     
      while (repeat) {
       
        repeat = false;
       
        assert vs.size() >= 3;
       
        for (int i = 0; i < vs.size(); i++) {
          int j = (i+1) % vs.size();
          int k = (i+2) % vs.size();
         
          //TODO: what about tex coords?
          if (vs.get(i).equals(vs.get(k))) {
           
            if (k > j) {
              removeVertex(k);
              removeVertex(j);
            } else {
              removeVertex(j);
              removeVertex(k);
            }
           
            repeat = true;
            break;
           
          }
         
        }
       
      }
     
    }
   
    private void insertVertex(int i, VectorXYZ vertex,
        List<List<VectorXZ>> insTexCoordLists, int texCoordPos) {
     
      this.vs.add(i, vertex);
     
      for (int list = 0; list < texCoordLists.size(); list++) {
       
        this.texCoordLists.get(list).add(i,
            insTexCoordLists.get(list).get(texCoordPos));
      }
     
    }
   
    private void removeVertex(int i) {
     
      this.vs.remove(i);
     
      for (int list = 0; list < texCoordLists.size(); list++) {
        this.texCoordLists.get(list).remove(i);
      }
     
    }
   
    @Override
    public String toString() {
      return vs.toString();
    }
   
  }
 
  protected final static class IsolatedTriangle {
   
    public final TriangleXYZ triangle;
    public final VectorXYZ normal;
    public final int texCoordOffset;
    public final List<List<VectorXZ>> texCoordLists;
   
    public IsolatedTriangle(TriangleXYZ triangle, VectorXYZ normal,
        int texCoordOffset, List<List<VectorXZ>> texCoordLists) {
     
      this.triangle = triangle;
      this.normal = normal;
      this.texCoordOffset = texCoordOffset;
      this.texCoordLists = texCoordLists;
     
    }
   
    @Override
    public String toString() {
      return triangle.toString();
    }
   
  }
 
  private final Multimap<Material, IsolatedTriangle> isolatedTriangles =
      HashMultimap.create();
 
  @Override
  public void drawTriangles(Material material,
      Collection<? extends TriangleXYZ> triangles,
      List<List<VectorXZ>> texCoordLists) {
   
    int i = 0;
   
    for (TriangleXYZ triangle : triangles) {
     
      if (reconstructFaces()) {
       
        VectorXYZ n = triangle.getNormal();
       
        if (Double.isNaN(n.x) || Double.isNaN(n.y) || Double.isNaN(n.z)) {
          continue; //TODO log
        }
       
        isolatedTriangles.put(material,
            new IsolatedTriangle(triangle, n, i*3, texCoordLists));
       
      } else {
       
        List<List<VectorXZ>> subLists = new ArrayList<List<VectorXZ>>();
        for (List<VectorXZ> list : texCoordLists) {
          subLists.add(list.subList(3*i, 3*(i+1)));
        }
       
        drawFace(material, triangle.getVertices(), null, subLists);
      }
     
      i++;
    }
  }
 
  @Override
  public void drawTrianglesWithNormals(Material material,
      Collection<? extends TriangleXYZWithNormals> triangles,
      List<List<VectorXZ>> texCoordLists) {
   
    drawTriangles(material, triangles, texCoordLists);
   
    //TODO keep normals information
   
  }
 
  @Override
  public void drawConvexPolygon(Material material, List<VectorXYZ> vs,
      List<List<VectorXZ>> texCoordLists) {
   
    if (reconstructFaces()) {
      super.drawConvexPolygon(material, vs, texCoordLists);
    } else {
      drawFace(material, vs, null, texCoordLists);
    }
   
  }
 
  @Override
  public void beginObject(WorldObject object) {
   
    drawAndClearCurrentFaces();
   
  }
 
  @Override
  public void finish() {
   
    drawAndClearCurrentFaces();
   
  }
 
  /**
   * integrates all {@link #isolatedTriangles} into faces,
   * then draws all faces and clears the collection.
   */
  private void drawAndClearCurrentFaces() {
   
    for (Material material : isolatedTriangles.keySet()) {
     
      Collection<Face> faces = combineTrianglesToFaces(isolatedTriangles.get(material));
     
      /* draw faces */
     
      for (Face face : faces) {
        drawFace(material, face.vs,
            nCopies(face.vs.size(), face.normal),
            face.texCoordLists);
      }
     
    }
   
    isolatedTriangles.clear();
   
  }
 
  /**
   * @param isolatedTriangles  non-empty collection of triangles
   */
  protected static Collection<Face> combineTrianglesToFaces(
      Collection<IsolatedTriangle> isolatedTriangles) {
   
    List<IsolatedTriangle> triangles =
        new LinkedList<IsolatedTriangle>(isolatedTriangles);
   
    Collection<Face> faces = new ArrayList<Face>();
   
    /* turn one triangle into a face */
   
    faces.add(createFaceFromTriangle(triangles.remove(0)));
   
    /* turn remaining triangles into faces or insert them into existing ones */
   
    trianglesToFacesLoop: while (!triangles.isEmpty()) {
     
      /* try to insert triangles into existing faces */
     
      for (IsolatedTriangle triangle : triangles) {
        for (Face face : faces) {
         
          if (normalAlmostEquals(face.normal, triangle.normal)) {
           
            boolean inserted = face.tryInsert(triangle);
           
            if (inserted) {
              triangles.remove(triangle);
              continue trianglesToFacesLoop;
            }
           
          }
         
        }
      }
     
      /* could not extend existing faces, start a new face instead */
     
      faces.add(createFaceFromTriangle(triangles.remove(0)));
     
    }
   
    /* eliminate duplicate edges */
   
    for (Face face : faces) {
      face.removeDuplicateEdges();
    }
   
    return faces;
   
  }
 
  protected static boolean normalAlmostEquals(VectorXYZ n1, VectorXYZ n2) {
   
    return abs(n1.x - n2.x) <= 0.01
        && abs(n1.y - n2.y) <= 0.01
        && abs(n1.z - n2.z) <= 0.01;
   
  }
 
  protected static Face createFaceFromTriangle(IsolatedTriangle t) {
   
    List<VectorXYZ> newFaceVs =
        new ArrayList<VectorXYZ>(t.triangle.getVertices());
   
    List<List<VectorXZ>> newFaceTCLists = new ArrayList<List<VectorXZ>>();
   
    for (int list = 0; list < t.texCoordLists.size(); list++) {
      newFaceTCLists.add(new ArrayList<VectorXZ>(
          t.texCoordLists.get(list).subList(
              t.texCoordOffset, t.texCoordOffset + 3)));
    }
   
    return new Face(newFaceVs,  newFaceTCLists, t.normal);
   
  }
 
}
TOP

Related Classes of org.osm2world.core.target.common.FaceTarget$IsolatedTriangle

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.