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


knowrob_cad_parser
Author(s): Stefan Profanter
autogenerated on Mon Oct 6 2014 01:29:56