• R/O
  • HTTP
  • SSH
  • HTTPS

提交

標籤
無標籤

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

Main repository of MikuMikuStudio


Commit MetaInfo

修訂efd91620eef696ee24a7ced20d92f74819163a7e (tree)
時間2013-07-07 00:22:49
作者kobayasi <kobayasi@pscn...>
Commiterkobayasi

Log Message

merge from jme10694

Change Summary

差異

--- a/engine/src/ogre/com/jme3/scene/plugins/ogre/AnimData.java
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/AnimData.java
@@ -1,5 +1,5 @@
11 /*
2- * Copyright (c) 2009-2010 jMonkeyEngine
2+ * Copyright (c) 2009-2012 jMonkeyEngine
33 * All rights reserved.
44 *
55 * Redistribution and use in source and binary forms, with or without
@@ -29,7 +29,6 @@
2929 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
3030 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3131 */
32-
3332 package com.jme3.scene.plugins.ogre;
3433
3534 import com.jme3.animation.Animation;
--- a/engine/src/ogre/com/jme3/scene/plugins/ogre/MaterialLoader.java
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/MaterialLoader.java
@@ -1,5 +1,5 @@
11 /*
2- * Copyright (c) 2009-2010 jMonkeyEngine
2+ * Copyright (c) 2009-2012 jMonkeyEngine
33 * All rights reserved.
44 *
55 * Redistribution and use in source and binary forms, with or without
@@ -29,15 +29,9 @@
2929 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
3030 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3131 */
32-
3332 package com.jme3.scene.plugins.ogre;
3433
35-import com.jme3.asset.AssetInfo;
36-import com.jme3.asset.AssetKey;
37-import com.jme3.asset.AssetLoader;
38-import com.jme3.asset.AssetManager;
39-import com.jme3.asset.AssetNotFoundException;
40-import com.jme3.asset.TextureKey;
34+import com.jme3.asset.*;
4135 import com.jme3.material.Material;
4236 import com.jme3.material.MaterialList;
4337 import com.jme3.material.RenderState;
@@ -45,17 +39,14 @@ import com.jme3.math.ColorRGBA;
4539 import com.jme3.scene.plugins.ogre.matext.MaterialExtensionLoader;
4640 import com.jme3.scene.plugins.ogre.matext.MaterialExtensionSet;
4741 import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey;
48-import com.jme3.texture.Image;
49-import com.jme3.texture.Image.Format;
5042 import com.jme3.texture.Texture;
5143 import com.jme3.texture.Texture.WrapMode;
5244 import com.jme3.texture.Texture2D;
53-import com.jme3.util.BufferUtils;
45+import com.jme3.util.PlaceholderAssets;
5446 import com.jme3.util.blockparser.BlockLanguageParser;
5547 import com.jme3.util.blockparser.Statement;
5648 import java.io.IOException;
5749 import java.io.InputStream;
58-import java.nio.ByteBuffer;
5950 import java.util.Arrays;
6051 import java.util.List;
6152 import java.util.Scanner;
@@ -81,7 +72,7 @@ public class MaterialLoader implements AssetLoader {
8172 private int texUnit = 0;
8273
8374 private ColorRGBA readColor(String content){
84- String[] split = content.split(" ");
75+ String[] split = content.split("\\s");
8576
8677 ColorRGBA color = new ColorRGBA();
8778 color.r = Float.parseFloat(split[0]);
@@ -137,23 +128,13 @@ public class MaterialLoader implements AssetLoader {
137128 cubic = true;
138129 }
139130
140- TextureKey key = new TextureKey(folderName + path, false);
141- key.setGenerateMips(genMips);
142- key.setAsCube(cubic);
131+ TextureKey texKey = new TextureKey(folderName + path, false);
132+ texKey.setGenerateMips(genMips);
133+ texKey.setAsCube(cubic);
143134
144- Texture loadedTexture;
145135 try {
146- loadedTexture = assetManager.loadTexture(key);
147- } catch (AssetNotFoundException ex){
148- logger.log(Level.WARNING, "Failed to load texture " + key + " for material " + matName, ex);
149- loadedTexture = null;
150- }
151- if (loadedTexture == null){
152- ByteBuffer tempData = BufferUtils.createByteBuffer(3);
153- tempData.put((byte)0xFF).put((byte)0x00).put((byte)0x00);
154- textures[texUnit].setImage(new Image(Format.RGB8, 1,1,tempData));
155- logger.log(Level.WARNING, "Using RED texture instead of {0}", path);
156- }else{
136+ Texture loadedTexture = assetManager.loadTexture(texKey);
137+
157138 textures[texUnit].setImage(loadedTexture.getImage());
158139 textures[texUnit].setMinFilter(loadedTexture.getMinFilter());
159140 textures[texUnit].setKey(loadedTexture.getKey());
@@ -164,8 +145,11 @@ public class MaterialLoader implements AssetLoader {
164145 textures[texUnit].setName(texName);
165146 texName = null;
166147 }else{
167- textures[texUnit].setName(key.getName());
148+ textures[texUnit].setName(texKey.getName());
168149 }
150+ } catch (AssetNotFoundException ex){
151+ logger.log(Level.WARNING, "Cannot locate {0} for material {1}", new Object[]{texKey, matName});
152+ textures[texUnit].setImage(PlaceholderAssets.getPlaceholderImage());
169153 }
170154 }
171155
@@ -243,7 +227,7 @@ public class MaterialLoader implements AssetLoader {
243227 }else if (keyword.equals("emissive")){
244228 emissive = readColor(split[1]);
245229 }else if (keyword.equals("specular")){
246- String[] subsplit = split[1].split(" ");
230+ String[] subsplit = split[1].split("\\s");
247231 specular = new ColorRGBA();
248232 specular.r = Float.parseFloat(subsplit[0]);
249233 specular.g = Float.parseFloat(subsplit[1]);
@@ -319,7 +303,7 @@ public class MaterialLoader implements AssetLoader {
319303 if (statement.getLine().startsWith("technique")){
320304 readTechnique(statement);
321305 }else if (statement.getLine().startsWith("receive_shadows")){
322- String isOn = statement.getLine().split(" ")[1];
306+ String isOn = statement.getLine().split("\\s")[1];
323307 if (isOn != null && isOn.equals("true")){
324308 }
325309 }
@@ -344,12 +328,16 @@ public class MaterialLoader implements AssetLoader {
344328 rs.setAlphaTest(true);
345329 rs.setAlphaFallOff(0.01f);
346330 rs.setBlendMode(RenderState.BlendMode.Alpha);
347- if (twoSide)
331+
332+ if (twoSide){
348333 rs.setFaceCullMode(RenderState.FaceCullMode.Off);
334+ }
335+
349336 // rs.setDepthWrite(false);
350337 mat.setTransparent(true);
351- if (!noLight)
338+ if (!noLight){
352339 mat.setBoolean("UseAlpha", true);
340+ }
353341 }else{
354342 if (twoSide){
355343 RenderState rs = mat.getAdditionalRenderState();
@@ -452,7 +440,7 @@ public class MaterialLoader implements AssetLoader {
452440 "Ogre3D materials with extended materials");
453441 }
454442
455- list = new MaterialExtensionLoader().load(assetManager, matExts, statements);
443+ list = new MaterialExtensionLoader().load(assetManager, key, matExts, statements);
456444 break;
457445 }else if (statement.getLine().startsWith("material")){
458446 if (list == null){
--- a/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshAnimationLoader.java
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshAnimationLoader.java
@@ -1,5 +1,5 @@
11 /*
2- * Copyright (c) 2009-2010 jMonkeyEngine
2+ * Copyright (c) 2009-2012 jMonkeyEngine
33 * All rights reserved.
44 *
55 * Redistribution and use in source and binary forms, with or without
@@ -29,7 +29,6 @@
2929 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
3030 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3131 */
32-
3332 package com.jme3.scene.plugins.ogre;
3433
3534 //import static com.jmex.model.XMLUtil.getAttribute;
--- a/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java
@@ -1,5 +1,5 @@
11 /*
2- * Copyright (c) 2009-2010 jMonkeyEngine
2+ * Copyright (c) 2009-2012 jMonkeyEngine
33 * All rights reserved.
44 *
55 * Redistribution and use in source and binary forms, with or without
@@ -31,54 +31,40 @@
3131 */
3232 package com.jme3.scene.plugins.ogre;
3333
34-import com.jme3.animation.Animation;
35-import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey;
3634 import com.jme3.animation.AnimControl;
35+import com.jme3.animation.Animation;
3736 import com.jme3.animation.SkeletonControl;
38-import com.jme3.asset.AssetInfo;
39-import com.jme3.asset.AssetKey;
40-import com.jme3.asset.AssetLoader;
41-import com.jme3.asset.AssetManager;
42-import com.jme3.asset.AssetNotFoundException;
37+import com.jme3.asset.*;
4338 import com.jme3.material.Material;
4439 import com.jme3.material.MaterialList;
4540 import com.jme3.math.ColorRGBA;
4641 import com.jme3.renderer.queue.RenderQueue.Bucket;
47-import com.jme3.scene.Geometry;
48-import com.jme3.scene.Mesh;
49-import com.jme3.scene.Node;
50-import com.jme3.scene.UserData;
51-import com.jme3.scene.VertexBuffer;
42+import com.jme3.scene.*;
5243 import com.jme3.scene.VertexBuffer.Format;
5344 import com.jme3.scene.VertexBuffer.Type;
5445 import com.jme3.scene.VertexBuffer.Usage;
46+import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey;
5547 import com.jme3.util.BufferUtils;
5648 import com.jme3.util.IntMap;
5749 import com.jme3.util.IntMap.Entry;
50+import com.jme3.util.PlaceholderAssets;
51+import static com.jme3.util.xml.SAXUtil.*;
5852 import java.io.IOException;
5953 import java.io.InputStreamReader;
60-import java.nio.Buffer;
61-import java.nio.ByteBuffer;
62-import java.nio.FloatBuffer;
63-import java.nio.IntBuffer;
64-import java.nio.ShortBuffer;
54+import java.nio.*;
6555 import java.util.ArrayList;
6656 import java.util.HashMap;
6757 import java.util.List;
6858 import java.util.logging.Level;
6959 import java.util.logging.Logger;
70-
7160 import javax.xml.parsers.ParserConfigurationException;
7261 import javax.xml.parsers.SAXParserFactory;
73-
7462 import org.xml.sax.Attributes;
7563 import org.xml.sax.InputSource;
7664 import org.xml.sax.SAXException;
7765 import org.xml.sax.XMLReader;
7866 import org.xml.sax.helpers.DefaultHandler;
7967
80-import static com.jme3.util.xml.SAXUtil.*;
81-
8268 /**
8369 * Loads Ogre3D mesh.xml files.
8470 */
@@ -86,7 +72,6 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
8672
8773 private static final Logger logger = Logger.getLogger(MeshLoader.class.getName());
8874 public static boolean AUTO_INTERLEAVE = true;
89- public static boolean HARDWARE_SKINNING = false;
9075 private static final Type[] TEXCOORD_TYPES =
9176 new Type[]{
9277 Type.TexCoord,
@@ -97,6 +82,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
9782 Type.TexCoord6,
9883 Type.TexCoord7,
9984 Type.TexCoord8,};
85+ private AssetKey key;
10086 private String meshName;
10187 private String folderName;
10288 private AssetManager assetManager;
@@ -110,15 +96,18 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
11096 private Geometry geom;
11197 private ByteBuffer indicesData;
11298 private FloatBuffer weightsFloatData;
99+ private boolean actuallyHasWeights = false;
113100 private int vertCount;
114101 private boolean usesSharedVerts;
115102 private boolean usesBigIndices;
103+ private boolean submeshNamesHack;
116104 // Global data
117105 private Mesh sharedMesh;
118106 private int meshIndex = 0;
119107 private int texCoordIndex = 0;
120108 private String ignoreUntilEnd = null;
121109 private List<Geometry> geoms = new ArrayList<Geometry>();
110+ private ArrayList<Boolean> usesSharedMesh = new ArrayList<Boolean>();
122111 private IntMap<List<VertexBuffer>> lodLevels = new IntMap<List<VertexBuffer>>();
123112 private AnimData animData;
124113
@@ -139,6 +128,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
139128 geom = null;
140129 sharedMesh = null;
141130
131+ usesSharedMesh.clear();
142132 usesSharedVerts = false;
143133 vertCount = 0;
144134 meshIndex = 0;
@@ -147,6 +137,8 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
147137
148138 animData = null;
149139
140+ actuallyHasWeights = false;
141+ submeshNamesHack = false;
150142 indicesData = null;
151143 weightsFloatData = null;
152144 }
@@ -155,42 +147,65 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
155147 public void endDocument() {
156148 }
157149
158- private void pushFace(String v1, String v2, String v3) throws SAXException {
159- int i1 = parseInt(v1);
160-
161- // TODO: fan/strip support
162- int i2 = parseInt(v2);
163- int i3 = parseInt(v3);
150+ private void pushIndex(int index) {
164151 if (ib != null) {
165- ib.put(i1).put(i2).put(i3);
152+ ib.put(index);
166153 } else {
167- sb.put((short) i1).put((short) i2).put((short) i3);
154+ sb.put((short) index);
168155 }
169156 }
170157
171- private boolean isUsingSharedVerts(Geometry geom) {
172- return geom.getUserData(UserData.JME_SHAREDMESH) != null;
158+ private void pushFace(String v1, String v2, String v3) throws SAXException {
159+ // TODO: fan/strip support
160+ switch (mesh.getMode()) {
161+ case Triangles:
162+ pushIndex(parseInt(v1));
163+ pushIndex(parseInt(v2));
164+ pushIndex(parseInt(v3));
165+ break;
166+ case Lines:
167+ pushIndex(parseInt(v1));
168+ pushIndex(parseInt(v2));
169+ break;
170+ case Points:
171+ pushIndex(parseInt(v1));
172+ break;
173+ }
173174 }
174175
176+// private boolean isUsingSharedVerts(Geometry geom) {
177+ // Old code for buffer sharer
178+ //return geom.getUserData(UserData.JME_SHAREDMESH) != null;
179+// }
175180 private void startFaces(String count) throws SAXException {
176181 int numFaces = parseInt(count);
177- int numIndices;
182+ int indicesPerFace = 0;
178183
179- if (mesh.getMode() == Mesh.Mode.Triangles) {
180- numIndices = numFaces * 3;
181- } else {
182- throw new SAXException("Triangle strip or fan not supported!");
184+ switch (mesh.getMode()) {
185+ case Triangles:
186+ indicesPerFace = 3;
187+ break;
188+ case Lines:
189+ indicesPerFace = 2;
190+ break;
191+ case Points:
192+ indicesPerFace = 1;
193+ break;
194+ default:
195+ throw new SAXException("Strips or fans not supported!");
183196 }
184197
198+ int numIndices = indicesPerFace * numFaces;
199+
185200 vb = new VertexBuffer(VertexBuffer.Type.Index);
186201 if (!usesBigIndices) {
187202 sb = BufferUtils.createShortBuffer(numIndices);
188203 ib = null;
189- vb.setupData(Usage.Static, 3, Format.UnsignedShort, sb);
204+ vb.setupData(Usage.Static, indicesPerFace, Format.UnsignedShort, sb);
190205 } else {
191206 ib = BufferUtils.createIntBuffer(numIndices);
192207 sb = null;
193- vb.setupData(Usage.Static, 3, Format.UnsignedInt, ib);
208+ vb.setupData(Usage.Static, indicesPerFace, Format.UnsignedInt, ib);
194209 }
195210 mesh.setBuffer(vb);
196211 }
@@ -199,19 +214,24 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
199214 Material mat = null;
200215 if (matName.endsWith(".j3m")) {
201216 // load as native jme3 material instance
202- mat = assetManager.loadMaterial(matName);
217+ try {
218+ mat = assetManager.loadMaterial(matName);
219+ } catch (AssetNotFoundException ex) {
220+ // Warning will be raised (see below)
221+ if (!ex.getMessage().equals(matName)) {
222+ throw ex;
223+ }
224+ }
203225 } else {
204226 if (materialList != null) {
205227 mat = materialList.get(matName);
206228 }
207- if (mat == null) {
208- logger.log(Level.WARNING, "Material {0} not found. Applying default material", matName);
209- mat = (Material) assetManager.loadMaterial("Common/Materials/RedColor.j3m");
210- }
211229 }
212230
213231 if (mat == null) {
214- throw new RuntimeException("Cannot locate material named " + matName);
232+ logger.log(Level.WARNING, "Cannot locate {0} for model {1}", new Object[]{matName, key});
233+ mat = PlaceholderAssets.getPlaceholderMaterial(assetManager);
234+ //mat.setKey(new MaterialKey(matName));
215235 }
216236
217237 if (mat.isTransparent()) {
@@ -225,20 +245,29 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
225245 mesh = new Mesh();
226246 if (opType == null || opType.equals("triangle_list")) {
227247 mesh.setMode(Mesh.Mode.Triangles);
228- } else if (opType.equals("triangle_strip")) {
229- mesh.setMode(Mesh.Mode.TriangleStrip);
230- } else if (opType.equals("triangle_fan")) {
231- mesh.setMode(Mesh.Mode.TriangleFan);
248+ //} else if (opType.equals("triangle_strip")) {
249+ // mesh.setMode(Mesh.Mode.TriangleStrip);
250+ //} else if (opType.equals("triangle_fan")) {
251+ // mesh.setMode(Mesh.Mode.TriangleFan);
252+ } else if (opType.equals("line_list")) {
253+ mesh.setMode(Mesh.Mode.Lines);
254+ } else {
255+ throw new SAXException("Unsupported operation type: " + opType);
232256 }
233257
234258 usesBigIndices = parseBool(use32bitIndices, false);
235259 usesSharedVerts = parseBool(usesharedvertices, false);
236260 if (usesSharedVerts) {
261+ usesSharedMesh.add(true);
262+
263+ // Old code for buffer sharer
237264 // import vertexbuffers from shared geom
238- IntMap<VertexBuffer> sharedBufs = sharedMesh.getBuffers();
239- for (Entry<VertexBuffer> entry : sharedBufs) {
240- mesh.setBuffer(entry.getValue());
241- }
265+// IntMap<VertexBuffer> sharedBufs = sharedMesh.getBuffers();
266+// for (Entry<VertexBuffer> entry : sharedBufs) {
267+// mesh.setBuffer(entry.getValue());
268+// }
269+ } else {
270+ usesSharedMesh.add(false);
242271 }
243272
244273 if (meshName == null) {
@@ -248,8 +277,9 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
248277 }
249278
250279 if (usesSharedVerts) {
280+ // Old code for buffer sharer
251281 // this mesh is shared!
252- geom.setUserData(UserData.JME_SHAREDMESH, sharedMesh);
282+ //geom.setUserData(UserData.JME_SHAREDMESH, sharedMesh);
253283 }
254284
255285 applyMaterial(geom, matName);
@@ -270,11 +300,23 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
270300 }
271301
272302 /**
273- * Normalizes weights if needed and finds largest amount of weights used
274- * for all vertices in the buffer.
303+ * Normalizes weights if needed and finds largest amount of weights used for
304+ * all vertices in the buffer.
275305 */
276306 private void endBoneAssigns() {
277- if (mesh != sharedMesh && isUsingSharedVerts(geom)) {
307+// if (mesh != sharedMesh && isUsingSharedVerts(geom)) {
308+// return;
309+// }
310+
311+ if (!actuallyHasWeights) {
312+ // No weights were actually written (the tag didn't have any entries)
313+ // remove those buffers
314+ mesh.clearBuffer(Type.BoneIndex);
315+ mesh.clearBuffer(Type.BoneWeight);
316+
317+ weightsFloatData = null;
318+ indicesData = null;
319+
278320 return;
279321 }
280322
@@ -301,7 +343,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
301343 if (sum != 1f) {
302344 weightsFloatData.position(weightsFloatData.position() - 4);
303345 // compute new vals based on sum
304- float sumToB = 1f / sum;
346+ float sumToB = sum == 0 ? 0 : 1f / sum;
305347 weightsFloatData.put(w0 * sumToB);
306348 weightsFloatData.put(w1 * sumToB);
307349 weightsFloatData.put(w2 * sumToB);
@@ -310,6 +352,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
310352 }
311353 weightsFloatData.rewind();
312354
355+ actuallyHasWeights = false;
313356 weightsFloatData = null;
314357 indicesData = null;
315358
@@ -327,24 +370,28 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
327370 // each vertex has
328371 // - 4 bone weights
329372 // - 4 bone indices
330- if (HARDWARE_SKINNING) {
331- weightsFloatData = BufferUtils.createFloatBuffer(vertCount * 4);
332- indicesData = BufferUtils.createByteBuffer(vertCount * 4);
333- } else {
334- // create array-backed buffers if software skinning for access speed
335- weightsFloatData = FloatBuffer.allocate(vertCount * 4);
336- indicesData = ByteBuffer.allocate(vertCount * 4);
337- }
373+ // create array-backed buffers for software skinning for access speed
374+ weightsFloatData = FloatBuffer.allocate(vertCount * 4);
375+ indicesData = ByteBuffer.allocate(vertCount * 4);
338376
339377 VertexBuffer weights = new VertexBuffer(Type.BoneWeight);
340378 VertexBuffer indices = new VertexBuffer(Type.BoneIndex);
341379
342- Usage usage = HARDWARE_SKINNING ? Usage.Static : Usage.CpuOnly;
343- weights.setupData(usage, 4, Format.Float, weightsFloatData);
344- indices.setupData(usage, 4, Format.UnsignedByte, indicesData);
345-
380+ weights.setupData(Usage.CpuOnly, 4, Format.Float, weightsFloatData);
381+ indices.setupData(Usage.CpuOnly, 4, Format.UnsignedByte, indicesData);
382+
346383 mesh.setBuffer(weights);
347384 mesh.setBuffer(indices);
385+
386+ //creating empty buffers for HW skinning
387+ //the buffers will be setup if ever used.
388+ VertexBuffer weightsHW = new VertexBuffer(Type.HWBoneWeight);
389+ VertexBuffer indicesHW = new VertexBuffer(Type.HWBoneIndex);
390+ //setting usage to cpuOnly so that the buffer is not send empty to the GPU
391+ indicesHW.setUsage(Usage.CpuOnly);
392+ weightsHW.setUsage(Usage.CpuOnly);
393+ mesh.setBuffer(weightsHW);
394+ mesh.setBuffer(indicesHW);
348395 }
349396
350397 private void startVertexBuffer(Attributes attribs) throws SAXException {
@@ -451,7 +498,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
451498 private void pushColor(Attributes attribs) throws SAXException {
452499 FloatBuffer buf = (FloatBuffer) mesh.getBuffer(Type.Color).getData();
453500 String value = parseString(attribs.getValue("value"));
454- String[] vals = value.split(" ");
501+ String[] vals = value.split("\\s");
455502 if (vals.length != 3 && vals.length != 4) {
456503 throw new SAXException("Color value must contain 3 or 4 components");
457504 }
@@ -471,21 +518,31 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
471518
472519 private void startLodFaceList(String submeshindex, String numfaces) {
473520 int index = Integer.parseInt(submeshindex);
521+ mesh = geoms.get(index).getMesh();
474522 int faceCount = Integer.parseInt(numfaces);
475523
524+ VertexBuffer originalIndexBuffer = mesh.getBuffer(Type.Index);
476525 vb = new VertexBuffer(VertexBuffer.Type.Index);
477- sb = BufferUtils.createShortBuffer(faceCount * 3);
478- ib = null;
479- vb.setupData(Usage.Static, 3, Format.UnsignedShort, sb);
526+ if (originalIndexBuffer.getFormat() == Format.UnsignedInt) {
527+ // LOD buffer should also be integer
528+ ib = BufferUtils.createIntBuffer(faceCount * 3);
529+ sb = null;
530+ vb.setupData(Usage.Static, 3, Format.UnsignedInt, ib);
531+ } else {
532+ sb = BufferUtils.createShortBuffer(faceCount * 3);
533+ ib = null;
534+ vb.setupData(Usage.Static, 3, Format.UnsignedShort, sb);
535+ }
480536
481537 List<VertexBuffer> levels = lodLevels.get(index);
482538 if (levels == null) {
539+ // Create the LOD levels list
483540 levels = new ArrayList<VertexBuffer>();
484- Mesh submesh = geoms.get(index).getMesh();
485- levels.add(submesh.getBuffer(Type.Index));
541+
542+ // Add the first LOD level (always the original index buffer)
543+ levels.add(originalIndexBuffer);
486544 lodLevels.put(index, levels);
487545 }
488-
489546 levels.add(vb);
490547 }
491548
@@ -531,15 +588,27 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
531588
532589 weightsFloatData.put(i, w);
533590 indicesData.put(i, bone);
591+ actuallyHasWeights = true;
534592 }
535593
536594 private void startSkeleton(String name) {
537- animData = (AnimData) assetManager.loadAsset(folderName + name + ".xml");
595+ AssetKey assetKey = new AssetKey(folderName + name + ".xml");
596+ try {
597+ animData = (AnimData) assetManager.loadAsset(assetKey);
598+ } catch (AssetNotFoundException ex) {
599+ logger.log(Level.WARNING, "Cannot locate {0} for model {1}", new Object[]{assetKey, key});
600+ animData = null;
601+ }
538602 }
539603
540604 private void startSubmeshName(String indexStr, String nameStr) {
541605 int index = Integer.parseInt(indexStr);
542- geoms.get(index).setName(nameStr);
606+ if (index >= geoms.size()) {
607+ logger.log(Level.WARNING, "Submesh name index is larger than number of geometries: {0} >= {1}",
608+ new Object[]{index, geoms.size()});
609+ } else {
610+ geoms.get(index).setName(nameStr);
611+ }
543612 }
544613
545614 @Override
@@ -590,10 +659,15 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
590659 } else if (qName.equals("boneassignments")) {
591660 startBoneAssigns();
592661 } else if (qName.equals("submesh")) {
593- startSubMesh(attribs.getValue("material"),
594- attribs.getValue("usesharedvertices"),
595- attribs.getValue("use32bitindexes"),
596- attribs.getValue("operationtype"));
662+ if (submeshNamesHack) {
663+ // Hack for blender2ogre only
664+ startSubmeshName(attribs.getValue("index"), attribs.getValue("name"));
665+ } else {
666+ startSubMesh(attribs.getValue("material"),
667+ attribs.getValue("usesharedvertices"),
668+ attribs.getValue("use32bitindexes"),
669+ attribs.getValue("operationtype"));
670+ }
597671 } else if (qName.equals("sharedgeometry")) {
598672 String count = attribs.getValue("vertexcount");
599673 if (count == null) {
@@ -609,6 +683,9 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
609683 startSkeleton(attribs.getValue("name"));
610684 } else if (qName.equals("submeshnames")) {
611685 // ok
686+ // setting submeshNamesHack to true will make "submesh" tag be interpreted
687+ // as a "submeshname" tag.
688+ submeshNamesHack = true;
612689 } else if (qName.equals("submeshname")) {
613690 startSubmeshName(attribs.getValue("index"), attribs.getValue("name"));
614691 } else if (qName.equals("mesh")) {
@@ -628,11 +705,14 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
628705 return;
629706 }
630707
631- if (qName.equals("submesh")) {
708+
709+ // If submesh hack is enabled, ignore any submesh/submeshes
710+ // end tags.
711+ if (qName.equals("submesh") && !submeshNamesHack) {
632712 usesBigIndices = false;
633713 geom = null;
634714 mesh = null;
635- } else if (qName.equals("submeshes")) {
715+ } else if (qName.equals("submeshes") && !submeshNamesHack) {
636716 // IMPORTANT: restore sharedmesh, for use with shared boneweights
637717 geom = null;
638718 mesh = sharedMesh;
@@ -653,9 +733,8 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
653733 } else if (qName.equals("geometry")
654734 || qName.equals("sharedgeometry")) {
655735 // finish writing to buffers
656- IntMap<VertexBuffer> bufs = mesh.getBuffers();
657- for (Entry<VertexBuffer> entry : bufs) {
658- Buffer data = entry.getValue().getData();
736+ for (VertexBuffer buf : mesh.getBufferList().getArray()) {
737+ Buffer data = buf.getData();
659738 if (data.position() != 0) {
660739 data.flip();
661740 }
@@ -675,6 +754,9 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
675754 endLevelOfDetail();
676755 } else if (qName.equals("boneassignments")) {
677756 endBoneAssigns();
757+ } else if (qName.equals("submeshnames")) {
758+ // Restore default handling for "submesh" tag.
759+ submeshNamesHack = false;
678760 }
679761 }
680762
@@ -688,9 +770,12 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
688770 for (int i = 0; i < geoms.size(); i++) {
689771 Geometry g = geoms.get(i);
690772 Mesh m = g.getMesh();
691- if (sharedMesh != null && isUsingSharedVerts(g)) {
692- m.setBound(sharedMesh.getBound().clone());
773+
774+ // New code for buffer extract
775+ if (sharedMesh != null && usesSharedMesh.get(i)) {
776+ m.extractVertexData(sharedMesh);
693777 }
778+
694779 model.attachChild(geoms.get(i));
695780 }
696781
@@ -699,45 +784,19 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
699784 if (animData != null) {
700785 // This model uses animation
701786
702- // generate bind pose for mesh
703- // ONLY if not using shared geometry
704- // This includes the shared geoemtry itself actually
705- if (sharedMesh != null) {
706- sharedMesh.generateBindPose(!HARDWARE_SKINNING);
707- }
708-
709787 for (int i = 0; i < geoms.size(); i++) {
710788 Geometry g = geoms.get(i);
711789 Mesh m = geoms.get(i).getMesh();
712- boolean useShared = isUsingSharedVerts(g);
713-
714-
715- if (!useShared) {
716- // create bind pose
717- m.generateBindPose(!HARDWARE_SKINNING);
718-// } else {
719- // Inherit animation data from shared mesh
720-// VertexBuffer bindPos = sharedMesh.getBuffer(Type.BindPosePosition);
721-// VertexBuffer bindNorm = sharedMesh.getBuffer(Type.BindPoseNormal);
722-// VertexBuffer boneIndex = sharedMesh.getBuffer(Type.BoneIndex);
723-// VertexBuffer boneWeight = sharedMesh.getBuffer(Type.BoneWeight);
724-//
725-// if (bindPos != null) {
726-// m.setBuffer(bindPos);
727-// }
728-//
729-// if (bindNorm != null) {
730-// m.setBuffer(bindNorm);
731-// }
732-//
733-// if (boneIndex != null) {
734-// m.setBuffer(boneIndex);
735-// }
736-//
737-// if (boneWeight != null) {
738-// m.setBuffer(boneWeight);
739-// }
740- }
790+
791+ //FIXME the parameter is now useless.
792+ //It was !HADWARE_SKINNING before, but since toggleing
793+ //HW skinning does not happen at load time it was always true.
794+ //We should use something similar as for the HWBoneIndex and
795+ //HWBoneWeight : create the vertex buffers empty so that they
796+ //are put in the cache, and really populate them the first time
797+ //software skinning is used on the mesh.
798+ m.generateBindPose(true);
799+
741800 }
742801
743802 // Put the animations in the AnimControl
@@ -764,7 +823,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
764823
765824 public Object load(AssetInfo info) throws IOException {
766825 try {
767- AssetKey key = info.getKey();
826+ key = info.getKey();
768827 meshName = key.getName();
769828 folderName = key.getFolder();
770829 String ext = key.getExtension();
@@ -774,36 +833,38 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
774833 }
775834 assetManager = info.getManager();
776835
777- OgreMeshKey meshKey = null;
778836 if (key instanceof OgreMeshKey) {
779- meshKey = (OgreMeshKey) key;
837+ // OgreMeshKey is being used, try getting the material list
838+ // from it
839+ OgreMeshKey meshKey = (OgreMeshKey) key;
780840 materialList = meshKey.getMaterialList();
781841 String materialName = meshKey.getMaterialName();
782- if (materialList == null) {
783- if (materialName != null) {
784- try {
785- materialList = (MaterialList) assetManager.loadAsset(new OgreMaterialKey(folderName + materialName + ".material"));
786- } catch (AssetNotFoundException e) {
787- logger.log(Level.WARNING, "Cannot locate {0}{1}.material for model {2}{3}.{4}", new Object[]{folderName, materialName, folderName, meshName, ext});
788- logger.log(Level.WARNING, "", e);
789- }
790- } else {
791- try {
792- materialList = (MaterialList) assetManager.loadAsset(new OgreMaterialKey(folderName + meshName + ".material"));
793- } catch (AssetNotFoundException e) {
794- logger.log(Level.WARNING, "Cannot locate {0}{1}.material for model {2}{3}.{4}", new Object[]{folderName, meshName, folderName, meshName, ext});
795- logger.log(Level.WARNING, "", e);
796- }
842+
843+ // Material list not set but material name is available
844+ if (materialList == null && materialName != null) {
845+ OgreMaterialKey materialKey = new OgreMaterialKey(folderName + materialName + ".material");
846+ try {
847+ materialList = (MaterialList) assetManager.loadAsset(materialKey);
848+ } catch (AssetNotFoundException e) {
849+ logger.log(Level.WARNING, "Cannot locate {0} for model {1}", new Object[]{materialKey, key});
797850 }
798851 }
799852 } else {
853+ // Make sure to reset it to null so that previous state
854+ // doesn't leak onto this one
855+ materialList = null;
856+ }
857+
858+ // If for some reason material list could not be found through
859+ // OgreMeshKey, or if regular ModelKey specified, load using
860+ // default method.
861+ if (materialList == null) {
862+ OgreMaterialKey materialKey = new OgreMaterialKey(folderName + meshName + ".material");
800863 try {
801- materialList = (MaterialList) assetManager.loadAsset(new OgreMaterialKey(folderName + meshName + ".material"));
864+ materialList = (MaterialList) assetManager.loadAsset(materialKey);
802865 } catch (AssetNotFoundException e) {
803- logger.log(Level.WARNING, "Cannot locate {0}{1}.material for model {2}{3}.{4}", new Object[]{folderName, meshName, folderName, meshName, ext});
804- logger.log(Level.WARNING, "", e);
805- }
806-
866+ logger.log(Level.WARNING, "Cannot locate {0} for model {1}", new Object[]{materialKey, key});
867+ }
807868 }
808869
809870 // Added by larynx 25.06.2011
@@ -813,21 +874,21 @@ public class MeshLoader extends DefaultHandler implements AssetLoader {
813874 // checking with JmeSystem.
814875 SAXParserFactory factory = SAXParserFactory.newInstance();
815876 factory.setNamespaceAware(true);
816-
877+
817878 XMLReader xr = factory.newSAXParser().getXMLReader();
818879 xr.setContentHandler(this);
819880 xr.setErrorHandler(this);
820-
881+
821882 InputStreamReader r = null;
822883 try {
823884 r = new InputStreamReader(info.openStream());
824885 xr.parse(new InputSource(r));
825886 } finally {
826- if (r != null){
887+ if (r != null) {
827888 r.close();
828889 }
829890 }
830-
891+
831892 return compileModel();
832893 } catch (SAXException ex) {
833894 IOException ioEx = new IOException("Error while parsing Ogre3D mesh.xml");
--- a/engine/src/ogre/com/jme3/scene/plugins/ogre/OgreMeshKey.java
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/OgreMeshKey.java
@@ -1,5 +1,5 @@
11 /*
2- * Copyright (c) 2009-2010 jMonkeyEngine
2+ * Copyright (c) 2009-2012 jMonkeyEngine
33 * All rights reserved.
44 *
55 * Redistribution and use in source and binary forms, with or without
@@ -29,12 +29,19 @@
2929 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
3030 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3131 */
32-
3332 package com.jme3.scene.plugins.ogre;
3433
3534 import com.jme3.asset.ModelKey;
3635 import com.jme3.material.MaterialList;
3736
37+/**
38+ * OgreMeshKey is used to load Ogre3D mesh.xml models with a specific
39+ * material file or list. This allows customizing from where the materials
40+ * are retrieved, instead of loading the material file as the same
41+ * name as the model (the default).
42+ *
43+ * @author Kirill Vainer
44+ */
3845 public class OgreMeshKey extends ModelKey {
3946
4047 private MaterialList materialList;
@@ -58,10 +65,44 @@ public class OgreMeshKey extends ModelKey {
5865 this.materialName = materialName;
5966 }
6067
68+ @Override
69+ public boolean equals(Object obj) {
70+ if (obj == null) {
71+ return false;
72+ }
73+ if (getClass() != obj.getClass()) {
74+ return false;
75+ }
76+ final OgreMeshKey other = (OgreMeshKey) obj;
77+ if (!super.equals(other)) {
78+ return false;
79+ }
80+ if (this.materialList != other.materialList && (this.materialList == null || !this.materialList.equals(other.materialList))) {
81+ return false;
82+ }
83+ if ((this.materialName == null) ? (other.materialName != null) : !this.materialName.equals(other.materialName)) {
84+ return false;
85+ }
86+ return true;
87+ }
88+
89+ @Override
90+ public int hashCode() {
91+ int hash = 5;
92+ hash = 31 * hash + (super.hashCode());
93+ hash = 31 * hash + (this.materialList != null ? this.materialList.hashCode() : 0);
94+ hash = 31 * hash + (this.materialName != null ? this.materialName.hashCode() : 0);
95+ return hash;
96+ }
97+
6198 public MaterialList getMaterialList() {
6299 return materialList;
63100 }
64101
102+ public void setMaterialList(MaterialList materialList){
103+ this.materialList = materialList;
104+ }
105+
65106 public String getMaterialName() {
66107 return materialName;
67108 }
--- a/engine/src/ogre/com/jme3/scene/plugins/ogre/SceneLoader.java
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/SceneLoader.java
@@ -1,465 +1,550 @@
1-/*
2- * Copyright (c) 2009-2010 jMonkeyEngine
3- * All rights reserved.
4- *
5- * Redistribution and use in source and binary forms, with or without
6- * modification, are permitted provided that the following conditions are
7- * met:
8- *
9- * * Redistributions of source code must retain the above copyright
10- * notice, this list of conditions and the following disclaimer.
11- *
12- * * Redistributions in binary form must reproduce the above copyright
13- * notice, this list of conditions and the following disclaimer in the
14- * documentation and/or other materials provided with the distribution.
15- *
16- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17- * may be used to endorse or promote products derived from this software
18- * without specific prior written permission.
19- *
20- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31- */
32-
33-package com.jme3.scene.plugins.ogre;
34-
35-import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey;
36-import com.jme3.material.MaterialList;
37-import com.jme3.asset.AssetInfo;
38-import com.jme3.asset.AssetLoader;
39-import com.jme3.asset.AssetManager;
40-import com.jme3.asset.AssetNotFoundException;
41-import com.jme3.light.DirectionalLight;
42-import com.jme3.light.Light;
43-import com.jme3.light.PointLight;
44-import com.jme3.light.SpotLight;
45-import com.jme3.math.FastMath;
46-import com.jme3.math.Quaternion;
47-import com.jme3.math.Vector3f;
48-import com.jme3.scene.Node;
49-import com.jme3.scene.Spatial;
50-import com.jme3.util.xml.SAXUtil;
51-import java.io.IOException;
52-import java.io.InputStreamReader;
53-import java.util.Stack;
54-import java.util.logging.Level;
55-import java.util.logging.Logger;
56-
57-import javax.xml.parsers.ParserConfigurationException;
58-import javax.xml.parsers.SAXParserFactory;
59-
60-import org.xml.sax.Attributes;
61-import org.xml.sax.InputSource;
62-import org.xml.sax.SAXException;
63-import org.xml.sax.XMLReader;
64-import org.xml.sax.helpers.DefaultHandler;
65-
66-import static com.jme3.util.xml.SAXUtil.*;
67-
68-public class SceneLoader extends DefaultHandler implements AssetLoader {
69-
70- private static final Logger logger = Logger.getLogger(SceneLoader.class.getName());
71-
72- private Stack<String> elementStack = new Stack<String>();
73- private String sceneName;
74- private String folderName;
75- private AssetManager assetManager;
76- private MaterialList materialList;
77- private Node root;
78- private Node node;
79- private Node entityNode;
80- private Light light;
81- private int nodeIdx = 0;
82- private static volatile int sceneIdx = 0;
83-
84- public SceneLoader(){
85- super();
86- }
87-
88- @Override
89- public void startDocument() {
90- }
91-
92- @Override
93- public void endDocument() {
94- }
95-
96- private void reset(){
97- elementStack.clear();
98- nodeIdx = 0;
99-
100- // NOTE: Setting some of those to null is only needed
101- // if the parsed file had an error e.g. startElement was called
102- // but not endElement
103- root = null;
104- node = null;
105- entityNode = null;
106- light = null;
107- }
108-
109- private void checkTopNode(String topNode) throws SAXException{
110- if (!elementStack.peek().equals(topNode)){
111- throw new SAXException("dotScene parse error: Expected parent node to be " + topNode);
112- }
113- }
114-
115- private Quaternion parseQuat(Attributes attribs) throws SAXException{
116- if (attribs.getValue("x") != null){
117- // defined as quaternion
118- float x = parseFloat(attribs.getValue("x"));
119- float y = parseFloat(attribs.getValue("y"));
120- float z = parseFloat(attribs.getValue("z"));
121- float w = parseFloat(attribs.getValue("w"));
122- return new Quaternion(x,y,z,w);
123- }else if (attribs.getValue("qx") != null){
124- // defined as quaternion with prefix "q"
125- float x = parseFloat(attribs.getValue("qx"));
126- float y = parseFloat(attribs.getValue("qy"));
127- float z = parseFloat(attribs.getValue("qz"));
128- float w = parseFloat(attribs.getValue("qw"));
129- return new Quaternion(x,y,z,w);
130- }else if (attribs.getValue("angle") != null){
131- // defined as angle + axis
132- float angle = parseFloat(attribs.getValue("angle"));
133- float axisX = parseFloat(attribs.getValue("axisX"));
134- float axisY = parseFloat(attribs.getValue("axisY"));
135- float axisZ = parseFloat(attribs.getValue("axisZ"));
136- Quaternion q = new Quaternion();
137- q.fromAngleAxis(angle, new Vector3f(axisX, axisY, axisZ));
138- return q;
139- }else{
140- // defines as 3 angles along XYZ axes
141- float angleX = parseFloat(attribs.getValue("angleX"));
142- float angleY = parseFloat(attribs.getValue("angleY"));
143- float angleZ = parseFloat(attribs.getValue("angleZ"));
144- Quaternion q = new Quaternion();
145- q.fromAngles(angleX, angleY, angleZ);
146- return q;
147- }
148- }
149-
150- private void parseLightNormal(Attributes attribs) throws SAXException {
151- checkTopNode("light");
152-
153- // SpotLight will be supporting a direction-normal, too.
154- if (light instanceof DirectionalLight)
155- ((DirectionalLight) light).setDirection(parseVector3(attribs));
156- else if (light instanceof SpotLight){
157- ((SpotLight) light).setDirection(parseVector3(attribs));
158- }
159- }
160-
161- private void parseLightAttenuation(Attributes attribs) throws SAXException {
162- // NOTE: Derives range based on "linear" if it is used solely
163- // for the attenuation. Otherwise derives it from "range"
164- checkTopNode("light");
165-
166- if (light instanceof PointLight || light instanceof SpotLight){
167- float range = parseFloat(attribs.getValue("range"));
168- float constant = parseFloat(attribs.getValue("constant"));
169- float linear = parseFloat(attribs.getValue("linear"));
170-
171- String quadraticStr = attribs.getValue("quadratic");
172- if (quadraticStr == null)
173- quadraticStr = attribs.getValue("quadric");
174-
175- float quadratic = parseFloat(quadraticStr);
176-
177- if (constant == 1 && quadratic == 0 && linear > 0){
178- range = 1f / linear;
179- }
180-
181- if (light instanceof PointLight){
182- ((PointLight) light).setRadius(range);
183- }else{
184- ((SpotLight)light).setSpotRange(range);
185- }
186- }
187- }
188-
189- private void parseLightSpotLightRange(Attributes attribs) throws SAXException{
190- checkTopNode("light");
191-
192- float outer = SAXUtil.parseFloat(attribs.getValue("outer"));
193- float inner = SAXUtil.parseFloat(attribs.getValue("inner"));
194-
195- if (!(light instanceof SpotLight)){
196- throw new SAXException("dotScene parse error: spotLightRange "
197- + "can only appear under 'spot' light elements");
198- }
199-
200- SpotLight sl = (SpotLight) light;
201- sl.setSpotInnerAngle(inner * 0.5f);
202- sl.setSpotOuterAngle(outer * 0.5f);
203- }
204-
205- private void parseLight(Attributes attribs) throws SAXException {
206- if (node == null || node.getParent() == null)
207- throw new SAXException("dotScene parse error: light can only appear under a node");
208-
209- checkTopNode("node");
210-
211- String lightType = parseString(attribs.getValue("type"), "point");
212- if(lightType.equals("point")) {
213- light = new PointLight();
214- } else if(lightType.equals("directional") || lightType.equals("sun")) {
215- light = new DirectionalLight();
216- // Assuming "normal" property is not provided
217- ((DirectionalLight)light).setDirection(Vector3f.UNIT_Z);
218- } else if(lightType.equals("spotLight") || lightType.equals("spot")) {
219- light = new SpotLight();
220- } else {
221- logger.log(Level.WARNING, "No matching jME3 LightType found for OGRE LightType: {0}", lightType);
222- }
223- logger.log(Level.FINEST, "{0} created.", light);
224-
225- if (!parseBool(attribs.getValue("visible"), true)){
226- // set to disabled
227- }
228-
229- // "attach" it to the parent of this node
230- if (light != null)
231- node.getParent().addLight(light);
232- }
233-
234- @Override
235- public void startElement(String uri, String localName, String qName, Attributes attribs) throws SAXException{
236- if (qName.equals("scene")){
237- if (elementStack.size() != 0){
238- throw new SAXException("dotScene parse error: 'scene' element must be the root XML element");
239- }
240-
241- String version = attribs.getValue("formatVersion");
242- if (version == null && !version.equals("1.0.0") && !version.equals("1.0.1"))
243- logger.log(Level.WARNING, "Unrecognized version number"
244- + " in dotScene file: {0}", version);
245-
246- }else if (qName.equals("nodes")){
247- if (root != null){
248- throw new SAXException("dotScene parse error: nodes element was specified twice");
249- }
250- if (sceneName == null)
251- root = new Node("OgreDotScene"+(++sceneIdx));
252- else
253- root = new Node(sceneName+"-scene_node");
254-
255- node = root;
256- }else if (qName.equals("externals")){
257- checkTopNode("scene");
258- // Not loaded currently
259- }else if (qName.equals("item")){
260- checkTopNode("externals");
261- }else if (qName.equals("file")){
262- checkTopNode("item");
263-
264- // XXX: Currently material file name is based
265- // on the scene's filename. THIS IS NOT CORRECT.
266- // To solve, port SceneLoader to use DOM instead of SAX
267-
268- //String matFile = folderName+attribs.getValue("name");
269- //try {
270- // materialList = (MaterialList) assetManager.loadAsset(new OgreMaterialKey(matFile));
271- //} catch (AssetNotFoundException ex){
272- // materialList = null;
273- // logger.log(Level.WARNING, "Cannot locate material file: {0}", matFile);
274- //}
275- }else if (qName.equals("node")){
276- String curElement = elementStack.peek();
277- if (!curElement.equals("node") && !curElement.equals("nodes")){
278- throw new SAXException("dotScene parse error: "
279- + "node element can only appear under 'node' or 'nodes'");
280- }
281-
282- String name = attribs.getValue("name");
283- if (name == null)
284- name = "OgreNode-" + (++nodeIdx);
285-
286- Node newNode = new Node(name);
287- if (node != null){
288- node.attachChild(newNode);
289- }
290- node = newNode;
291- }else if (qName.equals("property")){
292- if (node != null){
293- String type = attribs.getValue("type");
294- String name = attribs.getValue("name");
295- String data = attribs.getValue("data");
296- if (type.equals("BOOL")){
297- node.setUserData(name, Boolean.parseBoolean(data)||data.equals("1"));
298- }else if (type.equals("FLOAT")){
299- node.setUserData(name, Float.parseFloat(data));
300- }else if (type.equals("STRING")){
301- node.setUserData(name, data);
302- }else if (type.equals("INT")){
303- node.setUserData(name, Integer.parseInt(data));
304- }
305- }
306- }else if (qName.equals("entity")){
307- checkTopNode("node");
308-
309- String name = attribs.getValue("name");
310- if (name == null)
311- name = "OgreEntity-" + (++nodeIdx);
312- else
313- name += "-entity";
314-
315- String meshFile = attribs.getValue("meshFile");
316- if (meshFile == null) {
317- throw new SAXException("Required attribute 'meshFile' missing for 'entity' node");
318- }
319-
320- // TODO: Not currently used
321- String materialName = attribs.getValue("materialName");
322-
323- if (folderName != null) {
324- meshFile = folderName + meshFile;
325- }
326-
327- // NOTE: append "xml" since its assumed mesh files are binary in dotScene
328- meshFile += ".xml";
329-
330- entityNode = new Node(name);
331- OgreMeshKey key = new OgreMeshKey(meshFile, materialList);
332- Spatial ogreMesh = assetManager.loadModel(key);
333-
334- entityNode.attachChild(ogreMesh);
335- node.attachChild(entityNode);
336- node = null;
337- }else if (qName.equals("position")){
338- if (elementStack.peek().equals("node")){
339- node.setLocalTranslation(SAXUtil.parseVector3(attribs));
340- }
341- }else if (qName.equals("quaternion") || qName.equals("rotation")){
342- node.setLocalRotation(parseQuat(attribs));
343- }else if (qName.equals("scale")){
344- node.setLocalScale(SAXUtil.parseVector3(attribs));
345- } else if (qName.equals("light")) {
346- parseLight(attribs);
347- } else if (qName.equals("colourDiffuse") || qName.equals("colorDiffuse")) {
348- if (elementStack.peek().equals("light")){
349- if (light != null){
350- light.setColor(parseColor(attribs));
351- }
352- }else{
353- checkTopNode("environment");
354- }
355- } else if (qName.equals("normal") || qName.equals("direction")) {
356- checkTopNode("light");
357- parseLightNormal(attribs);
358- } else if (qName.equals("lightAttenuation")) {
359- parseLightAttenuation(attribs);
360- } else if (qName.equals("spotLightRange") || qName.equals("lightRange")) {
361- parseLightSpotLightRange(attribs);
362- }
363-
364- elementStack.push(qName);
365- }
366-
367- @Override
368- public void endElement(String uri, String name, String qName) throws SAXException {
369- if (qName.equals("node")){
370- node = node.getParent();
371- }else if (qName.equals("nodes")){
372- node = null;
373- }else if (qName.equals("entity")){
374- node = entityNode.getParent();
375- entityNode = null;
376- }else if (qName.equals("light")){
377- // apply the node's world transform on the light..
378- root.updateGeometricState();
379- if (light != null){
380- if (light instanceof DirectionalLight){
381- DirectionalLight dl = (DirectionalLight) light;
382- Quaternion q = node.getWorldRotation();
383- Vector3f dir = dl.getDirection();
384- q.multLocal(dir);
385- dl.setDirection(dir);
386- }else if (light instanceof PointLight){
387- PointLight pl = (PointLight) light;
388- Vector3f pos = node.getWorldTranslation();
389- pl.setPosition(pos);
390- }else if (light instanceof SpotLight){
391- SpotLight sl = (SpotLight) light;
392-
393- Vector3f pos = node.getWorldTranslation();
394- sl.setPosition(pos);
395-
396- Quaternion q = node.getWorldRotation();
397- Vector3f dir = sl.getDirection();
398- q.multLocal(dir);
399- sl.setDirection(dir);
400- }
401- }
402- light = null;
403- }
404- checkTopNode(qName);
405- elementStack.pop();
406- }
407-
408- @Override
409- public void characters(char ch[], int start, int length) {
410- }
411-
412- public Object load(AssetInfo info) throws IOException {
413- try{
414- assetManager = info.getManager();
415- sceneName = info.getKey().getName();
416- String ext = info.getKey().getExtension();
417- folderName = info.getKey().getFolder();
418- sceneName = sceneName.substring(0, sceneName.length() - ext.length() - 1);
419-
420- try {
421- materialList = (MaterialList)
422- assetManager.loadAsset(new OgreMaterialKey(sceneName+".material"));
423- } catch (AssetNotFoundException ex){
424- logger.log(Level.WARNING, "Cannot locate material file {0}", ex.getMessage());
425- materialList = null;
426- }
427-
428- reset();
429-
430- // Added by larynx 25.06.2011
431- // Android needs the namespace aware flag set to true
432- // Kirill 30.06.2011
433- // Now, hack is applied for both desktop and android to avoid
434- // checking with JmeSystem.
435- SAXParserFactory factory = SAXParserFactory.newInstance();
436- factory.setNamespaceAware(true);
437- XMLReader xr = factory.newSAXParser().getXMLReader();
438-
439- xr.setContentHandler(this);
440- xr.setErrorHandler(this);
441-
442- InputStreamReader r = null;
443-
444- try {
445- r = new InputStreamReader(info.openStream());
446- xr.parse(new InputSource(r));
447- } finally {
448- if (r != null){
449- r.close();
450- }
451- }
452-
453- return root;
454- }catch (SAXException ex){
455- IOException ioEx = new IOException("Error while parsing Ogre3D dotScene");
456- ioEx.initCause(ex);
457- throw ioEx;
458- } catch (ParserConfigurationException ex) {
459- IOException ioEx = new IOException("Error while parsing Ogre3D dotScene");
460- ioEx.initCause(ex);
461- throw ioEx;
462- }
463- }
464-
465-}
1+/*
2+ * Copyright (c) 2009-2012 jMonkeyEngine
3+ * All rights reserved.
4+ *
5+ * Redistribution and use in source and binary forms, with or without
6+ * modification, are permitted provided that the following conditions are
7+ * met:
8+ *
9+ * * Redistributions of source code must retain the above copyright
10+ * notice, this list of conditions and the following disclaimer.
11+ *
12+ * * Redistributions in binary form must reproduce the above copyright
13+ * notice, this list of conditions and the following disclaimer in the
14+ * documentation and/or other materials provided with the distribution.
15+ *
16+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17+ * may be used to endorse or promote products derived from this software
18+ * without specific prior written permission.
19+ *
20+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+ */
32+package com.jme3.scene.plugins.ogre;
33+
34+import com.jme3.asset.*;
35+import com.jme3.light.AmbientLight;
36+import com.jme3.light.DirectionalLight;
37+import com.jme3.light.Light;
38+import com.jme3.light.PointLight;
39+import com.jme3.light.SpotLight;
40+import com.jme3.material.MaterialList;
41+import com.jme3.math.ColorRGBA;
42+import com.jme3.math.FastMath;
43+import com.jme3.math.Quaternion;
44+import com.jme3.math.Vector3f;
45+import com.jme3.renderer.Camera;
46+import com.jme3.scene.CameraNode;
47+import com.jme3.scene.LightNode;
48+import com.jme3.scene.Spatial;
49+import com.jme3.scene.control.CameraControl.ControlDirection;
50+import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey;
51+import com.jme3.util.PlaceholderAssets;
52+import com.jme3.util.xml.SAXUtil;
53+import static com.jme3.util.xml.SAXUtil.*;
54+import java.io.IOException;
55+import java.io.InputStreamReader;
56+import java.util.Stack;
57+import java.util.logging.Level;
58+import java.util.logging.Logger;
59+import javax.xml.parsers.ParserConfigurationException;
60+import javax.xml.parsers.SAXParserFactory;
61+import org.xml.sax.Attributes;
62+import org.xml.sax.InputSource;
63+import org.xml.sax.SAXException;
64+import org.xml.sax.XMLReader;
65+import org.xml.sax.helpers.DefaultHandler;
66+
67+public class SceneLoader extends DefaultHandler implements AssetLoader {
68+
69+ private static final int DEFAULT_CAM_WIDTH = 640;
70+ private static final int DEFAULT_CAM_HEIGHT = 480;
71+
72+ private static final Logger logger = Logger.getLogger(SceneLoader.class.getName());
73+ private SceneMaterialLoader materialLoader = new SceneMaterialLoader();
74+ private Stack<String> elementStack = new Stack<String>();
75+ private AssetKey key;
76+ private String sceneName;
77+ private String folderName;
78+ private AssetManager assetManager;
79+ private MaterialList materialList;
80+ private com.jme3.scene.Node root;
81+ private com.jme3.scene.Node node;
82+ private com.jme3.scene.Node entityNode;
83+ private Light light;
84+ private Camera camera;
85+ private CameraNode cameraNode;
86+ private int nodeIdx = 0;
87+ private static volatile int sceneIdx = 0;
88+
89+ public SceneLoader() {
90+ super();
91+ }
92+
93+ @Override
94+ public void startDocument() {
95+ }
96+
97+ @Override
98+ public void endDocument() {
99+ }
100+
101+ private void reset() {
102+ elementStack.clear();
103+ nodeIdx = 0;
104+
105+ // NOTE: Setting some of those to null is only needed
106+ // if the parsed file had an error e.g. startElement was called
107+ // but not endElement
108+ root = null;
109+ node = null;
110+ entityNode = null;
111+ light = null;
112+ camera = null;
113+ cameraNode = null;
114+ }
115+
116+ private void checkTopNode(String topNode) throws SAXException {
117+ if (!elementStack.peek().equals(topNode)) {
118+ throw new SAXException("dotScene parse error: Expected parent node to be " + topNode);
119+ }
120+ }
121+
122+ private Quaternion parseQuat(Attributes attribs) throws SAXException {
123+ if (attribs.getValue("x") != null) {
124+ // defined as quaternion
125+ float x = parseFloat(attribs.getValue("x"));
126+ float y = parseFloat(attribs.getValue("y"));
127+ float z = parseFloat(attribs.getValue("z"));
128+ float w = parseFloat(attribs.getValue("w"));
129+ return new Quaternion(x, y, z, w);
130+ } else if (attribs.getValue("qx") != null) {
131+ // defined as quaternion with prefix "q"
132+ float x = parseFloat(attribs.getValue("qx"));
133+ float y = parseFloat(attribs.getValue("qy"));
134+ float z = parseFloat(attribs.getValue("qz"));
135+ float w = parseFloat(attribs.getValue("qw"));
136+ return new Quaternion(x, y, z, w);
137+ } else if (attribs.getValue("angle") != null) {
138+ // defined as angle + axis
139+ float angle = parseFloat(attribs.getValue("angle"));
140+ float axisX = parseFloat(attribs.getValue("axisX"));
141+ float axisY = parseFloat(attribs.getValue("axisY"));
142+ float axisZ = parseFloat(attribs.getValue("axisZ"));
143+ Quaternion q = new Quaternion();
144+ q.fromAngleAxis(angle, new Vector3f(axisX, axisY, axisZ));
145+ return q;
146+ } else {
147+ // defines as 3 angles along XYZ axes
148+ float angleX = parseFloat(attribs.getValue("angleX"));
149+ float angleY = parseFloat(attribs.getValue("angleY"));
150+ float angleZ = parseFloat(attribs.getValue("angleZ"));
151+ Quaternion q = new Quaternion();
152+ q.fromAngles(angleX, angleY, angleZ);
153+ return q;
154+ }
155+ }
156+
157+ private void parseLightNormal(Attributes attribs) throws SAXException {
158+ checkTopNode("light");
159+
160+ // SpotLight will be supporting a direction-normal, too.
161+ if (light instanceof DirectionalLight) {
162+ ((DirectionalLight) light).setDirection(parseVector3(attribs));
163+ } else if (light instanceof SpotLight) {
164+ ((SpotLight) light).setDirection(parseVector3(attribs));
165+ }
166+ }
167+
168+ private void parseLightAttenuation(Attributes attribs) throws SAXException {
169+ // NOTE: Derives range based on "linear" if it is used solely
170+ // for the attenuation. Otherwise derives it from "range"
171+ checkTopNode("light");
172+
173+ if (light instanceof PointLight || light instanceof SpotLight) {
174+ float range = parseFloat(attribs.getValue("range"));
175+ float constant = parseFloat(attribs.getValue("constant"));
176+ float linear = parseFloat(attribs.getValue("linear"));
177+
178+ String quadraticStr = attribs.getValue("quadratic");
179+ if (quadraticStr == null) {
180+ quadraticStr = attribs.getValue("quadric");
181+ }
182+
183+ float quadratic = parseFloat(quadraticStr);
184+
185+ if (constant == 1 && quadratic == 0 && linear > 0) {
186+ range = 1f / linear;
187+ }
188+
189+ if (light instanceof PointLight) {
190+ ((PointLight) light).setRadius(range);
191+ } else {
192+ ((SpotLight) light).setSpotRange(range);
193+ }
194+ }
195+ }
196+
197+ private void parseLightSpotLightRange(Attributes attribs) throws SAXException {
198+ checkTopNode("light");
199+
200+ float outer = SAXUtil.parseFloat(attribs.getValue("outer"));
201+ float inner = SAXUtil.parseFloat(attribs.getValue("inner"));
202+
203+ if (!(light instanceof SpotLight)) {
204+ throw new SAXException("dotScene parse error: spotLightRange "
205+ + "can only appear under 'spot' light elements");
206+ }
207+
208+ SpotLight sl = (SpotLight) light;
209+ sl.setSpotInnerAngle(inner * 0.5f);
210+ sl.setSpotOuterAngle(outer * 0.5f);
211+ }
212+
213+ private void parseLight(Attributes attribs) throws SAXException {
214+ if (node == null || node.getParent() == null) {
215+ throw new SAXException("dotScene parse error: light can only appear under a node");
216+ }
217+
218+ checkTopNode("node");
219+
220+ String lightType = parseString(attribs.getValue("type"), "point");
221+ if (lightType.equals("point")) {
222+ light = new PointLight();
223+ } else if (lightType.equals("directional") || lightType.equals("sun")) {
224+ light = new DirectionalLight();
225+ // Assuming "normal" property is not provided
226+ ((DirectionalLight) light).setDirection(Vector3f.UNIT_Z);
227+ } else if (lightType.equals("spotLight") || lightType.equals("spot")) {
228+ light = new SpotLight();
229+ } else if (lightType.equals("omni")) {
230+ // XXX: It doesn't seem any exporters actually emit this type?
231+ light = new AmbientLight();
232+ } else {
233+ logger.log(Level.WARNING, "No matching jME3 LightType found for OGRE LightType: {0}", lightType);
234+ }
235+ logger.log(Level.FINEST, "{0} created.", light);
236+
237+ if (!parseBool(attribs.getValue("visible"), true)) {
238+ // set to disabled
239+ }
240+
241+ // "attach" it to the parent of this node
242+ if (light != null) {
243+ node.getParent().addLight(light);
244+ }
245+ }
246+
247+ private void parseCameraClipping(Attributes attribs) throws SAXException {
248+ if (attribs.getValue("near") != null) {
249+ camera.setFrustumNear(SAXUtil.parseFloat(attribs.getValue("near")));
250+ camera.setFrustumFar(SAXUtil.parseFloat(attribs.getValue("far")));
251+ } else {
252+ camera.setFrustumNear(SAXUtil.parseFloat(attribs.getValue("nearPlaneDist")));
253+ camera.setFrustumFar(SAXUtil.parseFloat(attribs.getValue("farPlaneDist")));
254+ }
255+ }
256+
257+ private void parseCamera(Attributes attribs) throws SAXException {
258+ camera = new Camera(DEFAULT_CAM_WIDTH, DEFAULT_CAM_HEIGHT);
259+ if (SAXUtil.parseString(attribs.getValue("projectionType"), "perspective").equals("parallel")){
260+ camera.setParallelProjection(true);
261+ }
262+ float fov = SAXUtil.parseFloat(attribs.getValue("fov"), 45f);
263+ if (fov < FastMath.PI) {
264+ // XXX: Most likely, it is in radians..
265+ fov = fov * FastMath.RAD_TO_DEG;
266+ }
267+ camera.setFrustumPerspective(fov, (float)DEFAULT_CAM_WIDTH / DEFAULT_CAM_HEIGHT, 1, 1000);
268+
269+ cameraNode = new CameraNode(attribs.getValue("name"), camera);
270+ cameraNode.setControlDir(ControlDirection.SpatialToCamera);
271+
272+ node.attachChild(cameraNode);
273+ node = null;
274+ }
275+
276+ private void parseEntity(Attributes attribs) throws SAXException {
277+ String name = attribs.getValue("name");
278+ if (name == null) {
279+ name = "OgreEntity-" + (++nodeIdx);
280+ } else {
281+ name += "-entity";
282+ }
283+
284+ String meshFile = attribs.getValue("meshFile");
285+ if (meshFile == null) {
286+ throw new SAXException("Required attribute 'meshFile' missing for 'entity' node");
287+ }
288+
289+ // TODO: Not currently used
290+ String materialName = attribs.getValue("materialName");
291+
292+ if (folderName != null) {
293+ meshFile = folderName + meshFile;
294+ }
295+
296+ // NOTE: append "xml" since its assumed mesh files are binary in dotScene
297+ meshFile += ".xml";
298+
299+ entityNode = new com.jme3.scene.Node(name);
300+ OgreMeshKey meshKey = new OgreMeshKey(meshFile, materialList);
301+ try {
302+ Spatial ogreMesh = assetManager.loadModel(meshKey);
303+ entityNode.attachChild(ogreMesh);
304+ } catch (AssetNotFoundException ex) {
305+ if (ex.getMessage().equals(meshFile)) {
306+ logger.log(Level.WARNING, "Cannot locate {0} for scene {1}", new Object[]{meshKey, key});
307+ // Attach placeholder asset.
308+ Spatial model = PlaceholderAssets.getPlaceholderModel(assetManager);
309+ model.setKey(key);
310+ entityNode.attachChild(model);
311+ } else {
312+ throw ex;
313+ }
314+ }
315+
316+ node.attachChild(entityNode);
317+ node = null;
318+ }
319+
320+ private void parseNode(Attributes attribs) throws SAXException {
321+ String name = attribs.getValue("name");
322+ if (name == null) {
323+ name = "OgreNode-" + (++nodeIdx);
324+ }
325+
326+ com.jme3.scene.Node newNode = new com.jme3.scene.Node(name);
327+ if (node != null) {
328+ node.attachChild(newNode);
329+ }
330+ node = newNode;
331+ }
332+
333+ @Override
334+ public void startElement(String uri, String localName, String qName, Attributes attribs) throws SAXException {
335+ if (qName.equals("scene")) {
336+ if (elementStack.size() != 0) {
337+ throw new SAXException("dotScene parse error: 'scene' element must be the root XML element");
338+ }
339+
340+ String version = attribs.getValue("formatVersion");
341+ if (version == null || (!version.equals("1.0.0") && !version.equals("1.0.1"))) {
342+ logger.log(Level.WARNING, "Unrecognized version number"
343+ + " in dotScene file: {0}", version);
344+ }
345+ } else if (qName.equals("nodes")) {
346+ if (root != null) {
347+ throw new SAXException("dotScene parse error: nodes element was specified twice");
348+ }
349+ if (sceneName == null) {
350+ root = new com.jme3.scene.Node("OgreDotScene" + (++sceneIdx));
351+ } else {
352+ root = new com.jme3.scene.Node(sceneName + "-scene_node");
353+ }
354+
355+ node = root;
356+ } else if (qName.equals("externals")) {
357+ checkTopNode("scene");
358+ } else if (qName.equals("item")) {
359+ checkTopNode("externals");
360+ } else if (qName.equals("file")) {
361+ checkTopNode("item");
362+
363+ // NOTE: This part of the file is ignored, it is parsed
364+ // by SceneMaterialLoader in the first pass.
365+ } else if (qName.equals("node")) {
366+ String curElement = elementStack.peek();
367+ if (!curElement.equals("node") && !curElement.equals("nodes")) {
368+ throw new SAXException("dotScene parse error: "
369+ + "node element can only appear under 'node' or 'nodes'");
370+ }
371+
372+ parseNode(attribs);
373+ } else if (qName.equals("property")) {
374+ if (node != null) {
375+ String type = attribs.getValue("type");
376+ String name = attribs.getValue("name");
377+ String data = attribs.getValue("data");
378+ if (type.equals("BOOL")) {
379+ node.setUserData(name, Boolean.parseBoolean(data) || data.equals("1"));
380+ } else if (type.equals("FLOAT")) {
381+ node.setUserData(name, Float.parseFloat(data));
382+ } else if (type.equals("STRING")) {
383+ node.setUserData(name, data);
384+ } else if (type.equals("INT")) {
385+ node.setUserData(name, Integer.parseInt(data));
386+ }
387+ }
388+ } else if (qName.equals("entity")) {
389+ checkTopNode("node");
390+ parseEntity(attribs);
391+ } else if (qName.equals("camera")) {
392+ checkTopNode("node");
393+ parseCamera(attribs);
394+ } else if (qName.equals("clipping")) {
395+ checkTopNode("camera");
396+ parseCameraClipping(attribs);
397+ } else if (qName.equals("position")) {
398+ if (elementStack.peek().equals("node")) {
399+ node.setLocalTranslation(SAXUtil.parseVector3(attribs));
400+ } else if (elementStack.peek().equals("camera")) {
401+ cameraNode.setLocalTranslation(SAXUtil.parseVector3(attribs));
402+ }
403+ } else if (qName.equals("quaternion") || qName.equals("rotation")) {
404+ node.setLocalRotation(parseQuat(attribs));
405+ } else if (qName.equals("scale")) {
406+ node.setLocalScale(SAXUtil.parseVector3(attribs));
407+ } else if (qName.equals("light")) {
408+ parseLight(attribs);
409+ } else if (qName.equals("colourDiffuse") || qName.equals("colorDiffuse")) {
410+ if (elementStack.peek().equals("light")) {
411+ if (light != null) {
412+ light.setColor(parseColor(attribs));
413+ }
414+ } else {
415+ checkTopNode("environment");
416+ }
417+ } else if (qName.equals("colourAmbient") || qName.equals("colorAmbient")) {
418+ if (elementStack.peek().equals("environment")) {
419+ ColorRGBA color = parseColor(attribs);
420+ if (!color.equals(ColorRGBA.Black) && !color.equals(ColorRGBA.BlackNoAlpha)) {
421+ // Lets add an ambient light to the scene.
422+ AmbientLight al = new AmbientLight();
423+ al.setColor(color);
424+ root.addLight(al);
425+ }
426+ }
427+ } else if (qName.equals("normal") || qName.equals("direction")) {
428+ checkTopNode("light");
429+ parseLightNormal(attribs);
430+ } else if (qName.equals("lightAttenuation")) {
431+ parseLightAttenuation(attribs);
432+ } else if (qName.equals("spotLightRange") || qName.equals("lightRange")) {
433+ parseLightSpotLightRange(attribs);
434+ }
435+
436+ elementStack.push(qName);
437+ }
438+
439+ @Override
440+ public void endElement(String uri, String name, String qName) throws SAXException {
441+ if (qName.equals("node")) {
442+ node = node.getParent();
443+ } else if (qName.equals("nodes")) {
444+ node = null;
445+ } else if (qName.equals("entity")) {
446+ node = entityNode.getParent();
447+ entityNode = null;
448+ } else if (qName.equals("camera")) {
449+ node = cameraNode.getParent();
450+ cameraNode = null;
451+ } else if (qName.equals("light")) {
452+ // apply the node's world transform on the light..
453+ root.updateGeometricState();
454+ if (light != null) {
455+ if (light instanceof DirectionalLight) {
456+ DirectionalLight dl = (DirectionalLight) light;
457+ Quaternion q = node.getWorldRotation();
458+ Vector3f dir = dl.getDirection();
459+ q.multLocal(dir);
460+ dl.setDirection(dir);
461+ } else if (light instanceof PointLight) {
462+ PointLight pl = (PointLight) light;
463+ Vector3f pos = node.getWorldTranslation();
464+ pl.setPosition(pos);
465+ } else if (light instanceof SpotLight) {
466+ SpotLight sl = (SpotLight) light;
467+
468+ Vector3f pos = node.getWorldTranslation();
469+ sl.setPosition(pos);
470+
471+ Quaternion q = node.getWorldRotation();
472+ Vector3f dir = sl.getDirection();
473+ q.multLocal(dir);
474+ sl.setDirection(dir);
475+ }
476+ }
477+ light = null;
478+ }
479+ checkTopNode(qName);
480+ elementStack.pop();
481+ }
482+
483+ @Override
484+ public void characters(char ch[], int start, int length) {
485+ }
486+
487+ public Object load(AssetInfo info) throws IOException {
488+ try {
489+ key = info.getKey();
490+ assetManager = info.getManager();
491+ sceneName = key.getName();
492+ String ext = key.getExtension();
493+ folderName = key.getFolder();
494+ sceneName = sceneName.substring(0, sceneName.length() - ext.length() - 1);
495+
496+ reset();
497+
498+ // == Run 1st pass over XML file to determine material list ==
499+ materialList = materialLoader.load(assetManager, folderName, info.openStream());
500+
501+ if (materialList == null || materialList.isEmpty()) {
502+ // NOTE: No materials were found by searching the externals section.
503+ // Try finding a similarly named material file in the same folder.
504+ // (Backward compatibility only!)
505+ OgreMaterialKey materialKey = new OgreMaterialKey(sceneName + ".material");
506+ try {
507+ materialList = (MaterialList) assetManager.loadAsset(materialKey);
508+ } catch (AssetNotFoundException ex) {
509+ logger.log(Level.WARNING, "Cannot locate {0} for scene {1}", new Object[]{materialKey, key});
510+ materialList = null;
511+ }
512+ }
513+
514+ // == Run 2nd pass to load entities and other objects ==
515+
516+ // Added by larynx 25.06.2011
517+ // Android needs the namespace aware flag set to true
518+ // Kirill 30.06.2011
519+ // Now, hack is applied for both desktop and android to avoid
520+ // checking with JmeSystem.
521+ SAXParserFactory factory = SAXParserFactory.newInstance();
522+ factory.setNamespaceAware(true);
523+ XMLReader xr = factory.newSAXParser().getXMLReader();
524+
525+ xr.setContentHandler(this);
526+ xr.setErrorHandler(this);
527+
528+ InputStreamReader r = null;
529+
530+ try {
531+ r = new InputStreamReader(info.openStream());
532+ xr.parse(new InputSource(r));
533+ } finally {
534+ if (r != null) {
535+ r.close();
536+ }
537+ }
538+
539+ return root;
540+ } catch (SAXException ex) {
541+ IOException ioEx = new IOException("Error while parsing Ogre3D dotScene");
542+ ioEx.initCause(ex);
543+ throw ioEx;
544+ } catch (ParserConfigurationException ex) {
545+ IOException ioEx = new IOException("Error while parsing Ogre3D dotScene");
546+ ioEx.initCause(ex);
547+ throw ioEx;
548+ }
549+ }
550+}
--- /dev/null
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/SceneMaterialLoader.java
@@ -0,0 +1,158 @@
1+/*
2+ * Copyright (c) 2009-2012 jMonkeyEngine
3+ * All rights reserved.
4+ *
5+ * Redistribution and use in source and binary forms, with or without
6+ * modification, are permitted provided that the following conditions are
7+ * met:
8+ *
9+ * * Redistributions of source code must retain the above copyright
10+ * notice, this list of conditions and the following disclaimer.
11+ *
12+ * * Redistributions in binary form must reproduce the above copyright
13+ * notice, this list of conditions and the following disclaimer in the
14+ * documentation and/or other materials provided with the distribution.
15+ *
16+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17+ * may be used to endorse or promote products derived from this software
18+ * without specific prior written permission.
19+ *
20+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+ */
32+package com.jme3.scene.plugins.ogre;
33+
34+import com.jme3.asset.AssetManager;
35+import com.jme3.asset.AssetNotFoundException;
36+import com.jme3.material.MaterialList;
37+import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey;
38+import java.io.File;
39+import java.io.IOException;
40+import java.io.InputStream;
41+import java.io.InputStreamReader;
42+import java.util.Stack;
43+import java.util.logging.Level;
44+import java.util.logging.Logger;
45+import javax.xml.parsers.ParserConfigurationException;
46+import javax.xml.parsers.SAXParserFactory;
47+import org.xml.sax.Attributes;
48+import org.xml.sax.InputSource;
49+import org.xml.sax.SAXException;
50+import org.xml.sax.XMLReader;
51+import org.xml.sax.helpers.DefaultHandler;
52+
53+/**
54+ * This is a utility class to load a {@link MaterialList} from a
55+ * .scene file. It is only needed because the parsing method
56+ * used by the SceneLoader doesn't support reading bottom XML nodes
57+ * before reading the top nodes.
58+ *
59+ * @author Kirill Vainer
60+ */
61+class SceneMaterialLoader extends DefaultHandler {
62+
63+ private static final Logger logger = Logger.getLogger(SceneMaterialLoader.class.getName());
64+ private Stack<String> elementStack = new Stack<String>();
65+ private String folderName;
66+ private MaterialList materialList;
67+ private AssetManager assetManager;
68+ private boolean ignoreItem = false;
69+
70+ private void reset(){
71+ elementStack.clear();
72+ materialList = null;
73+ ignoreItem = false;
74+ }
75+
76+ private void checkTopNode(String topNode) throws SAXException{
77+ if (!elementStack.peek().equals(topNode)){
78+ throw new SAXException("dotScene parse error: Expected parent node to be " + topNode);
79+ }
80+ }
81+
82+ @Override
83+ public void startElement(String uri, String localName, String qName, Attributes attribs) throws SAXException {
84+ if (qName.equals("externals")) {
85+ checkTopNode("scene");
86+
87+ // Has an externals block, create material list.
88+ materialList = new MaterialList();
89+ } else if (qName.equals("item")) {
90+ checkTopNode("externals");
91+ if (!attribs.getValue("type").equals("material")) {
92+ // This is not a material external. Ignore it.
93+ ignoreItem = true;
94+ }
95+ } else if (qName.equals("file")) {
96+ checkTopNode("item");
97+
98+ if (!ignoreItem) {
99+ String materialPath = attribs.getValue("name");
100+ String materialName = new File(materialPath).getName();
101+ String matFile = folderName + materialName;
102+ try {
103+ MaterialList loadedMaterialList = (MaterialList) assetManager.loadAsset(new OgreMaterialKey(matFile));
104+ materialList.putAll(loadedMaterialList);
105+ } catch (AssetNotFoundException ex) {
106+ logger.log(Level.WARNING, "Cannot locate material file: {0}", matFile);
107+ }
108+ }
109+ }
110+ elementStack.push(qName);
111+ }
112+
113+ @Override
114+ public void endElement(String uri, String name, String qName) throws SAXException {
115+ if (qName.equals("item") && ignoreItem) {
116+ ignoreItem = false;
117+ }
118+ checkTopNode(qName);
119+ elementStack.pop();
120+ }
121+
122+ public MaterialList load(AssetManager assetManager, String folderName, InputStream in) throws IOException {
123+ try {
124+ this.assetManager = assetManager;
125+ this.folderName = folderName;
126+
127+ reset();
128+
129+ SAXParserFactory factory = SAXParserFactory.newInstance();
130+ factory.setNamespaceAware(true);
131+ XMLReader xr = factory.newSAXParser().getXMLReader();
132+
133+ xr.setContentHandler(this);
134+ xr.setErrorHandler(this);
135+
136+ InputStreamReader r = null;
137+
138+ try {
139+ r = new InputStreamReader(in);
140+ xr.parse(new InputSource(r));
141+ } finally {
142+ if (r != null){
143+ r.close();
144+ }
145+ }
146+
147+ return materialList;
148+ } catch (SAXException ex) {
149+ IOException ioEx = new IOException("Error while parsing Ogre3D dotScene");
150+ ioEx.initCause(ex);
151+ throw ioEx;
152+ } catch (ParserConfigurationException ex) {
153+ IOException ioEx = new IOException("Error while parsing Ogre3D dotScene");
154+ ioEx.initCause(ex);
155+ throw ioEx;
156+ }
157+ }
158+}
--- a/engine/src/ogre/com/jme3/scene/plugins/ogre/SkeletonLoader.java
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/SkeletonLoader.java
@@ -1,5 +1,5 @@
11 /*
2- * Copyright (c) 2009-2010 jMonkeyEngine
2+ * Copyright (c) 2009-2012 jMonkeyEngine
33 * All rights reserved.
44 *
55 * Redistribution and use in source and binary forms, with or without
@@ -31,6 +31,16 @@
3131 */
3232 package com.jme3.scene.plugins.ogre;
3333
34+import com.jme3.animation.Animation;
35+import com.jme3.animation.Bone;
36+import com.jme3.animation.BoneTrack;
37+import com.jme3.animation.Skeleton;
38+import com.jme3.asset.AssetInfo;
39+import com.jme3.asset.AssetLoader;
40+import com.jme3.asset.AssetManager;
41+import com.jme3.math.Quaternion;
42+import com.jme3.math.Vector3f;
43+import com.jme3.util.xml.SAXUtil;
3444 import java.io.IOException;
3545 import java.io.InputStream;
3646 import java.io.InputStreamReader;
@@ -39,27 +49,14 @@ import java.util.HashMap;
3949 import java.util.Map;
4050 import java.util.Stack;
4151 import java.util.logging.Logger;
42-
4352 import javax.xml.parsers.ParserConfigurationException;
4453 import javax.xml.parsers.SAXParserFactory;
45-
4654 import org.xml.sax.Attributes;
4755 import org.xml.sax.InputSource;
4856 import org.xml.sax.SAXException;
4957 import org.xml.sax.XMLReader;
5058 import org.xml.sax.helpers.DefaultHandler;
5159
52-import com.jme3.animation.Animation;
53-import com.jme3.animation.Bone;
54-import com.jme3.animation.BoneTrack;
55-import com.jme3.animation.Skeleton;
56-import com.jme3.asset.AssetInfo;
57-import com.jme3.asset.AssetLoader;
58-import com.jme3.asset.AssetManager;
59-import com.jme3.math.Quaternion;
60-import com.jme3.math.Vector3f;
61-import com.jme3.util.xml.SAXUtil;
62-
6360 public class SkeletonLoader extends DefaultHandler implements AssetLoader {
6461
6562 private static final Logger logger = Logger.getLogger(SceneLoader.class.getName());
--- a/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtension.java
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtension.java
@@ -1,5 +1,5 @@
11 /*
2- * Copyright (c) 2009-2010 jMonkeyEngine
2+ * Copyright (c) 2009-2012 jMonkeyEngine
33 * All rights reserved.
44 *
55 * Redistribution and use in source and binary forms, with or without
@@ -29,7 +29,6 @@
2929 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
3030 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3131 */
32-
3332 package com.jme3.scene.plugins.ogre.matext;
3433
3534 import java.util.HashMap;
--- a/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionLoader.java
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionLoader.java
@@ -1,5 +1,5 @@
11 /*
2- * Copyright (c) 2009-2010 jMonkeyEngine
2+ * Copyright (c) 2009-2012 jMonkeyEngine
33 * All rights reserved.
44 *
55 * Redistribution and use in source and binary forms, with or without
@@ -29,20 +29,22 @@
2929 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
3030 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3131 */
32-
3332 package com.jme3.scene.plugins.ogre.matext;
3433
34+import com.jme3.asset.AssetKey;
3535 import com.jme3.asset.AssetManager;
36+import com.jme3.asset.AssetNotFoundException;
3637 import com.jme3.asset.TextureKey;
3738 import com.jme3.material.Material;
3839 import com.jme3.material.MaterialList;
3940 import com.jme3.scene.plugins.ogre.MaterialLoader;
4041 import com.jme3.texture.Texture;
4142 import com.jme3.texture.Texture.WrapMode;
43+import com.jme3.texture.Texture2D;
44+import com.jme3.util.PlaceholderAssets;
4245 import com.jme3.util.blockparser.Statement;
4346 import java.io.IOException;
4447 import java.util.List;
45-import java.util.Scanner;
4648 import java.util.logging.Level;
4749 import java.util.logging.Logger;
4850
@@ -53,6 +55,7 @@ public class MaterialExtensionLoader {
5355
5456 private static final Logger logger = Logger.getLogger(MaterialExtensionLoader.class.getName());
5557
58+ private AssetKey key;
5659 private AssetManager assetManager;
5760 private MaterialList list;
5861 private MaterialExtensionSet matExts;
@@ -60,6 +63,7 @@ public class MaterialExtensionLoader {
6063 private String matName;
6164 private Material material;
6265
66+
6367 private void readExtendingMaterialStatement(Statement statement) throws IOException {
6468 if (statement.getLine().startsWith("set_texture_alias")){
6569 String[] split = statement.getLine().split(" ", 3);
@@ -68,14 +72,21 @@ public class MaterialExtensionLoader {
6872
6973 String jmeParamName = matExt.getTextureMapping(aliasName);
7074
71- TextureKey key = new TextureKey(texturePath, false);
72- key.setGenerateMips(true);
73- key.setAsCube(false);
74- Texture tex = assetManager.loadTexture(key);
75- if (tex == null)
76- throw new IOException("Cannot load texture: " + texturePath);
77- tex.setWrap(WrapMode.Repeat);
78-
75+ TextureKey texKey = new TextureKey(texturePath, false);
76+ texKey.setGenerateMips(true);
77+ texKey.setAsCube(false);
78+ Texture tex;
79+
80+ try {
81+ tex = assetManager.loadTexture(texKey);
82+ tex.setWrap(WrapMode.Repeat);
83+ } catch (AssetNotFoundException ex){
84+ logger.log(Level.WARNING, "Cannot locate {0} for material {1}", new Object[]{texKey, key});
85+ tex = new Texture2D( PlaceholderAssets.getPlaceholderImage() );
86+ tex.setWrap(WrapMode.Repeat);
87+ tex.setKey(texKey);
88+ }
89+
7990 material.setTexture(jmeParamName, tex);
8091 }
8192 }
@@ -100,10 +111,11 @@ public class MaterialExtensionLoader {
100111 return material;
101112 }
102113
103- public MaterialList load(AssetManager assetManager, MaterialExtensionSet matExts,
114+ public MaterialList load(AssetManager assetManager, AssetKey key, MaterialExtensionSet matExts,
104115 List<Statement> statements) throws IOException{
105116 this.assetManager = assetManager;
106117 this.matExts = matExts;
118+ this.key = key;
107119
108120 list = new MaterialList();
109121
--- a/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionSet.java
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/MaterialExtensionSet.java
@@ -1,5 +1,5 @@
11 /*
2- * Copyright (c) 2009-2010 jMonkeyEngine
2+ * Copyright (c) 2009-2012 jMonkeyEngine
33 * All rights reserved.
44 *
55 * Redistribution and use in source and binary forms, with or without
@@ -29,7 +29,6 @@
2929 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
3030 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3131 */
32-
3332 package com.jme3.scene.plugins.ogre.matext;
3433
3534 import java.util.ArrayList;
@@ -42,44 +41,46 @@ import java.util.List;
4241 * {@link OgreMaterialKey}s used.
4342 */
4443 public class MaterialExtensionSet {
45- private HashMap<String, MaterialExtension> extensions
46- = new HashMap<String, MaterialExtension>();
44+
45+ private HashMap<String, MaterialExtension> extensions = new HashMap<String, MaterialExtension>();
4746 private HashMap<String, List<String>> nameMappings = new HashMap<String, List<String>>();
4847
4948 /**
5049 * Adds a new material extension to the set of extensions.
50+ *
5151 * @param extension The {@link MaterialExtension} to add.
5252 */
53- public void addMaterialExtension(MaterialExtension extension){
53+ public void addMaterialExtension(MaterialExtension extension) {
5454 extensions.put(extension.getBaseMaterialName(), extension);
5555 }
5656
5757 /**
58- * Returns the {@link MaterialExtension} for a given Ogre3D base
59- * material name.
58+ * Returns the {@link MaterialExtension} for a given Ogre3D base material
59+ * name.
6060 *
6161 * @param baseMatName The ogre3D base material name.
6262 * @return {@link MaterialExtension} that is set, or null if not set.
6363 */
64- public MaterialExtension getMaterialExtension(String baseMatName){
64+ public MaterialExtension getMaterialExtension(String baseMatName) {
6565 return extensions.get(baseMatName);
6666 }
67-
67+
6868 /**
6969 * Adds an alternative name for a material
70+ *
7071 * @param name The material name to be found in a .mesh.xml file
7172 * @param alias The material name to be found in a .material file
7273 */
73- public void setNameMapping(String name, String alias){
74+ public void setNameMapping(String name, String alias) {
7475 List<String> list = nameMappings.get(name);
75- if(list==null){
76+ if (list == null) {
7677 list = new ArrayList<String>();
7778 nameMappings.put(name, list);
7879 }
7980 list.add(alias);
8081 }
81-
82- public List<String> getNameMappings(String name){
82+
83+ public List<String> getNameMappings(String name) {
8384 return nameMappings.get(name);
8485 }
8586 }
--- a/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/OgreMaterialKey.java
+++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/matext/OgreMaterialKey.java
@@ -1,5 +1,5 @@
11 /*
2- * Copyright (c) 2009-2010 jMonkeyEngine
2+ * Copyright (c) 2009-2012 jMonkeyEngine
33 * All rights reserved.
44 *
55 * Redistribution and use in source and binary forms, with or without
@@ -29,43 +29,70 @@
2929 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
3030 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3131 */
32-
3332 package com.jme3.scene.plugins.ogre.matext;
3433
3534 import com.jme3.asset.AssetKey;
3635 import com.jme3.material.MaterialList;
3736
3837 /**
39- * <code>OgreMaterialKey</code> allows specifying material extensions,
40- * which map from Ogre3D base materials to jME3 materials
38+ * <code>OgreMaterialKey</code> allows specifying material extensions, which map
39+ * from Ogre3D base materials to jME3 materials
4140 */
4241 public class OgreMaterialKey extends AssetKey<MaterialList> {
4342
4443 private MaterialExtensionSet matExts;
4544
46- public OgreMaterialKey(String name){
45+ public OgreMaterialKey(String name) {
4746 super(name);
4847 }
4948
50- public OgreMaterialKey(){
49+ public OgreMaterialKey() {
5150 super();
5251 }
5352
53+ @Override
54+ public boolean equals(Object obj) {
55+ if (obj == null) {
56+ return false;
57+ }
58+ if (getClass() != obj.getClass()) {
59+ return false;
60+ }
61+ final OgreMaterialKey other = (OgreMaterialKey) obj;
62+ if (!super.equals(other)) {
63+ return false;
64+ }
65+ if (this.matExts != other.matExts && (this.matExts == null || !this.matExts.equals(other.matExts))) {
66+ return false;
67+ }
68+ return true;
69+ }
70+
71+ @Override
72+ public int hashCode() {
73+ int hash = 5;
74+ hash = 71 * hash + (super.hashCode());
75+ hash = 71 * hash + (this.matExts != null ? this.matExts.hashCode() : 0);
76+ return hash;
77+ }
78+
5479 /**
55- * Set the {@link MaterialExtensionSet} to use for mapping
56- * base materials to jME3 matdefs when loading.
57- * Set to <code>null</code> to disable this functionality.
80+ * Set the {@link MaterialExtensionSet} to use for mapping base materials to
81+ * jME3 matdefs when loading. Set to
82+ * <code>null</code> to disable this functionality.
5883 *
59- * @param extension The {@link MaterialExtensionSet} to use
84+ * @param matExts The {@link MaterialExtensionSet} to use
6085 */
61- public void setMaterialExtensionSet(MaterialExtensionSet matExts){
86+ public void setMaterialExtensionSet(MaterialExtensionSet matExts) {
6287 this.matExts = matExts;
6388 }
6489
6590 /**
6691 * Returns the {@link MaterialExtensionSet} previously set using
67- * {@link OgreMaterialKey#setMaterialExtensionSet(com.jme3.scene.plugins.ogre.matext.MaterialExtensionSet) } method.
68- * @return
92+ * {@link OgreMaterialKey#setMaterialExtensionSet(com.jme3.scene.plugins.ogre.matext.MaterialExtensionSet)
93+ * } method.
94+ *
95+ * @return the {@link MaterialExtensionSet}
6996 */
7097 public MaterialExtensionSet getMaterialExtensionSet() {
7198 return matExts;