From cf2bd1de24e4356fce7b748f5837b5b837c30b67 Mon Sep 17 00:00:00 2001 From: "Sha..rd" Date: Sat, 15 Sep 2012 20:29:55 +0000 Subject: [PATCH] * dotScene loader can now load cameras (loads them as CameraNode). Fixes issue 226 * dotScene loader now correctly loads materials in tag. This means models exported in "Separated Materials" mode now work. Also models exported in single material mode no longer require you to rename them to match the scene name. Fixes issue 519. * dotScene loader also considers the ambient color in the tag and adds an AmbientLight to the scene. git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9736 75d07b2b-3a1a-0410-a2c5-0572b91ccdca --- .../jme3/scene/plugins/ogre/SceneLoader.java | 400 +++++++++++------- .../plugins/ogre/SceneMaterialLoader.java | 159 +++++++ 2 files changed, 397 insertions(+), 162 deletions(-) create mode 100644 engine/src/ogre/com/jme3/scene/plugins/ogre/SceneMaterialLoader.java diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/SceneLoader.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/SceneLoader.java index b59275e45..1c9d95730 100644 --- a/engine/src/ogre/com/jme3/scene/plugins/ogre/SceneLoader.java +++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/SceneLoader.java @@ -29,18 +29,24 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package com.jme3.scene.plugins.ogre; import com.jme3.asset.*; +import com.jme3.light.AmbientLight; import com.jme3.light.DirectionalLight; import com.jme3.light.Light; import com.jme3.light.PointLight; import com.jme3.light.SpotLight; import com.jme3.material.MaterialList; +import com.jme3.math.ColorRGBA; +import com.jme3.math.FastMath; import com.jme3.math.Quaternion; import com.jme3.math.Vector3f; +import com.jme3.renderer.Camera; +import com.jme3.scene.CameraNode; +import com.jme3.scene.LightNode; import com.jme3.scene.Spatial; +import com.jme3.scene.control.CameraControl.ControlDirection; import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey; import com.jme3.util.PlaceholderAssets; import com.jme3.util.xml.SAXUtil; @@ -60,8 +66,11 @@ import org.xml.sax.helpers.DefaultHandler; public class SceneLoader extends DefaultHandler implements AssetLoader { + private static final int DEFAULT_CAM_WIDTH = 640; + private static final int DEFAULT_CAM_HEIGHT = 480; + private static final Logger logger = Logger.getLogger(SceneLoader.class.getName()); - + private SceneMaterialLoader materialLoader = new SceneMaterialLoader(); private Stack elementStack = new Stack(); private AssetKey key; private String sceneName; @@ -72,10 +81,12 @@ public class SceneLoader extends DefaultHandler implements AssetLoader { private com.jme3.scene.Node node; private com.jme3.scene.Node entityNode; private Light light; + private Camera camera; + private CameraNode cameraNode; private int nodeIdx = 0; private static volatile int sceneIdx = 0; - public SceneLoader(){ + public SceneLoader() { super(); } @@ -86,11 +97,11 @@ public class SceneLoader extends DefaultHandler implements AssetLoader { @Override public void endDocument() { } - - private void reset(){ + + private void reset() { elementStack.clear(); nodeIdx = 0; - + // NOTE: Setting some of those to null is only needed // if the parsed file had an error e.g. startElement was called // but not endElement @@ -98,30 +109,32 @@ public class SceneLoader extends DefaultHandler implements AssetLoader { node = null; entityNode = null; light = null; + camera = null; + cameraNode = null; } - private void checkTopNode(String topNode) throws SAXException{ - if (!elementStack.peek().equals(topNode)){ + private void checkTopNode(String topNode) throws SAXException { + if (!elementStack.peek().equals(topNode)) { throw new SAXException("dotScene parse error: Expected parent node to be " + topNode); } } - - private Quaternion parseQuat(Attributes attribs) throws SAXException{ - if (attribs.getValue("x") != null){ + + private Quaternion parseQuat(Attributes attribs) throws SAXException { + if (attribs.getValue("x") != null) { // defined as quaternion float x = parseFloat(attribs.getValue("x")); float y = parseFloat(attribs.getValue("y")); float z = parseFloat(attribs.getValue("z")); float w = parseFloat(attribs.getValue("w")); - return new Quaternion(x,y,z,w); - }else if (attribs.getValue("qx") != null){ + return new Quaternion(x, y, z, w); + } else if (attribs.getValue("qx") != null) { // defined as quaternion with prefix "q" float x = parseFloat(attribs.getValue("qx")); float y = parseFloat(attribs.getValue("qy")); float z = parseFloat(attribs.getValue("qz")); float w = parseFloat(attribs.getValue("qw")); - return new Quaternion(x,y,z,w); - }else if (attribs.getValue("angle") != null){ + return new Quaternion(x, y, z, w); + } else if (attribs.getValue("angle") != null) { // defined as angle + axis float angle = parseFloat(attribs.getValue("angle")); float axisX = parseFloat(attribs.getValue("axisX")); @@ -130,7 +143,7 @@ public class SceneLoader extends DefaultHandler implements AssetLoader { Quaternion q = new Quaternion(); q.fromAngleAxis(angle, new Vector3f(axisX, axisY, axisZ)); return q; - }else{ + } else { // defines as 3 angles along XYZ axes float angleX = parseFloat(attribs.getValue("angleX")); float angleY = parseFloat(attribs.getValue("angleY")); @@ -143,11 +156,11 @@ public class SceneLoader extends DefaultHandler implements AssetLoader { private void parseLightNormal(Attributes attribs) throws SAXException { checkTopNode("light"); - + // SpotLight will be supporting a direction-normal, too. - if (light instanceof DirectionalLight) + if (light instanceof DirectionalLight) { ((DirectionalLight) light).setDirection(parseVector3(attribs)); - else if (light instanceof SpotLight){ + } else if (light instanceof SpotLight) { ((SpotLight) light).setDirection(parseVector3(attribs)); } } @@ -157,201 +170,254 @@ public class SceneLoader extends DefaultHandler implements AssetLoader { // for the attenuation. Otherwise derives it from "range" checkTopNode("light"); - if (light instanceof PointLight || light instanceof SpotLight){ + if (light instanceof PointLight || light instanceof SpotLight) { float range = parseFloat(attribs.getValue("range")); float constant = parseFloat(attribs.getValue("constant")); float linear = parseFloat(attribs.getValue("linear")); String quadraticStr = attribs.getValue("quadratic"); - if (quadraticStr == null) + if (quadraticStr == null) { quadraticStr = attribs.getValue("quadric"); + } float quadratic = parseFloat(quadraticStr); - - if (constant == 1 && quadratic == 0 && linear > 0){ + + if (constant == 1 && quadratic == 0 && linear > 0) { range = 1f / linear; } - - if (light instanceof PointLight){ + + if (light instanceof PointLight) { ((PointLight) light).setRadius(range); - }else{ - ((SpotLight)light).setSpotRange(range); + } else { + ((SpotLight) light).setSpotRange(range); } } } - private void parseLightSpotLightRange(Attributes attribs) throws SAXException{ + private void parseLightSpotLightRange(Attributes attribs) throws SAXException { checkTopNode("light"); - + float outer = SAXUtil.parseFloat(attribs.getValue("outer")); float inner = SAXUtil.parseFloat(attribs.getValue("inner")); - - if (!(light instanceof SpotLight)){ + + if (!(light instanceof SpotLight)) { throw new SAXException("dotScene parse error: spotLightRange " + "can only appear under 'spot' light elements"); } - + SpotLight sl = (SpotLight) light; sl.setSpotInnerAngle(inner * 0.5f); sl.setSpotOuterAngle(outer * 0.5f); } - + private void parseLight(Attributes attribs) throws SAXException { - if (node == null || node.getParent() == null) + if (node == null || node.getParent() == null) { throw new SAXException("dotScene parse error: light can only appear under a node"); - + } + checkTopNode("node"); - + String lightType = parseString(attribs.getValue("type"), "point"); - if(lightType.equals("point")) { + if (lightType.equals("point")) { light = new PointLight(); - } else if(lightType.equals("directional") || lightType.equals("sun")) { + } else if (lightType.equals("directional") || lightType.equals("sun")) { light = new DirectionalLight(); // Assuming "normal" property is not provided - ((DirectionalLight)light).setDirection(Vector3f.UNIT_Z); - } else if(lightType.equals("spotLight") || lightType.equals("spot")) { + ((DirectionalLight) light).setDirection(Vector3f.UNIT_Z); + } else if (lightType.equals("spotLight") || lightType.equals("spot")) { light = new SpotLight(); + } else if (lightType.equals("omni")) { + // XXX: It doesn't seem any exporters actually emit this type? + light = new AmbientLight(); } else { logger.log(Level.WARNING, "No matching jME3 LightType found for OGRE LightType: {0}", lightType); } logger.log(Level.FINEST, "{0} created.", light); - if (!parseBool(attribs.getValue("visible"), true)){ + if (!parseBool(attribs.getValue("visible"), true)) { // set to disabled } // "attach" it to the parent of this node - if (light != null) + if (light != null) { node.getParent().addLight(light); + } + } + + private void parseCameraClipping(Attributes attribs) throws SAXException { + if (attribs.getValue("near") != null) { + camera.setFrustumNear(SAXUtil.parseFloat(attribs.getValue("near"))); + camera.setFrustumFar(SAXUtil.parseFloat(attribs.getValue("far"))); + } else { + camera.setFrustumNear(SAXUtil.parseFloat(attribs.getValue("nearPlaneDist"))); + camera.setFrustumFar(SAXUtil.parseFloat(attribs.getValue("farPlaneDist"))); + } + } + + private void parseCamera(Attributes attribs) throws SAXException { + camera = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT); + if (SAXUtil.parseString(attribs.getValue("projectionType"), "perspective").equals("parallel")){ + camera.setParallelProjection(true); + } + float fov = SAXUtil.parseFloat(attribs.getValue("fov"), 45f); + if (fov < FastMath.PI) { + // XXX: Most likely, it is in radians.. + fov = fov * FastMath.RAD_TO_DEG; + } + camera.setFrustumPerspective(fov, (float)DEFAULT_CAM_WIDTH / DEFAULT_CAM_HEIGHT, 1, 1000); + + cameraNode = new CameraNode(attribs.getValue("name"), camera); + cameraNode.setControlDir(ControlDirection.SpatialToCamera); + + node.attachChild(cameraNode); + node = null; + } + + private void parseEntity(Attributes attribs) throws SAXException { + String name = attribs.getValue("name"); + if (name == null) { + name = "OgreEntity-" + (++nodeIdx); + } else { + name += "-entity"; + } + + String meshFile = attribs.getValue("meshFile"); + if (meshFile == null) { + throw new SAXException("Required attribute 'meshFile' missing for 'entity' node"); + } + + // TODO: Not currently used + String materialName = attribs.getValue("materialName"); + + if (folderName != null) { + meshFile = folderName + meshFile; + } + + // NOTE: append "xml" since its assumed mesh files are binary in dotScene + meshFile += ".xml"; + + entityNode = new com.jme3.scene.Node(name); + OgreMeshKey meshKey = new OgreMeshKey(meshFile, materialList); + try { + Spatial ogreMesh = assetManager.loadModel(meshKey); + entityNode.attachChild(ogreMesh); + } catch (AssetNotFoundException ex) { + logger.log(Level.WARNING, "Cannot locate {0} for scene {1}", new Object[]{meshKey, key}); + // Attach placeholder asset. + entityNode.attachChild(PlaceholderAssets.getPlaceholderModel(assetManager)); + } + + node.attachChild(entityNode); + node = null; + } + + private void parseNode(Attributes attribs) throws SAXException { + String name = attribs.getValue("name"); + if (name == null) { + name = "OgreNode-" + (++nodeIdx); + } + + com.jme3.scene.Node newNode = new com.jme3.scene.Node(name); + if (node != null) { + node.attachChild(newNode); + } + node = newNode; } @Override - public void startElement(String uri, String localName, String qName, Attributes attribs) throws SAXException{ - if (qName.equals("scene")){ - if (elementStack.size() != 0){ + public void startElement(String uri, String localName, String qName, Attributes attribs) throws SAXException { + if (qName.equals("scene")) { + if (elementStack.size() != 0) { throw new SAXException("dotScene parse error: 'scene' element must be the root XML element"); } - + String version = attribs.getValue("formatVersion"); - if (version == null || (!version.equals("1.0.0") && !version.equals("1.0.1"))) + if (version == null || (!version.equals("1.0.0") && !version.equals("1.0.1"))) { logger.log(Level.WARNING, "Unrecognized version number" + " in dotScene file: {0}", version); - - }else if (qName.equals("nodes")){ - if (root != null){ + } + } else if (qName.equals("nodes")) { + if (root != null) { throw new SAXException("dotScene parse error: nodes element was specified twice"); } - if (sceneName == null) - root = new com.jme3.scene.Node("OgreDotScene"+(++sceneIdx)); - else - root = new com.jme3.scene.Node(sceneName+"-scene_node"); - + if (sceneName == null) { + root = new com.jme3.scene.Node("OgreDotScene" + (++sceneIdx)); + } else { + root = new com.jme3.scene.Node(sceneName + "-scene_node"); + } + node = root; - }else if (qName.equals("externals")){ + } else if (qName.equals("externals")) { checkTopNode("scene"); - // Not loaded currently - }else if (qName.equals("item")){ + } else if (qName.equals("item")) { checkTopNode("externals"); - }else if (qName.equals("file")){ + } else if (qName.equals("file")) { checkTopNode("item"); - - // XXX: Currently material file name is based - // on the scene's filename. THIS IS NOT CORRECT. - // To solve, port SceneLoader to use DOM instead of SAX - - //String matFile = folderName+attribs.getValue("name"); - //try { - // materialList = (MaterialList) assetManager.loadAsset(new OgreMaterialKey(matFile)); - //} catch (AssetNotFoundException ex){ - // materialList = null; - // logger.log(Level.WARNING, "Cannot locate material file: {0}", matFile); - //} - }else if (qName.equals("node")){ + + // NOTE: This part of the file is ignored, it is parsed + // by SceneMaterialLoader in the first pass. + } else if (qName.equals("node")) { String curElement = elementStack.peek(); - if (!curElement.equals("node") && !curElement.equals("nodes")){ + if (!curElement.equals("node") && !curElement.equals("nodes")) { throw new SAXException("dotScene parse error: " + "node element can only appear under 'node' or 'nodes'"); } - - String name = attribs.getValue("name"); - if (name == null) - name = "OgreNode-" + (++nodeIdx); - - com.jme3.scene.Node newNode = new com.jme3.scene.Node(name); - if (node != null){ - node.attachChild(newNode); - } - node = newNode; - }else if (qName.equals("property")){ - if (node != null){ + + parseNode(attribs); + } else if (qName.equals("property")) { + if (node != null) { String type = attribs.getValue("type"); String name = attribs.getValue("name"); String data = attribs.getValue("data"); - if (type.equals("BOOL")){ - node.setUserData(name, Boolean.parseBoolean(data)||data.equals("1")); - }else if (type.equals("FLOAT")){ + if (type.equals("BOOL")) { + node.setUserData(name, Boolean.parseBoolean(data) || data.equals("1")); + } else if (type.equals("FLOAT")) { node.setUserData(name, Float.parseFloat(data)); - }else if (type.equals("STRING")){ + } else if (type.equals("STRING")) { node.setUserData(name, data); - }else if (type.equals("INT")){ + } else if (type.equals("INT")) { node.setUserData(name, Integer.parseInt(data)); } } - }else if (qName.equals("entity")){ + } else if (qName.equals("entity")) { checkTopNode("node"); - - String name = attribs.getValue("name"); - if (name == null) - name = "OgreEntity-" + (++nodeIdx); - else - name += "-entity"; - - String meshFile = attribs.getValue("meshFile"); - if (meshFile == null) { - throw new SAXException("Required attribute 'meshFile' missing for 'entity' node"); - } - - // TODO: Not currently used - String materialName = attribs.getValue("materialName"); - - if (folderName != null) { - meshFile = folderName + meshFile; - } - - // NOTE: append "xml" since its assumed mesh files are binary in dotScene - meshFile += ".xml"; - - entityNode = new com.jme3.scene.Node(name); - OgreMeshKey meshKey = new OgreMeshKey(meshFile, materialList); - try { - Spatial ogreMesh = assetManager.loadModel(meshKey); - entityNode.attachChild(ogreMesh); - } catch (AssetNotFoundException ex){ - logger.log(Level.WARNING, "Cannot locate {0} for scene {1}", new Object[]{meshKey, key}); - // Attach placeholder asset. - entityNode.attachChild(PlaceholderAssets.getPlaceholderModel(assetManager)); - } - - node.attachChild(entityNode); - node = null; - }else if (qName.equals("position")){ - if (elementStack.peek().equals("node")){ + parseEntity(attribs); + } else if (qName.equals("camera")) { + checkTopNode("node"); + parseCamera(attribs); + } else if (qName.equals("clipping")) { + checkTopNode("camera"); + parseCameraClipping(attribs); + } else if (qName.equals("position")) { + if (elementStack.peek().equals("node")) { node.setLocalTranslation(SAXUtil.parseVector3(attribs)); + } else if (elementStack.peek().equals("camera")) { + cameraNode.setLocalTranslation(SAXUtil.parseVector3(attribs)); } - }else if (qName.equals("quaternion") || qName.equals("rotation")){ + } else if (qName.equals("quaternion") || qName.equals("rotation")) { node.setLocalRotation(parseQuat(attribs)); - }else if (qName.equals("scale")){ + } else if (qName.equals("scale")) { node.setLocalScale(SAXUtil.parseVector3(attribs)); } else if (qName.equals("light")) { parseLight(attribs); } else if (qName.equals("colourDiffuse") || qName.equals("colorDiffuse")) { - if (elementStack.peek().equals("light")){ - if (light != null){ + if (elementStack.peek().equals("light")) { + if (light != null) { light.setColor(parseColor(attribs)); } - }else{ + } else { checkTopNode("environment"); } + } else if (qName.equals("colourAmbient") || qName.equals("colorAmbient")) { + if (elementStack.peek().equals("environment")) { + ColorRGBA color = parseColor(attribs); + if (!color.equals(ColorRGBA.Black) && !color.equals(ColorRGBA.BlackNoAlpha)) { + // Lets add an ambient light to the scene. + AmbientLight al = new AmbientLight(); + al.setColor(color); + root.addLight(al); + } + } } else if (qName.equals("normal") || qName.equals("direction")) { checkTopNode("light"); parseLightNormal(attribs); @@ -366,33 +432,36 @@ public class SceneLoader extends DefaultHandler implements AssetLoader { @Override public void endElement(String uri, String name, String qName) throws SAXException { - if (qName.equals("node")){ + if (qName.equals("node")) { node = node.getParent(); - }else if (qName.equals("nodes")){ + } else if (qName.equals("nodes")) { node = null; - }else if (qName.equals("entity")){ + } else if (qName.equals("entity")) { node = entityNode.getParent(); entityNode = null; - }else if (qName.equals("light")){ + } else if (qName.equals("camera")) { + node = cameraNode.getParent(); + cameraNode = null; + } else if (qName.equals("light")) { // apply the node's world transform on the light.. root.updateGeometricState(); - if (light != null){ - if (light instanceof DirectionalLight){ + if (light != null) { + if (light instanceof DirectionalLight) { DirectionalLight dl = (DirectionalLight) light; Quaternion q = node.getWorldRotation(); Vector3f dir = dl.getDirection(); q.multLocal(dir); dl.setDirection(dir); - }else if (light instanceof PointLight){ + } else if (light instanceof PointLight) { PointLight pl = (PointLight) light; Vector3f pos = node.getWorldTranslation(); pl.setPosition(pos); - }else if (light instanceof SpotLight){ + } else if (light instanceof SpotLight) { SpotLight sl = (SpotLight) light; - + Vector3f pos = node.getWorldTranslation(); sl.setPosition(pos); - + Quaternion q = node.getWorldRotation(); Vector3f dir = sl.getDirection(); q.multLocal(dir); @@ -409,10 +478,8 @@ public class SceneLoader extends DefaultHandler implements AssetLoader { public void characters(char ch[], int start, int length) { } - - public Object load(AssetInfo info) throws IOException { - try{ + try { key = info.getKey(); assetManager = info.getManager(); sceneName = key.getName(); @@ -420,16 +487,26 @@ public class SceneLoader extends DefaultHandler implements AssetLoader { folderName = key.getFolder(); sceneName = sceneName.substring(0, sceneName.length() - ext.length() - 1); - OgreMaterialKey materialKey = new OgreMaterialKey(sceneName+".material"); - try { - materialList = (MaterialList) assetManager.loadAsset(materialKey); - } catch (AssetNotFoundException ex){ - logger.log(Level.WARNING, "Cannot locate {0} for scene {1}", new Object[]{materialKey, key}); - materialList = null; + reset(); + + // == Run 1st pass over XML file to determine material list == + materialList = materialLoader.load(assetManager, folderName, info.openStream()); + + if (materialList.isEmpty()) { + // NOTE: No materials were found by searching the externals section. + // Try finding a similarly named material file in the same folder. + // (Backward compatibility only!) + OgreMaterialKey materialKey = new OgreMaterialKey(sceneName + ".material"); + try { + materialList = (MaterialList) assetManager.loadAsset(materialKey); + } catch (AssetNotFoundException ex) { + logger.log(Level.WARNING, "Cannot locate {0} for scene {1}", new Object[]{materialKey, key}); + materialList = null; + } } - reset(); - + // == Run 2nd pass to load entities and other objects == + // Added by larynx 25.06.2011 // Android needs the namespace aware flag set to true // Kirill 30.06.2011 @@ -437,24 +514,24 @@ public class SceneLoader extends DefaultHandler implements AssetLoader { // checking with JmeSystem. SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setNamespaceAware(true); - XMLReader xr = factory.newSAXParser().getXMLReader(); - + XMLReader xr = factory.newSAXParser().getXMLReader(); + xr.setContentHandler(this); xr.setErrorHandler(this); - + InputStreamReader r = null; - + try { r = new InputStreamReader(info.openStream()); xr.parse(new InputSource(r)); } finally { - if (r != null){ + if (r != null) { r.close(); } } - + return root; - }catch (SAXException ex){ + } catch (SAXException ex) { IOException ioEx = new IOException("Error while parsing Ogre3D dotScene"); ioEx.initCause(ex); throw ioEx; @@ -464,5 +541,4 @@ public class SceneLoader extends DefaultHandler implements AssetLoader { throw ioEx; } } - } diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/SceneMaterialLoader.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/SceneMaterialLoader.java new file mode 100644 index 000000000..4f361f5b9 --- /dev/null +++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/SceneMaterialLoader.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jme3.scene.plugins.ogre; + +import com.jme3.asset.AssetManager; +import com.jme3.asset.AssetNotFoundException; +import com.jme3.material.MaterialList; +import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Stack; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParserFactory; +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.DefaultHandler; + +/** + * This is a utility class to load a {@link MaterialList} from a + * .scene file. It is only needed because the parsing method + * used by the SceneLoader doesn't support reading bottom XML nodes + * before reading the top nodes. + * + * @author Kirill Vainer + */ +class SceneMaterialLoader extends DefaultHandler { + + private static final Logger logger = Logger.getLogger(SceneMaterialLoader.class.getName()); + private Stack elementStack = new Stack(); + private String folderName; + private MaterialList materialList; + private AssetManager assetManager; + private boolean ignoreItem = false; + + private void reset(){ + elementStack.clear(); + materialList = null; + ignoreItem = false; + } + + private void checkTopNode(String topNode) throws SAXException{ + if (!elementStack.peek().equals(topNode)){ + throw new SAXException("dotScene parse error: Expected parent node to be " + topNode); + } + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attribs) throws SAXException { + if (qName.equals("externals")) { + checkTopNode("scene"); + + // Has an externals block, create material list. + materialList = new MaterialList(); + } else if (qName.equals("item")) { + checkTopNode("externals"); + if (!attribs.getValue("type").equals("material")) { + // This is not a material external. Ignore it. + ignoreItem = true; + } + } else if (qName.equals("file")) { + checkTopNode("item"); + + if (!ignoreItem) { + String materialPath = attribs.getValue("name"); + String materialName = new File(materialPath).getName(); + String matFile = folderName + materialName; + try { + MaterialList loadedMaterialList = (MaterialList) assetManager.loadAsset(new OgreMaterialKey(matFile)); + materialList.putAll(loadedMaterialList); + } catch (AssetNotFoundException ex) { + logger.log(Level.WARNING, "Cannot locate material file: {0}", matFile); + } + } + } + elementStack.push(qName); + } + + @Override + public void endElement(String uri, String name, String qName) throws SAXException { + if (qName.equals("item") && ignoreItem) { + ignoreItem = false; + } + checkTopNode(qName); + elementStack.pop(); + } + + public MaterialList load(AssetManager assetManager, String folderName, InputStream in) throws IOException { + try { + this.assetManager = assetManager; + this.folderName = folderName; + + reset(); + + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + XMLReader xr = factory.newSAXParser().getXMLReader(); + + xr.setContentHandler(this); + xr.setErrorHandler(this); + + InputStreamReader r = null; + + try { + r = new InputStreamReader(in); + xr.parse(new InputSource(r)); + } finally { + if (r != null){ + r.close(); + } + } + + return materialList; + } catch (SAXException ex) { + IOException ioEx = new IOException("Error while parsing Ogre3D dotScene"); + ioEx.initCause(ex); + throw ioEx; + } catch (ParserConfigurationException ex) { + IOException ioEx = new IOException("Error while parsing Ogre3D dotScene"); + ioEx.initCause(ex); + throw ioEx; + } + } +}