Home | History | Annotate | Download | only in scenegraph
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.scenegraph;
     18 import com.android.scenegraph.CompoundTransform.TranslateComponent;
     19 import com.android.scenegraph.CompoundTransform.RotateComponent;
     20 import com.android.scenegraph.CompoundTransform.ScaleComponent;
     21 import java.io.IOException;
     22 import java.io.InputStream;
     23 import java.util.ArrayList;
     24 import java.util.Iterator;
     25 import java.util.List;
     26 import java.util.StringTokenizer;
     27 import java.util.HashMap;
     28 
     29 import javax.xml.parsers.DocumentBuilder;
     30 import javax.xml.parsers.DocumentBuilderFactory;
     31 import javax.xml.parsers.ParserConfigurationException;
     32 
     33 import org.w3c.dom.Document;
     34 import org.w3c.dom.Element;
     35 import org.w3c.dom.Node;
     36 import org.w3c.dom.NodeList;
     37 import org.xml.sax.SAXException;
     38 
     39 import android.renderscript.*;
     40 import android.util.Log;
     41 
     42 public class ColladaParser {
     43     static final String TAG = "ColladaParser";
     44     Document mDom;
     45 
     46     HashMap<String, LightBase> mLights;
     47     HashMap<String, Camera> mCameras;
     48     HashMap<String, ArrayList<ShaderParam> > mEffectsParams;
     49     HashMap<String, Texture2D> mImages;
     50     HashMap<String, Texture2D> mSamplerImageMap;
     51     HashMap<String, String> mMeshIdNameMap;
     52     Scene mScene;
     53 
     54     String mRootDir;
     55 
     56     String toString(Float3 v) {
     57         String valueStr = v.x + " " + v.y + " " + v.z;
     58         return valueStr;
     59     }
     60 
     61     String toString(Float4 v) {
     62         String valueStr = v.x + " " + v.y + " " + v.z + " " + v.w;
     63         return valueStr;
     64     }
     65 
     66     public ColladaParser(){
     67         mLights = new HashMap<String, LightBase>();
     68         mCameras = new HashMap<String, Camera>();
     69         mEffectsParams = new HashMap<String, ArrayList<ShaderParam> >();
     70         mImages = new HashMap<String, Texture2D>();
     71         mMeshIdNameMap = new HashMap<String, String>();
     72     }
     73 
     74     public void init(InputStream is, String rootDir) {
     75         mLights.clear();
     76         mCameras.clear();
     77         mEffectsParams.clear();
     78 
     79         mRootDir = rootDir;
     80 
     81         long start = System.currentTimeMillis();
     82         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
     83         try {
     84             DocumentBuilder db = dbf.newDocumentBuilder();
     85             mDom = db.parse(is);
     86         } catch(ParserConfigurationException e) {
     87             e.printStackTrace();
     88         } catch(SAXException e) {
     89             e.printStackTrace();
     90         } catch(IOException e) {
     91             e.printStackTrace();
     92         }
     93         long end = System.currentTimeMillis();
     94         Log.v("TIMER", "    Parse time: " + (end - start));
     95         exportSceneData();
     96     }
     97 
     98     Scene getScene() {
     99         return mScene;
    100     }
    101 
    102     private void exportSceneData(){
    103         mScene = new Scene();
    104 
    105         Element docEle = mDom.getDocumentElement();
    106         NodeList nl = docEle.getElementsByTagName("light");
    107         if (nl != null) {
    108             for(int i = 0; i < nl.getLength(); i++) {
    109                 Element l = (Element)nl.item(i);
    110                 convertLight(l);
    111             }
    112         }
    113 
    114         nl = docEle.getElementsByTagName("camera");
    115         if (nl != null) {
    116             for(int i = 0; i < nl.getLength(); i++) {
    117                 Element c = (Element)nl.item(i);
    118                 convertCamera(c);
    119             }
    120         }
    121 
    122         nl = docEle.getElementsByTagName("image");
    123         if (nl != null) {
    124             for(int i = 0; i < nl.getLength(); i++) {
    125                 Element img = (Element)nl.item(i);
    126                 convertImage(img);
    127             }
    128         }
    129 
    130         nl = docEle.getElementsByTagName("effect");
    131         if (nl != null) {
    132             for(int i = 0; i < nl.getLength(); i++) {
    133                 Element e = (Element)nl.item(i);
    134                 convertEffects(e);
    135             }
    136         }
    137 
    138         // Material is just a link to the effect
    139         nl = docEle.getElementsByTagName("material");
    140         if (nl != null) {
    141             for(int i = 0; i < nl.getLength(); i++) {
    142                 Element m = (Element)nl.item(i);
    143                 convertMaterials(m);
    144             }
    145         }
    146 
    147         // Look through the geometry list and build up a correlation between id's and names
    148         nl = docEle.getElementsByTagName("geometry");
    149         if (nl != null) {
    150             for(int i = 0; i < nl.getLength(); i++) {
    151                 Element m = (Element)nl.item(i);
    152                 convertGeometries(m);
    153             }
    154         }
    155 
    156 
    157         nl = docEle.getElementsByTagName("visual_scene");
    158         if (nl != null) {
    159             for(int i = 0; i < nl.getLength(); i++) {
    160                 Element s = (Element)nl.item(i);
    161                 getScene(s);
    162             }
    163         }
    164     }
    165 
    166     private void getRenderable(Element shape, Transform t) {
    167         String geoURL = shape.getAttribute("url").substring(1);
    168         String geoName = mMeshIdNameMap.get(geoURL);
    169         if (geoName != null) {
    170             geoURL = geoName;
    171         }
    172         //RenderableGroup group = new RenderableGroup();
    173         //group.setName(geoURL.substring(1));
    174         //mScene.appendRenderable(group);
    175         NodeList nl = shape.getElementsByTagName("instance_material");
    176         if (nl != null) {
    177             for(int i = 0; i < nl.getLength(); i++) {
    178                 Element materialRef = (Element)nl.item(i);
    179                 String meshIndexName = materialRef.getAttribute("symbol");
    180                 String materialName = materialRef.getAttribute("target");
    181 
    182                 Renderable d = new Renderable();
    183                 d.setMesh(geoURL, meshIndexName);
    184                 d.setMaterialName(materialName.substring(1));
    185                 d.setName(geoURL);
    186 
    187                 //Log.v(TAG, "Created drawable geo " + geoURL + " index " + meshIndexName + " material " + materialName);
    188 
    189                 d.setTransform(t);
    190                 //Log.v(TAG, "Set source param " + t.getName());
    191 
    192                 // Now find all the parameters that exist on the material
    193                 ArrayList<ShaderParam> materialParams;
    194                 materialParams = mEffectsParams.get(materialName.substring(1));
    195                 for (int pI = 0; pI < materialParams.size(); pI ++) {
    196                     d.appendSourceParams(materialParams.get(pI));
    197                     //Log.v(TAG, "Set source param i: " + pI + " name " + materialParams.get(pI).getParamName());
    198                 }
    199                 mScene.appendRenderable(d);
    200                 //group.appendChildren(d);
    201             }
    202         }
    203     }
    204 
    205     private void updateLight(Element shape, Transform t) {
    206         String lightURL = shape.getAttribute("url");
    207         // collada uses a uri structure to link things,
    208         // but we ignore it for now and do a simple search
    209         LightBase light = mLights.get(lightURL.substring(1));
    210         if (light != null) {
    211             light.setTransform(t);
    212             //Log.v(TAG, "Set Light " + light.getName() + " " + t.getName());
    213         }
    214     }
    215 
    216     private void updateCamera(Element shape, Transform t) {
    217         String camURL = shape.getAttribute("url");
    218         // collada uses a uri structure to link things,
    219         // but we ignore it for now and do a simple search
    220         Camera cam = mCameras.get(camURL.substring(1));
    221         if (cam != null) {
    222             cam.setTransform(t);
    223             //Log.v(TAG, "Set Camera " + cam.getName() + " " + t.getName());
    224         }
    225     }
    226 
    227     private void getNode(Element node, Transform parent, String indent) {
    228         String name = node.getAttribute("name");
    229         String id = node.getAttribute("id");
    230         CompoundTransform current = new CompoundTransform();
    231         current.setName(name);
    232         if (parent != null) {
    233             parent.appendChild(current);
    234         } else {
    235             mScene.appendTransform(current);
    236         }
    237 
    238         mScene.addToTransformMap(current);
    239 
    240         //Log.v(TAG, indent + "|");
    241         //Log.v(TAG, indent + "[" + name + "]");
    242 
    243         Node childNode = node.getFirstChild();
    244         while (childNode != null) {
    245             if (childNode.getNodeType() == Node.ELEMENT_NODE) {
    246                 Element field = (Element)childNode;
    247                 String fieldName = field.getTagName();
    248                 String description = field.getAttribute("sid");
    249                 if (fieldName.equals("translate")) {
    250                     Float3 value = getFloat3(field);
    251                     current.addTranslate(description, value);
    252                     //Log.v(TAG, indent + " translate " + description + toString(value));
    253                 } else if (fieldName.equals("rotate")) {
    254                     Float4 value = getFloat4(field);
    255                     //Log.v(TAG, indent + " rotate " + description + toString(value));
    256                     Float3 axis = new Float3(value.x, value.y, value.z);
    257                     current.addRotate(description, axis, value.w);
    258                 } else if (fieldName.equals("scale")) {
    259                     Float3 value = getFloat3(field);
    260                     //Log.v(TAG, indent + " scale " + description + toString(value));
    261                     current.addScale(description, value);
    262                 } else if (fieldName.equals("instance_geometry")) {
    263                     getRenderable(field, current);
    264                 } else if (fieldName.equals("instance_light")) {
    265                     updateLight(field, current);
    266                 } else if (fieldName.equals("instance_camera")) {
    267                     updateCamera(field, current);
    268                 } else if (fieldName.equals("node")) {
    269                     getNode(field, current, indent + "   ");
    270                 }
    271             }
    272             childNode = childNode.getNextSibling();
    273         }
    274     }
    275 
    276     // This will find the actual texture node, which is sometimes hidden behind a sampler
    277     // and sometimes referenced directly
    278     Texture2D getTexture(String samplerName) {
    279         String texName = samplerName;
    280 
    281         // Check to see if the image file is hidden by a sampler surface link combo
    282         Element sampler = mDom.getElementById(samplerName);
    283         if (sampler != null) {
    284             NodeList nl = sampler.getElementsByTagName("source");
    285             if (nl != null && nl.getLength() == 1) {
    286                 Element ref = (Element)nl.item(0);
    287                 String surfaceName = getString(ref);
    288                 if (surfaceName == null) {
    289                     return null;
    290                 }
    291 
    292                 Element surface = mDom.getElementById(surfaceName);
    293                 if (surface == null) {
    294                     return null;
    295                 }
    296                 nl = surface.getElementsByTagName("init_from");
    297                 if (nl != null && nl.getLength() == 1) {
    298                     ref = (Element)nl.item(0);
    299                     texName = getString(ref);
    300                 }
    301             }
    302         }
    303 
    304         //Log.v(TAG, "Extracted texture name " + texName);
    305         return mImages.get(texName);
    306     }
    307 
    308     void extractParams(Element fx, ArrayList<ShaderParam> params) {
    309         Node paramNode = fx.getFirstChild();
    310         while (paramNode != null) {
    311             if (paramNode.getNodeType() == Node.ELEMENT_NODE) {
    312                 String name = paramNode.getNodeName();
    313                 // Now find what type it is
    314                 Node typeNode = paramNode.getFirstChild();
    315                 while (typeNode != null && typeNode.getNodeType() != Node.ELEMENT_NODE) {
    316                     typeNode = typeNode.getNextSibling();
    317                 }
    318                 String paramType = typeNode.getNodeName();
    319                 Element typeElem = (Element)typeNode;
    320                 ShaderParam sceneParam = null;
    321                 if (paramType.equals("color")) {
    322                     Float4Param f4p = new Float4Param(name);
    323                     Float4 value = getFloat4(typeElem);
    324                     f4p.setValue(value);
    325                     sceneParam = f4p;
    326                     //Log.v(TAG, "Extracted " + sceneParam.getParamName() + " value " + toString(value));
    327                 } else if (paramType.equals("float")) {
    328                     Float4Param f4p = new Float4Param(name);
    329                     float value = getFloat(typeElem);
    330                     f4p.setValue(new Float4(value, value, value, value));
    331                     sceneParam = f4p;
    332                     //Log.v(TAG, "Extracted " + sceneParam.getParamName() + " value " + value);
    333                 }  else if (paramType.equals("texture")) {
    334                     String samplerName = typeElem.getAttribute("texture");
    335                     Texture2D tex = getTexture(samplerName);
    336                     TextureParam texP = new TextureParam(name);
    337                     texP.setTexture(tex);
    338                     sceneParam = texP;
    339                     //Log.v(TAG, "Extracted texture " + tex);
    340                 }
    341                 if (sceneParam != null) {
    342                     params.add(sceneParam);
    343                 }
    344             }
    345             paramNode = paramNode.getNextSibling();
    346         }
    347     }
    348 
    349     private void convertMaterials(Element mat) {
    350         String id = mat.getAttribute("id");
    351         NodeList nl = mat.getElementsByTagName("instance_effect");
    352         if (nl != null && nl.getLength() == 1) {
    353             Element ref = (Element)nl.item(0);
    354             String url = ref.getAttribute("url");
    355             ArrayList<ShaderParam> params = mEffectsParams.get(url.substring(1));
    356             mEffectsParams.put(id, params);
    357         }
    358     }
    359 
    360     private void convertGeometries(Element geo) {
    361         String id = geo.getAttribute("id");
    362         String name = geo.getAttribute("name");
    363         if (!id.equals(name)) {
    364             mMeshIdNameMap.put(id, name);
    365         }
    366     }
    367 
    368     private void convertEffects(Element fx) {
    369         String id = fx.getAttribute("id");
    370         ArrayList<ShaderParam> params = new ArrayList<ShaderParam>();
    371 
    372         NodeList nl = fx.getElementsByTagName("newparam");
    373         if (nl != null) {
    374             for(int i = 0; i < nl.getLength(); i++) {
    375                 Element field = (Element)nl.item(i);
    376                 field.setIdAttribute("sid", true);
    377             }
    378         }
    379 
    380         nl = fx.getElementsByTagName("blinn");
    381         if (nl != null) {
    382             for(int i = 0; i < nl.getLength(); i++) {
    383                 Element field = (Element)nl.item(i);
    384                 //Log.v(TAG, "blinn");
    385                 extractParams(field, params);
    386             }
    387         }
    388         nl = fx.getElementsByTagName("lambert");
    389         if (nl != null) {
    390             for(int i = 0; i < nl.getLength(); i++) {
    391                 Element field = (Element)nl.item(i);
    392                 //Log.v(TAG, "lambert");
    393                 extractParams(field, params);
    394             }
    395         }
    396         nl = fx.getElementsByTagName("phong");
    397         if (nl != null) {
    398             for(int i = 0; i < nl.getLength(); i++) {
    399                 Element field = (Element)nl.item(i);
    400                 //Log.v(TAG, "phong");
    401                 extractParams(field, params);
    402             }
    403         }
    404         mEffectsParams.put(id, params);
    405     }
    406 
    407     private void convertLight(Element light) {
    408         String name = light.getAttribute("name");
    409         String id = light.getAttribute("id");
    410 
    411         // Determine type
    412         String[] knownTypes = { "point", "spot", "directional" };
    413         final int POINT_LIGHT = 0;
    414         final int SPOT_LIGHT = 1;
    415         final int DIR_LIGHT = 2;
    416         int type = -1;
    417         for (int i = 0; i < knownTypes.length; i ++) {
    418             NodeList nl = light.getElementsByTagName(knownTypes[i]);
    419             if (nl != null && nl.getLength() != 0) {
    420                 type = i;
    421                 break;
    422             }
    423         }
    424 
    425         //Log.v(TAG, "Found Light Type " + type);
    426 
    427         LightBase sceneLight = null;
    428         switch (type) {
    429         case POINT_LIGHT:
    430             sceneLight = new PointLight();
    431             break;
    432         case SPOT_LIGHT: // TODO: finish light types
    433             break;
    434         case DIR_LIGHT: // TODO: finish light types
    435             break;
    436         }
    437 
    438         if (sceneLight == null) {
    439             return;
    440         }
    441 
    442         Float3 color = getFloat3(light, "color");
    443         sceneLight.setColor(color.x, color.y, color.z);
    444         sceneLight.setName(name);
    445         mScene.appendLight(sceneLight);
    446         mLights.put(id, sceneLight);
    447 
    448         //Log.v(TAG, "Light " + name + " color " + toString(color));
    449     }
    450 
    451     private void convertCamera(Element camera) {
    452         String name = camera.getAttribute("name");
    453         String id = camera.getAttribute("id");
    454         float fov = 30.0f;
    455         if (getString(camera, "yfov") != null) {
    456             fov = getFloat(camera, "yfov");
    457         } else if(getString(camera, "xfov") != null) {
    458             float aspect = getFloat(camera, "aspect_ratio");
    459             fov = getFloat(camera, "xfov") / aspect;
    460         }
    461 
    462         float near = getFloat(camera, "znear");
    463         float far = getFloat(camera, "zfar");
    464 
    465         Camera sceneCamera = new Camera();
    466         sceneCamera.setFOV(fov);
    467         sceneCamera.setNear(near);
    468         sceneCamera.setFar(far);
    469         sceneCamera.setName(name);
    470         mScene.appendCamera(sceneCamera);
    471         mCameras.put(id, sceneCamera);
    472     }
    473 
    474     private void convertImage(Element img) {
    475         String name = img.getAttribute("name");
    476         String id = img.getAttribute("id");
    477         String file = getString(img, "init_from");
    478 
    479         Texture2D tex = new Texture2D();
    480         tex.setFileName(file);
    481         tex.setFileDir(mRootDir);
    482         mScene.appendTextures(tex);
    483         mImages.put(id, tex);
    484     }
    485 
    486     private void getScene(Element scene) {
    487         String name = scene.getAttribute("name");
    488         String id = scene.getAttribute("id");
    489 
    490         Node childNode = scene.getFirstChild();
    491         while (childNode != null) {
    492             if (childNode.getNodeType() == Node.ELEMENT_NODE) {
    493                 String indent = "";
    494                 getNode((Element)childNode, null, indent);
    495             }
    496             childNode = childNode.getNextSibling();
    497         }
    498     }
    499 
    500     private String getString(Element elem, String name) {
    501         String text = null;
    502         NodeList nl = elem.getElementsByTagName(name);
    503         if (nl != null && nl.getLength() != 0) {
    504             text = ((Element)nl.item(0)).getFirstChild().getNodeValue();
    505         }
    506         return text;
    507     }
    508 
    509     private String getString(Element elem) {
    510         String text = null;
    511         text = elem.getFirstChild().getNodeValue();
    512         return text;
    513     }
    514 
    515     private int getInt(Element elem, String name) {
    516         return Integer.parseInt(getString(elem, name));
    517     }
    518 
    519     private float getFloat(Element elem, String name) {
    520         return Float.parseFloat(getString(elem, name));
    521     }
    522 
    523     private float getFloat(Element elem) {
    524         return Float.parseFloat(getString(elem));
    525     }
    526 
    527     private Float3 parseFloat3(String valueString) {
    528         StringTokenizer st = new StringTokenizer(valueString);
    529         float x = Float.parseFloat(st.nextToken());
    530         float y = Float.parseFloat(st.nextToken());
    531         float z = Float.parseFloat(st.nextToken());
    532         return new Float3(x, y, z);
    533     }
    534 
    535     private Float4 parseFloat4(String valueString) {
    536         StringTokenizer st = new StringTokenizer(valueString);
    537         float x = Float.parseFloat(st.nextToken());
    538         float y = Float.parseFloat(st.nextToken());
    539         float z = Float.parseFloat(st.nextToken());
    540         float w = Float.parseFloat(st.nextToken());
    541         return new Float4(x, y, z, w);
    542     }
    543 
    544     private Float3 getFloat3(Element elem, String name) {
    545         String valueString = getString(elem, name);
    546         return parseFloat3(valueString);
    547     }
    548 
    549     private Float4 getFloat4(Element elem, String name) {
    550         String valueString = getString(elem, name);
    551         return parseFloat4(valueString);
    552     }
    553 
    554     private Float3 getFloat3(Element elem) {
    555         String valueString = getString(elem);
    556         return parseFloat3(valueString);
    557     }
    558 
    559     private Float4 getFloat4(Element elem) {
    560         String valueString = getString(elem);
    561         return parseFloat4(valueString);
    562     }
    563 }
    564