From efd91620eef696ee24a7ced20d92f74819163a7e Mon Sep 17 00:00:00 2001 From: kobayasi Date: Sun, 7 Jul 2013 00:22:49 +0900 Subject: [PATCH] merge from jme10694 --- .../ogre/com/jme3/scene/plugins/ogre/AnimData.java | 3 +- .../jme3/scene/plugins/ogre/MaterialLoader.java | 56 +- .../scene/plugins/ogre/MeshAnimationLoader.java | 3 +- .../com/jme3/scene/plugins/ogre/MeshLoader.java | 369 ++++--- .../com/jme3/scene/plugins/ogre/OgreMeshKey.java | 45 +- .../com/jme3/scene/plugins/ogre/SceneLoader.java | 1015 +++++++++++--------- .../scene/plugins/ogre/SceneMaterialLoader.java | 158 +++ .../jme3/scene/plugins/ogre/SkeletonLoader.java | 25 +- .../plugins/ogre/matext/MaterialExtension.java | 3 +- .../ogre/matext/MaterialExtensionLoader.java | 36 +- .../plugins/ogre/matext/MaterialExtensionSet.java | 27 +- .../scene/plugins/ogre/matext/OgreMaterialKey.java | 53 +- 12 files changed, 1080 insertions(+), 713 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/AnimData.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/AnimData.java index f8d99aa6a..1b1eb2a9b 100644 --- a/engine/src/ogre/com/jme3/scene/plugins/ogre/AnimData.java +++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/AnimData.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2010 jMonkeyEngine + * Copyright (c) 2009-2012 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,7 +29,6 @@ * 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.animation.Animation; diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/MaterialLoader.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/MaterialLoader.java index 4b4f115a9..74e871b36 100644 --- a/engine/src/ogre/com/jme3/scene/plugins/ogre/MaterialLoader.java +++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/MaterialLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2010 jMonkeyEngine + * Copyright (c) 2009-2012 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,15 +29,9 @@ * 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.AssetInfo; -import com.jme3.asset.AssetKey; -import com.jme3.asset.AssetLoader; -import com.jme3.asset.AssetManager; -import com.jme3.asset.AssetNotFoundException; -import com.jme3.asset.TextureKey; +import com.jme3.asset.*; import com.jme3.material.Material; import com.jme3.material.MaterialList; import com.jme3.material.RenderState; @@ -45,17 +39,14 @@ import com.jme3.math.ColorRGBA; import com.jme3.scene.plugins.ogre.matext.MaterialExtensionLoader; import com.jme3.scene.plugins.ogre.matext.MaterialExtensionSet; import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; import com.jme3.texture.Texture; import com.jme3.texture.Texture.WrapMode; import com.jme3.texture.Texture2D; -import com.jme3.util.BufferUtils; +import com.jme3.util.PlaceholderAssets; import com.jme3.util.blockparser.BlockLanguageParser; import com.jme3.util.blockparser.Statement; import java.io.IOException; import java.io.InputStream; -import java.nio.ByteBuffer; import java.util.Arrays; import java.util.List; import java.util.Scanner; @@ -81,7 +72,7 @@ public class MaterialLoader implements AssetLoader { private int texUnit = 0; private ColorRGBA readColor(String content){ - String[] split = content.split(" "); + String[] split = content.split("\\s"); ColorRGBA color = new ColorRGBA(); color.r = Float.parseFloat(split[0]); @@ -137,23 +128,13 @@ public class MaterialLoader implements AssetLoader { cubic = true; } - TextureKey key = new TextureKey(folderName + path, false); - key.setGenerateMips(genMips); - key.setAsCube(cubic); + TextureKey texKey = new TextureKey(folderName + path, false); + texKey.setGenerateMips(genMips); + texKey.setAsCube(cubic); - Texture loadedTexture; try { - loadedTexture = assetManager.loadTexture(key); - } catch (AssetNotFoundException ex){ - logger.log(Level.WARNING, "Failed to load texture " + key + " for material " + matName, ex); - loadedTexture = null; - } - if (loadedTexture == null){ - ByteBuffer tempData = BufferUtils.createByteBuffer(3); - tempData.put((byte)0xFF).put((byte)0x00).put((byte)0x00); - textures[texUnit].setImage(new Image(Format.RGB8, 1,1,tempData)); - logger.log(Level.WARNING, "Using RED texture instead of {0}", path); - }else{ + Texture loadedTexture = assetManager.loadTexture(texKey); + textures[texUnit].setImage(loadedTexture.getImage()); textures[texUnit].setMinFilter(loadedTexture.getMinFilter()); textures[texUnit].setKey(loadedTexture.getKey()); @@ -164,8 +145,11 @@ public class MaterialLoader implements AssetLoader { textures[texUnit].setName(texName); texName = null; }else{ - textures[texUnit].setName(key.getName()); + textures[texUnit].setName(texKey.getName()); } + } catch (AssetNotFoundException ex){ + logger.log(Level.WARNING, "Cannot locate {0} for material {1}", new Object[]{texKey, matName}); + textures[texUnit].setImage(PlaceholderAssets.getPlaceholderImage()); } } @@ -243,7 +227,7 @@ public class MaterialLoader implements AssetLoader { }else if (keyword.equals("emissive")){ emissive = readColor(split[1]); }else if (keyword.equals("specular")){ - String[] subsplit = split[1].split(" "); + String[] subsplit = split[1].split("\\s"); specular = new ColorRGBA(); specular.r = Float.parseFloat(subsplit[0]); specular.g = Float.parseFloat(subsplit[1]); @@ -319,7 +303,7 @@ public class MaterialLoader implements AssetLoader { if (statement.getLine().startsWith("technique")){ readTechnique(statement); }else if (statement.getLine().startsWith("receive_shadows")){ - String isOn = statement.getLine().split(" ")[1]; + String isOn = statement.getLine().split("\\s")[1]; if (isOn != null && isOn.equals("true")){ } } @@ -344,12 +328,16 @@ public class MaterialLoader implements AssetLoader { rs.setAlphaTest(true); rs.setAlphaFallOff(0.01f); rs.setBlendMode(RenderState.BlendMode.Alpha); - if (twoSide) + + if (twoSide){ rs.setFaceCullMode(RenderState.FaceCullMode.Off); + } + // rs.setDepthWrite(false); mat.setTransparent(true); - if (!noLight) + if (!noLight){ mat.setBoolean("UseAlpha", true); + } }else{ if (twoSide){ RenderState rs = mat.getAdditionalRenderState(); @@ -452,7 +440,7 @@ public class MaterialLoader implements AssetLoader { "Ogre3D materials with extended materials"); } - list = new MaterialExtensionLoader().load(assetManager, matExts, statements); + list = new MaterialExtensionLoader().load(assetManager, key, matExts, statements); break; }else if (statement.getLine().startsWith("material")){ if (list == null){ diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshAnimationLoader.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshAnimationLoader.java index 75ff86579..3d198fd85 100644 --- a/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshAnimationLoader.java +++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshAnimationLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2010 jMonkeyEngine + * Copyright (c) 2009-2012 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,7 +29,6 @@ * 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 static com.jmex.model.XMLUtil.getAttribute; diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java index db5eba180..5f2653281 100644 --- a/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java +++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2010 jMonkeyEngine + * Copyright (c) 2009-2012 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,54 +31,40 @@ */ package com.jme3.scene.plugins.ogre; -import com.jme3.animation.Animation; -import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey; import com.jme3.animation.AnimControl; +import com.jme3.animation.Animation; import com.jme3.animation.SkeletonControl; -import com.jme3.asset.AssetInfo; -import com.jme3.asset.AssetKey; -import com.jme3.asset.AssetLoader; -import com.jme3.asset.AssetManager; -import com.jme3.asset.AssetNotFoundException; +import com.jme3.asset.*; import com.jme3.material.Material; import com.jme3.material.MaterialList; import com.jme3.math.ColorRGBA; import com.jme3.renderer.queue.RenderQueue.Bucket; -import com.jme3.scene.Geometry; -import com.jme3.scene.Mesh; -import com.jme3.scene.Node; -import com.jme3.scene.UserData; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.*; import com.jme3.scene.VertexBuffer.Format; import com.jme3.scene.VertexBuffer.Type; import com.jme3.scene.VertexBuffer.Usage; +import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey; import com.jme3.util.BufferUtils; import com.jme3.util.IntMap; import com.jme3.util.IntMap.Entry; +import com.jme3.util.PlaceholderAssets; +import static com.jme3.util.xml.SAXUtil.*; import java.io.IOException; import java.io.InputStreamReader; -import java.nio.Buffer; -import java.nio.ByteBuffer; -import java.nio.FloatBuffer; -import java.nio.IntBuffer; -import java.nio.ShortBuffer; +import java.nio.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; 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.*; - /** * Loads Ogre3D mesh.xml files. */ @@ -86,7 +72,6 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { private static final Logger logger = Logger.getLogger(MeshLoader.class.getName()); public static boolean AUTO_INTERLEAVE = true; - public static boolean HARDWARE_SKINNING = false; private static final Type[] TEXCOORD_TYPES = new Type[]{ Type.TexCoord, @@ -97,6 +82,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { Type.TexCoord6, Type.TexCoord7, Type.TexCoord8,}; + private AssetKey key; private String meshName; private String folderName; private AssetManager assetManager; @@ -110,15 +96,18 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { private Geometry geom; private ByteBuffer indicesData; private FloatBuffer weightsFloatData; + private boolean actuallyHasWeights = false; private int vertCount; private boolean usesSharedVerts; private boolean usesBigIndices; + private boolean submeshNamesHack; // Global data private Mesh sharedMesh; private int meshIndex = 0; private int texCoordIndex = 0; private String ignoreUntilEnd = null; private List geoms = new ArrayList(); + private ArrayList usesSharedMesh = new ArrayList(); private IntMap> lodLevels = new IntMap>(); private AnimData animData; @@ -139,6 +128,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { geom = null; sharedMesh = null; + usesSharedMesh.clear(); usesSharedVerts = false; vertCount = 0; meshIndex = 0; @@ -147,6 +137,8 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { animData = null; + actuallyHasWeights = false; + submeshNamesHack = false; indicesData = null; weightsFloatData = null; } @@ -155,42 +147,65 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { public void endDocument() { } - private void pushFace(String v1, String v2, String v3) throws SAXException { - int i1 = parseInt(v1); - - // TODO: fan/strip support - int i2 = parseInt(v2); - int i3 = parseInt(v3); + private void pushIndex(int index) { if (ib != null) { - ib.put(i1).put(i2).put(i3); + ib.put(index); } else { - sb.put((short) i1).put((short) i2).put((short) i3); + sb.put((short) index); } } - private boolean isUsingSharedVerts(Geometry geom) { - return geom.getUserData(UserData.JME_SHAREDMESH) != null; + private void pushFace(String v1, String v2, String v3) throws SAXException { + // TODO: fan/strip support + switch (mesh.getMode()) { + case Triangles: + pushIndex(parseInt(v1)); + pushIndex(parseInt(v2)); + pushIndex(parseInt(v3)); + break; + case Lines: + pushIndex(parseInt(v1)); + pushIndex(parseInt(v2)); + break; + case Points: + pushIndex(parseInt(v1)); + break; + } } +// private boolean isUsingSharedVerts(Geometry geom) { + // Old code for buffer sharer + //return geom.getUserData(UserData.JME_SHAREDMESH) != null; +// } private void startFaces(String count) throws SAXException { int numFaces = parseInt(count); - int numIndices; + int indicesPerFace = 0; - if (mesh.getMode() == Mesh.Mode.Triangles) { - numIndices = numFaces * 3; - } else { - throw new SAXException("Triangle strip or fan not supported!"); + switch (mesh.getMode()) { + case Triangles: + indicesPerFace = 3; + break; + case Lines: + indicesPerFace = 2; + break; + case Points: + indicesPerFace = 1; + break; + default: + throw new SAXException("Strips or fans not supported!"); } + int numIndices = indicesPerFace * numFaces; + vb = new VertexBuffer(VertexBuffer.Type.Index); if (!usesBigIndices) { sb = BufferUtils.createShortBuffer(numIndices); ib = null; - vb.setupData(Usage.Static, 3, Format.UnsignedShort, sb); + vb.setupData(Usage.Static, indicesPerFace, Format.UnsignedShort, sb); } else { ib = BufferUtils.createIntBuffer(numIndices); sb = null; - vb.setupData(Usage.Static, 3, Format.UnsignedInt, ib); + vb.setupData(Usage.Static, indicesPerFace, Format.UnsignedInt, ib); } mesh.setBuffer(vb); } @@ -199,19 +214,24 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { Material mat = null; if (matName.endsWith(".j3m")) { // load as native jme3 material instance - mat = assetManager.loadMaterial(matName); + try { + mat = assetManager.loadMaterial(matName); + } catch (AssetNotFoundException ex) { + // Warning will be raised (see below) + if (!ex.getMessage().equals(matName)) { + throw ex; + } + } } else { if (materialList != null) { mat = materialList.get(matName); } - if (mat == null) { - logger.log(Level.WARNING, "Material {0} not found. Applying default material", matName); - mat = (Material) assetManager.loadMaterial("Common/Materials/RedColor.j3m"); - } } if (mat == null) { - throw new RuntimeException("Cannot locate material named " + matName); + logger.log(Level.WARNING, "Cannot locate {0} for model {1}", new Object[]{matName, key}); + mat = PlaceholderAssets.getPlaceholderMaterial(assetManager); + //mat.setKey(new MaterialKey(matName)); } if (mat.isTransparent()) { @@ -225,20 +245,29 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { mesh = new Mesh(); if (opType == null || opType.equals("triangle_list")) { mesh.setMode(Mesh.Mode.Triangles); - } else if (opType.equals("triangle_strip")) { - mesh.setMode(Mesh.Mode.TriangleStrip); - } else if (opType.equals("triangle_fan")) { - mesh.setMode(Mesh.Mode.TriangleFan); + //} else if (opType.equals("triangle_strip")) { + // mesh.setMode(Mesh.Mode.TriangleStrip); + //} else if (opType.equals("triangle_fan")) { + // mesh.setMode(Mesh.Mode.TriangleFan); + } else if (opType.equals("line_list")) { + mesh.setMode(Mesh.Mode.Lines); + } else { + throw new SAXException("Unsupported operation type: " + opType); } usesBigIndices = parseBool(use32bitIndices, false); usesSharedVerts = parseBool(usesharedvertices, false); if (usesSharedVerts) { + usesSharedMesh.add(true); + + // Old code for buffer sharer // import vertexbuffers from shared geom - IntMap sharedBufs = sharedMesh.getBuffers(); - for (Entry entry : sharedBufs) { - mesh.setBuffer(entry.getValue()); - } +// IntMap sharedBufs = sharedMesh.getBuffers(); +// for (Entry entry : sharedBufs) { +// mesh.setBuffer(entry.getValue()); +// } + } else { + usesSharedMesh.add(false); } if (meshName == null) { @@ -248,8 +277,9 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { } if (usesSharedVerts) { + // Old code for buffer sharer // this mesh is shared! - geom.setUserData(UserData.JME_SHAREDMESH, sharedMesh); + //geom.setUserData(UserData.JME_SHAREDMESH, sharedMesh); } applyMaterial(geom, matName); @@ -270,11 +300,23 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { } /** - * Normalizes weights if needed and finds largest amount of weights used - * for all vertices in the buffer. + * Normalizes weights if needed and finds largest amount of weights used for + * all vertices in the buffer. */ private void endBoneAssigns() { - if (mesh != sharedMesh && isUsingSharedVerts(geom)) { +// if (mesh != sharedMesh && isUsingSharedVerts(geom)) { +// return; +// } + + if (!actuallyHasWeights) { + // No weights were actually written (the tag didn't have any entries) + // remove those buffers + mesh.clearBuffer(Type.BoneIndex); + mesh.clearBuffer(Type.BoneWeight); + + weightsFloatData = null; + indicesData = null; + return; } @@ -301,7 +343,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { if (sum != 1f) { weightsFloatData.position(weightsFloatData.position() - 4); // compute new vals based on sum - float sumToB = 1f / sum; + float sumToB = sum == 0 ? 0 : 1f / sum; weightsFloatData.put(w0 * sumToB); weightsFloatData.put(w1 * sumToB); weightsFloatData.put(w2 * sumToB); @@ -310,6 +352,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { } weightsFloatData.rewind(); + actuallyHasWeights = false; weightsFloatData = null; indicesData = null; @@ -327,24 +370,28 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { // each vertex has // - 4 bone weights // - 4 bone indices - if (HARDWARE_SKINNING) { - weightsFloatData = BufferUtils.createFloatBuffer(vertCount * 4); - indicesData = BufferUtils.createByteBuffer(vertCount * 4); - } else { - // create array-backed buffers if software skinning for access speed - weightsFloatData = FloatBuffer.allocate(vertCount * 4); - indicesData = ByteBuffer.allocate(vertCount * 4); - } + // create array-backed buffers for software skinning for access speed + weightsFloatData = FloatBuffer.allocate(vertCount * 4); + indicesData = ByteBuffer.allocate(vertCount * 4); VertexBuffer weights = new VertexBuffer(Type.BoneWeight); VertexBuffer indices = new VertexBuffer(Type.BoneIndex); - Usage usage = HARDWARE_SKINNING ? Usage.Static : Usage.CpuOnly; - weights.setupData(usage, 4, Format.Float, weightsFloatData); - indices.setupData(usage, 4, Format.UnsignedByte, indicesData); - + weights.setupData(Usage.CpuOnly, 4, Format.Float, weightsFloatData); + indices.setupData(Usage.CpuOnly, 4, Format.UnsignedByte, indicesData); + mesh.setBuffer(weights); mesh.setBuffer(indices); + + //creating empty buffers for HW skinning + //the buffers will be setup if ever used. + VertexBuffer weightsHW = new VertexBuffer(Type.HWBoneWeight); + VertexBuffer indicesHW = new VertexBuffer(Type.HWBoneIndex); + //setting usage to cpuOnly so that the buffer is not send empty to the GPU + indicesHW.setUsage(Usage.CpuOnly); + weightsHW.setUsage(Usage.CpuOnly); + mesh.setBuffer(weightsHW); + mesh.setBuffer(indicesHW); } private void startVertexBuffer(Attributes attribs) throws SAXException { @@ -451,7 +498,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { private void pushColor(Attributes attribs) throws SAXException { FloatBuffer buf = (FloatBuffer) mesh.getBuffer(Type.Color).getData(); String value = parseString(attribs.getValue("value")); - String[] vals = value.split(" "); + String[] vals = value.split("\\s"); if (vals.length != 3 && vals.length != 4) { throw new SAXException("Color value must contain 3 or 4 components"); } @@ -471,21 +518,31 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { private void startLodFaceList(String submeshindex, String numfaces) { int index = Integer.parseInt(submeshindex); + mesh = geoms.get(index).getMesh(); int faceCount = Integer.parseInt(numfaces); + VertexBuffer originalIndexBuffer = mesh.getBuffer(Type.Index); vb = new VertexBuffer(VertexBuffer.Type.Index); - sb = BufferUtils.createShortBuffer(faceCount * 3); - ib = null; - vb.setupData(Usage.Static, 3, Format.UnsignedShort, sb); + if (originalIndexBuffer.getFormat() == Format.UnsignedInt) { + // LOD buffer should also be integer + ib = BufferUtils.createIntBuffer(faceCount * 3); + sb = null; + vb.setupData(Usage.Static, 3, Format.UnsignedInt, ib); + } else { + sb = BufferUtils.createShortBuffer(faceCount * 3); + ib = null; + vb.setupData(Usage.Static, 3, Format.UnsignedShort, sb); + } List levels = lodLevels.get(index); if (levels == null) { + // Create the LOD levels list levels = new ArrayList(); - Mesh submesh = geoms.get(index).getMesh(); - levels.add(submesh.getBuffer(Type.Index)); + + // Add the first LOD level (always the original index buffer) + levels.add(originalIndexBuffer); lodLevels.put(index, levels); } - levels.add(vb); } @@ -531,15 +588,27 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { weightsFloatData.put(i, w); indicesData.put(i, bone); + actuallyHasWeights = true; } private void startSkeleton(String name) { - animData = (AnimData) assetManager.loadAsset(folderName + name + ".xml"); + AssetKey assetKey = new AssetKey(folderName + name + ".xml"); + try { + animData = (AnimData) assetManager.loadAsset(assetKey); + } catch (AssetNotFoundException ex) { + logger.log(Level.WARNING, "Cannot locate {0} for model {1}", new Object[]{assetKey, key}); + animData = null; + } } private void startSubmeshName(String indexStr, String nameStr) { int index = Integer.parseInt(indexStr); - geoms.get(index).setName(nameStr); + if (index >= geoms.size()) { + logger.log(Level.WARNING, "Submesh name index is larger than number of geometries: {0} >= {1}", + new Object[]{index, geoms.size()}); + } else { + geoms.get(index).setName(nameStr); + } } @Override @@ -590,10 +659,15 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { } else if (qName.equals("boneassignments")) { startBoneAssigns(); } else if (qName.equals("submesh")) { - startSubMesh(attribs.getValue("material"), - attribs.getValue("usesharedvertices"), - attribs.getValue("use32bitindexes"), - attribs.getValue("operationtype")); + if (submeshNamesHack) { + // Hack for blender2ogre only + startSubmeshName(attribs.getValue("index"), attribs.getValue("name")); + } else { + startSubMesh(attribs.getValue("material"), + attribs.getValue("usesharedvertices"), + attribs.getValue("use32bitindexes"), + attribs.getValue("operationtype")); + } } else if (qName.equals("sharedgeometry")) { String count = attribs.getValue("vertexcount"); if (count == null) { @@ -609,6 +683,9 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { startSkeleton(attribs.getValue("name")); } else if (qName.equals("submeshnames")) { // ok + // setting submeshNamesHack to true will make "submesh" tag be interpreted + // as a "submeshname" tag. + submeshNamesHack = true; } else if (qName.equals("submeshname")) { startSubmeshName(attribs.getValue("index"), attribs.getValue("name")); } else if (qName.equals("mesh")) { @@ -628,11 +705,14 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { return; } - if (qName.equals("submesh")) { + + // If submesh hack is enabled, ignore any submesh/submeshes + // end tags. + if (qName.equals("submesh") && !submeshNamesHack) { usesBigIndices = false; geom = null; mesh = null; - } else if (qName.equals("submeshes")) { + } else if (qName.equals("submeshes") && !submeshNamesHack) { // IMPORTANT: restore sharedmesh, for use with shared boneweights geom = null; mesh = sharedMesh; @@ -653,9 +733,8 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { } else if (qName.equals("geometry") || qName.equals("sharedgeometry")) { // finish writing to buffers - IntMap bufs = mesh.getBuffers(); - for (Entry entry : bufs) { - Buffer data = entry.getValue().getData(); + for (VertexBuffer buf : mesh.getBufferList().getArray()) { + Buffer data = buf.getData(); if (data.position() != 0) { data.flip(); } @@ -675,6 +754,9 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { endLevelOfDetail(); } else if (qName.equals("boneassignments")) { endBoneAssigns(); + } else if (qName.equals("submeshnames")) { + // Restore default handling for "submesh" tag. + submeshNamesHack = false; } } @@ -688,9 +770,12 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { for (int i = 0; i < geoms.size(); i++) { Geometry g = geoms.get(i); Mesh m = g.getMesh(); - if (sharedMesh != null && isUsingSharedVerts(g)) { - m.setBound(sharedMesh.getBound().clone()); + + // New code for buffer extract + if (sharedMesh != null && usesSharedMesh.get(i)) { + m.extractVertexData(sharedMesh); } + model.attachChild(geoms.get(i)); } @@ -699,45 +784,19 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { if (animData != null) { // This model uses animation - // generate bind pose for mesh - // ONLY if not using shared geometry - // This includes the shared geoemtry itself actually - if (sharedMesh != null) { - sharedMesh.generateBindPose(!HARDWARE_SKINNING); - } - for (int i = 0; i < geoms.size(); i++) { Geometry g = geoms.get(i); Mesh m = geoms.get(i).getMesh(); - boolean useShared = isUsingSharedVerts(g); - - - if (!useShared) { - // create bind pose - m.generateBindPose(!HARDWARE_SKINNING); -// } else { - // Inherit animation data from shared mesh -// VertexBuffer bindPos = sharedMesh.getBuffer(Type.BindPosePosition); -// VertexBuffer bindNorm = sharedMesh.getBuffer(Type.BindPoseNormal); -// VertexBuffer boneIndex = sharedMesh.getBuffer(Type.BoneIndex); -// VertexBuffer boneWeight = sharedMesh.getBuffer(Type.BoneWeight); -// -// if (bindPos != null) { -// m.setBuffer(bindPos); -// } -// -// if (bindNorm != null) { -// m.setBuffer(bindNorm); -// } -// -// if (boneIndex != null) { -// m.setBuffer(boneIndex); -// } -// -// if (boneWeight != null) { -// m.setBuffer(boneWeight); -// } - } + + //FIXME the parameter is now useless. + //It was !HADWARE_SKINNING before, but since toggleing + //HW skinning does not happen at load time it was always true. + //We should use something similar as for the HWBoneIndex and + //HWBoneWeight : create the vertex buffers empty so that they + //are put in the cache, and really populate them the first time + //software skinning is used on the mesh. + m.generateBindPose(true); + } // Put the animations in the AnimControl @@ -764,7 +823,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { public Object load(AssetInfo info) throws IOException { try { - AssetKey key = info.getKey(); + key = info.getKey(); meshName = key.getName(); folderName = key.getFolder(); String ext = key.getExtension(); @@ -774,36 +833,38 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { } assetManager = info.getManager(); - OgreMeshKey meshKey = null; if (key instanceof OgreMeshKey) { - meshKey = (OgreMeshKey) key; + // OgreMeshKey is being used, try getting the material list + // from it + OgreMeshKey meshKey = (OgreMeshKey) key; materialList = meshKey.getMaterialList(); String materialName = meshKey.getMaterialName(); - if (materialList == null) { - if (materialName != null) { - try { - materialList = (MaterialList) assetManager.loadAsset(new OgreMaterialKey(folderName + materialName + ".material")); - } catch (AssetNotFoundException e) { - logger.log(Level.WARNING, "Cannot locate {0}{1}.material for model {2}{3}.{4}", new Object[]{folderName, materialName, folderName, meshName, ext}); - logger.log(Level.WARNING, "", e); - } - } else { - try { - materialList = (MaterialList) assetManager.loadAsset(new OgreMaterialKey(folderName + meshName + ".material")); - } catch (AssetNotFoundException e) { - logger.log(Level.WARNING, "Cannot locate {0}{1}.material for model {2}{3}.{4}", new Object[]{folderName, meshName, folderName, meshName, ext}); - logger.log(Level.WARNING, "", e); - } + + // Material list not set but material name is available + if (materialList == null && materialName != null) { + OgreMaterialKey materialKey = new OgreMaterialKey(folderName + materialName + ".material"); + try { + materialList = (MaterialList) assetManager.loadAsset(materialKey); + } catch (AssetNotFoundException e) { + logger.log(Level.WARNING, "Cannot locate {0} for model {1}", new Object[]{materialKey, key}); } } } else { + // Make sure to reset it to null so that previous state + // doesn't leak onto this one + materialList = null; + } + + // If for some reason material list could not be found through + // OgreMeshKey, or if regular ModelKey specified, load using + // default method. + if (materialList == null) { + OgreMaterialKey materialKey = new OgreMaterialKey(folderName + meshName + ".material"); try { - materialList = (MaterialList) assetManager.loadAsset(new OgreMaterialKey(folderName + meshName + ".material")); + materialList = (MaterialList) assetManager.loadAsset(materialKey); } catch (AssetNotFoundException e) { - logger.log(Level.WARNING, "Cannot locate {0}{1}.material for model {2}{3}.{4}", new Object[]{folderName, meshName, folderName, meshName, ext}); - logger.log(Level.WARNING, "", e); - } - + logger.log(Level.WARNING, "Cannot locate {0} for model {1}", new Object[]{materialKey, key}); + } } // Added by larynx 25.06.2011 @@ -813,21 +874,21 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { // 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){ + if (r != null) { r.close(); } } - + return compileModel(); } catch (SAXException ex) { IOException ioEx = new IOException("Error while parsing Ogre3D mesh.xml"); diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/OgreMeshKey.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/OgreMeshKey.java index a2b54f087..d0458899b 100644 --- a/engine/src/ogre/com/jme3/scene/plugins/ogre/OgreMeshKey.java +++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/OgreMeshKey.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2010 jMonkeyEngine + * Copyright (c) 2009-2012 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,12 +29,19 @@ * 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.ModelKey; import com.jme3.material.MaterialList; +/** + * OgreMeshKey is used to load Ogre3D mesh.xml models with a specific + * material file or list. This allows customizing from where the materials + * are retrieved, instead of loading the material file as the same + * name as the model (the default). + * + * @author Kirill Vainer + */ public class OgreMeshKey extends ModelKey { private MaterialList materialList; @@ -58,10 +65,44 @@ public class OgreMeshKey extends ModelKey { this.materialName = materialName; } + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final OgreMeshKey other = (OgreMeshKey) obj; + if (!super.equals(other)) { + return false; + } + if (this.materialList != other.materialList && (this.materialList == null || !this.materialList.equals(other.materialList))) { + return false; + } + if ((this.materialName == null) ? (other.materialName != null) : !this.materialName.equals(other.materialName)) { + return false; + } + return true; + } + + @Override + public int hashCode() { + int hash = 5; + hash = 31 * hash + (super.hashCode()); + hash = 31 * hash + (this.materialList != null ? this.materialList.hashCode() : 0); + hash = 31 * hash + (this.materialName != null ? this.materialName.hashCode() : 0); + return hash; + } + public MaterialList getMaterialList() { return materialList; } + public void setMaterialList(MaterialList materialList){ + this.materialList = materialList; + } + public String getMaterialName() { return materialName; } 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 a4d9576f7..a15104e1b 100644 --- a/engine/src/ogre/com/jme3/scene/plugins/ogre/SceneLoader.java +++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/SceneLoader.java @@ -1,465 +1,550 @@ -/* - * 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.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.FastMath; -import com.jme3.math.Quaternion; -import com.jme3.math.Vector3f; -import com.jme3.scene.Node; -import com.jme3.scene.Spatial; -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 elementStack = new Stack(); - private String sceneName; - private String folderName; - private AssetManager assetManager; - private MaterialList materialList; - private Node root; - private Node node; - private 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 Node("OgreDotScene"+(++sceneIdx)); - else - root = new 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); - - Node newNode = new 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 Node(name); - OgreMeshKey key = new OgreMeshKey(meshFile, materialList); - Spatial ogreMesh = assetManager.loadModel(key); - - entityNode.attachChild(ogreMesh); - 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{ - assetManager = info.getManager(); - sceneName = info.getKey().getName(); - String ext = info.getKey().getExtension(); - folderName = info.getKey().getFolder(); - sceneName = sceneName.substring(0, sceneName.length() - ext.length() - 1); - - try { - materialList = (MaterialList) - assetManager.loadAsset(new OgreMaterialKey(sceneName+".material")); - } catch (AssetNotFoundException ex){ - logger.log(Level.WARNING, "Cannot locate material file {0}", ex.getMessage()); - 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-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.*; +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; +import static 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; + +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; + 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 Camera camera; + private CameraNode cameraNode; + 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; + camera = null; + cameraNode = 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 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)) { + // set to disabled + } + + // "attach" it to the parent of this node + 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) { + if (ex.getMessage().equals(meshFile)) { + logger.log(Level.WARNING, "Cannot locate {0} for scene {1}", new Object[]{meshKey, key}); + // Attach placeholder asset. + Spatial model = PlaceholderAssets.getPlaceholderModel(assetManager); + model.setKey(key); + entityNode.attachChild(model); + } else { + throw ex; + } + } + + 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) { + 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"); + } else if (qName.equals("item")) { + checkTopNode("externals"); + } else if (qName.equals("file")) { + checkTopNode("item"); + + // 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")) { + throw new SAXException("dotScene parse error: " + + "node element can only appear under 'node' or 'nodes'"); + } + + 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")) { + 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"); + 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")) { + 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("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); + } 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("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) { + 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); + + reset(); + + // == Run 1st pass over XML file to determine material list == + materialList = materialLoader.load(assetManager, folderName, info.openStream()); + + if (materialList == null || 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; + } + } + + // == 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 + // 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; + } + } +} 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..3507de2f7 --- /dev/null +++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/SceneMaterialLoader.java @@ -0,0 +1,158 @@ +/* + * 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; + } + } +} diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/SkeletonLoader.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/SkeletonLoader.java index 69c62e004..56dc95bb6 100644 --- a/engine/src/ogre/com/jme3/scene/plugins/ogre/SkeletonLoader.java +++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/SkeletonLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2010 jMonkeyEngine + * Copyright (c) 2009-2012 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,6 +31,16 @@ */ package com.jme3.scene.plugins.ogre; +import com.jme3.animation.Animation; +import com.jme3.animation.Bone; +import com.jme3.animation.BoneTrack; +import com.jme3.animation.Skeleton; +import com.jme3.asset.AssetInfo; +import com.jme3.asset.AssetLoader; +import com.jme3.asset.AssetManager; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector3f; +import com.jme3.util.xml.SAXUtil; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -39,27 +49,14 @@ import java.util.HashMap; import java.util.Map; import java.util.Stack; 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 com.jme3.animation.Animation; -import com.jme3.animation.Bone; -import com.jme3.animation.BoneTrack; -import com.jme3.animation.Skeleton; -import com.jme3.asset.AssetInfo; -import com.jme3.asset.AssetLoader; -import com.jme3.asset.AssetManager; -import com.jme3.math.Quaternion; -import com.jme3.math.Vector3f; -import com.jme3.util.xml.SAXUtil; - public class SkeletonLoader extends DefaultHandler implements AssetLoader { private static final Logger logger = Logger.getLogger(SceneLoader.class.getName()); diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtension.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtension.java index d68b7ae16..192cbd909 100644 --- a/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtension.java +++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2010 jMonkeyEngine + * Copyright (c) 2009-2012 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,7 +29,6 @@ * 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.matext; import java.util.HashMap; diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionLoader.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionLoader.java index ff70f9cd3..da13751d8 100644 --- a/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionLoader.java +++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2010 jMonkeyEngine + * Copyright (c) 2009-2012 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,20 +29,22 @@ * 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.matext; +import com.jme3.asset.AssetKey; import com.jme3.asset.AssetManager; +import com.jme3.asset.AssetNotFoundException; import com.jme3.asset.TextureKey; import com.jme3.material.Material; import com.jme3.material.MaterialList; import com.jme3.scene.plugins.ogre.MaterialLoader; import com.jme3.texture.Texture; import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.Texture2D; +import com.jme3.util.PlaceholderAssets; import com.jme3.util.blockparser.Statement; import java.io.IOException; import java.util.List; -import java.util.Scanner; import java.util.logging.Level; import java.util.logging.Logger; @@ -53,6 +55,7 @@ public class MaterialExtensionLoader { private static final Logger logger = Logger.getLogger(MaterialExtensionLoader.class.getName()); + private AssetKey key; private AssetManager assetManager; private MaterialList list; private MaterialExtensionSet matExts; @@ -60,6 +63,7 @@ public class MaterialExtensionLoader { private String matName; private Material material; + private void readExtendingMaterialStatement(Statement statement) throws IOException { if (statement.getLine().startsWith("set_texture_alias")){ String[] split = statement.getLine().split(" ", 3); @@ -68,14 +72,21 @@ public class MaterialExtensionLoader { String jmeParamName = matExt.getTextureMapping(aliasName); - TextureKey key = new TextureKey(texturePath, false); - key.setGenerateMips(true); - key.setAsCube(false); - Texture tex = assetManager.loadTexture(key); - if (tex == null) - throw new IOException("Cannot load texture: " + texturePath); - tex.setWrap(WrapMode.Repeat); - + TextureKey texKey = new TextureKey(texturePath, false); + texKey.setGenerateMips(true); + texKey.setAsCube(false); + Texture tex; + + try { + tex = assetManager.loadTexture(texKey); + tex.setWrap(WrapMode.Repeat); + } catch (AssetNotFoundException ex){ + logger.log(Level.WARNING, "Cannot locate {0} for material {1}", new Object[]{texKey, key}); + tex = new Texture2D( PlaceholderAssets.getPlaceholderImage() ); + tex.setWrap(WrapMode.Repeat); + tex.setKey(texKey); + } + material.setTexture(jmeParamName, tex); } } @@ -100,10 +111,11 @@ public class MaterialExtensionLoader { return material; } - public MaterialList load(AssetManager assetManager, MaterialExtensionSet matExts, + public MaterialList load(AssetManager assetManager, AssetKey key, MaterialExtensionSet matExts, List statements) throws IOException{ this.assetManager = assetManager; this.matExts = matExts; + this.key = key; list = new MaterialList(); diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionSet.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionSet.java index 2c81e7bf9..b153da37e 100644 --- a/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionSet.java +++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2010 jMonkeyEngine + * Copyright (c) 2009-2012 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,7 +29,6 @@ * 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.matext; import java.util.ArrayList; @@ -42,44 +41,46 @@ import java.util.List; * {@link OgreMaterialKey}s used. */ public class MaterialExtensionSet { - private HashMap extensions - = new HashMap(); + + private HashMap extensions = new HashMap(); private HashMap> nameMappings = new HashMap>(); /** * Adds a new material extension to the set of extensions. + * * @param extension The {@link MaterialExtension} to add. */ - public void addMaterialExtension(MaterialExtension extension){ + public void addMaterialExtension(MaterialExtension extension) { extensions.put(extension.getBaseMaterialName(), extension); } /** - * Returns the {@link MaterialExtension} for a given Ogre3D base - * material name. + * Returns the {@link MaterialExtension} for a given Ogre3D base material + * name. * * @param baseMatName The ogre3D base material name. * @return {@link MaterialExtension} that is set, or null if not set. */ - public MaterialExtension getMaterialExtension(String baseMatName){ + public MaterialExtension getMaterialExtension(String baseMatName) { return extensions.get(baseMatName); } - + /** * Adds an alternative name for a material + * * @param name The material name to be found in a .mesh.xml file * @param alias The material name to be found in a .material file */ - public void setNameMapping(String name, String alias){ + public void setNameMapping(String name, String alias) { List list = nameMappings.get(name); - if(list==null){ + if (list == null) { list = new ArrayList(); nameMappings.put(name, list); } list.add(alias); } - - public List getNameMappings(String name){ + + public List getNameMappings(String name) { return nameMappings.get(name); } } diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/OgreMaterialKey.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/OgreMaterialKey.java index 2bf9f9a17..c300783ef 100644 --- a/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/OgreMaterialKey.java +++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/OgreMaterialKey.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2010 jMonkeyEngine + * Copyright (c) 2009-2012 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,43 +29,70 @@ * 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.matext; import com.jme3.asset.AssetKey; import com.jme3.material.MaterialList; /** - * OgreMaterialKey allows specifying material extensions, - * which map from Ogre3D base materials to jME3 materials + * OgreMaterialKey allows specifying material extensions, which map + * from Ogre3D base materials to jME3 materials */ public class OgreMaterialKey extends AssetKey { private MaterialExtensionSet matExts; - public OgreMaterialKey(String name){ + public OgreMaterialKey(String name) { super(name); } - public OgreMaterialKey(){ + public OgreMaterialKey() { super(); } + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final OgreMaterialKey other = (OgreMaterialKey) obj; + if (!super.equals(other)) { + return false; + } + if (this.matExts != other.matExts && (this.matExts == null || !this.matExts.equals(other.matExts))) { + return false; + } + return true; + } + + @Override + public int hashCode() { + int hash = 5; + hash = 71 * hash + (super.hashCode()); + hash = 71 * hash + (this.matExts != null ? this.matExts.hashCode() : 0); + return hash; + } + /** - * Set the {@link MaterialExtensionSet} to use for mapping - * base materials to jME3 matdefs when loading. - * Set to null to disable this functionality. + * Set the {@link MaterialExtensionSet} to use for mapping base materials to + * jME3 matdefs when loading. Set to + * null to disable this functionality. * - * @param extension The {@link MaterialExtensionSet} to use + * @param matExts The {@link MaterialExtensionSet} to use */ - public void setMaterialExtensionSet(MaterialExtensionSet matExts){ + public void setMaterialExtensionSet(MaterialExtensionSet matExts) { this.matExts = matExts; } /** * Returns the {@link MaterialExtensionSet} previously set using - * {@link OgreMaterialKey#setMaterialExtensionSet(com.jme3.scene.plugins.ogre.matext.MaterialExtensionSet) } method. - * @return + * {@link OgreMaterialKey#setMaterialExtensionSet(com.jme3.scene.plugins.ogre.matext.MaterialExtensionSet) + * } method. + * + * @return the {@link MaterialExtensionSet} */ public MaterialExtensionSet getMaterialExtensionSet() { return matExts; -- 2.11.0