OSDN Git Service

merge from jme10694
authorkobayasi <kobayasi@pscnet.co.jp>
Sat, 6 Jul 2013 15:22:49 +0000 (00:22 +0900)
committerkobayasi <kobayasi@pscnet.co.jp>
Sat, 6 Jul 2013 15:22:49 +0000 (00:22 +0900)
12 files changed:
engine/src/ogre/com/jme3/scene/plugins/ogre/AnimData.java
engine/src/ogre/com/jme3/scene/plugins/ogre/MaterialLoader.java
engine/src/ogre/com/jme3/scene/plugins/ogre/MeshAnimationLoader.java
engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java
engine/src/ogre/com/jme3/scene/plugins/ogre/OgreMeshKey.java
engine/src/ogre/com/jme3/scene/plugins/ogre/SceneLoader.java
engine/src/ogre/com/jme3/scene/plugins/ogre/SceneMaterialLoader.java [new file with mode: 0644]
engine/src/ogre/com/jme3/scene/plugins/ogre/SkeletonLoader.java
engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtension.java
engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionLoader.java
engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionSet.java
engine/src/ogre/com/jme3/scene/plugins/ogre/matext/OgreMaterialKey.java

index f8d99aa..1b1eb2a 100644 (file)
@@ -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;
index 4b4f115..74e871b 100644 (file)
@@ -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
  * 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){
index 75ff865..3d198fd 100644 (file)
@@ -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;
index db5eba1..5f26532 100644 (file)
@@ -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
  */
 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<Geometry> geoms = new ArrayList<Geometry>();
+    private ArrayList<Boolean> usesSharedMesh = new ArrayList<Boolean>();
     private IntMap<List<VertexBuffer>> lodLevels = new IntMap<List<VertexBuffer>>();
     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<VertexBuffer> sharedBufs = sharedMesh.getBuffers();
-            for (Entry<VertexBuffer> entry : sharedBufs) {
-                mesh.setBuffer(entry.getValue());
-            }
+//            IntMap<VertexBuffer> sharedBufs = sharedMesh.getBuffers();
+//            for (Entry<VertexBuffer> 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<VertexBuffer> levels = lodLevels.get(index);
         if (levels == null) {
+            // Create the LOD levels list
             levels = new ArrayList<VertexBuffer>();
-            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<VertexBuffer> bufs = mesh.getBuffers();
-            for (Entry<VertexBuffer> 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");
index a2b54f0..d045889 100644 (file)
@@ -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
  * 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;
     }
index a4d9576..a15104e 100644 (file)
-/*\r
- * Copyright (c) 2009-2010 jMonkeyEngine\r
- * All rights reserved.\r
- *\r
- * Redistribution and use in source and binary forms, with or without\r
- * modification, are permitted provided that the following conditions are\r
- * met:\r
- *\r
- * * Redistributions of source code must retain the above copyright\r
- *   notice, this list of conditions and the following disclaimer.\r
- *\r
- * * Redistributions in binary form must reproduce the above copyright\r
- *   notice, this list of conditions and the following disclaimer in the\r
- *   documentation and/or other materials provided with the distribution.\r
- *\r
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors\r
- *   may be used to endorse or promote products derived from this software\r
- *   without specific prior written permission.\r
- *\r
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\r
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\r
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\r
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\r
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\r
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\r
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\r
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
- */\r
-\r
-package com.jme3.scene.plugins.ogre;\r
-\r
-import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey;\r
-import com.jme3.material.MaterialList;\r
-import com.jme3.asset.AssetInfo;\r
-import com.jme3.asset.AssetLoader;\r
-import com.jme3.asset.AssetManager;\r
-import com.jme3.asset.AssetNotFoundException;\r
-import com.jme3.light.DirectionalLight;\r
-import com.jme3.light.Light;\r
-import com.jme3.light.PointLight;\r
-import com.jme3.light.SpotLight;\r
-import com.jme3.math.FastMath;\r
-import com.jme3.math.Quaternion;\r
-import com.jme3.math.Vector3f;\r
-import com.jme3.scene.Node;\r
-import com.jme3.scene.Spatial;\r
-import com.jme3.util.xml.SAXUtil;\r
-import java.io.IOException;\r
-import java.io.InputStreamReader;\r
-import java.util.Stack;\r
-import java.util.logging.Level;\r
-import java.util.logging.Logger;\r
-\r
-import javax.xml.parsers.ParserConfigurationException;\r
-import javax.xml.parsers.SAXParserFactory;\r
-\r
-import org.xml.sax.Attributes;\r
-import org.xml.sax.InputSource;\r
-import org.xml.sax.SAXException;\r
-import org.xml.sax.XMLReader;\r
-import org.xml.sax.helpers.DefaultHandler;\r
-\r
-import static com.jme3.util.xml.SAXUtil.*;\r
-\r
-public class SceneLoader extends DefaultHandler implements AssetLoader {\r
-\r
-    private static final Logger logger = Logger.getLogger(SceneLoader.class.getName());\r
-\r
-    private Stack<String> elementStack = new Stack<String>();\r
-    private String sceneName;\r
-    private String folderName;\r
-    private AssetManager assetManager;\r
-    private MaterialList materialList;\r
-    private Node root;\r
-    private Node node;\r
-    private Node entityNode;\r
-    private Light light;\r
-    private int nodeIdx = 0;\r
-    private static volatile int sceneIdx = 0;\r
-\r
-    public SceneLoader(){\r
-        super();\r
-    }\r
-\r
-    @Override\r
-    public void startDocument() {\r
-    }\r
-\r
-    @Override\r
-    public void endDocument() {\r
-    }\r
-    \r
-    private void reset(){\r
-        elementStack.clear();\r
-        nodeIdx = 0;\r
-        \r
-        // NOTE: Setting some of those to null is only needed\r
-        // if the parsed file had an error e.g. startElement was called\r
-        // but not endElement\r
-        root = null;\r
-        node = null;\r
-        entityNode = null;\r
-        light = null;\r
-    }\r
-\r
-    private void checkTopNode(String topNode) throws SAXException{\r
-        if (!elementStack.peek().equals(topNode)){\r
-            throw new SAXException("dotScene parse error: Expected parent node to be " + topNode);\r
-        }\r
-    }\r
-    \r
-    private Quaternion parseQuat(Attributes attribs) throws SAXException{\r
-        if (attribs.getValue("x") != null){\r
-            // defined as quaternion\r
-            float x = parseFloat(attribs.getValue("x"));\r
-            float y = parseFloat(attribs.getValue("y"));\r
-            float z = parseFloat(attribs.getValue("z"));\r
-            float w = parseFloat(attribs.getValue("w"));\r
-            return new Quaternion(x,y,z,w);\r
-        }else if (attribs.getValue("qx") != null){\r
-            // defined as quaternion with prefix "q"\r
-            float x = parseFloat(attribs.getValue("qx"));\r
-            float y = parseFloat(attribs.getValue("qy"));\r
-            float z = parseFloat(attribs.getValue("qz"));\r
-            float w = parseFloat(attribs.getValue("qw"));\r
-            return new Quaternion(x,y,z,w);\r
-        }else if (attribs.getValue("angle") != null){\r
-            // defined as angle + axis\r
-            float angle = parseFloat(attribs.getValue("angle"));\r
-            float axisX = parseFloat(attribs.getValue("axisX"));\r
-            float axisY = parseFloat(attribs.getValue("axisY"));\r
-            float axisZ = parseFloat(attribs.getValue("axisZ"));\r
-            Quaternion q = new Quaternion();\r
-            q.fromAngleAxis(angle, new Vector3f(axisX, axisY, axisZ));\r
-            return q;\r
-        }else{\r
-            // defines as 3 angles along XYZ axes\r
-            float angleX = parseFloat(attribs.getValue("angleX"));\r
-            float angleY = parseFloat(attribs.getValue("angleY"));\r
-            float angleZ = parseFloat(attribs.getValue("angleZ"));\r
-            Quaternion q = new Quaternion();\r
-            q.fromAngles(angleX, angleY, angleZ);\r
-            return q;\r
-        }\r
-    }\r
-\r
-    private void parseLightNormal(Attributes attribs) throws SAXException {\r
-        checkTopNode("light");\r
-        \r
-        // SpotLight will be supporting a direction-normal, too.\r
-        if (light instanceof DirectionalLight)\r
-            ((DirectionalLight) light).setDirection(parseVector3(attribs));\r
-        else if (light instanceof SpotLight){\r
-            ((SpotLight) light).setDirection(parseVector3(attribs));\r
-        }\r
-    }\r
-\r
-    private void parseLightAttenuation(Attributes attribs) throws SAXException {\r
-        // NOTE: Derives range based on "linear" if it is used solely\r
-        // for the attenuation. Otherwise derives it from "range"\r
-        checkTopNode("light");\r
-\r
-        if (light instanceof PointLight || light instanceof SpotLight){\r
-            float range = parseFloat(attribs.getValue("range"));\r
-            float constant = parseFloat(attribs.getValue("constant"));\r
-            float linear = parseFloat(attribs.getValue("linear"));\r
-\r
-            String quadraticStr = attribs.getValue("quadratic");\r
-            if (quadraticStr == null)\r
-                quadraticStr = attribs.getValue("quadric");\r
-\r
-            float quadratic = parseFloat(quadraticStr);\r
-            \r
-            if (constant == 1 && quadratic == 0 && linear > 0){\r
-                range = 1f / linear;\r
-            }\r
-            \r
-            if (light instanceof PointLight){\r
-                ((PointLight) light).setRadius(range);\r
-            }else{\r
-                ((SpotLight)light).setSpotRange(range);\r
-            }\r
-        }\r
-    }\r
-\r
-    private void parseLightSpotLightRange(Attributes attribs) throws SAXException{\r
-        checkTopNode("light");\r
-        \r
-        float outer = SAXUtil.parseFloat(attribs.getValue("outer"));\r
-        float inner = SAXUtil.parseFloat(attribs.getValue("inner"));\r
-        \r
-        if (!(light instanceof SpotLight)){\r
-            throw new SAXException("dotScene parse error: spotLightRange "\r
-                    + "can only appear under 'spot' light elements");\r
-        }\r
-        \r
-        SpotLight sl = (SpotLight) light;\r
-        sl.setSpotInnerAngle(inner * 0.5f);\r
-        sl.setSpotOuterAngle(outer * 0.5f);\r
-    }\r
-    \r
-    private void parseLight(Attributes attribs) throws SAXException {\r
-        if (node == null || node.getParent() == null)\r
-            throw new SAXException("dotScene parse error: light can only appear under a node");\r
-        \r
-        checkTopNode("node");\r
-        \r
-        String lightType = parseString(attribs.getValue("type"), "point");\r
-        if(lightType.equals("point")) {\r
-            light = new PointLight();\r
-        } else if(lightType.equals("directional") || lightType.equals("sun")) {\r
-            light = new DirectionalLight();\r
-            // Assuming "normal" property is not provided\r
-            ((DirectionalLight)light).setDirection(Vector3f.UNIT_Z);\r
-        } else if(lightType.equals("spotLight") || lightType.equals("spot")) {\r
-            light = new SpotLight();\r
-        } else {\r
-            logger.log(Level.WARNING, "No matching jME3 LightType found for OGRE LightType: {0}", lightType);\r
-        }\r
-        logger.log(Level.FINEST, "{0} created.", light);\r
-\r
-        if (!parseBool(attribs.getValue("visible"), true)){\r
-            // set to disabled\r
-        }\r
-\r
-        // "attach" it to the parent of this node\r
-        if (light != null)\r
-            node.getParent().addLight(light);\r
-    }\r
-\r
-    @Override\r
-    public void startElement(String uri, String localName, String qName, Attributes attribs) throws SAXException{\r
-        if (qName.equals("scene")){\r
-            if (elementStack.size() != 0){\r
-                throw new SAXException("dotScene parse error: 'scene' element must be the root XML element");\r
-            }\r
-            \r
-            String version = attribs.getValue("formatVersion");\r
-            if (version == null && !version.equals("1.0.0") && !version.equals("1.0.1"))\r
-                logger.log(Level.WARNING, "Unrecognized version number"\r
-                        + " in dotScene file: {0}", version);\r
-            \r
-        }else if (qName.equals("nodes")){\r
-            if (root != null){\r
-                throw new SAXException("dotScene parse error: nodes element was specified twice");\r
-            }\r
-            if (sceneName == null)\r
-                root = new Node("OgreDotScene"+(++sceneIdx));\r
-            else\r
-                root = new Node(sceneName+"-scene_node");\r
-            \r
-            node = root;\r
-        }else if (qName.equals("externals")){\r
-            checkTopNode("scene");\r
-            // Not loaded currently\r
-        }else if (qName.equals("item")){\r
-            checkTopNode("externals");\r
-        }else if (qName.equals("file")){\r
-            checkTopNode("item");\r
-            \r
-            // XXX: Currently material file name is based\r
-            // on the scene's filename. THIS IS NOT CORRECT.\r
-            // To solve, port SceneLoader to use DOM instead of SAX\r
-            \r
-            //String matFile = folderName+attribs.getValue("name");\r
-            //try {\r
-            //    materialList = (MaterialList) assetManager.loadAsset(new OgreMaterialKey(matFile));\r
-            //} catch (AssetNotFoundException ex){\r
-            //    materialList = null;\r
-            //    logger.log(Level.WARNING, "Cannot locate material file: {0}", matFile);\r
-            //}\r
-        }else if (qName.equals("node")){\r
-            String curElement = elementStack.peek();\r
-            if (!curElement.equals("node") && !curElement.equals("nodes")){\r
-                throw new SAXException("dotScene parse error: "\r
-                        + "node element can only appear under 'node' or 'nodes'");\r
-            }\r
-            \r
-            String name = attribs.getValue("name");\r
-            if (name == null)\r
-                name = "OgreNode-" + (++nodeIdx);\r
-\r
-            Node newNode = new Node(name);\r
-            if (node != null){\r
-                node.attachChild(newNode);\r
-            }\r
-            node = newNode;\r
-        }else if (qName.equals("property")){\r
-            if (node != null){\r
-                String type = attribs.getValue("type");\r
-                String name = attribs.getValue("name");\r
-                String data = attribs.getValue("data");\r
-                if (type.equals("BOOL")){\r
-                    node.setUserData(name, Boolean.parseBoolean(data)||data.equals("1"));\r
-                }else if (type.equals("FLOAT")){\r
-                    node.setUserData(name, Float.parseFloat(data));\r
-                }else if (type.equals("STRING")){\r
-                    node.setUserData(name, data);\r
-                }else if (type.equals("INT")){\r
-                    node.setUserData(name, Integer.parseInt(data));\r
-                }\r
-            }\r
-        }else if (qName.equals("entity")){\r
-            checkTopNode("node");\r
-            \r
-            String name = attribs.getValue("name");\r
-            if (name == null)\r
-                name = "OgreEntity-" + (++nodeIdx);\r
-            else\r
-                name += "-entity";\r
-\r
-            String meshFile = attribs.getValue("meshFile");\r
-            if (meshFile == null) {\r
-                throw new SAXException("Required attribute 'meshFile' missing for 'entity' node");\r
-            }\r
-\r
-            // TODO: Not currently used\r
-            String materialName = attribs.getValue("materialName");\r
-\r
-            if (folderName != null) {\r
-                meshFile = folderName + meshFile;\r
-            }\r
-            \r
-            // NOTE: append "xml" since its assumed mesh files are binary in dotScene\r
-            meshFile += ".xml";\r
-            \r
-            entityNode = new Node(name);\r
-            OgreMeshKey key = new OgreMeshKey(meshFile, materialList);\r
-            Spatial ogreMesh = assetManager.loadModel(key);\r
-            \r
-            entityNode.attachChild(ogreMesh);\r
-            node.attachChild(entityNode);\r
-            node = null;\r
-        }else if (qName.equals("position")){\r
-            if (elementStack.peek().equals("node")){\r
-                node.setLocalTranslation(SAXUtil.parseVector3(attribs));\r
-            }\r
-        }else if (qName.equals("quaternion") || qName.equals("rotation")){\r
-            node.setLocalRotation(parseQuat(attribs));\r
-        }else if (qName.equals("scale")){\r
-            node.setLocalScale(SAXUtil.parseVector3(attribs));\r
-        } else if (qName.equals("light")) {\r
-            parseLight(attribs);\r
-        } else if (qName.equals("colourDiffuse") || qName.equals("colorDiffuse")) {\r
-            if (elementStack.peek().equals("light")){\r
-                if (light != null){\r
-                    light.setColor(parseColor(attribs));\r
-                }\r
-            }else{\r
-                checkTopNode("environment");\r
-            }\r
-        } else if (qName.equals("normal") || qName.equals("direction")) {\r
-            checkTopNode("light");\r
-            parseLightNormal(attribs);\r
-        } else if (qName.equals("lightAttenuation")) {\r
-            parseLightAttenuation(attribs);\r
-        } else if (qName.equals("spotLightRange") || qName.equals("lightRange")) {\r
-            parseLightSpotLightRange(attribs);\r
-        }\r
-\r
-        elementStack.push(qName);\r
-    }\r
-\r
-    @Override\r
-    public void endElement(String uri, String name, String qName) throws SAXException {\r
-        if (qName.equals("node")){\r
-            node = node.getParent();\r
-        }else if (qName.equals("nodes")){\r
-            node = null;\r
-        }else if (qName.equals("entity")){\r
-            node = entityNode.getParent();\r
-            entityNode = null;\r
-        }else if (qName.equals("light")){\r
-            // apply the node's world transform on the light..\r
-            root.updateGeometricState();\r
-            if (light != null){\r
-                if (light instanceof DirectionalLight){\r
-                    DirectionalLight dl = (DirectionalLight) light;\r
-                    Quaternion q = node.getWorldRotation();\r
-                    Vector3f dir = dl.getDirection();\r
-                    q.multLocal(dir);\r
-                    dl.setDirection(dir);\r
-                }else if (light instanceof PointLight){\r
-                    PointLight pl = (PointLight) light;\r
-                    Vector3f pos = node.getWorldTranslation();\r
-                    pl.setPosition(pos);\r
-                }else if (light instanceof SpotLight){\r
-                    SpotLight sl = (SpotLight) light;\r
-                    \r
-                    Vector3f pos = node.getWorldTranslation();\r
-                    sl.setPosition(pos);\r
-                    \r
-                    Quaternion q = node.getWorldRotation();\r
-                    Vector3f dir = sl.getDirection();\r
-                    q.multLocal(dir);\r
-                    sl.setDirection(dir);\r
-                }\r
-            }\r
-            light = null;\r
-        }\r
-        checkTopNode(qName);\r
-        elementStack.pop();\r
-    }\r
-\r
-    @Override\r
-    public void characters(char ch[], int start, int length) {\r
-    }\r
-\r
-    public Object load(AssetInfo info) throws IOException {\r
-        try{\r
-            assetManager = info.getManager();\r
-            sceneName = info.getKey().getName();\r
-            String ext = info.getKey().getExtension();\r
-            folderName = info.getKey().getFolder();\r
-            sceneName = sceneName.substring(0, sceneName.length() - ext.length() - 1);\r
-\r
-            try {\r
-                materialList = (MaterialList) \r
-                      assetManager.loadAsset(new OgreMaterialKey(sceneName+".material"));\r
-            } catch (AssetNotFoundException ex){\r
-                logger.log(Level.WARNING, "Cannot locate material file {0}", ex.getMessage());\r
-                materialList = null;\r
-            }\r
-\r
-            reset();\r
-            \r
-            // Added by larynx 25.06.2011\r
-            // Android needs the namespace aware flag set to true \r
-            // Kirill 30.06.2011\r
-            // Now, hack is applied for both desktop and android to avoid\r
-            // checking with JmeSystem.\r
-            SAXParserFactory factory = SAXParserFactory.newInstance();\r
-            factory.setNamespaceAware(true);\r
-            XMLReader xr = factory.newSAXParser().getXMLReader();  \r
-            \r
-            xr.setContentHandler(this);\r
-            xr.setErrorHandler(this);\r
-            \r
-            InputStreamReader r = null;\r
-            \r
-            try {\r
-                r = new InputStreamReader(info.openStream());\r
-                xr.parse(new InputSource(r));\r
-            } finally {\r
-                if (r != null){\r
-                    r.close();\r
-                }\r
-            }\r
-            \r
-            return root;\r
-        }catch (SAXException ex){\r
-            IOException ioEx = new IOException("Error while parsing Ogre3D dotScene");\r
-            ioEx.initCause(ex);\r
-            throw ioEx;\r
-        } catch (ParserConfigurationException ex) {\r
-            IOException ioEx = new IOException("Error while parsing Ogre3D dotScene");\r
-            ioEx.initCause(ex);\r
-            throw ioEx;\r
-        }\r
-    }\r
-\r
-}\r
+/*
+ * 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<String> elementStack = new Stack<String>();
+    private AssetKey key;
+    private String sceneName;
+    private String folderName;
+    private AssetManager assetManager;
+    private MaterialList materialList;
+    private com.jme3.scene.Node root;
+    private com.jme3.scene.Node node;
+    private com.jme3.scene.Node entityNode;
+    private Light light;
+    private 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 (file)
index 0000000..3507de2
--- /dev/null
@@ -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<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;
+        }
+    }
+}
index 69c62e0..56dc95b 100644 (file)
@@ -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
  */
 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());
index d68b7ae..192cbd9 100644 (file)
@@ -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;
index ff70f9c..da13751 100644 (file)
@@ -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
  * 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<Statement> statements) throws IOException{
         this.assetManager = assetManager;
         this.matExts = matExts;
+        this.key = key;
         
         list = new MaterialList();
         
index 2c81e7b..b153da3 100644 (file)
@@ -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<String, MaterialExtension> extensions
-            = new HashMap<String, MaterialExtension>();
+
+    private HashMap<String, MaterialExtension> extensions = new HashMap<String, MaterialExtension>();
     private HashMap<String, List<String>> nameMappings = new HashMap<String, List<String>>();
 
     /**
      * 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<String> list = nameMappings.get(name);
-        if(list==null){
+        if (list == null) {
             list = new ArrayList<String>();
             nameMappings.put(name, list);
         }
         list.add(alias);
     }
-    
-    public List<String> getNameMappings(String name){
+
+    public List<String> getNameMappings(String name) {
         return nameMappings.get(name);
     }
 }
index 2bf9f9a..c300783 100644 (file)
@@ -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
  * 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;
 
 /**
- * <code>OgreMaterialKey</code> allows specifying material extensions,
- * which map from Ogre3D base materials to jME3 materials
+ * <code>OgreMaterialKey</code> allows specifying material extensions, which map
+ * from Ogre3D base materials to jME3 materials
  */
 public class OgreMaterialKey extends AssetKey<MaterialList> {
 
     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 <code>null</code> to disable this functionality.
+     * Set the {@link MaterialExtensionSet} to use for mapping base materials to
+     * jME3 matdefs when loading. Set to
+     * <code>null</code> 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;