|
@@ -1,475 +1,475 @@
|
|
|
-/*
|
|
|
- * Copyright (c) 2009-2010 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.AssetKey;
|
|
|
-import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey;
|
|
|
-import com.jme3.material.MaterialList;
|
|
|
-import com.jme3.asset.AssetInfo;
|
|
|
-import com.jme3.asset.AssetLoader;
|
|
|
-import com.jme3.asset.AssetManager;
|
|
|
-import com.jme3.asset.AssetNotFoundException;
|
|
|
-import com.jme3.light.DirectionalLight;
|
|
|
-import com.jme3.light.Light;
|
|
|
-import com.jme3.light.PointLight;
|
|
|
-import com.jme3.light.SpotLight;
|
|
|
-import com.jme3.math.Quaternion;
|
|
|
-import com.jme3.math.Vector3f;
|
|
|
-import com.jme3.scene.Spatial;
|
|
|
-import com.jme3.util.PlaceholderAssets;
|
|
|
-import com.jme3.util.xml.SAXUtil;
|
|
|
-import java.io.IOException;
|
|
|
-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;
|
|
|
-
|
|
|
-import static com.jme3.util.xml.SAXUtil.*;
|
|
|
-
|
|
|
-public class SceneLoader extends DefaultHandler implements AssetLoader {
|
|
|
-
|
|
|
- private static final Logger logger = Logger.getLogger(SceneLoader.class.getName());
|
|
|
-
|
|
|
- private Stack<String> elementStack = new Stack<String>();
|
|
|
- private AssetKey key;
|
|
|
- private String sceneName;
|
|
|
- private String folderName;
|
|
|
- private AssetManager assetManager;
|
|
|
- private MaterialList materialList;
|
|
|
- private com.jme3.scene.Node root;
|
|
|
- private com.jme3.scene.Node node;
|
|
|
- private com.jme3.scene.Node entityNode;
|
|
|
- private Light light;
|
|
|
- private int nodeIdx = 0;
|
|
|
- private static volatile int sceneIdx = 0;
|
|
|
-
|
|
|
- public SceneLoader(){
|
|
|
- super();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void startDocument() {
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void endDocument() {
|
|
|
- }
|
|
|
-
|
|
|
- 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
|
|
|
- root = null;
|
|
|
- node = null;
|
|
|
- entityNode = null;
|
|
|
- light = null;
|
|
|
- }
|
|
|
-
|
|
|
- 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){
|
|
|
- // 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){
|
|
|
- // 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){
|
|
|
- // defined as angle + axis
|
|
|
- float angle = parseFloat(attribs.getValue("angle"));
|
|
|
- float axisX = parseFloat(attribs.getValue("axisX"));
|
|
|
- float axisY = parseFloat(attribs.getValue("axisY"));
|
|
|
- float axisZ = parseFloat(attribs.getValue("axisZ"));
|
|
|
- Quaternion q = new Quaternion();
|
|
|
- q.fromAngleAxis(angle, new Vector3f(axisX, axisY, axisZ));
|
|
|
- return q;
|
|
|
- }else{
|
|
|
- // defines as 3 angles along XYZ axes
|
|
|
- float angleX = parseFloat(attribs.getValue("angleX"));
|
|
|
- float angleY = parseFloat(attribs.getValue("angleY"));
|
|
|
- float angleZ = parseFloat(attribs.getValue("angleZ"));
|
|
|
- Quaternion q = new Quaternion();
|
|
|
- q.fromAngles(angleX, angleY, angleZ);
|
|
|
- return q;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void parseLightNormal(Attributes attribs) throws SAXException {
|
|
|
- checkTopNode("light");
|
|
|
-
|
|
|
- // SpotLight will be supporting a direction-normal, too.
|
|
|
- if (light instanceof DirectionalLight)
|
|
|
- ((DirectionalLight) light).setDirection(parseVector3(attribs));
|
|
|
- else if (light instanceof SpotLight){
|
|
|
- ((SpotLight) light).setDirection(parseVector3(attribs));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void parseLightAttenuation(Attributes attribs) throws SAXException {
|
|
|
- // NOTE: Derives range based on "linear" if it is used solely
|
|
|
- // for the attenuation. Otherwise derives it from "range"
|
|
|
- checkTopNode("light");
|
|
|
-
|
|
|
- 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)
|
|
|
- quadraticStr = attribs.getValue("quadric");
|
|
|
-
|
|
|
- float quadratic = parseFloat(quadraticStr);
|
|
|
-
|
|
|
- if (constant == 1 && quadratic == 0 && linear > 0){
|
|
|
- range = 1f / linear;
|
|
|
- }
|
|
|
-
|
|
|
- if (light instanceof PointLight){
|
|
|
- ((PointLight) light).setRadius(range);
|
|
|
- }else{
|
|
|
- ((SpotLight)light).setSpotRange(range);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- 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)){
|
|
|
- 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)
|
|
|
- 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")) {
|
|
|
- light = new PointLight();
|
|
|
- } 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")) {
|
|
|
- light = new SpotLight();
|
|
|
- } 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)){
|
|
|
- // set to disabled
|
|
|
- }
|
|
|
-
|
|
|
- // "attach" it to the parent of this node
|
|
|
- if (light != null)
|
|
|
- node.getParent().addLight(light);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- 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"))
|
|
|
- logger.log(Level.WARNING, "Unrecognized version number"
|
|
|
- + " in dotScene file: {0}", version);
|
|
|
-
|
|
|
- }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");
|
|
|
-
|
|
|
- node = root;
|
|
|
- }else if (qName.equals("externals")){
|
|
|
- checkTopNode("scene");
|
|
|
- // Not loaded currently
|
|
|
- }else if (qName.equals("item")){
|
|
|
- checkTopNode("externals");
|
|
|
- }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")){
|
|
|
- String curElement = elementStack.peek();
|
|
|
- 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){
|
|
|
- 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")){
|
|
|
- node.setUserData(name, Float.parseFloat(data));
|
|
|
- }else if (type.equals("STRING")){
|
|
|
- node.setUserData(name, data);
|
|
|
- }else if (type.equals("INT")){
|
|
|
- node.setUserData(name, Integer.parseInt(data));
|
|
|
- }
|
|
|
- }
|
|
|
- }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")){
|
|
|
- node.setLocalTranslation(SAXUtil.parseVector3(attribs));
|
|
|
- }
|
|
|
- }else if (qName.equals("quaternion") || qName.equals("rotation")){
|
|
|
- node.setLocalRotation(parseQuat(attribs));
|
|
|
- }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){
|
|
|
- light.setColor(parseColor(attribs));
|
|
|
- }
|
|
|
- }else{
|
|
|
- checkTopNode("environment");
|
|
|
- }
|
|
|
- } else if (qName.equals("normal") || qName.equals("direction")) {
|
|
|
- checkTopNode("light");
|
|
|
- parseLightNormal(attribs);
|
|
|
- } else if (qName.equals("lightAttenuation")) {
|
|
|
- parseLightAttenuation(attribs);
|
|
|
- } else if (qName.equals("spotLightRange") || qName.equals("lightRange")) {
|
|
|
- parseLightSpotLightRange(attribs);
|
|
|
- }
|
|
|
-
|
|
|
- elementStack.push(qName);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void endElement(String uri, String name, String qName) throws SAXException {
|
|
|
- if (qName.equals("node")){
|
|
|
- node = node.getParent();
|
|
|
- }else if (qName.equals("nodes")){
|
|
|
- node = null;
|
|
|
- }else if (qName.equals("entity")){
|
|
|
- node = entityNode.getParent();
|
|
|
- entityNode = null;
|
|
|
- }else if (qName.equals("light")){
|
|
|
- // apply the node's world transform on the light..
|
|
|
- root.updateGeometricState();
|
|
|
- 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){
|
|
|
- PointLight pl = (PointLight) light;
|
|
|
- Vector3f pos = node.getWorldTranslation();
|
|
|
- pl.setPosition(pos);
|
|
|
- }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);
|
|
|
- sl.setDirection(dir);
|
|
|
- }
|
|
|
- }
|
|
|
- light = null;
|
|
|
- }
|
|
|
- checkTopNode(qName);
|
|
|
- elementStack.pop();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void characters(char ch[], int start, int length) {
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- public Object load(AssetInfo info) throws IOException {
|
|
|
- try{
|
|
|
- key = info.getKey();
|
|
|
- assetManager = info.getManager();
|
|
|
- sceneName = key.getName();
|
|
|
- String ext = key.getExtension();
|
|
|
- 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();
|
|
|
-
|
|
|
- // Added by larynx 25.06.2011
|
|
|
- // Android needs the namespace aware flag set to true
|
|
|
- // Kirill 30.06.2011
|
|
|
- // Now, hack is applied for both desktop and android to avoid
|
|
|
- // checking with JmeSystem.
|
|
|
- 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(info.openStream());
|
|
|
- xr.parse(new InputSource(r));
|
|
|
- } finally {
|
|
|
- if (r != null){
|
|
|
- r.close();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return root;
|
|
|
- }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;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-}
|
|
|
+/*
|
|
|
+ * Copyright (c) 2009-2010 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.AssetKey;
|
|
|
+import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey;
|
|
|
+import com.jme3.material.MaterialList;
|
|
|
+import com.jme3.asset.AssetInfo;
|
|
|
+import com.jme3.asset.AssetLoader;
|
|
|
+import com.jme3.asset.AssetManager;
|
|
|
+import com.jme3.asset.AssetNotFoundException;
|
|
|
+import com.jme3.light.DirectionalLight;
|
|
|
+import com.jme3.light.Light;
|
|
|
+import com.jme3.light.PointLight;
|
|
|
+import com.jme3.light.SpotLight;
|
|
|
+import com.jme3.math.Quaternion;
|
|
|
+import com.jme3.math.Vector3f;
|
|
|
+import com.jme3.scene.Spatial;
|
|
|
+import com.jme3.util.PlaceholderAssets;
|
|
|
+import com.jme3.util.xml.SAXUtil;
|
|
|
+import java.io.IOException;
|
|
|
+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;
|
|
|
+
|
|
|
+import static com.jme3.util.xml.SAXUtil.*;
|
|
|
+
|
|
|
+public class SceneLoader extends DefaultHandler implements AssetLoader {
|
|
|
+
|
|
|
+ private static final Logger logger = Logger.getLogger(SceneLoader.class.getName());
|
|
|
+
|
|
|
+ private Stack<String> elementStack = new Stack<String>();
|
|
|
+ private AssetKey key;
|
|
|
+ private String sceneName;
|
|
|
+ private String folderName;
|
|
|
+ private AssetManager assetManager;
|
|
|
+ private MaterialList materialList;
|
|
|
+ private com.jme3.scene.Node root;
|
|
|
+ private com.jme3.scene.Node node;
|
|
|
+ private com.jme3.scene.Node entityNode;
|
|
|
+ private Light light;
|
|
|
+ private int nodeIdx = 0;
|
|
|
+ private static volatile int sceneIdx = 0;
|
|
|
+
|
|
|
+ public SceneLoader(){
|
|
|
+ super();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void startDocument() {
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void endDocument() {
|
|
|
+ }
|
|
|
+
|
|
|
+ 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
|
|
|
+ root = null;
|
|
|
+ node = null;
|
|
|
+ entityNode = null;
|
|
|
+ light = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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){
|
|
|
+ // 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){
|
|
|
+ // 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){
|
|
|
+ // defined as angle + axis
|
|
|
+ float angle = parseFloat(attribs.getValue("angle"));
|
|
|
+ float axisX = parseFloat(attribs.getValue("axisX"));
|
|
|
+ float axisY = parseFloat(attribs.getValue("axisY"));
|
|
|
+ float axisZ = parseFloat(attribs.getValue("axisZ"));
|
|
|
+ Quaternion q = new Quaternion();
|
|
|
+ q.fromAngleAxis(angle, new Vector3f(axisX, axisY, axisZ));
|
|
|
+ return q;
|
|
|
+ }else{
|
|
|
+ // defines as 3 angles along XYZ axes
|
|
|
+ float angleX = parseFloat(attribs.getValue("angleX"));
|
|
|
+ float angleY = parseFloat(attribs.getValue("angleY"));
|
|
|
+ float angleZ = parseFloat(attribs.getValue("angleZ"));
|
|
|
+ Quaternion q = new Quaternion();
|
|
|
+ q.fromAngles(angleX, angleY, angleZ);
|
|
|
+ return q;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void parseLightNormal(Attributes attribs) throws SAXException {
|
|
|
+ checkTopNode("light");
|
|
|
+
|
|
|
+ // SpotLight will be supporting a direction-normal, too.
|
|
|
+ if (light instanceof DirectionalLight)
|
|
|
+ ((DirectionalLight) light).setDirection(parseVector3(attribs));
|
|
|
+ else if (light instanceof SpotLight){
|
|
|
+ ((SpotLight) light).setDirection(parseVector3(attribs));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void parseLightAttenuation(Attributes attribs) throws SAXException {
|
|
|
+ // NOTE: Derives range based on "linear" if it is used solely
|
|
|
+ // for the attenuation. Otherwise derives it from "range"
|
|
|
+ checkTopNode("light");
|
|
|
+
|
|
|
+ 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)
|
|
|
+ quadraticStr = attribs.getValue("quadric");
|
|
|
+
|
|
|
+ float quadratic = parseFloat(quadraticStr);
|
|
|
+
|
|
|
+ if (constant == 1 && quadratic == 0 && linear > 0){
|
|
|
+ range = 1f / linear;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (light instanceof PointLight){
|
|
|
+ ((PointLight) light).setRadius(range);
|
|
|
+ }else{
|
|
|
+ ((SpotLight)light).setSpotRange(range);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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)){
|
|
|
+ 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)
|
|
|
+ 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")) {
|
|
|
+ light = new PointLight();
|
|
|
+ } 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")) {
|
|
|
+ light = new SpotLight();
|
|
|
+ } 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)){
|
|
|
+ // set to disabled
|
|
|
+ }
|
|
|
+
|
|
|
+ // "attach" it to the parent of this node
|
|
|
+ if (light != null)
|
|
|
+ node.getParent().addLight(light);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ 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")))
|
|
|
+ logger.log(Level.WARNING, "Unrecognized version number"
|
|
|
+ + " in dotScene file: {0}", version);
|
|
|
+
|
|
|
+ }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");
|
|
|
+
|
|
|
+ node = root;
|
|
|
+ }else if (qName.equals("externals")){
|
|
|
+ checkTopNode("scene");
|
|
|
+ // Not loaded currently
|
|
|
+ }else if (qName.equals("item")){
|
|
|
+ checkTopNode("externals");
|
|
|
+ }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")){
|
|
|
+ String curElement = elementStack.peek();
|
|
|
+ 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){
|
|
|
+ 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")){
|
|
|
+ node.setUserData(name, Float.parseFloat(data));
|
|
|
+ }else if (type.equals("STRING")){
|
|
|
+ node.setUserData(name, data);
|
|
|
+ }else if (type.equals("INT")){
|
|
|
+ node.setUserData(name, Integer.parseInt(data));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }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")){
|
|
|
+ node.setLocalTranslation(SAXUtil.parseVector3(attribs));
|
|
|
+ }
|
|
|
+ }else if (qName.equals("quaternion") || qName.equals("rotation")){
|
|
|
+ node.setLocalRotation(parseQuat(attribs));
|
|
|
+ }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){
|
|
|
+ light.setColor(parseColor(attribs));
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ checkTopNode("environment");
|
|
|
+ }
|
|
|
+ } else if (qName.equals("normal") || qName.equals("direction")) {
|
|
|
+ checkTopNode("light");
|
|
|
+ parseLightNormal(attribs);
|
|
|
+ } else if (qName.equals("lightAttenuation")) {
|
|
|
+ parseLightAttenuation(attribs);
|
|
|
+ } else if (qName.equals("spotLightRange") || qName.equals("lightRange")) {
|
|
|
+ parseLightSpotLightRange(attribs);
|
|
|
+ }
|
|
|
+
|
|
|
+ elementStack.push(qName);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void endElement(String uri, String name, String qName) throws SAXException {
|
|
|
+ if (qName.equals("node")){
|
|
|
+ node = node.getParent();
|
|
|
+ }else if (qName.equals("nodes")){
|
|
|
+ node = null;
|
|
|
+ }else if (qName.equals("entity")){
|
|
|
+ node = entityNode.getParent();
|
|
|
+ entityNode = null;
|
|
|
+ }else if (qName.equals("light")){
|
|
|
+ // apply the node's world transform on the light..
|
|
|
+ root.updateGeometricState();
|
|
|
+ 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){
|
|
|
+ PointLight pl = (PointLight) light;
|
|
|
+ Vector3f pos = node.getWorldTranslation();
|
|
|
+ pl.setPosition(pos);
|
|
|
+ }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);
|
|
|
+ sl.setDirection(dir);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ light = null;
|
|
|
+ }
|
|
|
+ checkTopNode(qName);
|
|
|
+ elementStack.pop();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void characters(char ch[], int start, int length) {
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ public Object load(AssetInfo info) throws IOException {
|
|
|
+ try{
|
|
|
+ key = info.getKey();
|
|
|
+ assetManager = info.getManager();
|
|
|
+ sceneName = key.getName();
|
|
|
+ String ext = key.getExtension();
|
|
|
+ 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();
|
|
|
+
|
|
|
+ // Added by larynx 25.06.2011
|
|
|
+ // Android needs the namespace aware flag set to true
|
|
|
+ // Kirill 30.06.2011
|
|
|
+ // Now, hack is applied for both desktop and android to avoid
|
|
|
+ // checking with JmeSystem.
|
|
|
+ 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(info.openStream());
|
|
|
+ xr.parse(new InputSource(r));
|
|
|
+ } finally {
|
|
|
+ if (r != null){
|
|
|
+ r.close();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return root;
|
|
|
+ }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;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|