OSDN Git Service

merge from jme10694
[mikumikustudio/MikuMikuStudio.git] / engine / src / ogre / com / jme3 / scene / plugins / ogre / MeshLoader.java
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.animation.AnimControl;
35 import com.jme3.animation.Animation;
36 import com.jme3.animation.SkeletonControl;
37 import com.jme3.asset.*;
38 import com.jme3.material.Material;
39 import com.jme3.material.MaterialList;
40 import com.jme3.math.ColorRGBA;
41 import com.jme3.renderer.queue.RenderQueue.Bucket;
42 import com.jme3.scene.*;
43 import com.jme3.scene.VertexBuffer.Format;
44 import com.jme3.scene.VertexBuffer.Type;
45 import com.jme3.scene.VertexBuffer.Usage;
46 import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey;
47 import com.jme3.util.BufferUtils;
48 import com.jme3.util.IntMap;
49 import com.jme3.util.IntMap.Entry;
50 import com.jme3.util.PlaceholderAssets;
51 import static com.jme3.util.xml.SAXUtil.*;
52 import java.io.IOException;
53 import java.io.InputStreamReader;
54 import java.nio.*;
55 import java.util.ArrayList;
56 import java.util.HashMap;
57 import java.util.List;
58 import java.util.logging.Level;
59 import java.util.logging.Logger;
60 import javax.xml.parsers.ParserConfigurationException;
61 import javax.xml.parsers.SAXParserFactory;
62 import org.xml.sax.Attributes;
63 import org.xml.sax.InputSource;
64 import org.xml.sax.SAXException;
65 import org.xml.sax.XMLReader;
66 import org.xml.sax.helpers.DefaultHandler;
67
68 /**
69  * Loads Ogre3D mesh.xml files.
70  */
71 public class MeshLoader extends DefaultHandler implements AssetLoader {
72
73     private static final Logger logger = Logger.getLogger(MeshLoader.class.getName());
74     public static boolean AUTO_INTERLEAVE = true;
75     private static final Type[] TEXCOORD_TYPES =
76             new Type[]{
77         Type.TexCoord,
78         Type.TexCoord2,
79         Type.TexCoord3,
80         Type.TexCoord4,
81         Type.TexCoord5,
82         Type.TexCoord6,
83         Type.TexCoord7,
84         Type.TexCoord8,};
85     private AssetKey key;
86     private String meshName;
87     private String folderName;
88     private AssetManager assetManager;
89     private MaterialList materialList;
90     // Data per submesh/sharedgeom
91     private ShortBuffer sb;
92     private IntBuffer ib;
93     private FloatBuffer fb;
94     private VertexBuffer vb;
95     private Mesh mesh;
96     private Geometry geom;
97     private ByteBuffer indicesData;
98     private FloatBuffer weightsFloatData;
99     private boolean actuallyHasWeights = false;
100     private int vertCount;
101     private boolean usesSharedVerts;
102     private boolean usesBigIndices;
103     private boolean submeshNamesHack;
104     // Global data
105     private Mesh sharedMesh;
106     private int meshIndex = 0;
107     private int texCoordIndex = 0;
108     private String ignoreUntilEnd = null;
109     private List<Geometry> geoms = new ArrayList<Geometry>();
110     private ArrayList<Boolean> usesSharedMesh = new ArrayList<Boolean>();
111     private IntMap<List<VertexBuffer>> lodLevels = new IntMap<List<VertexBuffer>>();
112     private AnimData animData;
113
114     public MeshLoader() {
115         super();
116     }
117
118     @Override
119     public void startDocument() {
120         geoms.clear();
121         lodLevels.clear();
122
123         sb = null;
124         ib = null;
125         fb = null;
126         vb = null;
127         mesh = null;
128         geom = null;
129         sharedMesh = null;
130
131         usesSharedMesh.clear();
132         usesSharedVerts = false;
133         vertCount = 0;
134         meshIndex = 0;
135         texCoordIndex = 0;
136         ignoreUntilEnd = null;
137
138         animData = null;
139
140         actuallyHasWeights = false;
141         submeshNamesHack = false;
142         indicesData = null;
143         weightsFloatData = null;
144     }
145
146     @Override
147     public void endDocument() {
148     }
149
150     private void pushIndex(int index) {
151         if (ib != null) {
152             ib.put(index);
153         } else {
154             sb.put((short) index);
155         }
156     }
157
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         }
174     }
175
176 //    private boolean isUsingSharedVerts(Geometry geom) {
177     // Old code for buffer sharer
178     //return geom.getUserData(UserData.JME_SHAREDMESH) != null;
179 //    }
180     private void startFaces(String count) throws SAXException {
181         int numFaces = parseInt(count);
182         int indicesPerFace = 0;
183
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!");
196         }
197
198         int numIndices = indicesPerFace * numFaces;
199
200         vb = new VertexBuffer(VertexBuffer.Type.Index);
201         if (!usesBigIndices) {
202             sb = BufferUtils.createShortBuffer(numIndices);
203             ib = null;
204             vb.setupData(Usage.Static, indicesPerFace, Format.UnsignedShort, sb);
205         } else {
206             ib = BufferUtils.createIntBuffer(numIndices);
207             sb = null;
208             vb.setupData(Usage.Static, indicesPerFace, Format.UnsignedInt, ib);
209         }
210         mesh.setBuffer(vb);
211     }
212
213     private void applyMaterial(Geometry geom, String matName) {
214         Material mat = null;
215         if (matName.endsWith(".j3m")) {
216             // load as native jme3 material instance
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             }
225         } else {
226             if (materialList != null) {
227                 mat = materialList.get(matName);
228             }
229         }
230
231         if (mat == null) {
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));
235         }
236
237         if (mat.isTransparent()) {
238             geom.setQueueBucket(Bucket.Transparent);
239         }
240
241         geom.setMaterial(mat);
242     }
243
244     private void startSubMesh(String matName, String usesharedvertices, String use32bitIndices, String opType) throws SAXException {
245         mesh = new Mesh();
246         if (opType == null || opType.equals("triangle_list")) {
247             mesh.setMode(Mesh.Mode.Triangles);
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);
256         }
257
258         usesBigIndices = parseBool(use32bitIndices, false);
259         usesSharedVerts = parseBool(usesharedvertices, false);
260         if (usesSharedVerts) {
261             usesSharedMesh.add(true);
262
263             // Old code for buffer sharer
264             // import vertexbuffers from shared geom
265 //            IntMap<VertexBuffer> sharedBufs = sharedMesh.getBuffers();
266 //            for (Entry<VertexBuffer> entry : sharedBufs) {
267 //                mesh.setBuffer(entry.getValue());
268 //            }
269         } else {
270             usesSharedMesh.add(false);
271         }
272
273         if (meshName == null) {
274             geom = new Geometry("OgreSubmesh-" + (++meshIndex), mesh);
275         } else {
276             geom = new Geometry(meshName + "-geom-" + (++meshIndex), mesh);
277         }
278
279         if (usesSharedVerts) {
280             // Old code for buffer sharer
281             // this mesh is shared!
282             //geom.setUserData(UserData.JME_SHAREDMESH, sharedMesh);
283         }
284
285         applyMaterial(geom, matName);
286         geoms.add(geom);
287     }
288
289     private void startSharedGeom(String vertexcount) throws SAXException {
290         sharedMesh = new Mesh();
291         vertCount = parseInt(vertexcount);
292         usesSharedVerts = false;
293
294         geom = null;
295         mesh = sharedMesh;
296     }
297
298     private void startGeometry(String vertexcount) throws SAXException {
299         vertCount = parseInt(vertexcount);
300     }
301
302     /**
303      * Normalizes weights if needed and finds largest amount of weights used for
304      * all vertices in the buffer.
305      */
306     private void endBoneAssigns() {
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
320             return;
321         }
322
323         //int vertCount = mesh.getVertexCount();
324         int maxWeightsPerVert = 0;
325         weightsFloatData.rewind();
326         for (int v = 0; v < vertCount; v++) {
327             float w0 = weightsFloatData.get(),
328                     w1 = weightsFloatData.get(),
329                     w2 = weightsFloatData.get(),
330                     w3 = weightsFloatData.get();
331
332             if (w3 != 0) {
333                 maxWeightsPerVert = Math.max(maxWeightsPerVert, 4);
334             } else if (w2 != 0) {
335                 maxWeightsPerVert = Math.max(maxWeightsPerVert, 3);
336             } else if (w1 != 0) {
337                 maxWeightsPerVert = Math.max(maxWeightsPerVert, 2);
338             } else if (w0 != 0) {
339                 maxWeightsPerVert = Math.max(maxWeightsPerVert, 1);
340             }
341
342             float sum = w0 + w1 + w2 + w3;
343             if (sum != 1f) {
344                 weightsFloatData.position(weightsFloatData.position() - 4);
345                 // compute new vals based on sum
346                 float sumToB = sum == 0 ? 0 : 1f / sum;
347                 weightsFloatData.put(w0 * sumToB);
348                 weightsFloatData.put(w1 * sumToB);
349                 weightsFloatData.put(w2 * sumToB);
350                 weightsFloatData.put(w3 * sumToB);
351             }
352         }
353         weightsFloatData.rewind();
354
355         actuallyHasWeights = false;
356         weightsFloatData = null;
357         indicesData = null;
358
359         mesh.setMaxNumWeights(maxWeightsPerVert);
360     }
361
362     private void startBoneAssigns() {
363         if (mesh != sharedMesh && usesSharedVerts) {
364             // will use bone assignments from shared mesh (?)
365             return;
366         }
367
368         // current mesh will have bone assigns
369         //int vertCount = mesh.getVertexCount();
370         // each vertex has
371         // - 4 bone weights
372         // - 4 bone indices
373         // create array-backed buffers for software skinning for access speed
374         weightsFloatData = FloatBuffer.allocate(vertCount * 4);
375         indicesData = ByteBuffer.allocate(vertCount * 4);
376
377         VertexBuffer weights = new VertexBuffer(Type.BoneWeight);
378         VertexBuffer indices = new VertexBuffer(Type.BoneIndex);
379
380         weights.setupData(Usage.CpuOnly, 4, Format.Float, weightsFloatData);
381         indices.setupData(Usage.CpuOnly, 4, Format.UnsignedByte, indicesData);
382         
383         mesh.setBuffer(weights);
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);
395     }
396
397     private void startVertexBuffer(Attributes attribs) throws SAXException {
398         if (parseBool(attribs.getValue("positions"), false)) {
399             vb = new VertexBuffer(Type.Position);
400             fb = BufferUtils.createFloatBuffer(vertCount * 3);
401             vb.setupData(Usage.Static, 3, Format.Float, fb);
402             mesh.setBuffer(vb);
403         }
404         if (parseBool(attribs.getValue("normals"), false)) {
405             vb = new VertexBuffer(Type.Normal);
406             fb = BufferUtils.createFloatBuffer(vertCount * 3);
407             vb.setupData(Usage.Static, 3, Format.Float, fb);
408             mesh.setBuffer(vb);
409         }
410         if (parseBool(attribs.getValue("colours_diffuse"), false)) {
411             vb = new VertexBuffer(Type.Color);
412             fb = BufferUtils.createFloatBuffer(vertCount * 4);
413             vb.setupData(Usage.Static, 4, Format.Float, fb);
414             mesh.setBuffer(vb);
415         }
416         if (parseBool(attribs.getValue("tangents"), false)) {
417             int dimensions = parseInt(attribs.getValue("tangent_dimensions"), 3);
418             vb = new VertexBuffer(Type.Tangent);
419             fb = BufferUtils.createFloatBuffer(vertCount * dimensions);
420             vb.setupData(Usage.Static, dimensions, Format.Float, fb);
421             mesh.setBuffer(vb);
422         }
423         if (parseBool(attribs.getValue("binormals"), false)) {
424             vb = new VertexBuffer(Type.Binormal);
425             fb = BufferUtils.createFloatBuffer(vertCount * 3);
426             vb.setupData(Usage.Static, 3, Format.Float, fb);
427             mesh.setBuffer(vb);
428         }
429
430         int texCoords = parseInt(attribs.getValue("texture_coords"), 0);
431         for (int i = 0; i < texCoords; i++) {
432             int dims = parseInt(attribs.getValue("texture_coord_dimensions_" + i), 2);
433             if (dims < 1 || dims > 4) {
434                 throw new SAXException("Texture coord dimensions must be 1 <= dims <= 4");
435             }
436
437             if (i <= 7) {
438                 vb = new VertexBuffer(TEXCOORD_TYPES[i]);
439             } else {
440                 // more than 8 texture coordinates are not supported by ogre.
441                 throw new SAXException("More than 8 texture coordinates not supported");
442             }
443             fb = BufferUtils.createFloatBuffer(vertCount * dims);
444             vb.setupData(Usage.Static, dims, Format.Float, fb);
445             mesh.setBuffer(vb);
446         }
447     }
448
449     private void startVertex() {
450         texCoordIndex = 0;
451     }
452
453     private void pushAttrib(Type type, Attributes attribs) throws SAXException {
454         try {
455             FloatBuffer buf = (FloatBuffer) mesh.getBuffer(type).getData();
456             buf.put(parseFloat(attribs.getValue("x"))).put(parseFloat(attribs.getValue("y"))).put(parseFloat(attribs.getValue("z")));
457         } catch (Exception ex) {
458             throw new SAXException("Failed to push attrib", ex);
459         }
460     }
461
462     private void pushTangent(Attributes attribs) throws SAXException {
463         try {
464             VertexBuffer tangentBuf = mesh.getBuffer(Type.Tangent);
465             FloatBuffer buf = (FloatBuffer) tangentBuf.getData();
466             buf.put(parseFloat(attribs.getValue("x"))).put(parseFloat(attribs.getValue("y"))).put(parseFloat(attribs.getValue("z")));
467             if (tangentBuf.getNumComponents() == 4) {
468                 buf.put(parseFloat(attribs.getValue("w")));
469             }
470         } catch (Exception ex) {
471             throw new SAXException("Failed to push attrib", ex);
472         }
473     }
474
475     private void pushTexCoord(Attributes attribs) throws SAXException {
476         if (texCoordIndex >= 8) {
477             return; // More than 8 not supported by ogre.
478         }
479         Type type = TEXCOORD_TYPES[texCoordIndex];
480
481         VertexBuffer tcvb = mesh.getBuffer(type);
482         FloatBuffer buf = (FloatBuffer) tcvb.getData();
483
484         buf.put(parseFloat(attribs.getValue("u")));
485         if (tcvb.getNumComponents() >= 2) {
486             buf.put(parseFloat(attribs.getValue("v")));
487             if (tcvb.getNumComponents() >= 3) {
488                 buf.put(parseFloat(attribs.getValue("w")));
489                 if (tcvb.getNumComponents() == 4) {
490                     buf.put(parseFloat(attribs.getValue("x")));
491                 }
492             }
493         }
494
495         texCoordIndex++;
496     }
497
498     private void pushColor(Attributes attribs) throws SAXException {
499         FloatBuffer buf = (FloatBuffer) mesh.getBuffer(Type.Color).getData();
500         String value = parseString(attribs.getValue("value"));
501         String[] vals = value.split("\\s");
502         if (vals.length != 3 && vals.length != 4) {
503             throw new SAXException("Color value must contain 3 or 4 components");
504         }
505
506         ColorRGBA color = new ColorRGBA();
507         color.r = parseFloat(vals[0]);
508         color.g = parseFloat(vals[1]);
509         color.b = parseFloat(vals[2]);
510         if (vals.length == 3) {
511             color.a = 1f;
512         } else {
513             color.a = parseFloat(vals[3]);
514         }
515
516         buf.put(color.r).put(color.g).put(color.b).put(color.a);
517     }
518
519     private void startLodFaceList(String submeshindex, String numfaces) {
520         int index = Integer.parseInt(submeshindex);
521         mesh = geoms.get(index).getMesh();
522         int faceCount = Integer.parseInt(numfaces);
523
524         VertexBuffer originalIndexBuffer = mesh.getBuffer(Type.Index);
525         vb = new VertexBuffer(VertexBuffer.Type.Index);
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         }
536
537         List<VertexBuffer> levels = lodLevels.get(index);
538         if (levels == null) {
539             // Create the LOD levels list
540             levels = new ArrayList<VertexBuffer>();
541
542             // Add the first LOD level (always the original index buffer)
543             levels.add(originalIndexBuffer);
544             lodLevels.put(index, levels);
545         }
546         levels.add(vb);
547     }
548
549     private void startLevelOfDetail(String numlevels) {
550 //        numLevels = Integer.parseInt(numlevels);
551     }
552
553     private void endLevelOfDetail() {
554         // set the lod data for each mesh
555         for (Entry<List<VertexBuffer>> entry : lodLevels) {
556             Mesh m = geoms.get(entry.getKey()).getMesh();
557             List<VertexBuffer> levels = entry.getValue();
558             VertexBuffer[] levelArray = new VertexBuffer[levels.size()];
559             levels.toArray(levelArray);
560             m.setLodLevels(levelArray);
561         }
562     }
563
564     private void startLodGenerated(String depthsqr) {
565     }
566
567     private void pushBoneAssign(String vertIndex, String boneIndex, String weight) throws SAXException {
568         int vert = parseInt(vertIndex);
569         float w = parseFloat(weight);
570         byte bone = (byte) parseInt(boneIndex);
571
572         assert bone >= 0;
573         assert vert >= 0 && vert < mesh.getVertexCount();
574
575         int i;
576         float v = 0;
577         // see which weights are unused for a given bone
578         for (i = vert * 4; i < vert * 4 + 4; i++) {
579             v = weightsFloatData.get(i);
580             if (v == 0) {
581                 break;
582             }
583         }
584         if (v != 0) {
585             logger.log(Level.WARNING, "Vertex {0} has more than 4 weights per vertex! Ignoring..", vert);
586             return;
587         }
588
589         weightsFloatData.put(i, w);
590         indicesData.put(i, bone);
591         actuallyHasWeights = true;
592     }
593
594     private void startSkeleton(String name) {
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         }
602     }
603
604     private void startSubmeshName(String indexStr, String nameStr) {
605         int index = Integer.parseInt(indexStr);
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         }
612     }
613
614     @Override
615     public void startElement(String uri, String localName, String qName, Attributes attribs) throws SAXException {
616         if (ignoreUntilEnd != null) {
617             return;
618         }
619
620         if (qName.equals("texcoord")) {
621             pushTexCoord(attribs);
622         } else if (qName.equals("vertexboneassignment")) {
623             pushBoneAssign(attribs.getValue("vertexindex"),
624                     attribs.getValue("boneindex"),
625                     attribs.getValue("weight"));
626         } else if (qName.equals("face")) {
627             pushFace(attribs.getValue("v1"),
628                     attribs.getValue("v2"),
629                     attribs.getValue("v3"));
630         } else if (qName.equals("position")) {
631             pushAttrib(Type.Position, attribs);
632         } else if (qName.equals("normal")) {
633             pushAttrib(Type.Normal, attribs);
634         } else if (qName.equals("tangent")) {
635             pushTangent(attribs);
636         } else if (qName.equals("binormal")) {
637             pushAttrib(Type.Binormal, attribs);
638         } else if (qName.equals("colour_diffuse")) {
639             pushColor(attribs);
640         } else if (qName.equals("vertex")) {
641             startVertex();
642         } else if (qName.equals("faces")) {
643             startFaces(attribs.getValue("count"));
644         } else if (qName.equals("geometry")) {
645             String count = attribs.getValue("vertexcount");
646             if (count == null) {
647                 count = attribs.getValue("count");
648             }
649             startGeometry(count);
650         } else if (qName.equals("vertexbuffer")) {
651             startVertexBuffer(attribs);
652         } else if (qName.equals("lodfacelist")) {
653             startLodFaceList(attribs.getValue("submeshindex"),
654                     attribs.getValue("numfaces"));
655         } else if (qName.equals("lodgenerated")) {
656             startLodGenerated(attribs.getValue("fromdepthsquared"));
657         } else if (qName.equals("levelofdetail")) {
658             startLevelOfDetail(attribs.getValue("numlevels"));
659         } else if (qName.equals("boneassignments")) {
660             startBoneAssigns();
661         } else if (qName.equals("submesh")) {
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             }
671         } else if (qName.equals("sharedgeometry")) {
672             String count = attribs.getValue("vertexcount");
673             if (count == null) {
674                 count = attribs.getValue("count");
675             }
676
677             if (count != null && !count.equals("0")) {
678                 startSharedGeom(count);
679             }
680         } else if (qName.equals("submeshes")) {
681             // ok
682         } else if (qName.equals("skeletonlink")) {
683             startSkeleton(attribs.getValue("name"));
684         } else if (qName.equals("submeshnames")) {
685             // ok
686             // setting submeshNamesHack to true will make "submesh" tag be interpreted
687             // as a "submeshname" tag.
688             submeshNamesHack = true;
689         } else if (qName.equals("submeshname")) {
690             startSubmeshName(attribs.getValue("index"), attribs.getValue("name"));
691         } else if (qName.equals("mesh")) {
692             // ok
693         } else {
694             logger.log(Level.WARNING, "Unknown tag: {0}. Ignoring.", qName);
695             ignoreUntilEnd = qName;
696         }
697     }
698
699     @Override
700     public void endElement(String uri, String name, String qName) {
701         if (ignoreUntilEnd != null) {
702             if (ignoreUntilEnd.equals(qName)) {
703                 ignoreUntilEnd = null;
704             }
705             return;
706         }
707
708
709         // If submesh hack is enabled, ignore any submesh/submeshes
710         // end tags.
711         if (qName.equals("submesh") && !submeshNamesHack) {
712             usesBigIndices = false;
713             geom = null;
714             mesh = null;
715         } else if (qName.equals("submeshes") && !submeshNamesHack) {
716             // IMPORTANT: restore sharedmesh, for use with shared boneweights
717             geom = null;
718             mesh = sharedMesh;
719             usesSharedVerts = false;
720         } else if (qName.equals("faces")) {
721             if (ib != null) {
722                 ib.flip();
723             } else {
724                 sb.flip();
725             }
726
727             vb = null;
728             ib = null;
729             sb = null;
730         } else if (qName.equals("vertexbuffer")) {
731             fb = null;
732             vb = null;
733         } else if (qName.equals("geometry")
734                 || qName.equals("sharedgeometry")) {
735             // finish writing to buffers
736             for (VertexBuffer buf : mesh.getBufferList().getArray()) {
737                 Buffer data = buf.getData();
738                 if (data.position() != 0) {
739                     data.flip();
740                 }
741             }
742             mesh.updateBound();
743             mesh.setStatic();
744
745             if (qName.equals("sharedgeometry")) {
746                 geom = null;
747                 mesh = null;
748             }
749         } else if (qName.equals("lodfacelist")) {
750             sb.flip();
751             vb = null;
752             sb = null;
753         } else if (qName.equals("levelofdetail")) {
754             endLevelOfDetail();
755         } else if (qName.equals("boneassignments")) {
756             endBoneAssigns();
757         } else if (qName.equals("submeshnames")) {
758             // Restore default handling for "submesh" tag.
759             submeshNamesHack = false;
760         }
761     }
762
763     @Override
764     public void characters(char ch[], int start, int length) {
765     }
766
767     private Node compileModel() {
768         Node model = new Node(meshName + "-ogremesh");
769
770         for (int i = 0; i < geoms.size(); i++) {
771             Geometry g = geoms.get(i);
772             Mesh m = g.getMesh();
773
774             // New code for buffer extract
775             if (sharedMesh != null && usesSharedMesh.get(i)) {
776                 m.extractVertexData(sharedMesh);
777             }
778
779             model.attachChild(geoms.get(i));
780         }
781
782         // Do not attach shared geometry to the node!
783
784         if (animData != null) {
785             // This model uses animation
786
787             for (int i = 0; i < geoms.size(); i++) {
788                 Geometry g = geoms.get(i);
789                 Mesh m = geoms.get(i).getMesh();
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
800             }
801
802             // Put the animations in the AnimControl
803             HashMap<String, Animation> anims = new HashMap<String, Animation>();
804             ArrayList<Animation> animList = animData.anims;
805             for (int i = 0; i < animList.size(); i++) {
806                 Animation anim = animList.get(i);
807                 anims.put(anim.getName(), anim);
808             }
809
810             AnimControl ctrl = new AnimControl(animData.skeleton);
811             ctrl.setAnimations(anims);
812             model.addControl(ctrl);
813
814             // Put the skeleton in the skeleton control
815             SkeletonControl skeletonControl = new SkeletonControl(animData.skeleton);
816
817             // This will acquire the targets from the node
818             model.addControl(skeletonControl);
819         }
820
821         return model;
822     }
823
824     public Object load(AssetInfo info) throws IOException {
825         try {
826             key = info.getKey();
827             meshName = key.getName();
828             folderName = key.getFolder();
829             String ext = key.getExtension();
830             meshName = meshName.substring(0, meshName.length() - ext.length() - 1);
831             if (folderName != null && folderName.length() > 0) {
832                 meshName = meshName.substring(folderName.length());
833             }
834             assetManager = info.getManager();
835
836             if (key instanceof OgreMeshKey) {
837                 // OgreMeshKey is being used, try getting the material list
838                 // from it
839                 OgreMeshKey meshKey = (OgreMeshKey) key;
840                 materialList = meshKey.getMaterialList();
841                 String materialName = meshKey.getMaterialName();
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});
850                     }
851                 }
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");
863                 try {
864                     materialList = (MaterialList) assetManager.loadAsset(materialKey);
865                 } catch (AssetNotFoundException e) {
866                     logger.log(Level.WARNING, "Cannot locate {0} for model {1}", new Object[]{materialKey, key});
867                 }
868             }
869
870             // Added by larynx 25.06.2011
871             // Android needs the namespace aware flag set to true                 
872             // Kirill 30.06.2011
873             // Now, hack is applied for both desktop and android to avoid
874             // checking with JmeSystem.
875             SAXParserFactory factory = SAXParserFactory.newInstance();
876             factory.setNamespaceAware(true);
877
878             XMLReader xr = factory.newSAXParser().getXMLReader();
879             xr.setContentHandler(this);
880             xr.setErrorHandler(this);
881
882             InputStreamReader r = null;
883             try {
884                 r = new InputStreamReader(info.openStream());
885                 xr.parse(new InputSource(r));
886             } finally {
887                 if (r != null) {
888                     r.close();
889                 }
890             }
891
892             return compileModel();
893         } catch (SAXException ex) {
894             IOException ioEx = new IOException("Error while parsing Ogre3D mesh.xml");
895             ioEx.initCause(ex);
896             throw ioEx;
897         } catch (ParserConfigurationException ex) {
898             IOException ioEx = new IOException("Error while parsing Ogre3D mesh.xml");
899             ioEx.initCause(ex);
900             throw ioEx;
901         }
902
903     }
904 }