$search
00001 /******************************************************************************* 00002 * Copyright (c) 2012 Stefan Profanter. All rights reserved. This program and the accompanying 00003 * materials are made available under the terms of the GNU Public License v3.0 which accompanies 00004 * this distribution, and is available at http://www.gnu.org/licenses/gpl.html 00005 * 00006 * Contributors: Stefan Profanter - initial API and implementation, Year: 2012 00007 ******************************************************************************/ 00008 package edu.tum.cs.vis.model.parser; 00009 00010 import java.awt.Color; 00011 import java.io.File; 00012 import java.io.FileNotFoundException; 00013 import java.io.IOException; 00014 import java.util.ArrayList; 00015 import java.util.HashMap; 00016 import java.util.LinkedList; 00017 import java.util.List; 00018 import java.util.regex.Matcher; 00019 00020 import javax.vecmath.Point2f; 00021 import javax.vecmath.Vector3f; 00022 import javax.xml.parsers.DocumentBuilder; 00023 import javax.xml.parsers.DocumentBuilderFactory; 00024 import javax.xml.parsers.ParserConfigurationException; 00025 00026 import org.w3c.dom.Document; 00027 import org.w3c.dom.Element; 00028 import org.w3c.dom.NodeList; 00029 import org.xml.sax.SAXException; 00030 00031 import com.dddviewr.collada.Collada; 00032 import com.dddviewr.collada.Input; 00033 import com.dddviewr.collada.Source; 00034 import com.dddviewr.collada.Unit; 00035 import com.dddviewr.collada.effects.Blinn; 00036 import com.dddviewr.collada.effects.Effect; 00037 import com.dddviewr.collada.effects.EffectAttribute; 00038 import com.dddviewr.collada.effects.Lambert; 00039 import com.dddviewr.collada.geometry.Geometry; 00040 import com.dddviewr.collada.geometry.Lines; 00041 import com.dddviewr.collada.geometry.Mesh; 00042 import com.dddviewr.collada.geometry.Primitives; 00043 import com.dddviewr.collada.geometry.Triangles; 00044 import com.dddviewr.collada.materials.Material; 00045 import com.dddviewr.collada.nodes.Node; 00046 import com.dddviewr.collada.visualscene.BaseXform; 00047 import com.dddviewr.collada.visualscene.InstanceGeometry; 00048 import com.dddviewr.collada.visualscene.InstanceMaterial; 00049 import com.dddviewr.collada.visualscene.InstanceNode; 00050 import com.dddviewr.collada.visualscene.Matrix; 00051 import com.dddviewr.collada.visualscene.VisualScene; 00052 00053 import edu.tum.cs.ias.knowrob.utils.FileUtil; 00054 import edu.tum.cs.ias.knowrob.utils.ResourceRetriever; 00055 import edu.tum.cs.vis.model.Model; 00056 import edu.tum.cs.vis.model.util.Appearance; 00057 import edu.tum.cs.vis.model.util.Group; 00058 import edu.tum.cs.vis.model.util.Line; 00059 import edu.tum.cs.vis.model.util.Triangle; 00060 import edu.tum.cs.vis.model.util.Vertex; 00061 00070 public class ColladaParser extends ModelParser { 00071 00081 private static Appearance getAppearance(Material mat, Collada collada) { 00082 Appearance appearance = new Appearance(); 00083 Effect effect = collada.findEffect(mat.getInstanceEffect().getUrl().substring(1)); 00084 if (effect.getEffectMaterial() instanceof Lambert) { 00085 Lambert lambert = (Lambert) effect.getEffectMaterial(); 00086 EffectAttribute diffuse = lambert.getDiffuse(); 00087 String texturePath = null; 00088 Color materialColor = new Color(0.5f, 0.5f, 0.5f, 1.0f); 00089 if (diffuse.getTexture() != null) { 00090 texturePath = collada.findImage( 00091 effect.findNewParam( 00092 effect.findNewParam(diffuse.getTexture().getTexture()) 00093 .getSampler2D().getSource()).getSurface().getInitFrom()) 00094 .getInitFrom(); 00095 if (texturePath.startsWith("./images/")) 00096 texturePath = "." + texturePath; 00097 } else { 00098 float[] colors = diffuse.getData(); 00099 // System.out.println("Diffuse"); 00100 materialColor = new Color(colors[0], colors[1], colors[2], colors[3]); 00101 } 00102 00103 if (texturePath != null) { 00104 appearance.setImageFileName(texturePath); 00105 } else { 00106 // Fill will be set to null if it is a line, Line will be set to null if it is a 00107 // Triangle 00108 appearance.setColorFill(materialColor); 00109 appearance.setColorLine(materialColor); 00110 } 00111 } else if (effect.getEffectMaterial() instanceof Blinn) { 00112 // <emission> + <ambient>*al + <diffuse> + <specular>^<shininess> 00113 Blinn blinn = (Blinn) effect.getEffectMaterial(); 00114 00115 String texturePath = null; 00116 if (blinn.getDiffuse().getTexture() != null) { 00117 texturePath = collada.findImage( 00118 effect.findNewParam( 00119 effect.findNewParam(blinn.getDiffuse().getTexture().getTexture()) 00120 .getSampler2D().getSource()).getSurface().getInitFrom()) 00121 .getInitFrom(); 00122 if (texturePath.startsWith("./images/")) 00123 texturePath = "." + texturePath; 00124 appearance.setImageFileName(texturePath); 00125 } else { 00126 00127 float[] emission = blinn.getEmission().getData(); 00128 float[] ambient = blinn.getAmbient().getData(); 00129 float[] diffuse = blinn.getDiffuse().getData(); 00130 float[] specular = blinn.getSpecular().getData(); 00131 float[] shininess = blinn.getShininess().getData(); 00132 float[] result = new float[4]; 00133 for (int i = 0; i < 4; i++) { 00134 result[i] = 0; 00135 if (emission != null) 00136 result[i] += emission[i]; 00137 if (ambient != null) 00138 result[i] += ambient[i]; 00139 if (diffuse != null) 00140 result[i] += diffuse[i]; 00141 if (specular != null && shininess != null) 00142 result[i] += (float) Math.pow(specular[i], shininess[0]); 00143 } 00144 // Fill will be set to null if it is a line, Line will be set to null if it is a 00145 // Triangle 00146 appearance.setColorFill(new Color(result[0], result[1], result[2], Math.min( 00147 result[3], 1f))); 00148 appearance.setColorLine(appearance.getColorFill()); 00149 } 00150 00151 } 00152 return appearance; 00153 } 00154 00163 private static String getDaeLocation(String tmpPath) { 00164 File kmlFile = new File(tmpPath + "doc.kml"); 00165 DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); 00166 DocumentBuilder dBuilder = null; 00167 try { 00168 dBuilder = dbFactory.newDocumentBuilder(); 00169 } catch (ParserConfigurationException e) { 00170 System.err.println("Couldn't get .dae location from doc.kml: " + e.getMessage()); 00171 return null; 00172 } 00173 Document doc; 00174 try { 00175 doc = dBuilder.parse(kmlFile); 00176 } catch (SAXException e) { 00177 System.err.println("Couldn't get .dae location from doc.kml: " + e.getMessage()); 00178 return null; 00179 } catch (IOException e) { 00180 System.err.println("Couldn't get .dae location from doc.kml: " + e.getMessage()); 00181 return null; 00182 } 00183 doc.getDocumentElement().normalize(); 00184 00185 if (!doc.getDocumentElement().getNodeName().equalsIgnoreCase("kml")) { 00186 System.out.println("Invalid doc.kml file. Root node must be 'kml'"); 00187 return null; 00188 } 00189 00190 LinkedList<String> nodeNames = new LinkedList<String>(); 00191 nodeNames.add("Placemark"); 00192 nodeNames.add("Model"); 00193 nodeNames.add("Link"); 00194 nodeNames.add("href"); 00195 00196 NodeList nList = doc.getElementsByTagName(nodeNames.poll()); 00197 00198 String loc = getNodeValue(nList, nodeNames); 00199 if (loc == null) { 00200 System.out.println("Couldn't get .dae location from doc.kml"); 00201 return null; 00202 } 00203 00204 String ret = tmpPath + loc; 00205 if (File.separator.equals("\\")) { 00206 ret = ret.replaceAll("/", Matcher.quoteReplacement(File.separator)); 00207 } else { 00208 ret = ret.replaceAll("\\\\", Matcher.quoteReplacement(File.separator)); 00209 } 00210 return ret; 00211 } 00212 00224 private static String getNodeValue(NodeList nList, LinkedList<String> nodeNames) { 00225 if (nodeNames.isEmpty() || nList == null) { 00226 return null; 00227 } 00228 if (nodeNames.size() == 1) { 00229 for (int temp = 0; temp < nList.getLength(); temp++) { 00230 org.w3c.dom.Node nNode = nList.item(temp); 00231 if (nNode.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) { 00232 Element element = (Element) nNode; 00233 NodeList nlList = element.getElementsByTagName(nodeNames.poll()); 00234 if (nlList == null) 00235 return null; 00236 nlList = nlList.item(0).getChildNodes(); 00237 00238 org.w3c.dom.Node nValue = nlList.item(0); 00239 return nValue.getNodeValue(); 00240 00241 } 00242 } 00243 } else { 00244 for (int temp = 0; temp < nList.getLength(); temp++) { 00245 00246 org.w3c.dom.Node nNode = nList.item(temp); 00247 00248 if (nNode.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) { 00249 00250 Element element = (Element) nNode; 00251 00252 LinkedList<String> tmpNodeNames = new LinkedList<String>(); 00253 tmpNodeNames.addAll(nodeNames); 00254 String val = getNodeValue(element.getElementsByTagName(tmpNodeNames.poll()), 00255 tmpNodeNames); 00256 if (val != null) 00257 return val; 00258 } 00259 } 00260 } 00261 return null; 00262 } 00263 00275 private static ArrayList<Vertex> getVerticesOfMesh(Mesh m, List<Input> inputs, String type) { 00276 Source vertexSource = null; 00277 for (Input in : inputs) { 00278 if (in.getSemantic().compareTo(type) == 0) { 00279 vertexSource = m.getSource(in.getSource().substring(1)); 00280 } 00281 } 00282 00283 if (vertexSource == null) { 00284 return null; 00285 } 00286 00287 float[][] vertPoints = SourceToFloat(vertexSource); 00288 00289 ArrayList<Vertex> vertices = new ArrayList<Vertex>(); 00290 for (int i = 0; i < vertPoints.length; i++) { 00291 vertices.add(new Vertex(vertPoints[i][0], vertPoints[i][1], vertPoints[i][2])); 00292 } 00293 00294 return vertices; 00295 } 00296 00310 private static void parseGeometry(Geometry g, Group currGroup, 00311 HashMap<String, String> instanceMaterial, ArrayList<BaseXform> transformations, 00312 Collada collada) { 00313 Mesh m = g.getMesh(); 00314 ArrayList<Vertex> vertices = getVerticesOfMesh(m, m.getVertices().getInputs(), "POSITION"); 00315 ArrayList<Vertex> normals = getVerticesOfMesh(m, m.getVertices().getInputs(), "NORMAL"); 00316 00317 for (int i = 0; i < vertices.size(); i++) { 00318 Vertex v = vertices.get(i); 00319 useTransformation(v, transformations, true); 00320 if (normals != null) { 00321 Vertex n = normals.get(i); 00322 useTransformation(n, transformations, false); 00323 v.setNormalVector(new Vector3f(n.x, n.y, n.z)); 00324 } 00325 } 00326 00327 currGroup.getModel().getVertices().addAll(vertices); 00328 00329 for (Primitives p : m.getPrimitives()) { 00330 if (p instanceof Triangles) { 00331 Triangles t = (Triangles) p; 00332 parseGeometryTriangle(t, currGroup, instanceMaterial, collada, vertices, m); 00333 } else if (p instanceof Lines) { 00334 Lines l = (Lines) p; 00335 parseGeometryLine(l, currGroup, instanceMaterial, collada, vertices); 00336 } 00337 } 00338 } 00339 00355 private static void parseGeometryLine(Lines l, Group currGroup, 00356 HashMap<String, String> instanceMaterial, Collada collada, ArrayList<Vertex> vertices) { 00357 Material mat = null; 00358 if (instanceMaterial.containsKey(l.getMaterial())) { 00359 mat = collada.findMaterial(instanceMaterial.get(l.getMaterial())); 00360 00361 } else { 00362 l.dump(System.err, 5); 00363 throw new RuntimeException("No material given for Line (see above) "); 00364 } 00365 00366 int stride = l.getInputs().size(); 00367 int count = l.getCount(); 00368 00369 int[][] indexes = new int[count * 3][stride]; 00370 00371 for (int i = 0, k = 0; i < count * 3; i++) { 00372 for (int j = 0; j < stride; j++, k++) 00373 indexes[i][j] = l.getData()[k]; 00374 } 00375 00376 int vertexOffset = 0; 00377 for (Input i : l.getInputs()) { 00378 if (i.getSemantic().compareTo("VERTEX") == 0) 00379 vertexOffset = i.getOffset(); 00380 00381 } 00382 00383 Appearance appearance = getAppearance(mat, collada); 00384 appearance.setColorFill(null); 00385 00386 // one single line from the line-set 00387 for (int i = 0; i < indexes.length && i + 2 < indexes.length; i++) { 00388 Line line = new Line(); 00389 line.setAppearance(appearance); 00390 00391 for (int v = 0; v < 2; v++) { 00392 // set the vertices for Point 00393 line.getPosition()[v] = vertices.get(indexes[i][vertexOffset]); 00394 00395 i++; 00396 } 00397 line.updateCentroid(); 00398 i -= 2; 00399 00400 // useTransformation(line, transformations); 00401 // add it to the Collection 00402 currGroup.getMesh().getLines().add(line); 00403 currGroup.getModel().getLines().add(line); 00404 } 00405 } 00406 00422 private static void parseGeometryTriangle(Triangles t, Group currGroup, 00423 HashMap<String, String> instanceMaterial, Collada collada, ArrayList<Vertex> vertices, 00424 Mesh m) { 00425 Appearance appearance; 00426 if (instanceMaterial.containsKey(t.getMaterial())) { 00427 Material mat = collada.findMaterial(instanceMaterial.get(t.getMaterial())); 00428 appearance = getAppearance(mat, collada); 00429 appearance.setColorLine(null); 00430 00431 } else { 00432 // Triangle has no material defined. 00433 appearance = new Appearance(); 00434 appearance.setColorFill(new Color(120, 120, 120)); 00435 } 00436 00437 int stride = t.getInputs().size(); 00438 int count = t.getCount(); 00439 00440 int[][] indexes = new int[count * 3][stride]; 00441 00442 for (int i = 0, k = 0; i < count * 3; i++) { 00443 for (int j = 0; j < stride; j++, k++) 00444 indexes[i][j] = t.getData()[k]; 00445 } 00446 00447 int vertexOffset = 0, textureOffset = 0, normalOffset = 0; 00448 Source textureSource = null; 00449 Source normalSource = null; 00450 for (Input i : t.getInputs()) { 00451 if (i.getSemantic().equals("VERTEX")) 00452 vertexOffset = i.getOffset(); 00453 else if (i.getSemantic().equals("TEXCOORD")) { 00454 textureOffset = i.getOffset(); 00455 textureSource = m.getSource(i.getSource().substring(1)); 00456 } else if (i.getSemantic().equals("NORMAL")) { 00457 normalOffset = i.getOffset(); 00458 normalSource = m.getSource(i.getSource().substring(1)); 00459 } 00460 00461 } 00462 00463 float[][] texturePoints = null; // dummy init 00464 if (textureSource != null) { 00465 // Triangle has texture 00466 texturePoints = SourceToFloat(textureSource); 00467 } 00468 00469 float[][] normalCoordinates = null; // dummy init 00470 if (normalSource != null) { 00471 // Triangle has texture 00472 normalCoordinates = SourceToFloat(normalSource); 00473 } 00474 00475 // one single triangle from the triangle-set 00476 for (int i = 0; i < indexes.length; i++) { 00477 Triangle tri = new Triangle(); 00478 tri.setAppearance(appearance); 00479 00480 if (tri.getAppearance().getImageFileName() != null) 00481 tri.setTexPosition(new Point2f[3]); 00482 // calculate triangle normal 00483 00484 for (int v = 0; v < 3; v++) { 00485 // set the vertices for Point 00486 Vertex vert = vertices.get(indexes[i][vertexOffset]); 00487 00488 tri.getPosition()[v] = vert; 00489 00490 // set texture position 00491 if (tri.getAppearance().getImageFileName() != null) { 00492 tri.getTexPosition()[v] = new Point2f( 00493 texturePoints[indexes[i][textureOffset]][0], 00494 texturePoints[indexes[i][textureOffset]][1]); 00495 } 00496 00497 if (normalCoordinates != null) { 00498 Vector3f norm = new Vector3f(normalCoordinates[indexes[i][normalOffset]][0], 00499 normalCoordinates[indexes[i][normalOffset]][1], 00500 normalCoordinates[indexes[i][normalOffset]][2]); 00501 vert.setNormalVector(norm); 00502 } 00503 00504 i++; 00505 } 00506 i--; 00507 00508 if (!tri.calculateNormalVector()) { 00509 continue; 00510 } 00511 tri.updateCentroid(); 00512 00513 // add it to the Collection 00514 currGroup.addTriangle(tri); 00515 } 00516 00517 } 00518 00526 private static float[][] SourceToFloat(Source s) { 00527 float ret[][] = new float[s.getAccessor().getCount()][s.getAccessor().getStride()]; 00528 for (int i = 0, k = 0; i < s.getAccessor().getCount(); i++) { 00529 for (int j = 0; j < s.getAccessor().getStride(); j++, k++) 00530 ret[i][j] = s.getFloatArray().get(k); 00531 } 00532 return ret; 00533 } 00534 00543 private static void useTransformation(Vertex vertex, ArrayList<BaseXform> transformations, 00544 boolean withTranslate) { 00545 for (int idx = transformations.size() - 1; idx >= 0; idx--) { 00546 BaseXform transform = transformations.get(idx); 00547 if (transform instanceof Matrix) { 00548 int cnt = 4; 00549 float matrix[][] = new float[cnt][cnt]; 00550 int v = 0; 00551 for (int ma_y = 0; ma_y < cnt; ma_y++) { 00552 for (int ma_x = 0; ma_x < cnt; ma_x++) { 00553 if (!withTranslate && (ma_y > 2 || ma_x > 2) && ma_y != ma_x) 00554 // only use rotation matrix (eg for rotating and not translating normal 00555 // vector) 00556 matrix[ma_y][ma_x] = 0; 00557 else 00558 matrix[ma_y][ma_x] = ((Matrix) transform).getData()[v]; 00559 00560 v++; 00561 } 00562 } 00563 vertex.transform(matrix); 00564 } else { 00565 System.out.println("Transformation not implemented: "); 00566 transform.dump(System.out, 4); 00567 } 00568 } 00569 } 00570 00575 private String textureBasePath = ""; 00576 00577 @Override 00578 protected boolean loadModel(String filename) { 00579 Collada collada = null; 00580 textureBasePath = null; 00581 String daeFile = null; 00582 00583 // Extract kmz if needed and determin location of .dae file 00584 String extension = getExtension(filename); 00585 if (extension.equalsIgnoreCase("kmz")) { 00586 String tmpPath = ResourceRetriever.createTempDirectory(); 00587 if (!tmpPath.endsWith("/") && !tmpPath.endsWith("\\")) 00588 tmpPath += File.separator; 00589 FileUtil.Unzip(filename, tmpPath); 00590 00591 daeFile = getDaeLocation(tmpPath); 00592 00593 textureBasePath = daeFile.substring(0, daeFile.lastIndexOf(File.separator)); 00594 00595 if (!textureBasePath.endsWith("/") && !textureBasePath.endsWith("\\")) 00596 textureBasePath += File.separator; 00597 00598 } else if (extension.equalsIgnoreCase("dae")) { 00599 String fullPath = new File(filename).getPath(); 00600 textureBasePath = fullPath.substring(0, fullPath.length() 00601 - new File(filename).getName().length()); 00602 daeFile = filename; 00603 } 00604 00605 // Parse dae with collada xml parser 00606 try { 00607 collada = Collada.readFile(daeFile); 00608 } catch (FileNotFoundException e) { 00609 System.err.println("File doesn't exists: " + daeFile); 00610 e.printStackTrace(); 00611 return false; 00612 } catch (SAXException e) { 00613 System.err.println("XML-Exception in : " + daeFile); 00614 e.printStackTrace(); 00615 return false; 00616 } catch (IOException e) { 00617 System.err.println("IO-Exception in : " + daeFile); 00618 e.printStackTrace(); 00619 return false; 00620 } 00621 00622 model = new Model(); 00623 model.setGroup(new Group(model)); 00624 00625 // collada.dump(System.out, 0); 00626 00627 // library_visual_scenes is where everything begins 00628 VisualScene scene = collada.getLibraryVisualScenes().getScene( 00629 collada.getScene().getInstanceVisualScene().getUrl()); 00630 00631 for (Node n : scene.getNodes()) { 00632 parseNode(n, model.getGroup(), new ArrayList<BaseXform>(), collada); 00633 } 00634 00635 model.setTextureBasePath(textureBasePath); 00636 00637 Unit unit = collada.getUnit(); 00638 if (unit != null && unit.getMeter() != 1.0) { 00639 model.scale(unit.getMeter()); 00640 } 00641 00642 model.mirrorX(); 00643 00644 return true; 00645 } 00646 00658 private void parseNode(Node node, Group currGroup, ArrayList<BaseXform> transformations, 00659 Collada collada) { 00660 00661 // Add transformations like a stack 00662 if (node.getXforms() != null) 00663 transformations.addAll(node.getXforms()); 00664 00665 // Parse all child nodes 00666 if (node.getChildNodes() != null) { 00667 for (Node child : node.getChildNodes()) { 00668 Group g = new Group(currGroup.getModel()); 00669 g.setName(child.getName()); 00670 currGroup.addChild(g); 00671 parseNode(child, g, transformations, collada); 00672 } 00673 } 00674 00675 // Parse referencing nodes 00676 InstanceNode instNode = node.getInstanceNode(); 00677 if (instNode != null) { 00678 parseNode(collada.findNode(instNode.getUrl()), currGroup, transformations, collada); 00679 } 00680 00681 // Parse referencing geometries and Material 00682 List<InstanceGeometry> instGeoList = node.getInstanceGeometry(); 00683 00684 for (InstanceGeometry instGeo : instGeoList) { 00685 HashMap<String, String> instanceMaterial = new HashMap<String, String>(); 00686 for (InstanceMaterial mat : instGeo.getInstanceMaterials()) { 00687 instanceMaterial.put(mat.getSymbol(), mat.getTarget()); 00688 } 00689 parseGeometry(collada.findGeometry(instGeo.getUrl()), currGroup, instanceMaterial, 00690 transformations, collada); 00691 } 00692 00693 // remove added transformations 00694 if (node.getXforms() != null) 00695 transformations.removeAll(node.getXforms()); 00696 } 00697 }