/*
* 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<String, LightBase> mLights;
HashMap<String, Camera> mCameras;
HashMap<String, ArrayList<ShaderParam> > mEffectsParams;
HashMap<String, Texture2D> mImages;
HashMap<String, Texture2D> mSamplerImageMap;
HashMap<String, String> 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<String, LightBase>();
mCameras = new HashMap<String, Camera>();
mEffectsParams = new HashMap<String, ArrayList<ShaderParam> >();
mImages = new HashMap<String, Texture2D>();
mMeshIdNameMap = new HashMap<String, String>();
}
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<ShaderParam> 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<ShaderParam> 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<ShaderParam> 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<ShaderParam> params = new ArrayList<ShaderParam>();
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);
}
}