Package org.osm2world.core.target.povray

Source Code of org.osm2world.core.target.povray.POVRayTarget

package org.osm2world.core.target.povray;

import java.awt.Color;
import java.io.PrintStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

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.common.AbstractTarget;
import org.osm2world.core.target.common.TextureData;
import org.osm2world.core.target.common.material.Material;
import org.osm2world.core.target.common.material.Materials;

public class POVRayTarget extends AbstractTarget<RenderableToPOVRay> {
 
  private static final String INDENT = "  ";
 
  // this is approximatly one millimeter
  private static final double SMALL_OFFSET = 1e-3;
 
  private final PrintStream output;
 
  private Map<TextureData, String> textureNames = new HashMap<TextureData, String>();
 
  public POVRayTarget(PrintStream output) {
    this.output = output;
  }
 
  @Override
  public Class<RenderableToPOVRay> getRenderableType() {
    return RenderableToPOVRay.class;
  }
 
  @Override
  public void render(RenderableToPOVRay renderable) {
    renderable.renderTo(this);
  }
 
//  int openBrackets = 0;
//
//  /**
//   * appends indentation based on {@link #INDENT} and {@link #openBrackets}
//   */
//  private void appendIndent() {
//    for (int i=0; i<openBrackets; i++) {
//      append(INDENT);
//    }
//  }
 
  /**
   * provides direct write access to the generated source code.
   * This is intended for Renderables using special POVRay features.
   */
  public void append(String code) {
    output.print(code);
//    if (code.contains("union") && openBrackets > 0) {
//      System.out.println(openBrackets);
//    }
//    for (int i=0; i<code.length(); i++) {
//      char c = code.charAt(i);
//      if (c == '{') {
//        openBrackets++;
//      } else if (c == '}') {
//        openBrackets--;
//      }
//    }
  }

  public void append(int value) {
    output.print(value);
  }
 
  public void append(double value) {
    output.print(value);
  }
 
//  private final LinkedList<StringBuilder> stack = new LinkedList<StringBuilder>();
//
//  int openBrackets = 0;
//
//  public void append(int value) {
//    stack.peek().append(value);
//  }
//
//  public void append(double value) {
//    stack.peek().append(value);
//  }
//
//  public void startBlock(String s) {
//    StringBuilder newBlock = new StringBuilder(s + "{");
//    stack.push(newBlock);
//  }
//
//  public void endBlock(String block) {
//    StringBuilder closedBlock = stack.poll();
//    if (stack.isEmpty()) {
//      output.append(closedBlock);
//    } else {
//      stack.peek().append(closedBlock);
//    }
//  }

  public void appendDefaultParameterValue(String name, String value) {
   
    append("#ifndef (" + name + ")\n");
    append("#declare " + name + " = " + value);
    append("\n#end\n\n");
   
  }
 
  public void appendMaterialDefinitions() {
   
    for (Material material : Materials.getMaterials()) {
     
      String uniqueName = Materials.getUniqueName(material);
      String name = "texture_" + uniqueName;
     
      append("#ifndef (" + name + ")\n");
      append("#declare " + name + " = ");
      appendMaterial(material);
      append("#end\n\n");
     
      if (material.getNumTextureLayers() == 1) {
       
        TextureData td = material.getTextureDataList().get(0);
       
        if (!td.colorable) {
          textureNames.put(td, uniqueName);
        }
       
      }
     
    }
   
  }

  @Override
  public void drawTriangles(Material material,
      Collection<? extends TriangleXYZ> triangles,
      List<List<VectorXZ>> texCoordLists) {

    if (!checkMeshValidity(triangles))
      return;
   
    for (TriangleXYZ triangle : triangles) {
      performNaNCheck(triangle);
    }
   
    if (material.getNumTextureLayers() > 1) {
     
      int count = 0;
     
      for (TextureData textureData : material.getTextureDataList()) {

        append("mesh {\n");

        drawTriangleMesh(triangles, texCoordLists.get(count), count);

        append("  uv_mapping ");
        appendMaterial(material, textureData);
       
        if (count > 0)
          append("  no_shadow");
        append("}\n");
        count++;
      }
    } else {

      append("mesh {\n");
   
      if (texCoordLists.size() > 0) {
        drawTriangleMesh(triangles, texCoordLists.get(0), 0);
      } else {
        for (TriangleXYZ triangle : triangles) {
          append(INDENT);
          appendTriangle(triangle.v1, triangle.v2, triangle.v3);
        }
      }
     
      append(" uv_mapping ");
      appendMaterialOrName(material);
   
      append("}\n");
    }
  }
   
  @Override
  public void drawTrianglesWithNormals(Material material,
      Collection<? extends TriangleXYZWithNormals> triangles,
      List<List<VectorXZ>> texCoordLists) {

    if (!checkMeshValidity(triangles))
      return;

    if (material.getNumTextureLayers() > 1) {
     
      int count = 0;
     
      for (TextureData textureData : material.getTextureDataList()) {
       
        append("mesh {\n");

        drawTriangleNormalMesh(triangles, texCoordLists.get(count), count);

        append("  uv_mapping ");
        appendMaterial(material, textureData);
       
        if (count > 0)
          append("  no_shadow");
        append("}\n");
        count++;
      }
    } else {
     
      append("mesh {\n");

      if (texCoordLists.size() > 0) {
        drawTriangleNormalMesh(triangles, texCoordLists.get(0), 0);
      } else {
        for (TriangleXYZWithNormals triangle : triangles) {
          append(INDENT);
          appendTriangle(triangle.v1, triangle.v2, triangle.v3,
              triangle.n1, triangle.n2, triangle.n3, true);
        }
      }

      append(" uv_mapping ");
      appendMaterialOrName(material);
      append("}\n");
    }
  }

  private void drawTriangleNormalMesh(Collection<? extends TriangleXYZWithNormals> triangles,
      List<VectorXZ> texCoordList, int depth) {

    Iterator<? extends TriangleXYZWithNormals> itr1 = triangles.iterator();
    Iterator<VectorXZ> itr2 = texCoordList.iterator();
   
    while (itr1.hasNext()) {
   
      TriangleXYZWithNormals triangle = itr1.next();
      VectorXYZ n1 = triangle.n1;
      VectorXYZ n2 = triangle.n2;
      VectorXYZ n3 = triangle.n3;
      VectorXZ tex1 = itr2.next();
      VectorXZ tex2 = itr2.next();
      VectorXZ tex3 = itr2.next();
     
      append(INDENT);
   
      if (depth > 0) {
     
        VectorXYZ d1 = n1.mult(depth*SMALL_OFFSET);
        VectorXYZ d2 = n2.mult(depth*SMALL_OFFSET);
        VectorXYZ d3 = n3.mult(depth*SMALL_OFFSET);
       
        appendTriangle(
            triangle.v1.add(d1),
            triangle.v2.add(d2),
            triangle.v3.add(d3),
            n1, n2, n3, tex1, tex2, tex3, false, true);
     
      } else {
     
        appendTriangle(triangle.v1, triangle.v2, triangle.v3,
            null, null, null, tex1, tex2, tex3, false, true);
      }
    }
  }

  private void drawTriangleMesh(Collection<? extends TriangleXYZ> triangles,
      List<VectorXZ> texCoordList, int depth) {

    Iterator<? extends TriangleXYZ> itr1 = triangles.iterator();
    Iterator<VectorXZ> itr2 = texCoordList.iterator();
   
    while (itr1.hasNext()) {
   
      TriangleXYZ triangle = itr1.next();
      VectorXYZ normal = triangle.getNormal();
      VectorXZ tex1 = itr2.next();
      VectorXZ tex2 = itr2.next();
      VectorXZ tex3 = itr2.next();
   
      append(INDENT);
   
      if (depth > 0) {
     
        normal = normal.mult(depth*SMALL_OFFSET);
        appendTriangle(
            triangle.v1.add(normal),
            triangle.v2.add(normal),
            triangle.v3.add(normal),
            null, null, null, tex1, tex2, tex3, false, true);
     
      } else {
     
        appendTriangle(triangle.v1, triangle.v2, triangle.v3,
            null, null, null, tex1, tex2, tex3, false, true);
      }
    }
  }
 
//  @Override
//  public void drawTriangleStrip(Material material, VectorXYZ... vectors) {
//
//    for (VectorXYZ vector : vectors) {
//      performNaNCheck(vector);
//    }
//
//    append("union {\n");
//
//    for (int triangle = 0; triangle + 2 < vectors.length; triangle++) {
//
//      append(INDENT);
//
//      appendTriangle(
//          vectors[triangle],
//          vectors[triangle + 1],
//          vectors[triangle + 2]);
//
//    }
//
//    appendMaterial(material);
//
//    append("}\n");
//
//  }
 
//  @Override
//  public void drawTriangleFan(Material material, List<? extends VectorXYZ> vs) {
//    for (VectorXYZ vector : vs) {
//      performNaNCheck(vector);
//    }
//
//    append("union {\n");
//
//    VectorXYZ center = vs.get(0);
//
//    for (int triangle = 0; triangle + 2 < vs.size(); triangle ++) {
//
//      append(INDENT);
//
//      appendTriangle(
//          center,
//          vs.get(triangle + 1),
//          vs.get(triangle + 2));
//
//    }
//
//    appendMaterial(material);
//
//    append("}\n");
//
//  }

  @Override
  public void drawConvexPolygon(Material material, List<VectorXYZ> vs,
      List<List<VectorXZ>> texCoordLists) {
   
    for (VectorXYZ vector : vs) {
      performNaNCheck(vector);
    }

    append("polygon {\n  ");
    append(vs.size());
    append(", ");
    for (VectorXYZ v : vs) {
      appendVector(v);
    }
   
    appendMaterialOrName(material);
   
    append("}\n");

  }

  @Override
  public void drawColumn(Material material, Integer corners, VectorXYZ base,
      double height, double radiusBottom, double radiusTop,
      boolean drawBottom, boolean drawTop) {

    performNaNCheck(base);
   
    if (height <= 0) return;

    if (corners == null) {

      if (radiusBottom == radiusTop) { // cylinder

        append("cylinder {\n  ");
        appendVector(base);
        append(", ");
        appendVector(base.y(base.y + height));
        append(", ");
        append(radiusTop);

      } else { // (truncated) cone

        append("cone {\n  ");
        appendVector(base);
        append(", "); append(radiusBottom); append(", ");
        appendVector(base.y(base.y + height));
        append(", "); append(radiusTop);

      }

      if (!drawBottom && !drawTop) {
        // TODO: incorrect if only one is false
        append(" open");
      }
     
      appendMaterialOrName(material);
     
      append("}\n");

    } else { // not round

      super.drawColumn(material, corners, base, height, radiusBottom, radiusTop, drawBottom, drawTop);

    }

  }

  /**
   * variant of {@link #drawColumn(Material, Integer, VectorXYZ, double, double, double, boolean, boolean)}
   * that allows arbitrarily placed columns
   */
  public void drawColumn(Material material, Integer corners, VectorXYZ base,
      VectorXYZ cap, double radiusBottom, double radiusTop,
      boolean drawBottom, boolean drawTop) {

    performNaNCheck(base);
    performNaNCheck(cap);
   
    if (cap.equals(base))
      return;

    if (corners == null) {

      if (radiusBottom == radiusTop) { // cylinder

        append("cylinder {\n  ");
        appendVector(base);
        append(", ");
        appendVector(cap);
        append(", "); append(radiusTop);

      } else { // (truncated) cone

        append("cone {\n  ");
        appendVector(base);
        append(", "); append(radiusBottom); append(", ");
        appendVector(cap);
        append(", "); append(radiusTop);

      }

      if (!drawBottom && !drawTop) {
        // TODO: incorrect if only one is false
        append(" open");
      }

    } else { // not round

      throw new UnsupportedOperationException(
          "drawing non-round columns isn't implemented yet");

    }
   
    appendMaterialOrName(material);
   
    append("}\n");

  }
 
  private boolean checkMeshValidity(Collection<? extends TriangleXYZ> triangles) {
   
    if (triangles.size() == 0)
      return false;
   
    boolean result = false;
    for (TriangleXYZ triangle : triangles) {

      result |= !isDegenerated(triangle);
    }
   
    return result;
  }
 
  private boolean isDegenerated(TriangleXYZ triangle) {

    VectorXYZ a = triangle.v1;
    VectorXYZ b = triangle.v2;
    VectorXYZ c = triangle.v3;
   
    if (a.equals(b) || a.equals(c) || b.equals(c)) {
      return true;
    } else if (a.x == b.x && b.x == c.x
        && a.y == b.y && b.y == c.y) {
      return true;
    } else if (a.x == b.x && b.x == c.x
        && a.z == b.z && b.z == c.z) {
      return true;
    } else if (a.y == b.y && b.y == c.y
        && a.z == b.z && b.z == c.z) {
      return true;
    }
    return false;
  }

 
  public void appendTriangle(VectorXYZ a, VectorXYZ b, VectorXYZ c) {

    appendTriangle(a, b, c, null, null, null, false);
  }

  public void appendTriangle(
      VectorXYZ a, VectorXYZ b, VectorXYZ c,
      VectorXYZ na, VectorXYZ nb, VectorXYZ nc,
      boolean smooth) {

    appendTriangle(a, b, c, na, nb, nc, null, null, null, smooth, false);
  }
   
  public void appendTriangle(
      VectorXYZ a, VectorXYZ b, VectorXYZ c,
      VectorXYZ na, VectorXYZ nb, VectorXYZ nc,
      VectorXZ ta, VectorXZ tb, VectorXZ tc,
      boolean smooth, boolean texture) {

    // append the triangle
   
    if (smooth) append("smooth_");
    append("triangle { ");
    appendVector(a);
    if (smooth) {
      append(", ");
      appendVector(na);
    }
    append(", ");
    appendVector(b);
    if (smooth) {
      append(", ");
      appendVector(nb);
    }
    append(", ");
    appendVector(c);
    if (smooth) {
      append(", ");
      appendVector(nc);
    }

    if (texture) {
      /*
      append(" uv_vectors ");
      appendInverseVector(ta);
      append(", ");
      appendInverseVector(tb);
      append(", ");
      appendInverseVector(tc);
      */
     
      append(" uv_vectors ");
      appendVector(ta);
      append(", ");
      appendVector(tb);
      append(", ");
      appendVector(tc);
    }

    append("}\n");
  }
 

  /**
   * adds a color. Syntax is "color rgb <x, y, z>".
   */
  public void appendRGBColor(Color color) {
   
    append("color rgb ");
    appendVector(
        color.getRed()/255f,
        color.getGreen()/255f,
        color.getBlue()/255f);
   
  }
 
  public void appendMaterialOrName(Material material) {
   
    String materialName = Materials.getUniqueName(material);

    if (materialName != null) {
      append(" texture { texture_" + materialName + " }");
    } else {
      appendMaterial(material);
    }
   
  }

  private void appendMaterial(Material material) {

    if (material.getNumTextureLayers() == 0) {

      append("  texture {\n");
      append("    pigment { ");
      appendRGBColor(material.getColor());
      append(" }\n    finish {\n");
      append("      ambient " + material.getAmbientFactor() + "\n");
      append("      diffuse " + material.getDiffuseFactor() + "\n");
      append("    }\n");
      append("  }\n");

    } else {

      for (TextureData textureData : material.getTextureDataList()) {

        appendMaterial(material, textureData);
      }
    }
  }

 
  private void appendMaterial(Material material, TextureData textureData) {

    String textureName = textureNames.get(textureData);
   
    if (textureName == null) {

      if (textureData.colorable) {
        append("  texture {\n");
        append("    pigment { ");
        appendRGBColor(material.getColor());
        append(" }\n    finish {\n");
        append("      ambient " + material.getAmbientFactor() + "\n");
        append("      diffuse " + material.getDiffuseFactor() + "\n");
        append("    }\n");
        append("  }\n");
      }

      append("  texture {\n");
      append("    pigment { ");
      appendImageMap(textureData);
      append(" }\n    finish {\n");
      append("      ambient " + material.getAmbientFactor() + "\n");
      append("      diffuse " + material.getDiffuseFactor() + "\n");
      append("    }\n");
      append("  }\n");
 
    } else {

      append("  texture { texture_" + textureName + "}");
    }
  }


  private void appendImageMap(TextureData textureData) {

    append("        image_map {\n");

    if (textureData.file.getName().toLowerCase().endsWith("png")) {
      append("             png \"" + textureData.file + "\"\n");
    } else {
      append("             jpeg \"" + textureData.file + "\"\n");
    }

    if (textureData.colorable) {
      append("             filter all 1.0\n");
    }
    append("\n          }");
  }
 
  /**
   * adds a vector to the String built by a StringBuilder.
   * Syntax is "<x, y, z>".
   */
  public void appendVector(float x, float y, float z) {

    if (Float.isNaN(x) || Float.isNaN(y) || Float.isNaN(z)) {
      throw new IllegalArgumentException("NaN vector " + x + ", " + y + ", " + z);
    }
   
    append("<");
    append(x);
    append(", ");
    append(y);
    append(", ");
    append(z);
    append(">");
   
  }

  public void appendVector(double x, double y, double z) {
   
    if (Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z)) {
      throw new IllegalArgumentException("NaN vector " + x + ", " + y + ", " + z);
    }
   
    append("<");
    append(x);
    append(", ");
    append(y);
    append(", ");
    append(z);
    append(">");
   
  }
 
  /**
   * alternative to {@link #appendVector(double, double)}
   * using a vector object as parameter instead of individual coordinates
   */
  public void appendVector(VectorXYZ vector) {
    appendVector(vector.getX(), vector.getY(), vector.getZ());
  }
 
  /**
   * adds a vector to the String built by a StringBuilder.
   * Syntax is "<v1, v2>".
   */
  public void appendVector(double x, double z) {
   
    append("<");
    append(x);
    append(", ");
    append(z);
    append(">");
   
  }
 
  /**
   * alternative to {@link #appendVector(double, double)}
   * using a vector object as parameter instead of individual coordinates
   */
  public void appendVector(VectorXZ vector) {
    appendVector(vector.x, vector.z);
  }

  /**
   * append a vector with inverted coordinates
   */
  public void appendInverseVector(VectorXZ vector) {
    appendVector(-vector.x, -vector.z);
  }

  /**
   *
   * @param vs  polygon vertices; first and last should be equal
   */
  public void appendPolygon(VectorXYZ... vs) {
   
    assert !vs[0].equals(vs[vs.length-1]) : "polygon not closed";

    append("polygon {\n  ");
    append(vs.length);
    append(", ");
    for (VectorXYZ v : vs) {
      appendVector(v);
    }
    append("}\n");
   
  }
 
  public void appendPrism(float y1, float y2, VectorXZ... vs) {
   
    append("prism {\n  ");
    append(y1);
    append(", ");
    append(y2);
    append(", ");
    append(vs.length);
    append(",\n  ");
    for (VectorXZ v : vs) {
      appendVector(v);
    }
    append("\n}");
       
  }
 
  //TODO: avoid having to do this

  private void performNaNCheck(TriangleXYZWithNormals triangle) {
    performNaNCheck(triangle.v1);
    performNaNCheck(triangle.v2);
    performNaNCheck(triangle.v3);
    performNaNCheck(triangle.n1);
    performNaNCheck(triangle.n2);
    performNaNCheck(triangle.n3);
  }
 
  private void performNaNCheck(TriangleXYZ triangle) {
    performNaNCheck(triangle.v1);
    performNaNCheck(triangle.v2);
    performNaNCheck(triangle.v3);
  }

  private void performNaNCheck(VectorXYZ v) {
    if (Double.isNaN(v.x) || Double.isNaN(v.y) || Double.isNaN(v.z)) {
      throw new IllegalArgumentException("NaN vector " + v.x + ", " + v.y + ", " + v.z);
    }
  }
 
}
TOP

Related Classes of org.osm2world.core.target.povray.POVRayTarget

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.