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