package tinyray.language;
import java.util.Iterator;
public class VRMLCode implements Visitor<String, String> {
// Der Zeilenumbruch des zugrundeliegenden Systems.
private final static String nl() {
return System.getProperty("line.separator");
}
// Die Einrückung.
private final static String INDENT = "\t";
// Die globalen Parameter.
private Defaults defaults;
public VRMLCode() {
// Setzt globale Parameter
this.defaults = new Defaults();
// color = [0.5, 0.5, 0.5]
this.defaults.parameters.visitables.add(new Vector(0.5, 0.5, 0.5));
// ambient = [0.1, 0.1, 0.1]
this.defaults.parameters.visitables.add(new Vector(0.1, 0.1, 0.1));
// specular = 0.5
this.defaults.parameters.visitables.add(new Real(0.5));
// shininess = 1000.0
this.defaults.parameters.visitables.add(new Real(1000.0));
// mirror = 0.4
this.defaults.parameters.visitables.add(new Real(0.4));
}
@Override
public String visit(Tinyray tinyray, String indent) {
StringBuilder result = new StringBuilder();
result.append(indent);
result.append("#VRML V2.0 utf8");
result.append(nl());
result.append(nl()).append(indent);
result.append("# Automatisch generierter VRML-Code.");
result.append(nl()).append(indent);
result.append("# Herkunft: Tinyray (http://www.stefan-baur.de)");
result.append(nl());
// Camera
result.append(nl());
result.append(tinyray.camera.accept(this, indent));
// Background
result.append(nl());
result.append(tinyray.background.accept(this, indent));
// Fog
if (tinyray.fog.density.value > 0.0) {
result.append(nl());
result.append(tinyray.fog.accept(this, indent));
}
// Ambience (global)
result.append(nl());
result.append(tinyray.ambience.accept(this, indent));
// Suns
Iterator<Sun> suns = tinyray.suns.iterator();
while (suns.hasNext()) {
result.append(nl());
result.append(suns.next().accept(this, indent));
}
// Geos
Iterator<Visitable> visitables = tinyray.visitables.iterator();
while (visitables.hasNext()) {
result.append(nl());
result.append(visitables.next().accept(this, indent));
}
// PrettyPrint
result.append(nl()).append(nl()).append(indent);
result.append("# Quelle: Tinyray-Code");
result.append(nl()).append(indent);
result.append('#');
result.append(nl());
result.append(tinyray.accept(new PrettyPrint(), indent + "# "));
result.append(nl());
return result.toString();
}
@Override
public String visit(Camera camera, String indent) {
StringBuilder result = new StringBuilder();
result.append(indent);
result.append("Viewpoint {");
result.append(nl()).append(indent).append(INDENT);
result.append("fieldOfView ");
result.append(30.0 * Math.PI / 180.0);
result.append(nl()).append(indent).append(INDENT);
result.append("position ");
result.append(camera.location.accept(this, ""));
// (location, lookat, up) -> (direction, rotation) = orientation
Vector a = Vector.norm(Vector.sub(camera.lookAt, camera.location));
Vector b = new Vector(a.y.value, -a.x.value, 0.0);
Vector c = new Vector(0.0, 1.0, 0.0);
double d = 1.0;
if (!Vector.isTiny(b)) {
double e = 0.5 * Math.acos(Math.max(-1.0, Math.min(1.0, -a.z.value)));
c = Vector.mult(Math.sin(e), Vector.norm(b));
d = Math.cos(e);
}
Vector f = new Vector(0.0, 1.0, 0.0);
Vector g = Vector.add(Vector.mult(d, f), Vector.cross(c, f));
Vector h = Vector.anti(c);
Vector i = Vector.add(Vector.mult(-Vector.dot(c, f), h),
Vector.add(Vector.mult(d, g), Vector.cross(g, h)));
Vector j = Vector.norm(camera.up);
Vector k = Vector.norm(Vector.sub(j, Vector.mult(Vector.dot(j, a), a)));
Vector l = Vector.cross(i, k);
if (Vector.isTiny(l)) l = new Vector(0.0, -k.z.value, k.y.value);
if (Vector.isTiny(l)) l = new Vector(k.z.value, 0.0, -k.x.value);
double m = 0.5 * Math.acos(Math.max(-1.0, Math.min(1.0, Vector.dot(i, k))));
Vector n = Vector.mult(Math.sin(m), Vector.norm(l));
double o = Math.cos(m);
Vector p = Vector.add(Vector.mult(o, c),
Vector.add(Vector.mult(d, n), Vector.cross(n, c)));
double q = Math.acos(o * d - Vector.dot(n, c));
double r = Math.sin(q);
// direction, rotation
Vector direction = (r != 0.0) ? Vector.mult(1.0 / r, p) : f;
double rotation = 2.0 * q;
result.append(nl()).append(indent).append(INDENT);
result.append("orientation");
result.append(nl()).append(indent).append(INDENT).append(INDENT);
result.append(direction.x.accept(this, ""));
result.append(nl()).append(indent).append(INDENT).append(INDENT);
result.append(direction.y.accept(this, ""));
result.append(nl()).append(indent).append(INDENT).append(INDENT);
result.append(direction.z.accept(this, ""));
result.append(nl()).append(indent).append(INDENT).append(INDENT);
result.append(rotation);
result.append(nl()).append(indent);
result.append("}");
return result.toString();
}
@Override
public String visit(Background background, String indent) {
StringBuilder result = new StringBuilder();
result.append(indent);
result.append("Background { skyColor ");
result.append(background.color.accept(this, ""));
result.append(" }");
return result.toString();
}
@Override
public String visit(Ambience ambience, String indent) {
return indent + "# global ambience is ignored";
}
@Override
public String visit(Fog fog, String indent) {
StringBuilder result = new StringBuilder();
result.append(indent);
result.append("Fog {");
result.append(nl()).append(indent).append(INDENT);
result.append("color ");
result.append(fog.color.accept(this, ""));
result.append(nl()).append(indent).append(INDENT);
result.append("fogType \"LINEAR\"");
if (fog.density.value != 0.0) {
result.append(nl()).append(indent).append(INDENT);
result.append("visibilityRange ");
result.append(2.0 / fog.density.value);
}
result.append(nl()).append(indent);
result.append('}');
return result.toString();
}
@Override
public String visit(Sun sun, String indent) {
StringBuilder result = new StringBuilder();
result.append(indent);
result.append("DirectionalLight {");
result.append(nl()).append(indent).append(INDENT);
result.append("color ");
result.append(sun.color.accept(this, ""));
result.append(nl()).append(indent).append(INDENT);
result.append("intensity 0.75");
result.append(nl()).append(indent).append(INDENT);
result.append("direction ");
result.append(Vector.anti(sun.direction).accept(this, ""));
result.append(nl()).append(indent);
result.append('}');
return result.toString();
}
@Override
public String visit(Bounding bounding, String indent) {
StringBuilder result = new StringBuilder();
Iterator<Visitable> visitables = bounding.visitables.iterator();
while (visitables.hasNext()) {
result.append(nl());
result.append(visitables.next().accept(this, indent));
}
return result.toString();
}
@Override
public String visit(Triangle triangle, String indent) {
StringBuilder result = new StringBuilder();
result.append(indent);
result.append("Shape {");
result.append(nl());
result.append(triangle.parameters.accept(this, indent + INDENT));
result.append(nl()).append(indent).append(INDENT);
result.append("geometry IndexedFaceSet {");
result.append(nl()).append(indent).append(INDENT).append(INDENT);
result.append("solid FALSE");
result.append(nl()).append(indent).append(INDENT).append(INDENT);
result.append("coord Coordinate {");
result.append(nl()).append(indent).append(INDENT).append(INDENT);
result.append(INDENT);
result.append("point [");
result.append(nl()).append(indent).append(INDENT).append(INDENT);
result.append(INDENT).append(INDENT);
result.append(triangle.x.accept(this, ""));
result.append(", # 0");
result.append(nl()).append(indent).append(INDENT).append(INDENT);
result.append(INDENT).append(INDENT);
result.append(triangle.y.accept(this, ""));
result.append(", # 1");
result.append(nl()).append(indent).append(INDENT).append(INDENT);
result.append(INDENT).append(INDENT);
result.append(triangle.z.accept(this, ""));
result.append(" # 2");
result.append(nl()).append(indent).append(INDENT).append(INDENT);
result.append(INDENT);
result.append(']');
result.append(nl()).append(indent).append(INDENT).append(INDENT);
result.append('}');
result.append(nl()).append(indent).append(INDENT).append(INDENT);
result.append("coordIndex [ 0, 1, 2, 0, -1 ]");
result.append(nl()).append(indent).append(INDENT);
result.append('}');
result.append(nl()).append(indent);
result.append('}');
return result.toString();
}
@Override
public String visit(Sphere sphere, String indent) {
StringBuilder result = new StringBuilder();
result.append(indent);
result.append("Transform {");
result.append(nl()).append(indent).append(INDENT);
result.append("translation ");
result.append(sphere.center.accept(this, ""));
result.append(nl()).append(indent).append(INDENT);
result.append("children Shape {");
result.append(nl());
result.append(sphere.parameters.accept(this, indent + INDENT + INDENT));
result.append(nl()).append(indent).append(INDENT).append(INDENT);
result.append("geometry Sphere { radius ");
result.append(sphere.radius.accept(this, ""));
result.append(" }");
result.append(nl()).append(indent).append(INDENT);
result.append('}');
result.append(nl()).append(indent);
result.append('}');
return result.toString();
}
@Override
public String visit(Plane plane, String indent) {
Vector c = Vector.mult(1.0 / 3.0,
Vector.add(plane.x, Vector.add(plane.y, plane.z)));
Vector rx = Vector.mult(10000.0, Vector.sub(plane.x, c));
Vector ry = Vector.mult(10000.0, Vector.sub(plane.y, c));
Vector rz = Vector.mult(10000.0, Vector.sub(plane.z, c));
Triangle triangle = new Triangle();
triangle.x = Vector.add(c, rx);
triangle.y = Vector.add(c, ry);
triangle.z = Vector.add(c, rz);
return triangle.accept(this, indent);
}
@Override
public String visit(Defaults defaults, String indent) {
int globalDefaultsSize = this.defaults.parameters.visitables.size();
int size = defaults.parameters.visitables.size();
for (int i = 0; i < Math.min(globalDefaultsSize, size); i++) {
Visitable v = defaults.parameters.visitables.get(i);
this.defaults.parameters.visitables.set(i, v);
}
return "";
}
@Override
public String visit(Parameters parameters, String indent) {
StringBuilder result = new StringBuilder();
int globalDefaultsSize = this.defaults.parameters.visitables.size();
int parametersSize = parameters.visitables.size();
// Paramter auf Default setzen
Parameters params = new Parameters();
for (int i = 0; i < globalDefaultsSize; i++) {
Visitable v = this.defaults.parameters.visitables.get(i);
params.visitables.add(v);
}
// Default-Paramter mit parameters überschreiben
for (int i = 0; i < Math.min(globalDefaultsSize, parametersSize); i++) {
Visitable v = parameters.visitables.get(i);
params.visitables.set(i, v);
}
int size = params.visitables.size();
result.append(indent);
result.append("appearance Appearance { ");
result.append(nl()).append(indent).append(INDENT);
result.append("material Material {");
if (0 < size) {
result.append(nl()).append(indent).append(INDENT).append(INDENT);
result.append("diffuseColor ");
result.append(params.visitables.get(0).accept(this, ""));
}
if (1 < size) {
result.append(nl()).append(indent).append(INDENT).append(INDENT);
result.append("emissiveColor ");
result.append(params.visitables.get(1).accept(this, ""));
}
if (2 < size) {
result.append(nl()).append(indent).append(INDENT).append(INDENT);
result.append("specularColor ");
result.append(params.visitables.get(2).accept(this, ""));
result.append(params.visitables.get(2).accept(this, " "));
result.append(params.visitables.get(2).accept(this, " "));
}
if (3 < size) {
result.append(nl()).append(indent).append(INDENT).append(INDENT);
result.append("shininess ");
result.append(params.visitables.get(3).accept(this, ""));
}
if (4 < size) {
result.append(nl()).append(indent).append(INDENT).append(INDENT);
result.append("# mirror/reflection is not supported by VRML");
}
result.append(nl()).append(indent).append(INDENT);
result.append('}');
result.append(nl()).append(indent);
result.append('}');
return result.toString();
}
@Override
public String visit(Vector vector, String indent) {
StringBuilder result = new StringBuilder();
result.append(vector.x.accept(this, indent));
result.append(vector.y.accept(this, " "));
result.append(vector.z.accept(this, " "));
return result.toString();
}
@Override
public String visit(Real real, String indent) {
return indent + real.value;
}
}