00001 package edu.tum.cs.vis.model.parser;
00002
00003 import java.io.File;
00004 import java.io.FileNotFoundException;
00005 import java.io.IOException;
00006 import java.util.ArrayList;
00007 import java.util.HashMap;
00008 import java.util.LinkedList;
00009 import javax.vecmath.Point2f;
00010 import javax.vecmath.Point3f;
00011 import javax.xml.parsers.DocumentBuilder;
00012 import javax.xml.parsers.DocumentBuilderFactory;
00013 import javax.xml.parsers.ParserConfigurationException;
00014
00015 import org.w3c.dom.Document;
00016 import org.w3c.dom.Element;
00017 import org.w3c.dom.NodeList;
00018 import org.xml.sax.SAXException;
00019
00020 import processing.core.PApplet;
00021 import processing.core.PImage;
00022
00023 import com.dddviewr.collada.Collada;
00024 import com.dddviewr.collada.Input;
00025 import com.dddviewr.collada.Source;
00026 import com.dddviewr.collada.Unit;
00027 import com.dddviewr.collada.effects.Blinn;
00028 import com.dddviewr.collada.effects.Effect;
00029 import com.dddviewr.collada.effects.EffectAttribute;
00030 import com.dddviewr.collada.effects.Lambert;
00031 import com.dddviewr.collada.geometry.Geometry;
00032 import com.dddviewr.collada.geometry.Lines;
00033 import com.dddviewr.collada.geometry.Mesh;
00034 import com.dddviewr.collada.geometry.Primitives;
00035 import com.dddviewr.collada.geometry.Triangles;
00036 import com.dddviewr.collada.materials.Material;
00037 import com.dddviewr.collada.nodes.Node;
00038 import com.dddviewr.collada.visualscene.BaseXform;
00039 import com.dddviewr.collada.visualscene.InstanceGeometry;
00040 import com.dddviewr.collada.visualscene.InstanceMaterial;
00041 import com.dddviewr.collada.visualscene.InstanceNode;
00042 import com.dddviewr.collada.visualscene.Matrix;
00043 import com.dddviewr.collada.visualscene.VisualScene;
00044
00045 import edu.tum.cs.vis.model.util.Appearance;
00046 import edu.tum.cs.vis.model.util.Color;
00047 import edu.tum.cs.vis.model.util.DrawObject;
00048 import edu.tum.cs.vis.model.util.Line;
00049 import edu.tum.cs.vis.model.util.Triangle;
00050
00059 public class ColladaParser extends ModelParser {
00060
00066 private String textureBasePath = "";
00067
00072 private Boolean texturesInitialized = false;
00073
00074
00081 private static float[][] SourceToFloat(Source s)
00082 {
00083 float ret[][] = new float[s.getAccessor().getCount()][s.getAccessor().getStride()];
00084 for (int i = 0, k = 0; i<s.getAccessor().getCount(); i++)
00085 {
00086 for (int j = 0; j < s.getAccessor().getStride(); j++, k++)
00087 ret[i][j] = s.getFloatArray().get(k);
00088 }
00089 return ret;
00090 }
00091
00092 @Override
00093 public void draw(PApplet applet) {
00094
00095 if (!texturesInitialized)
00096 setTextureImage(applet);
00097
00098 applet.noStroke();
00099
00100 applet.fill(127);
00101
00102 drawTriangles(applet);
00103 drawLines(applet);
00104 }
00105
00113 private static String getNodeValue(NodeList nList,
00114 LinkedList<String> nodeNames) {
00115 if (nodeNames.isEmpty() || nList == null) {
00116 return null;
00117 }
00118 if (nodeNames.size() == 1) {
00119 for (int temp = 0; temp < nList.getLength(); temp++) {
00120 org.w3c.dom.Node nNode = nList.item(temp);
00121 if (nNode.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
00122 Element element = (Element) nNode;
00123 NodeList nlList = element
00124 .getElementsByTagName(nodeNames
00125 .poll());
00126 if (nlList == null)
00127 return null;
00128 nlList = nlList.item(0).getChildNodes();
00129
00130 org.w3c.dom.Node nValue = (org.w3c.dom.Node) nlList.item(0);
00131 return nValue.getNodeValue();
00132
00133 }
00134 }
00135 } else
00136 {
00137 for (int temp = 0; temp < nList.getLength(); temp++) {
00138
00139 org.w3c.dom.Node nNode = nList.item(temp);
00140
00141 if (nNode.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
00142
00143 Element element = (Element) nNode;
00144
00145 LinkedList<String> tmpNodeNames = new LinkedList<String>();
00146 tmpNodeNames.addAll(nodeNames);
00147 String val = getNodeValue(
00148 element.getElementsByTagName(tmpNodeNames.poll()),
00149 tmpNodeNames);
00150 if (val != null)
00151 return val;
00152 }
00153 }
00154 }
00155 return null;
00156 }
00157
00164 private static String getDaeLocation(String tmpPath) {
00165 File kmlFile = new File(tmpPath + "doc.kml");
00166 DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
00167 DocumentBuilder dBuilder = null;
00168 try {
00169 dBuilder = dbFactory.newDocumentBuilder();
00170 } catch (ParserConfigurationException e) {
00171 System.err.println("Couldn't get .dae location from doc.kml: "
00172 + e.getMessage());
00173 return null;
00174 }
00175 Document doc;
00176 try {
00177 doc = dBuilder.parse(kmlFile);
00178 } catch (SAXException e) {
00179 System.err.println("Couldn't get .dae location from doc.kml: "
00180 + e.getMessage());
00181 return null;
00182 } catch (IOException e) {
00183 System.err.println("Couldn't get .dae location from doc.kml: "
00184 + e.getMessage());
00185 return null;
00186 }
00187 doc.getDocumentElement().normalize();
00188
00189 if (!doc.getDocumentElement().getNodeName().equalsIgnoreCase("kml")) {
00190 System.out.println("Invalid doc.kml file. Root node must be 'kml'");
00191 return null;
00192 }
00193
00194 LinkedList<String> nodeNames = new LinkedList<String>();
00195 nodeNames.add("Placemark");
00196 nodeNames.add("Model");
00197 nodeNames.add("Link");
00198 nodeNames.add("href");
00199
00200 NodeList nList = doc.getElementsByTagName(nodeNames.poll());
00201
00202 String loc = getNodeValue(nList, nodeNames);
00203 if (loc == null) {
00204 System.out.println("Couldn't get .dae location from doc.kml");
00205 return null;
00206 }
00207 return tmpPath + loc;
00208 }
00209
00210 @Override
00211 public boolean loadModel(String filename) {
00212 Collada collada = null;
00213 textureBasePath = null;
00214 String daeFile = null;
00215
00216 if (!checkExtension(filename)) {
00217 return false;
00218 }
00219
00220
00221 String extension = getExtension(filename);
00222 if (extension.equalsIgnoreCase(
00223 "kmz")) {
00224 String tmpPath = null;
00225 try {
00226 tmpPath = createTempDirectory().getAbsolutePath();
00227 } catch (IOException e) {
00228 System.err.println("Couldn't create temporary directory.");
00229 e.printStackTrace();
00230 return false;
00231 }
00232 if (!tmpPath.endsWith("/") && !tmpPath.endsWith("\\"))
00233 tmpPath += "/";
00234 Unzip(filename, tmpPath);
00235
00236 daeFile = getDaeLocation(tmpPath);
00237
00238 textureBasePath = daeFile.substring(0,
00239 daeFile.lastIndexOf(File.separator));
00240
00241 if (!textureBasePath.endsWith("/")
00242 && !textureBasePath.endsWith("\\"))
00243 textureBasePath += "/";
00244
00245 } else if (extension
00246 .equalsIgnoreCase("dae")) {
00247 textureBasePath = "";
00248 daeFile = filename;
00249 }
00250
00251 try {
00252 collada = Collada.readFile(daeFile);
00253 } catch (FileNotFoundException e) {
00254 System.err.println("File doesn't exists: " + daeFile);
00255 e.printStackTrace();
00256 return false;
00257 } catch (SAXException e) {
00258 System.err.println("XML-Exception in : " + daeFile);
00259 e.printStackTrace();
00260 return false;
00261 } catch (IOException e) {
00262 System.err.println("IO-Exception in : " + daeFile);
00263 e.printStackTrace();
00264 return false;
00265 }
00266
00267 texturesInitialized = false;
00268 triangles.clear();
00269 lines.clear();
00270
00271
00272 VisualScene scene = collada.getLibraryVisualScenes().getScene(
00273 collada.getScene().getInstanceVisualScene().getUrl());
00274
00275 for (Node n : scene.getNodes()) {
00276 parseNode(n, new ArrayList<BaseXform>(), collada);
00277 }
00278
00279 centerModel();
00280
00281 Unit unit = collada.getUnit();
00282 if (unit != null && unit.getMeter() != 1.0) {
00283 scaleModel(unit.getMeter());
00284 }
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298 String axis = collada.getUpAxis();
00299 if (axis!= null && axis.equalsIgnoreCase("X_UP"))
00300 {
00301 for (Triangle tri : triangles) {
00302 for (int v = 0; v < tri.position.length; v++) {
00303 float tmp = tri.position[v].y;
00304 tri.position[v].y = tri.position[v].x;
00305 tri.position[v].x = -tmp;
00306 }
00307 }
00308 for (Line line : lines) {
00309 for (int v = 0; v < line.position.length; v++) {
00310 float tmp = line.position[v].y;
00311 line.position[v].y = line.position[v].x;
00312 line.position[v].x = -tmp;
00313 }
00314 }
00315 float tmp = minY;
00316 minY = minX;
00317 minX = -tmp;
00318 tmp = maxY;
00319 maxY = maxX;
00320 maxX = -tmp;
00321
00322 } else if (axis!= null && axis.equalsIgnoreCase("Z_UP"))
00323 {
00324 for (Triangle tri : triangles) {
00325 for (int v = 0; v < tri.position.length; v++) {
00326 float tmp = tri.position[v].y;
00327 tri.position[v].y = tri.position[v].z;
00328 tri.position[v].z = -tmp;
00329 }
00330 }
00331 for (Line line : lines) {
00332 for (int v = 0; v < line.position.length; v++) {
00333 float tmp = line.position[v].y;
00334 line.position[v].y = line.position[v].z;
00335 line.position[v].z = -tmp;
00336 }
00337 }
00338 }
00339 float tmp = minY;
00340 minY = minZ;
00341 minZ = -tmp;
00342 tmp = maxY;
00343 maxY = maxZ;
00344 maxZ = -tmp;
00345
00346 return true;
00347 }
00348
00356 private void parseNode(Node node, ArrayList<BaseXform> transformations,
00357 Collada collada) {
00358 if (node.getChildNodes() != null) {
00359 for (Node child : node.getChildNodes())
00360 parseNode(child, transformations, collada);
00361 }
00362
00363
00364 if (node.getXforms() != null)
00365 transformations.addAll(node.getXforms());
00366
00367 InstanceNode instNode = node.getInstanceNode();
00368 if (instNode != null) {
00369 parseNode(collada.findNode(instNode.getUrl()), transformations,
00370 collada);
00371 }
00372 InstanceGeometry instGeo = node.getInstanceGeometry();
00373 if (instGeo != null) {
00374 HashMap<String, String> instanceMaterial = new HashMap<String, String>();
00375 for (InstanceMaterial mat : instGeo.getInstanceMaterials()) {
00376 instanceMaterial.put(mat.getSymbol(), mat.getTarget());
00377 }
00378 parseGeometry(collada.findGeometry(instGeo.getUrl()),
00379 instanceMaterial, transformations, collada);
00380 }
00381
00382
00383 if (node.getXforms() != null)
00384 transformations.removeAll(node.getXforms());
00385 }
00386
00395 private void parseGeometry(Geometry g,
00396 HashMap<String, String> instanceMaterial,
00397 ArrayList<BaseXform> transformations, Collada collada) {
00398 Mesh m = g.getMesh();
00399 for (Primitives p : m.getPrimitives()) {
00400 if (p instanceof Triangles) {
00401 Triangles t = (Triangles) p;
00402 parseGeometryTriangle(t, m, instanceMaterial, transformations,
00403 collada);
00404 } else if (p instanceof Lines) {
00405 Lines l = (Lines) p;
00406 parseGeometryLine(l, m, instanceMaterial, transformations,
00407 collada);
00408 }
00409 }
00410 }
00411
00421 private void parseGeometryLine(Lines l, Mesh m,
00422 HashMap<String, String> instanceMaterial,
00423 ArrayList<BaseXform> transformations, Collada collada) {
00424 Material mat = null;
00425 if (instanceMaterial.containsKey(l.getMaterial())) {
00426 mat = collada.findMaterial(instanceMaterial.get(l.getMaterial()));
00427
00428 } else {
00429 l.dump(System.err, 5);
00430 throw new RuntimeException(
00431 "No material given for Line (see above) ");
00432 }
00433
00434 Source vertexSource = null;
00435 for (Input in : m.getVertices().getInputs()) {
00436 if (in.getSemantic().compareTo("POSITION") == 0) {
00437 vertexSource = m.getSource(in.getSource().substring(1));
00438 }
00439 }
00440
00441 float[][] vertPoints = SourceToFloat(vertexSource);
00442
00443 int stride = l.getInputs().size();
00444 int count = l.getCount();
00445
00446 int[][] indexes = new int[count * 3][stride];
00447
00448 for (int i = 0, k = 0; i < count * 3; i++) {
00449 for (int j = 0; j < stride; j++, k++)
00450 indexes[i][j] = l.getData()[k];
00451 }
00452
00453 int vertexOffset = 0;
00454 for (Input i : l.getInputs()) {
00455 if (i.getSemantic().compareTo("VERTEX") == 0)
00456 vertexOffset = i.getOffset();
00457
00458 }
00459
00460 Appearance appearance = getAppearance(mat, collada);
00461
00462
00463
00464 for (int i = 0; i < indexes.length && i + 2 < indexes.length; i++) {
00465 Line line = new Line();
00466 line.appearance = appearance;
00467
00468 for (int v = 0; v < 2; v++) {
00469
00470 line.position[v] = new Point3f(
00471 vertPoints[indexes[i][vertexOffset]][0],
00472 vertPoints[indexes[i][vertexOffset]][1],
00473 vertPoints[indexes[i][vertexOffset]][2]);
00474
00475 i++;
00476 }
00477 i -= 2;
00478
00479 useTransformation(line, transformations);
00480
00481 lines.add(line);
00482 }
00483 }
00484
00494 private void parseGeometryTriangle(Triangles t, Mesh m,
00495 HashMap<String, String> instanceMaterial,
00496 ArrayList<BaseXform> transformations, Collada collada) {
00497 Material mat = null;
00498 if (instanceMaterial.containsKey(t.getMaterial())) {
00499 mat = collada.findMaterial(instanceMaterial.get(t.getMaterial()));
00500
00501 } else {
00502
00503 t.dump(System.err, 5);
00504 throw new RuntimeException(
00505 "No material given for Triangle (see above) ");
00506 }
00507
00508 Source vertexSource = null;
00509 for (Input in : m.getVertices().getInputs()) {
00510 if (in.getSemantic().compareTo("POSITION") == 0) {
00511 vertexSource = m.getSource(in.getSource().substring(1));
00512 }
00513 }
00514
00515
00516 float[][] vertPoints = SourceToFloat(vertexSource);
00517
00518 int stride = t.getInputs().size();
00519 int count = t.getCount();
00520
00521 int[][] indexes = new int[count * 3][stride];
00522
00523 for (int i = 0, k = 0; i < count * 3; i++) {
00524 for (int j = 0; j < stride; j++, k++)
00525 indexes[i][j] = t.getData()[k];
00526 }
00527
00528 int vertexOffset = 0, textureOffset = 0;
00529 Source textureSource = null;
00530 for (Input i : t.getInputs()) {
00531 if (i.getSemantic().equals("VERTEX"))
00532 vertexOffset = i.getOffset();
00533 else if (i.getSemantic().equals("TEXCOORD")) {
00534 textureOffset = i.getOffset();
00535 textureSource = m.getSource(i.getSource().substring(1));
00536 }
00537
00538 }
00539
00540 float[][] texturePoints = null;
00541 if (textureSource != null) {
00542
00543 texturePoints = SourceToFloat(textureSource);
00544 }
00545
00546 Appearance appearance = getAppearance(mat, collada);
00547
00548
00549 for (int i = 0; i < indexes.length; i++) {
00550 Triangle tri = new Triangle();
00551 tri.appearance = appearance;
00552
00553 if (tri.appearance.containsTexture)
00554 tri.texPosition = new Point2f[3];
00555
00556 for (int v = 0; v < 3; v++) {
00557
00558 tri.position[v] = new Point3f(
00559 vertPoints[indexes[i][vertexOffset]][0],
00560 vertPoints[indexes[i][vertexOffset]][1],
00561 vertPoints[indexes[i][vertexOffset]][2]);
00562
00563
00564 if (tri.appearance.containsTexture) {
00565 tri.texPosition[v] = new Point2f(
00566 texturePoints[indexes[i][textureOffset]][0],
00567 texturePoints[indexes[i][textureOffset]][1]);
00568 }
00569
00570 i++;
00571 }
00572 i--;
00573
00574 useTransformation(tri, transformations);
00575
00576
00577 triangles.add(tri);
00578 }
00579
00580 }
00581
00587 private static void useTransformation(DrawObject obj,
00588 ArrayList<BaseXform> transformations) {
00589 for (int idx = transformations.size() - 1; idx >= 0; idx--) {
00590 BaseXform transform = transformations.get(idx);
00591 if (transform instanceof Matrix) {
00592 float matrix[][] = new float[4][4];
00593 int v = 0;
00594 for (int ma_y = 0; ma_y < 4; ma_y++) {
00595 for (int ma_x = 0; ma_x < 4; ma_x++) {
00596 matrix[ma_y][ma_x] = ((Matrix) transform).getData()[v];
00597 v++;
00598 }
00599 }
00600 obj.transform(matrix);
00601 } else {
00602 System.out.println("Transformation not implemented: ");
00603 transform.dump(System.out, 4);
00604 }
00605 }
00606 }
00607
00614 private Appearance getAppearance(Material mat, Collada collada) {
00615 Appearance appearance = new Appearance();
00616 Effect effect = collada.findEffect(mat.getInstanceEffect().getUrl()
00617 .substring(1));
00618 if (effect.getEffectMaterial() instanceof Lambert) {
00619 Lambert lambert = (Lambert) effect.getEffectMaterial();
00620 EffectAttribute diffuse = lambert.getDiffuse();
00621 String texturePath = null;
00622 Color materialColor = new Color(0.5f, 0.5f, 0.5f, 1.0f);
00623 if (diffuse.getTexture() != null) {
00624 texturePath = collada.findImage(
00625 effect.findNewParam(
00626 effect.findNewParam(
00627 diffuse.getTexture().getTexture())
00628 .getSampler2D().getSource())
00629 .getSurface().getInitFrom()).getInitFrom();
00630 } else {
00631 float[] colors = diffuse.getData();
00632 materialColor = new Color(colors[0], colors[1], colors[2],
00633 colors[3]);
00634 }
00635
00636 if (texturePath != null) {
00637 appearance.imageFileName = texturePath;
00638 appearance.containsTexture = true;
00639 } else {
00640 appearance.colour = materialColor;
00641 }
00642 } else if (effect.getEffectMaterial() instanceof Blinn) {
00643
00644 Blinn blinn = (Blinn) effect.getEffectMaterial();
00645
00646 String texturePath = null;
00647 if (blinn.getDiffuse().getTexture() != null) {
00648 texturePath = collada.findImage(
00649 effect.findNewParam(
00650 effect.findNewParam(
00651 blinn.getDiffuse().getTexture()
00652 .getTexture()).getSampler2D()
00653 .getSource()).getSurface()
00654 .getInitFrom()).getInitFrom();
00655 appearance.imageFileName = texturePath;
00656 appearance.containsTexture = true;
00657 } else {
00658
00659 float[] emission = blinn.getEmission().getData();
00660 float[] ambient = blinn.getAmbient().getData();
00661 float[] diffuse = blinn.getDiffuse().getData();
00662 float[] specular = blinn.getSpecular().getData();
00663 float[] shininess = blinn.getShininess().getData();
00664 float[] result = new float[4];
00665 for (int i = 0; i < 4; i++) {
00666 result[i] = 0;
00667 if (emission != null)
00668 result[i] += emission[i];
00669 if (ambient != null)
00670 result[i] += ambient[i];
00671 if (diffuse != null)
00672 result[i] += diffuse[i];
00673 if (specular != null && shininess != null)
00674 result[i] += (float) Math
00675 .pow(specular[i], shininess[0]);
00676 }
00677
00678
00679 appearance.colour = new Color(result);
00680 }
00681
00682 }
00683 return appearance;
00684 }
00685
00694 public static String getAbsoluteFilePath(String root, String path) {
00695 File file = new File(path);
00696 if (file.isAbsolute())
00697 return file.getAbsolutePath();
00698
00699 if (root == null)
00700 return null;
00701
00702 return new File(new File(root), path).getAbsolutePath();
00703 }
00704
00711 private void setTextureImage(PApplet applet) {
00712
00713 HashMap<String, PImage> pictures = new HashMap<String, PImage>();
00714 for (Triangle tri : triangles) {
00715 if (!tri.appearance.containsTexture)
00716 continue;
00717 String texfile = getAbsoluteFilePath(textureBasePath,
00718 tri.appearance.imageFileName);
00719 if (tri.appearance.containsTexture && pictures.get(texfile) == null) {
00720 PImage tex = applet.loadImage(texfile);
00721 pictures.put(texfile, tex);
00722 }
00723 }
00724
00725 for (Triangle tri : triangles) {
00726 if (tri.appearance.containsTexture) {
00727 String texfile = getAbsoluteFilePath(textureBasePath,
00728 tri.appearance.imageFileName);
00729
00730 PImage tex = pictures.get(texfile);
00731 float AprocX = tri.texPosition[0].x * tex.width;
00732 float AprocY = tex.height - tri.texPosition[0].y * tex.height;
00733 float BprocX = tri.texPosition[1].x * tex.width;
00734 float BprocY = tex.height - tri.texPosition[1].y * tex.height;
00735 float CprocX = tri.texPosition[2].x * tex.width;
00736 float CprocY = tex.height - tri.texPosition[2].y * tex.height;
00737
00738 tri.texPosition[0].x = AprocX;
00739 tri.texPosition[0].y = AprocY;
00740 tri.texPosition[1].x = BprocX;
00741 tri.texPosition[1].y = BprocY;
00742 tri.texPosition[2].x = CprocX;
00743 tri.texPosition[2].y = CprocY;
00744
00745 tri.appearance.imageReference = tex;
00746 }
00747 }
00748 texturesInitialized = true;
00749 }
00750 }