/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.scenegraph; import com.android.scenegraph.CompoundTransform.TranslateComponent; import com.android.scenegraph.CompoundTransform.RotateComponent; import com.android.scenegraph.CompoundTransform.ScaleComponent; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.StringTokenizer; import java.util.HashMap; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import android.renderscript.*; import android.util.Log; public class ColladaParser { static final String TAG = "ColladaParser"; Document mDom; HashMap mLights; HashMap mCameras; HashMap > mEffectsParams; HashMap mImages; HashMap mSamplerImageMap; HashMap mMeshIdNameMap; Scene mScene; String mRootDir; String toString(Float3 v) { String valueStr = v.x + " " + v.y + " " + v.z; return valueStr; } String toString(Float4 v) { String valueStr = v.x + " " + v.y + " " + v.z + " " + v.w; return valueStr; } public ColladaParser(){ mLights = new HashMap(); mCameras = new HashMap(); mEffectsParams = new HashMap >(); mImages = new HashMap(); mMeshIdNameMap = new HashMap(); } public void init(InputStream is, String rootDir) { mLights.clear(); mCameras.clear(); mEffectsParams.clear(); mRootDir = rootDir; long start = System.currentTimeMillis(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); try { DocumentBuilder db = dbf.newDocumentBuilder(); mDom = db.parse(is); } catch(ParserConfigurationException e) { e.printStackTrace(); } catch(SAXException e) { e.printStackTrace(); } catch(IOException e) { e.printStackTrace(); } long end = System.currentTimeMillis(); Log.v("TIMER", " Parse time: " + (end - start)); exportSceneData(); } Scene getScene() { return mScene; } private void exportSceneData(){ mScene = new Scene(); Element docEle = mDom.getDocumentElement(); NodeList nl = docEle.getElementsByTagName("light"); if (nl != null) { for(int i = 0; i < nl.getLength(); i++) { Element l = (Element)nl.item(i); convertLight(l); } } nl = docEle.getElementsByTagName("camera"); if (nl != null) { for(int i = 0; i < nl.getLength(); i++) { Element c = (Element)nl.item(i); convertCamera(c); } } nl = docEle.getElementsByTagName("image"); if (nl != null) { for(int i = 0; i < nl.getLength(); i++) { Element img = (Element)nl.item(i); convertImage(img); } } nl = docEle.getElementsByTagName("effect"); if (nl != null) { for(int i = 0; i < nl.getLength(); i++) { Element e = (Element)nl.item(i); convertEffects(e); } } // Material is just a link to the effect nl = docEle.getElementsByTagName("material"); if (nl != null) { for(int i = 0; i < nl.getLength(); i++) { Element m = (Element)nl.item(i); convertMaterials(m); } } // Look through the geometry list and build up a correlation between id's and names nl = docEle.getElementsByTagName("geometry"); if (nl != null) { for(int i = 0; i < nl.getLength(); i++) { Element m = (Element)nl.item(i); convertGeometries(m); } } nl = docEle.getElementsByTagName("visual_scene"); if (nl != null) { for(int i = 0; i < nl.getLength(); i++) { Element s = (Element)nl.item(i); getScene(s); } } } private void getRenderable(Element shape, Transform t) { String geoURL = shape.getAttribute("url").substring(1); String geoName = mMeshIdNameMap.get(geoURL); if (geoName != null) { geoURL = geoName; } //RenderableGroup group = new RenderableGroup(); //group.setName(geoURL.substring(1)); //mScene.appendRenderable(group); NodeList nl = shape.getElementsByTagName("instance_material"); if (nl != null) { for(int i = 0; i < nl.getLength(); i++) { Element materialRef = (Element)nl.item(i); String meshIndexName = materialRef.getAttribute("symbol"); String materialName = materialRef.getAttribute("target"); Renderable d = new Renderable(); d.setMesh(geoURL, meshIndexName); d.setMaterialName(materialName.substring(1)); d.setName(geoURL); //Log.v(TAG, "Created drawable geo " + geoURL + " index " + meshIndexName + " material " + materialName); d.setTransform(t); //Log.v(TAG, "Set source param " + t.getName()); // Now find all the parameters that exist on the material ArrayList materialParams; materialParams = mEffectsParams.get(materialName.substring(1)); for (int pI = 0; pI < materialParams.size(); pI ++) { d.appendSourceParams(materialParams.get(pI)); //Log.v(TAG, "Set source param i: " + pI + " name " + materialParams.get(pI).getParamName()); } mScene.appendRenderable(d); //group.appendChildren(d); } } } private void updateLight(Element shape, Transform t) { String lightURL = shape.getAttribute("url"); // collada uses a uri structure to link things, // but we ignore it for now and do a simple search LightBase light = mLights.get(lightURL.substring(1)); if (light != null) { light.setTransform(t); //Log.v(TAG, "Set Light " + light.getName() + " " + t.getName()); } } private void updateCamera(Element shape, Transform t) { String camURL = shape.getAttribute("url"); // collada uses a uri structure to link things, // but we ignore it for now and do a simple search Camera cam = mCameras.get(camURL.substring(1)); if (cam != null) { cam.setTransform(t); //Log.v(TAG, "Set Camera " + cam.getName() + " " + t.getName()); } } private void getNode(Element node, Transform parent, String indent) { String name = node.getAttribute("name"); String id = node.getAttribute("id"); CompoundTransform current = new CompoundTransform(); current.setName(name); if (parent != null) { parent.appendChild(current); } else { mScene.appendTransform(current); } mScene.addToTransformMap(current); //Log.v(TAG, indent + "|"); //Log.v(TAG, indent + "[" + name + "]"); Node childNode = node.getFirstChild(); while (childNode != null) { if (childNode.getNodeType() == Node.ELEMENT_NODE) { Element field = (Element)childNode; String fieldName = field.getTagName(); String description = field.getAttribute("sid"); if (fieldName.equals("translate")) { Float3 value = getFloat3(field); current.addTranslate(description, value); //Log.v(TAG, indent + " translate " + description + toString(value)); } else if (fieldName.equals("rotate")) { Float4 value = getFloat4(field); //Log.v(TAG, indent + " rotate " + description + toString(value)); Float3 axis = new Float3(value.x, value.y, value.z); current.addRotate(description, axis, value.w); } else if (fieldName.equals("scale")) { Float3 value = getFloat3(field); //Log.v(TAG, indent + " scale " + description + toString(value)); current.addScale(description, value); } else if (fieldName.equals("instance_geometry")) { getRenderable(field, current); } else if (fieldName.equals("instance_light")) { updateLight(field, current); } else if (fieldName.equals("instance_camera")) { updateCamera(field, current); } else if (fieldName.equals("node")) { getNode(field, current, indent + " "); } } childNode = childNode.getNextSibling(); } } // This will find the actual texture node, which is sometimes hidden behind a sampler // and sometimes referenced directly Texture2D getTexture(String samplerName) { String texName = samplerName; // Check to see if the image file is hidden by a sampler surface link combo Element sampler = mDom.getElementById(samplerName); if (sampler != null) { NodeList nl = sampler.getElementsByTagName("source"); if (nl != null && nl.getLength() == 1) { Element ref = (Element)nl.item(0); String surfaceName = getString(ref); if (surfaceName == null) { return null; } Element surface = mDom.getElementById(surfaceName); if (surface == null) { return null; } nl = surface.getElementsByTagName("init_from"); if (nl != null && nl.getLength() == 1) { ref = (Element)nl.item(0); texName = getString(ref); } } } //Log.v(TAG, "Extracted texture name " + texName); return mImages.get(texName); } void extractParams(Element fx, ArrayList params) { Node paramNode = fx.getFirstChild(); while (paramNode != null) { if (paramNode.getNodeType() == Node.ELEMENT_NODE) { String name = paramNode.getNodeName(); // Now find what type it is Node typeNode = paramNode.getFirstChild(); while (typeNode != null && typeNode.getNodeType() != Node.ELEMENT_NODE) { typeNode = typeNode.getNextSibling(); } String paramType = typeNode.getNodeName(); Element typeElem = (Element)typeNode; ShaderParam sceneParam = null; if (paramType.equals("color")) { Float4Param f4p = new Float4Param(name); Float4 value = getFloat4(typeElem); f4p.setValue(value); sceneParam = f4p; //Log.v(TAG, "Extracted " + sceneParam.getParamName() + " value " + toString(value)); } else if (paramType.equals("float")) { Float4Param f4p = new Float4Param(name); float value = getFloat(typeElem); f4p.setValue(new Float4(value, value, value, value)); sceneParam = f4p; //Log.v(TAG, "Extracted " + sceneParam.getParamName() + " value " + value); } else if (paramType.equals("texture")) { String samplerName = typeElem.getAttribute("texture"); Texture2D tex = getTexture(samplerName); TextureParam texP = new TextureParam(name); texP.setTexture(tex); sceneParam = texP; //Log.v(TAG, "Extracted texture " + tex); } if (sceneParam != null) { params.add(sceneParam); } } paramNode = paramNode.getNextSibling(); } } private void convertMaterials(Element mat) { String id = mat.getAttribute("id"); NodeList nl = mat.getElementsByTagName("instance_effect"); if (nl != null && nl.getLength() == 1) { Element ref = (Element)nl.item(0); String url = ref.getAttribute("url"); ArrayList params = mEffectsParams.get(url.substring(1)); mEffectsParams.put(id, params); } } private void convertGeometries(Element geo) { String id = geo.getAttribute("id"); String name = geo.getAttribute("name"); if (!id.equals(name)) { mMeshIdNameMap.put(id, name); } } private void convertEffects(Element fx) { String id = fx.getAttribute("id"); ArrayList params = new ArrayList(); NodeList nl = fx.getElementsByTagName("newparam"); if (nl != null) { for(int i = 0; i < nl.getLength(); i++) { Element field = (Element)nl.item(i); field.setIdAttribute("sid", true); } } nl = fx.getElementsByTagName("blinn"); if (nl != null) { for(int i = 0; i < nl.getLength(); i++) { Element field = (Element)nl.item(i); //Log.v(TAG, "blinn"); extractParams(field, params); } } nl = fx.getElementsByTagName("lambert"); if (nl != null) { for(int i = 0; i < nl.getLength(); i++) { Element field = (Element)nl.item(i); //Log.v(TAG, "lambert"); extractParams(field, params); } } nl = fx.getElementsByTagName("phong"); if (nl != null) { for(int i = 0; i < nl.getLength(); i++) { Element field = (Element)nl.item(i); //Log.v(TAG, "phong"); extractParams(field, params); } } mEffectsParams.put(id, params); } private void convertLight(Element light) { String name = light.getAttribute("name"); String id = light.getAttribute("id"); // Determine type String[] knownTypes = { "point", "spot", "directional" }; final int POINT_LIGHT = 0; final int SPOT_LIGHT = 1; final int DIR_LIGHT = 2; int type = -1; for (int i = 0; i < knownTypes.length; i ++) { NodeList nl = light.getElementsByTagName(knownTypes[i]); if (nl != null && nl.getLength() != 0) { type = i; break; } } //Log.v(TAG, "Found Light Type " + type); LightBase sceneLight = null; switch (type) { case POINT_LIGHT: sceneLight = new PointLight(); break; case SPOT_LIGHT: // TODO: finish light types break; case DIR_LIGHT: // TODO: finish light types break; } if (sceneLight == null) { return; } Float3 color = getFloat3(light, "color"); sceneLight.setColor(color.x, color.y, color.z); sceneLight.setName(name); mScene.appendLight(sceneLight); mLights.put(id, sceneLight); //Log.v(TAG, "Light " + name + " color " + toString(color)); } private void convertCamera(Element camera) { String name = camera.getAttribute("name"); String id = camera.getAttribute("id"); float fov = 30.0f; if (getString(camera, "yfov") != null) { fov = getFloat(camera, "yfov"); } else if(getString(camera, "xfov") != null) { float aspect = getFloat(camera, "aspect_ratio"); fov = getFloat(camera, "xfov") / aspect; } float near = getFloat(camera, "znear"); float far = getFloat(camera, "zfar"); Camera sceneCamera = new Camera(); sceneCamera.setFOV(fov); sceneCamera.setNear(near); sceneCamera.setFar(far); sceneCamera.setName(name); mScene.appendCamera(sceneCamera); mCameras.put(id, sceneCamera); } private void convertImage(Element img) { String name = img.getAttribute("name"); String id = img.getAttribute("id"); String file = getString(img, "init_from"); Texture2D tex = new Texture2D(); tex.setFileName(file); tex.setFileDir(mRootDir); mScene.appendTextures(tex); mImages.put(id, tex); } private void getScene(Element scene) { String name = scene.getAttribute("name"); String id = scene.getAttribute("id"); Node childNode = scene.getFirstChild(); while (childNode != null) { if (childNode.getNodeType() == Node.ELEMENT_NODE) { String indent = ""; getNode((Element)childNode, null, indent); } childNode = childNode.getNextSibling(); } } private String getString(Element elem, String name) { String text = null; NodeList nl = elem.getElementsByTagName(name); if (nl != null && nl.getLength() != 0) { text = ((Element)nl.item(0)).getFirstChild().getNodeValue(); } return text; } private String getString(Element elem) { String text = null; text = elem.getFirstChild().getNodeValue(); return text; } private int getInt(Element elem, String name) { return Integer.parseInt(getString(elem, name)); } private float getFloat(Element elem, String name) { return Float.parseFloat(getString(elem, name)); } private float getFloat(Element elem) { return Float.parseFloat(getString(elem)); } private Float3 parseFloat3(String valueString) { StringTokenizer st = new StringTokenizer(valueString); float x = Float.parseFloat(st.nextToken()); float y = Float.parseFloat(st.nextToken()); float z = Float.parseFloat(st.nextToken()); return new Float3(x, y, z); } private Float4 parseFloat4(String valueString) { StringTokenizer st = new StringTokenizer(valueString); float x = Float.parseFloat(st.nextToken()); float y = Float.parseFloat(st.nextToken()); float z = Float.parseFloat(st.nextToken()); float w = Float.parseFloat(st.nextToken()); return new Float4(x, y, z, w); } private Float3 getFloat3(Element elem, String name) { String valueString = getString(elem, name); return parseFloat3(valueString); } private Float4 getFloat4(Element elem, String name) { String valueString = getString(elem, name); return parseFloat4(valueString); } private Float3 getFloat3(Element elem) { String valueString = getString(elem); return parseFloat3(valueString); } private Float4 getFloat4(Element elem) { String valueString = getString(elem); return parseFloat4(valueString); } }