Main repository of MikuMikuStudio
修訂 | efd91620eef696ee24a7ced20d92f74819163a7e (tree) |
---|---|
時間 | 2013-07-07 00:22:49 |
作者 | kobayasi <kobayasi@pscn...> |
Commiter | kobayasi |
merge from jme10694
@@ -1,5 +1,5 @@ | ||
1 | 1 | /* |
2 | - * Copyright (c) 2009-2010 jMonkeyEngine | |
2 | + * Copyright (c) 2009-2012 jMonkeyEngine | |
3 | 3 | * All rights reserved. |
4 | 4 | * |
5 | 5 | * Redistribution and use in source and binary forms, with or without |
@@ -29,7 +29,6 @@ | ||
29 | 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
30 | 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
31 | 31 | */ |
32 | - | |
33 | 32 | package com.jme3.scene.plugins.ogre; |
34 | 33 | |
35 | 34 | import com.jme3.animation.Animation; |
@@ -1,5 +1,5 @@ | ||
1 | 1 | /* |
2 | - * Copyright (c) 2009-2010 jMonkeyEngine | |
2 | + * Copyright (c) 2009-2012 jMonkeyEngine | |
3 | 3 | * All rights reserved. |
4 | 4 | * |
5 | 5 | * Redistribution and use in source and binary forms, with or without |
@@ -29,15 +29,9 @@ | ||
29 | 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
30 | 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
31 | 31 | */ |
32 | - | |
33 | 32 | package com.jme3.scene.plugins.ogre; |
34 | 33 | |
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.*; | |
41 | 35 | import com.jme3.material.Material; |
42 | 36 | import com.jme3.material.MaterialList; |
43 | 37 | import com.jme3.material.RenderState; |
@@ -45,17 +39,14 @@ import com.jme3.math.ColorRGBA; | ||
45 | 39 | import com.jme3.scene.plugins.ogre.matext.MaterialExtensionLoader; |
46 | 40 | import com.jme3.scene.plugins.ogre.matext.MaterialExtensionSet; |
47 | 41 | import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey; |
48 | -import com.jme3.texture.Image; | |
49 | -import com.jme3.texture.Image.Format; | |
50 | 42 | import com.jme3.texture.Texture; |
51 | 43 | import com.jme3.texture.Texture.WrapMode; |
52 | 44 | import com.jme3.texture.Texture2D; |
53 | -import com.jme3.util.BufferUtils; | |
45 | +import com.jme3.util.PlaceholderAssets; | |
54 | 46 | import com.jme3.util.blockparser.BlockLanguageParser; |
55 | 47 | import com.jme3.util.blockparser.Statement; |
56 | 48 | import java.io.IOException; |
57 | 49 | import java.io.InputStream; |
58 | -import java.nio.ByteBuffer; | |
59 | 50 | import java.util.Arrays; |
60 | 51 | import java.util.List; |
61 | 52 | import java.util.Scanner; |
@@ -81,7 +72,7 @@ public class MaterialLoader implements AssetLoader { | ||
81 | 72 | private int texUnit = 0; |
82 | 73 | |
83 | 74 | private ColorRGBA readColor(String content){ |
84 | - String[] split = content.split(" "); | |
75 | + String[] split = content.split("\\s"); | |
85 | 76 | |
86 | 77 | ColorRGBA color = new ColorRGBA(); |
87 | 78 | color.r = Float.parseFloat(split[0]); |
@@ -137,23 +128,13 @@ public class MaterialLoader implements AssetLoader { | ||
137 | 128 | cubic = true; |
138 | 129 | } |
139 | 130 | |
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); | |
143 | 134 | |
144 | - Texture loadedTexture; | |
145 | 135 | 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 | + | |
157 | 138 | textures[texUnit].setImage(loadedTexture.getImage()); |
158 | 139 | textures[texUnit].setMinFilter(loadedTexture.getMinFilter()); |
159 | 140 | textures[texUnit].setKey(loadedTexture.getKey()); |
@@ -164,8 +145,11 @@ public class MaterialLoader implements AssetLoader { | ||
164 | 145 | textures[texUnit].setName(texName); |
165 | 146 | texName = null; |
166 | 147 | }else{ |
167 | - textures[texUnit].setName(key.getName()); | |
148 | + textures[texUnit].setName(texKey.getName()); | |
168 | 149 | } |
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()); | |
169 | 153 | } |
170 | 154 | } |
171 | 155 |
@@ -243,7 +227,7 @@ public class MaterialLoader implements AssetLoader { | ||
243 | 227 | }else if (keyword.equals("emissive")){ |
244 | 228 | emissive = readColor(split[1]); |
245 | 229 | }else if (keyword.equals("specular")){ |
246 | - String[] subsplit = split[1].split(" "); | |
230 | + String[] subsplit = split[1].split("\\s"); | |
247 | 231 | specular = new ColorRGBA(); |
248 | 232 | specular.r = Float.parseFloat(subsplit[0]); |
249 | 233 | specular.g = Float.parseFloat(subsplit[1]); |
@@ -319,7 +303,7 @@ public class MaterialLoader implements AssetLoader { | ||
319 | 303 | if (statement.getLine().startsWith("technique")){ |
320 | 304 | readTechnique(statement); |
321 | 305 | }else if (statement.getLine().startsWith("receive_shadows")){ |
322 | - String isOn = statement.getLine().split(" ")[1]; | |
306 | + String isOn = statement.getLine().split("\\s")[1]; | |
323 | 307 | if (isOn != null && isOn.equals("true")){ |
324 | 308 | } |
325 | 309 | } |
@@ -344,12 +328,16 @@ public class MaterialLoader implements AssetLoader { | ||
344 | 328 | rs.setAlphaTest(true); |
345 | 329 | rs.setAlphaFallOff(0.01f); |
346 | 330 | rs.setBlendMode(RenderState.BlendMode.Alpha); |
347 | - if (twoSide) | |
331 | + | |
332 | + if (twoSide){ | |
348 | 333 | rs.setFaceCullMode(RenderState.FaceCullMode.Off); |
334 | + } | |
335 | + | |
349 | 336 | // rs.setDepthWrite(false); |
350 | 337 | mat.setTransparent(true); |
351 | - if (!noLight) | |
338 | + if (!noLight){ | |
352 | 339 | mat.setBoolean("UseAlpha", true); |
340 | + } | |
353 | 341 | }else{ |
354 | 342 | if (twoSide){ |
355 | 343 | RenderState rs = mat.getAdditionalRenderState(); |
@@ -452,7 +440,7 @@ public class MaterialLoader implements AssetLoader { | ||
452 | 440 | "Ogre3D materials with extended materials"); |
453 | 441 | } |
454 | 442 | |
455 | - list = new MaterialExtensionLoader().load(assetManager, matExts, statements); | |
443 | + list = new MaterialExtensionLoader().load(assetManager, key, matExts, statements); | |
456 | 444 | break; |
457 | 445 | }else if (statement.getLine().startsWith("material")){ |
458 | 446 | if (list == null){ |
@@ -1,5 +1,5 @@ | ||
1 | 1 | /* |
2 | - * Copyright (c) 2009-2010 jMonkeyEngine | |
2 | + * Copyright (c) 2009-2012 jMonkeyEngine | |
3 | 3 | * All rights reserved. |
4 | 4 | * |
5 | 5 | * Redistribution and use in source and binary forms, with or without |
@@ -29,7 +29,6 @@ | ||
29 | 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
30 | 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
31 | 31 | */ |
32 | - | |
33 | 32 | package com.jme3.scene.plugins.ogre; |
34 | 33 | |
35 | 34 | //import static com.jmex.model.XMLUtil.getAttribute; |
@@ -1,5 +1,5 @@ | ||
1 | 1 | /* |
2 | - * Copyright (c) 2009-2010 jMonkeyEngine | |
2 | + * Copyright (c) 2009-2012 jMonkeyEngine | |
3 | 3 | * All rights reserved. |
4 | 4 | * |
5 | 5 | * Redistribution and use in source and binary forms, with or without |
@@ -31,54 +31,40 @@ | ||
31 | 31 | */ |
32 | 32 | package com.jme3.scene.plugins.ogre; |
33 | 33 | |
34 | -import com.jme3.animation.Animation; | |
35 | -import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey; | |
36 | 34 | import com.jme3.animation.AnimControl; |
35 | +import com.jme3.animation.Animation; | |
37 | 36 | 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.*; | |
43 | 38 | import com.jme3.material.Material; |
44 | 39 | import com.jme3.material.MaterialList; |
45 | 40 | import com.jme3.math.ColorRGBA; |
46 | 41 | 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.*; | |
52 | 43 | import com.jme3.scene.VertexBuffer.Format; |
53 | 44 | import com.jme3.scene.VertexBuffer.Type; |
54 | 45 | import com.jme3.scene.VertexBuffer.Usage; |
46 | +import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey; | |
55 | 47 | import com.jme3.util.BufferUtils; |
56 | 48 | import com.jme3.util.IntMap; |
57 | 49 | import com.jme3.util.IntMap.Entry; |
50 | +import com.jme3.util.PlaceholderAssets; | |
51 | +import static com.jme3.util.xml.SAXUtil.*; | |
58 | 52 | import java.io.IOException; |
59 | 53 | 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.*; | |
65 | 55 | import java.util.ArrayList; |
66 | 56 | import java.util.HashMap; |
67 | 57 | import java.util.List; |
68 | 58 | import java.util.logging.Level; |
69 | 59 | import java.util.logging.Logger; |
70 | - | |
71 | 60 | import javax.xml.parsers.ParserConfigurationException; |
72 | 61 | import javax.xml.parsers.SAXParserFactory; |
73 | - | |
74 | 62 | import org.xml.sax.Attributes; |
75 | 63 | import org.xml.sax.InputSource; |
76 | 64 | import org.xml.sax.SAXException; |
77 | 65 | import org.xml.sax.XMLReader; |
78 | 66 | import org.xml.sax.helpers.DefaultHandler; |
79 | 67 | |
80 | -import static com.jme3.util.xml.SAXUtil.*; | |
81 | - | |
82 | 68 | /** |
83 | 69 | * Loads Ogre3D mesh.xml files. |
84 | 70 | */ |
@@ -86,7 +72,6 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { | ||
86 | 72 | |
87 | 73 | private static final Logger logger = Logger.getLogger(MeshLoader.class.getName()); |
88 | 74 | public static boolean AUTO_INTERLEAVE = true; |
89 | - public static boolean HARDWARE_SKINNING = false; | |
90 | 75 | private static final Type[] TEXCOORD_TYPES = |
91 | 76 | new Type[]{ |
92 | 77 | Type.TexCoord, |
@@ -97,6 +82,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { | ||
97 | 82 | Type.TexCoord6, |
98 | 83 | Type.TexCoord7, |
99 | 84 | Type.TexCoord8,}; |
85 | + private AssetKey key; | |
100 | 86 | private String meshName; |
101 | 87 | private String folderName; |
102 | 88 | private AssetManager assetManager; |
@@ -110,15 +96,18 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { | ||
110 | 96 | private Geometry geom; |
111 | 97 | private ByteBuffer indicesData; |
112 | 98 | private FloatBuffer weightsFloatData; |
99 | + private boolean actuallyHasWeights = false; | |
113 | 100 | private int vertCount; |
114 | 101 | private boolean usesSharedVerts; |
115 | 102 | private boolean usesBigIndices; |
103 | + private boolean submeshNamesHack; | |
116 | 104 | // Global data |
117 | 105 | private Mesh sharedMesh; |
118 | 106 | private int meshIndex = 0; |
119 | 107 | private int texCoordIndex = 0; |
120 | 108 | private String ignoreUntilEnd = null; |
121 | 109 | private List<Geometry> geoms = new ArrayList<Geometry>(); |
110 | + private ArrayList<Boolean> usesSharedMesh = new ArrayList<Boolean>(); | |
122 | 111 | private IntMap<List<VertexBuffer>> lodLevels = new IntMap<List<VertexBuffer>>(); |
123 | 112 | private AnimData animData; |
124 | 113 |
@@ -139,6 +128,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { | ||
139 | 128 | geom = null; |
140 | 129 | sharedMesh = null; |
141 | 130 | |
131 | + usesSharedMesh.clear(); | |
142 | 132 | usesSharedVerts = false; |
143 | 133 | vertCount = 0; |
144 | 134 | meshIndex = 0; |
@@ -147,6 +137,8 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { | ||
147 | 137 | |
148 | 138 | animData = null; |
149 | 139 | |
140 | + actuallyHasWeights = false; | |
141 | + submeshNamesHack = false; | |
150 | 142 | indicesData = null; |
151 | 143 | weightsFloatData = null; |
152 | 144 | } |
@@ -155,42 +147,65 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { | ||
155 | 147 | public void endDocument() { |
156 | 148 | } |
157 | 149 | |
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) { | |
164 | 151 | if (ib != null) { |
165 | - ib.put(i1).put(i2).put(i3); | |
152 | + ib.put(index); | |
166 | 153 | } else { |
167 | - sb.put((short) i1).put((short) i2).put((short) i3); | |
154 | + sb.put((short) index); | |
168 | 155 | } |
169 | 156 | } |
170 | 157 | |
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 | + } | |
173 | 174 | } |
174 | 175 | |
176 | +// private boolean isUsingSharedVerts(Geometry geom) { | |
177 | + // Old code for buffer sharer | |
178 | + //return geom.getUserData(UserData.JME_SHAREDMESH) != null; | |
179 | +// } | |
175 | 180 | private void startFaces(String count) throws SAXException { |
176 | 181 | int numFaces = parseInt(count); |
177 | - int numIndices; | |
182 | + int indicesPerFace = 0; | |
178 | 183 | |
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!"); | |
183 | 196 | } |
184 | 197 | |
198 | + int numIndices = indicesPerFace * numFaces; | |
199 | + | |
185 | 200 | vb = new VertexBuffer(VertexBuffer.Type.Index); |
186 | 201 | if (!usesBigIndices) { |
187 | 202 | sb = BufferUtils.createShortBuffer(numIndices); |
188 | 203 | ib = null; |
189 | - vb.setupData(Usage.Static, 3, Format.UnsignedShort, sb); | |
204 | + vb.setupData(Usage.Static, indicesPerFace, Format.UnsignedShort, sb); | |
190 | 205 | } else { |
191 | 206 | ib = BufferUtils.createIntBuffer(numIndices); |
192 | 207 | sb = null; |
193 | - vb.setupData(Usage.Static, 3, Format.UnsignedInt, ib); | |
208 | + vb.setupData(Usage.Static, indicesPerFace, Format.UnsignedInt, ib); | |
194 | 209 | } |
195 | 210 | mesh.setBuffer(vb); |
196 | 211 | } |
@@ -199,19 +214,24 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { | ||
199 | 214 | Material mat = null; |
200 | 215 | if (matName.endsWith(".j3m")) { |
201 | 216 | // 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 | + } | |
203 | 225 | } else { |
204 | 226 | if (materialList != null) { |
205 | 227 | mat = materialList.get(matName); |
206 | 228 | } |
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 | - } | |
211 | 229 | } |
212 | 230 | |
213 | 231 | 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)); | |
215 | 235 | } |
216 | 236 | |
217 | 237 | if (mat.isTransparent()) { |
@@ -225,20 +245,29 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { | ||
225 | 245 | mesh = new Mesh(); |
226 | 246 | if (opType == null || opType.equals("triangle_list")) { |
227 | 247 | 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); | |
232 | 256 | } |
233 | 257 | |
234 | 258 | usesBigIndices = parseBool(use32bitIndices, false); |
235 | 259 | usesSharedVerts = parseBool(usesharedvertices, false); |
236 | 260 | if (usesSharedVerts) { |
261 | + usesSharedMesh.add(true); | |
262 | + | |
263 | + // Old code for buffer sharer | |
237 | 264 | // 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); | |
242 | 271 | } |
243 | 272 | |
244 | 273 | if (meshName == null) { |
@@ -248,8 +277,9 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { | ||
248 | 277 | } |
249 | 278 | |
250 | 279 | if (usesSharedVerts) { |
280 | + // Old code for buffer sharer | |
251 | 281 | // this mesh is shared! |
252 | - geom.setUserData(UserData.JME_SHAREDMESH, sharedMesh); | |
282 | + //geom.setUserData(UserData.JME_SHAREDMESH, sharedMesh); | |
253 | 283 | } |
254 | 284 | |
255 | 285 | applyMaterial(geom, matName); |
@@ -270,11 +300,23 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { | ||
270 | 300 | } |
271 | 301 | |
272 | 302 | /** |
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. | |
275 | 305 | */ |
276 | 306 | 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 | + | |
278 | 320 | return; |
279 | 321 | } |
280 | 322 |
@@ -301,7 +343,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { | ||
301 | 343 | if (sum != 1f) { |
302 | 344 | weightsFloatData.position(weightsFloatData.position() - 4); |
303 | 345 | // compute new vals based on sum |
304 | - float sumToB = 1f / sum; | |
346 | + float sumToB = sum == 0 ? 0 : 1f / sum; | |
305 | 347 | weightsFloatData.put(w0 * sumToB); |
306 | 348 | weightsFloatData.put(w1 * sumToB); |
307 | 349 | weightsFloatData.put(w2 * sumToB); |
@@ -310,6 +352,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { | ||
310 | 352 | } |
311 | 353 | weightsFloatData.rewind(); |
312 | 354 | |
355 | + actuallyHasWeights = false; | |
313 | 356 | weightsFloatData = null; |
314 | 357 | indicesData = null; |
315 | 358 |
@@ -327,24 +370,28 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { | ||
327 | 370 | // each vertex has |
328 | 371 | // - 4 bone weights |
329 | 372 | // - 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); | |
338 | 376 | |
339 | 377 | VertexBuffer weights = new VertexBuffer(Type.BoneWeight); |
340 | 378 | VertexBuffer indices = new VertexBuffer(Type.BoneIndex); |
341 | 379 | |
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 | + | |
346 | 383 | mesh.setBuffer(weights); |
347 | 384 | 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); | |
348 | 395 | } |
349 | 396 | |
350 | 397 | private void startVertexBuffer(Attributes attribs) throws SAXException { |
@@ -451,7 +498,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { | ||
451 | 498 | private void pushColor(Attributes attribs) throws SAXException { |
452 | 499 | FloatBuffer buf = (FloatBuffer) mesh.getBuffer(Type.Color).getData(); |
453 | 500 | String value = parseString(attribs.getValue("value")); |
454 | - String[] vals = value.split(" "); | |
501 | + String[] vals = value.split("\\s"); | |
455 | 502 | if (vals.length != 3 && vals.length != 4) { |
456 | 503 | throw new SAXException("Color value must contain 3 or 4 components"); |
457 | 504 | } |
@@ -471,21 +518,31 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { | ||
471 | 518 | |
472 | 519 | private void startLodFaceList(String submeshindex, String numfaces) { |
473 | 520 | int index = Integer.parseInt(submeshindex); |
521 | + mesh = geoms.get(index).getMesh(); | |
474 | 522 | int faceCount = Integer.parseInt(numfaces); |
475 | 523 | |
524 | + VertexBuffer originalIndexBuffer = mesh.getBuffer(Type.Index); | |
476 | 525 | 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 | + } | |
480 | 536 | |
481 | 537 | List<VertexBuffer> levels = lodLevels.get(index); |
482 | 538 | if (levels == null) { |
539 | + // Create the LOD levels list | |
483 | 540 | 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); | |
486 | 544 | lodLevels.put(index, levels); |
487 | 545 | } |
488 | - | |
489 | 546 | levels.add(vb); |
490 | 547 | } |
491 | 548 |
@@ -531,15 +588,27 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { | ||
531 | 588 | |
532 | 589 | weightsFloatData.put(i, w); |
533 | 590 | indicesData.put(i, bone); |
591 | + actuallyHasWeights = true; | |
534 | 592 | } |
535 | 593 | |
536 | 594 | 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 | + } | |
538 | 602 | } |
539 | 603 | |
540 | 604 | private void startSubmeshName(String indexStr, String nameStr) { |
541 | 605 | 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 | + } | |
543 | 612 | } |
544 | 613 | |
545 | 614 | @Override |
@@ -590,10 +659,15 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { | ||
590 | 659 | } else if (qName.equals("boneassignments")) { |
591 | 660 | startBoneAssigns(); |
592 | 661 | } 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 | + } | |
597 | 671 | } else if (qName.equals("sharedgeometry")) { |
598 | 672 | String count = attribs.getValue("vertexcount"); |
599 | 673 | if (count == null) { |
@@ -609,6 +683,9 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { | ||
609 | 683 | startSkeleton(attribs.getValue("name")); |
610 | 684 | } else if (qName.equals("submeshnames")) { |
611 | 685 | // ok |
686 | + // setting submeshNamesHack to true will make "submesh" tag be interpreted | |
687 | + // as a "submeshname" tag. | |
688 | + submeshNamesHack = true; | |
612 | 689 | } else if (qName.equals("submeshname")) { |
613 | 690 | startSubmeshName(attribs.getValue("index"), attribs.getValue("name")); |
614 | 691 | } else if (qName.equals("mesh")) { |
@@ -628,11 +705,14 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { | ||
628 | 705 | return; |
629 | 706 | } |
630 | 707 | |
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) { | |
632 | 712 | usesBigIndices = false; |
633 | 713 | geom = null; |
634 | 714 | mesh = null; |
635 | - } else if (qName.equals("submeshes")) { | |
715 | + } else if (qName.equals("submeshes") && !submeshNamesHack) { | |
636 | 716 | // IMPORTANT: restore sharedmesh, for use with shared boneweights |
637 | 717 | geom = null; |
638 | 718 | mesh = sharedMesh; |
@@ -653,9 +733,8 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { | ||
653 | 733 | } else if (qName.equals("geometry") |
654 | 734 | || qName.equals("sharedgeometry")) { |
655 | 735 | // 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(); | |
659 | 738 | if (data.position() != 0) { |
660 | 739 | data.flip(); |
661 | 740 | } |
@@ -675,6 +754,9 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { | ||
675 | 754 | endLevelOfDetail(); |
676 | 755 | } else if (qName.equals("boneassignments")) { |
677 | 756 | endBoneAssigns(); |
757 | + } else if (qName.equals("submeshnames")) { | |
758 | + // Restore default handling for "submesh" tag. | |
759 | + submeshNamesHack = false; | |
678 | 760 | } |
679 | 761 | } |
680 | 762 |
@@ -688,9 +770,12 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { | ||
688 | 770 | for (int i = 0; i < geoms.size(); i++) { |
689 | 771 | Geometry g = geoms.get(i); |
690 | 772 | 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); | |
693 | 777 | } |
778 | + | |
694 | 779 | model.attachChild(geoms.get(i)); |
695 | 780 | } |
696 | 781 |
@@ -699,45 +784,19 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { | ||
699 | 784 | if (animData != null) { |
700 | 785 | // This model uses animation |
701 | 786 | |
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 | - | |
709 | 787 | for (int i = 0; i < geoms.size(); i++) { |
710 | 788 | Geometry g = geoms.get(i); |
711 | 789 | 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 | + | |
741 | 800 | } |
742 | 801 | |
743 | 802 | // Put the animations in the AnimControl |
@@ -764,7 +823,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { | ||
764 | 823 | |
765 | 824 | public Object load(AssetInfo info) throws IOException { |
766 | 825 | try { |
767 | - AssetKey key = info.getKey(); | |
826 | + key = info.getKey(); | |
768 | 827 | meshName = key.getName(); |
769 | 828 | folderName = key.getFolder(); |
770 | 829 | String ext = key.getExtension(); |
@@ -774,36 +833,38 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { | ||
774 | 833 | } |
775 | 834 | assetManager = info.getManager(); |
776 | 835 | |
777 | - OgreMeshKey meshKey = null; | |
778 | 836 | 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; | |
780 | 840 | materialList = meshKey.getMaterialList(); |
781 | 841 | 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}); | |
797 | 850 | } |
798 | 851 | } |
799 | 852 | } 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"); | |
800 | 863 | try { |
801 | - materialList = (MaterialList) assetManager.loadAsset(new OgreMaterialKey(folderName + meshName + ".material")); | |
864 | + materialList = (MaterialList) assetManager.loadAsset(materialKey); | |
802 | 865 | } 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 | + } | |
807 | 868 | } |
808 | 869 | |
809 | 870 | // Added by larynx 25.06.2011 |
@@ -813,21 +874,21 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { | ||
813 | 874 | // checking with JmeSystem. |
814 | 875 | SAXParserFactory factory = SAXParserFactory.newInstance(); |
815 | 876 | factory.setNamespaceAware(true); |
816 | - | |
877 | + | |
817 | 878 | XMLReader xr = factory.newSAXParser().getXMLReader(); |
818 | 879 | xr.setContentHandler(this); |
819 | 880 | xr.setErrorHandler(this); |
820 | - | |
881 | + | |
821 | 882 | InputStreamReader r = null; |
822 | 883 | try { |
823 | 884 | r = new InputStreamReader(info.openStream()); |
824 | 885 | xr.parse(new InputSource(r)); |
825 | 886 | } finally { |
826 | - if (r != null){ | |
887 | + if (r != null) { | |
827 | 888 | r.close(); |
828 | 889 | } |
829 | 890 | } |
830 | - | |
891 | + | |
831 | 892 | return compileModel(); |
832 | 893 | } catch (SAXException ex) { |
833 | 894 | IOException ioEx = new IOException("Error while parsing Ogre3D mesh.xml"); |
@@ -1,5 +1,5 @@ | ||
1 | 1 | /* |
2 | - * Copyright (c) 2009-2010 jMonkeyEngine | |
2 | + * Copyright (c) 2009-2012 jMonkeyEngine | |
3 | 3 | * All rights reserved. |
4 | 4 | * |
5 | 5 | * Redistribution and use in source and binary forms, with or without |
@@ -29,12 +29,19 @@ | ||
29 | 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
30 | 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
31 | 31 | */ |
32 | - | |
33 | 32 | package com.jme3.scene.plugins.ogre; |
34 | 33 | |
35 | 34 | import com.jme3.asset.ModelKey; |
36 | 35 | import com.jme3.material.MaterialList; |
37 | 36 | |
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 | + */ | |
38 | 45 | public class OgreMeshKey extends ModelKey { |
39 | 46 | |
40 | 47 | private MaterialList materialList; |
@@ -58,10 +65,44 @@ public class OgreMeshKey extends ModelKey { | ||
58 | 65 | this.materialName = materialName; |
59 | 66 | } |
60 | 67 | |
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 | + | |
61 | 98 | public MaterialList getMaterialList() { |
62 | 99 | return materialList; |
63 | 100 | } |
64 | 101 | |
102 | + public void setMaterialList(MaterialList materialList){ | |
103 | + this.materialList = materialList; | |
104 | + } | |
105 | + | |
65 | 106 | public String getMaterialName() { |
66 | 107 | return materialName; |
67 | 108 | } |
@@ -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 | +} |
@@ -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 | +} |
@@ -1,5 +1,5 @@ | ||
1 | 1 | /* |
2 | - * Copyright (c) 2009-2010 jMonkeyEngine | |
2 | + * Copyright (c) 2009-2012 jMonkeyEngine | |
3 | 3 | * All rights reserved. |
4 | 4 | * |
5 | 5 | * Redistribution and use in source and binary forms, with or without |
@@ -31,6 +31,16 @@ | ||
31 | 31 | */ |
32 | 32 | package com.jme3.scene.plugins.ogre; |
33 | 33 | |
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; | |
34 | 44 | import java.io.IOException; |
35 | 45 | import java.io.InputStream; |
36 | 46 | import java.io.InputStreamReader; |
@@ -39,27 +49,14 @@ import java.util.HashMap; | ||
39 | 49 | import java.util.Map; |
40 | 50 | import java.util.Stack; |
41 | 51 | import java.util.logging.Logger; |
42 | - | |
43 | 52 | import javax.xml.parsers.ParserConfigurationException; |
44 | 53 | import javax.xml.parsers.SAXParserFactory; |
45 | - | |
46 | 54 | import org.xml.sax.Attributes; |
47 | 55 | import org.xml.sax.InputSource; |
48 | 56 | import org.xml.sax.SAXException; |
49 | 57 | import org.xml.sax.XMLReader; |
50 | 58 | import org.xml.sax.helpers.DefaultHandler; |
51 | 59 | |
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 | - | |
63 | 60 | public class SkeletonLoader extends DefaultHandler implements AssetLoader { |
64 | 61 | |
65 | 62 | private static final Logger logger = Logger.getLogger(SceneLoader.class.getName()); |
@@ -1,5 +1,5 @@ | ||
1 | 1 | /* |
2 | - * Copyright (c) 2009-2010 jMonkeyEngine | |
2 | + * Copyright (c) 2009-2012 jMonkeyEngine | |
3 | 3 | * All rights reserved. |
4 | 4 | * |
5 | 5 | * Redistribution and use in source and binary forms, with or without |
@@ -29,7 +29,6 @@ | ||
29 | 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
30 | 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
31 | 31 | */ |
32 | - | |
33 | 32 | package com.jme3.scene.plugins.ogre.matext; |
34 | 33 | |
35 | 34 | import java.util.HashMap; |
@@ -1,5 +1,5 @@ | ||
1 | 1 | /* |
2 | - * Copyright (c) 2009-2010 jMonkeyEngine | |
2 | + * Copyright (c) 2009-2012 jMonkeyEngine | |
3 | 3 | * All rights reserved. |
4 | 4 | * |
5 | 5 | * Redistribution and use in source and binary forms, with or without |
@@ -29,20 +29,22 @@ | ||
29 | 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
30 | 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
31 | 31 | */ |
32 | - | |
33 | 32 | package com.jme3.scene.plugins.ogre.matext; |
34 | 33 | |
34 | +import com.jme3.asset.AssetKey; | |
35 | 35 | import com.jme3.asset.AssetManager; |
36 | +import com.jme3.asset.AssetNotFoundException; | |
36 | 37 | import com.jme3.asset.TextureKey; |
37 | 38 | import com.jme3.material.Material; |
38 | 39 | import com.jme3.material.MaterialList; |
39 | 40 | import com.jme3.scene.plugins.ogre.MaterialLoader; |
40 | 41 | import com.jme3.texture.Texture; |
41 | 42 | import com.jme3.texture.Texture.WrapMode; |
43 | +import com.jme3.texture.Texture2D; | |
44 | +import com.jme3.util.PlaceholderAssets; | |
42 | 45 | import com.jme3.util.blockparser.Statement; |
43 | 46 | import java.io.IOException; |
44 | 47 | import java.util.List; |
45 | -import java.util.Scanner; | |
46 | 48 | import java.util.logging.Level; |
47 | 49 | import java.util.logging.Logger; |
48 | 50 |
@@ -53,6 +55,7 @@ public class MaterialExtensionLoader { | ||
53 | 55 | |
54 | 56 | private static final Logger logger = Logger.getLogger(MaterialExtensionLoader.class.getName()); |
55 | 57 | |
58 | + private AssetKey key; | |
56 | 59 | private AssetManager assetManager; |
57 | 60 | private MaterialList list; |
58 | 61 | private MaterialExtensionSet matExts; |
@@ -60,6 +63,7 @@ public class MaterialExtensionLoader { | ||
60 | 63 | private String matName; |
61 | 64 | private Material material; |
62 | 65 | |
66 | + | |
63 | 67 | private void readExtendingMaterialStatement(Statement statement) throws IOException { |
64 | 68 | if (statement.getLine().startsWith("set_texture_alias")){ |
65 | 69 | String[] split = statement.getLine().split(" ", 3); |
@@ -68,14 +72,21 @@ public class MaterialExtensionLoader { | ||
68 | 72 | |
69 | 73 | String jmeParamName = matExt.getTextureMapping(aliasName); |
70 | 74 | |
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 | + | |
79 | 90 | material.setTexture(jmeParamName, tex); |
80 | 91 | } |
81 | 92 | } |
@@ -100,10 +111,11 @@ public class MaterialExtensionLoader { | ||
100 | 111 | return material; |
101 | 112 | } |
102 | 113 | |
103 | - public MaterialList load(AssetManager assetManager, MaterialExtensionSet matExts, | |
114 | + public MaterialList load(AssetManager assetManager, AssetKey key, MaterialExtensionSet matExts, | |
104 | 115 | List<Statement> statements) throws IOException{ |
105 | 116 | this.assetManager = assetManager; |
106 | 117 | this.matExts = matExts; |
118 | + this.key = key; | |
107 | 119 | |
108 | 120 | list = new MaterialList(); |
109 | 121 |
@@ -1,5 +1,5 @@ | ||
1 | 1 | /* |
2 | - * Copyright (c) 2009-2010 jMonkeyEngine | |
2 | + * Copyright (c) 2009-2012 jMonkeyEngine | |
3 | 3 | * All rights reserved. |
4 | 4 | * |
5 | 5 | * Redistribution and use in source and binary forms, with or without |
@@ -29,7 +29,6 @@ | ||
29 | 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
30 | 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
31 | 31 | */ |
32 | - | |
33 | 32 | package com.jme3.scene.plugins.ogre.matext; |
34 | 33 | |
35 | 34 | import java.util.ArrayList; |
@@ -42,44 +41,46 @@ import java.util.List; | ||
42 | 41 | * {@link OgreMaterialKey}s used. |
43 | 42 | */ |
44 | 43 | 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>(); | |
47 | 46 | private HashMap<String, List<String>> nameMappings = new HashMap<String, List<String>>(); |
48 | 47 | |
49 | 48 | /** |
50 | 49 | * Adds a new material extension to the set of extensions. |
50 | + * | |
51 | 51 | * @param extension The {@link MaterialExtension} to add. |
52 | 52 | */ |
53 | - public void addMaterialExtension(MaterialExtension extension){ | |
53 | + public void addMaterialExtension(MaterialExtension extension) { | |
54 | 54 | extensions.put(extension.getBaseMaterialName(), extension); |
55 | 55 | } |
56 | 56 | |
57 | 57 | /** |
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. | |
60 | 60 | * |
61 | 61 | * @param baseMatName The ogre3D base material name. |
62 | 62 | * @return {@link MaterialExtension} that is set, or null if not set. |
63 | 63 | */ |
64 | - public MaterialExtension getMaterialExtension(String baseMatName){ | |
64 | + public MaterialExtension getMaterialExtension(String baseMatName) { | |
65 | 65 | return extensions.get(baseMatName); |
66 | 66 | } |
67 | - | |
67 | + | |
68 | 68 | /** |
69 | 69 | * Adds an alternative name for a material |
70 | + * | |
70 | 71 | * @param name The material name to be found in a .mesh.xml file |
71 | 72 | * @param alias The material name to be found in a .material file |
72 | 73 | */ |
73 | - public void setNameMapping(String name, String alias){ | |
74 | + public void setNameMapping(String name, String alias) { | |
74 | 75 | List<String> list = nameMappings.get(name); |
75 | - if(list==null){ | |
76 | + if (list == null) { | |
76 | 77 | list = new ArrayList<String>(); |
77 | 78 | nameMappings.put(name, list); |
78 | 79 | } |
79 | 80 | list.add(alias); |
80 | 81 | } |
81 | - | |
82 | - public List<String> getNameMappings(String name){ | |
82 | + | |
83 | + public List<String> getNameMappings(String name) { | |
83 | 84 | return nameMappings.get(name); |
84 | 85 | } |
85 | 86 | } |
@@ -1,5 +1,5 @@ | ||
1 | 1 | /* |
2 | - * Copyright (c) 2009-2010 jMonkeyEngine | |
2 | + * Copyright (c) 2009-2012 jMonkeyEngine | |
3 | 3 | * All rights reserved. |
4 | 4 | * |
5 | 5 | * Redistribution and use in source and binary forms, with or without |
@@ -29,43 +29,70 @@ | ||
29 | 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
30 | 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
31 | 31 | */ |
32 | - | |
33 | 32 | package com.jme3.scene.plugins.ogre.matext; |
34 | 33 | |
35 | 34 | import com.jme3.asset.AssetKey; |
36 | 35 | import com.jme3.material.MaterialList; |
37 | 36 | |
38 | 37 | /** |
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 | |
41 | 40 | */ |
42 | 41 | public class OgreMaterialKey extends AssetKey<MaterialList> { |
43 | 42 | |
44 | 43 | private MaterialExtensionSet matExts; |
45 | 44 | |
46 | - public OgreMaterialKey(String name){ | |
45 | + public OgreMaterialKey(String name) { | |
47 | 46 | super(name); |
48 | 47 | } |
49 | 48 | |
50 | - public OgreMaterialKey(){ | |
49 | + public OgreMaterialKey() { | |
51 | 50 | super(); |
52 | 51 | } |
53 | 52 | |
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 | + | |
54 | 79 | /** |
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. | |
58 | 83 | * |
59 | - * @param extension The {@link MaterialExtensionSet} to use | |
84 | + * @param matExts The {@link MaterialExtensionSet} to use | |
60 | 85 | */ |
61 | - public void setMaterialExtensionSet(MaterialExtensionSet matExts){ | |
86 | + public void setMaterialExtensionSet(MaterialExtensionSet matExts) { | |
62 | 87 | this.matExts = matExts; |
63 | 88 | } |
64 | 89 | |
65 | 90 | /** |
66 | 91 | * 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} | |
69 | 96 | */ |
70 | 97 | public MaterialExtensionSet getMaterialExtensionSet() { |
71 | 98 | return matExts; |