* dotScene loader can now load cameras (loads them as CameraNode). Fixes issue 226

* dotScene loader now correctly loads materials in <externals> 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 <environment> tag and adds an AmbientLight to the scene.

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9736 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
Sha..rd 13 years ago
parent a5275875a7
commit cf2bd1de24
  1. 218
      engine/src/ogre/com/jme3/scene/plugins/ogre/SceneLoader.java
  2. 159
      engine/src/ogre/com/jme3/scene/plugins/ogre/SceneMaterialLoader.java

@ -29,18 +29,24 @@
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.jme3.scene.plugins.ogre; package com.jme3.scene.plugins.ogre;
import com.jme3.asset.*; import com.jme3.asset.*;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight; import com.jme3.light.DirectionalLight;
import com.jme3.light.Light; import com.jme3.light.Light;
import com.jme3.light.PointLight; import com.jme3.light.PointLight;
import com.jme3.light.SpotLight; import com.jme3.light.SpotLight;
import com.jme3.material.MaterialList; import com.jme3.material.MaterialList;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion; import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f; 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.Spatial;
import com.jme3.scene.control.CameraControl.ControlDirection;
import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey; import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey;
import com.jme3.util.PlaceholderAssets; import com.jme3.util.PlaceholderAssets;
import com.jme3.util.xml.SAXUtil; import com.jme3.util.xml.SAXUtil;
@ -60,8 +66,11 @@ import org.xml.sax.helpers.DefaultHandler;
public class SceneLoader extends DefaultHandler implements AssetLoader { public class SceneLoader extends DefaultHandler implements AssetLoader {
private static final Logger logger = Logger.getLogger(SceneLoader.class.getName()); 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<String> elementStack = new Stack<String>(); private Stack<String> elementStack = new Stack<String>();
private AssetKey key; private AssetKey key;
private String sceneName; private String sceneName;
@ -72,6 +81,8 @@ public class SceneLoader extends DefaultHandler implements AssetLoader {
private com.jme3.scene.Node node; private com.jme3.scene.Node node;
private com.jme3.scene.Node entityNode; private com.jme3.scene.Node entityNode;
private Light light; private Light light;
private Camera camera;
private CameraNode cameraNode;
private int nodeIdx = 0; private int nodeIdx = 0;
private static volatile int sceneIdx = 0; private static volatile int sceneIdx = 0;
@ -98,6 +109,8 @@ public class SceneLoader extends DefaultHandler implements AssetLoader {
node = null; node = null;
entityNode = null; entityNode = null;
light = null; light = null;
camera = null;
cameraNode = null;
} }
private void checkTopNode(String topNode) throws SAXException { private void checkTopNode(String topNode) throws SAXException {
@ -145,9 +158,9 @@ public class SceneLoader extends DefaultHandler implements AssetLoader {
checkTopNode("light"); checkTopNode("light");
// SpotLight will be supporting a direction-normal, too. // SpotLight will be supporting a direction-normal, too.
if (light instanceof DirectionalLight) if (light instanceof DirectionalLight) {
((DirectionalLight) light).setDirection(parseVector3(attribs)); ((DirectionalLight) light).setDirection(parseVector3(attribs));
else if (light instanceof SpotLight){ } else if (light instanceof SpotLight) {
((SpotLight) light).setDirection(parseVector3(attribs)); ((SpotLight) light).setDirection(parseVector3(attribs));
} }
} }
@ -163,8 +176,9 @@ public class SceneLoader extends DefaultHandler implements AssetLoader {
float linear = parseFloat(attribs.getValue("linear")); float linear = parseFloat(attribs.getValue("linear"));
String quadraticStr = attribs.getValue("quadratic"); String quadraticStr = attribs.getValue("quadratic");
if (quadraticStr == null) if (quadraticStr == null) {
quadraticStr = attribs.getValue("quadric"); quadraticStr = attribs.getValue("quadric");
}
float quadratic = parseFloat(quadraticStr); float quadratic = parseFloat(quadraticStr);
@ -197,8 +211,9 @@ public class SceneLoader extends DefaultHandler implements AssetLoader {
} }
private void parseLight(Attributes attribs) throws SAXException { 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"); throw new SAXException("dotScene parse error: light can only appear under a node");
}
checkTopNode("node"); checkTopNode("node");
@ -211,6 +226,9 @@ public class SceneLoader extends DefaultHandler implements AssetLoader {
((DirectionalLight) light).setDirection(Vector3f.UNIT_Z); ((DirectionalLight) light).setDirection(Vector3f.UNIT_Z);
} else if (lightType.equals("spotLight") || lightType.equals("spot")) { } else if (lightType.equals("spotLight") || lightType.equals("spot")) {
light = new SpotLight(); light = new SpotLight();
} else if (lightType.equals("omni")) {
// XXX: It doesn't seem any exporters actually emit this type?
light = new AmbientLight();
} else { } else {
logger.log(Level.WARNING, "No matching jME3 LightType found for OGRE LightType: {0}", lightType); logger.log(Level.WARNING, "No matching jME3 LightType found for OGRE LightType: {0}", lightType);
} }
@ -221,9 +239,90 @@ public class SceneLoader extends DefaultHandler implements AssetLoader {
} }
// "attach" it to the parent of this node // "attach" it to the parent of this node
if (light != null) if (light != null) {
node.getParent().addLight(light); 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 @Override
public void startElement(String uri, String localName, String qName, Attributes attribs) throws SAXException { public void startElement(String uri, String localName, String qName, Attributes attribs) throws SAXException {
@ -233,39 +332,30 @@ public class SceneLoader extends DefaultHandler implements AssetLoader {
} }
String version = attribs.getValue("formatVersion"); 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" logger.log(Level.WARNING, "Unrecognized version number"
+ " in dotScene file: {0}", version); + " in dotScene file: {0}", version);
}
} else if (qName.equals("nodes")) { } else if (qName.equals("nodes")) {
if (root != null) { if (root != null) {
throw new SAXException("dotScene parse error: nodes element was specified twice"); throw new SAXException("dotScene parse error: nodes element was specified twice");
} }
if (sceneName == null) if (sceneName == null) {
root = new com.jme3.scene.Node("OgreDotScene" + (++sceneIdx)); root = new com.jme3.scene.Node("OgreDotScene" + (++sceneIdx));
else } else {
root = new com.jme3.scene.Node(sceneName + "-scene_node"); root = new com.jme3.scene.Node(sceneName + "-scene_node");
}
node = root; node = root;
} else if (qName.equals("externals")) { } else if (qName.equals("externals")) {
checkTopNode("scene"); checkTopNode("scene");
// Not loaded currently
} else if (qName.equals("item")) { } else if (qName.equals("item")) {
checkTopNode("externals"); checkTopNode("externals");
} else if (qName.equals("file")) { } else if (qName.equals("file")) {
checkTopNode("item"); checkTopNode("item");
// XXX: Currently material file name is based // NOTE: This part of the file is ignored, it is parsed
// on the scene's filename. THIS IS NOT CORRECT. // by SceneMaterialLoader in the first pass.
// 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")) { } else if (qName.equals("node")) {
String curElement = elementStack.peek(); String curElement = elementStack.peek();
if (!curElement.equals("node") && !curElement.equals("nodes")) { if (!curElement.equals("node") && !curElement.equals("nodes")) {
@ -273,15 +363,7 @@ public class SceneLoader extends DefaultHandler implements AssetLoader {
+ "node element can only appear under 'node' or 'nodes'"); + "node element can only appear under 'node' or 'nodes'");
} }
String name = attribs.getValue("name"); parseNode(attribs);
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")) { } else if (qName.equals("property")) {
if (node != null) { if (node != null) {
String type = attribs.getValue("type"); String type = attribs.getValue("type");
@ -299,44 +381,18 @@ public class SceneLoader extends DefaultHandler implements AssetLoader {
} }
} else if (qName.equals("entity")) { } else if (qName.equals("entity")) {
checkTopNode("node"); checkTopNode("node");
parseEntity(attribs);
String name = attribs.getValue("name"); } else if (qName.equals("camera")) {
if (name == null) checkTopNode("node");
name = "OgreEntity-" + (++nodeIdx); parseCamera(attribs);
else } else if (qName.equals("clipping")) {
name += "-entity"; checkTopNode("camera");
parseCameraClipping(attribs);
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")) { } else if (qName.equals("position")) {
if (elementStack.peek().equals("node")) { if (elementStack.peek().equals("node")) {
node.setLocalTranslation(SAXUtil.parseVector3(attribs)); 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)); node.setLocalRotation(parseQuat(attribs));
@ -352,6 +408,16 @@ public class SceneLoader extends DefaultHandler implements AssetLoader {
} else { } else {
checkTopNode("environment"); 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")) { } else if (qName.equals("normal") || qName.equals("direction")) {
checkTopNode("light"); checkTopNode("light");
parseLightNormal(attribs); parseLightNormal(attribs);
@ -373,6 +439,9 @@ public class SceneLoader extends DefaultHandler implements AssetLoader {
} else if (qName.equals("entity")) { } else if (qName.equals("entity")) {
node = entityNode.getParent(); node = entityNode.getParent();
entityNode = null; entityNode = null;
} else if (qName.equals("camera")) {
node = cameraNode.getParent();
cameraNode = null;
} else if (qName.equals("light")) { } else if (qName.equals("light")) {
// apply the node's world transform on the light.. // apply the node's world transform on the light..
root.updateGeometricState(); root.updateGeometricState();
@ -409,8 +478,6 @@ public class SceneLoader extends DefaultHandler implements AssetLoader {
public void characters(char ch[], int start, int length) { public void characters(char ch[], int start, int length) {
} }
public Object load(AssetInfo info) throws IOException { public Object load(AssetInfo info) throws IOException {
try { try {
key = info.getKey(); key = info.getKey();
@ -420,6 +487,15 @@ public class SceneLoader extends DefaultHandler implements AssetLoader {
folderName = key.getFolder(); folderName = key.getFolder();
sceneName = sceneName.substring(0, sceneName.length() - ext.length() - 1); sceneName = sceneName.substring(0, sceneName.length() - ext.length() - 1);
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"); OgreMaterialKey materialKey = new OgreMaterialKey(sceneName + ".material");
try { try {
materialList = (MaterialList) assetManager.loadAsset(materialKey); materialList = (MaterialList) assetManager.loadAsset(materialKey);
@ -427,8 +503,9 @@ public class SceneLoader extends DefaultHandler implements AssetLoader {
logger.log(Level.WARNING, "Cannot locate {0} for scene {1}", new Object[]{materialKey, key}); logger.log(Level.WARNING, "Cannot locate {0} for scene {1}", new Object[]{materialKey, key});
materialList = null; materialList = null;
} }
}
reset(); // == Run 2nd pass to load entities and other objects ==
// Added by larynx 25.06.2011 // Added by larynx 25.06.2011
// Android needs the namespace aware flag set to true // Android needs the namespace aware flag set to true
@ -464,5 +541,4 @@ public class SceneLoader extends DefaultHandler implements AssetLoader {
throw ioEx; throw ioEx;
} }
} }
} }

@ -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<String> elementStack = new Stack<String>();
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;
}
}
}
Loading…
Cancel
Save