2 * Copyright (c) 2009-2010 jMonkeyEngine
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
32 package com.jme3.scene.plugins.ogre;
34 import com.jme3.animation.Animation;
35 import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey;
36 import com.jme3.animation.AnimControl;
37 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;
43 import com.jme3.material.Material;
44 import com.jme3.material.MaterialList;
45 import com.jme3.math.ColorRGBA;
46 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;
52 import com.jme3.scene.VertexBuffer.Format;
53 import com.jme3.scene.VertexBuffer.Type;
54 import com.jme3.scene.VertexBuffer.Usage;
55 import com.jme3.util.BufferUtils;
56 import com.jme3.util.IntMap;
57 import com.jme3.util.IntMap.Entry;
58 import java.io.IOException;
59 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;
65 import java.util.ArrayList;
66 import java.util.HashMap;
67 import java.util.List;
68 import java.util.logging.Level;
69 import java.util.logging.Logger;
71 import javax.xml.parsers.ParserConfigurationException;
72 import javax.xml.parsers.SAXParserFactory;
74 import org.xml.sax.Attributes;
75 import org.xml.sax.InputSource;
76 import org.xml.sax.SAXException;
77 import org.xml.sax.XMLReader;
78 import org.xml.sax.helpers.DefaultHandler;
80 import static com.jme3.util.xml.SAXUtil.*;
83 * Loads Ogre3D mesh.xml files.
85 public class MeshLoader extends DefaultHandler implements AssetLoader {
87 private static final Logger logger = Logger.getLogger(MeshLoader.class.getName());
88 public static boolean AUTO_INTERLEAVE = true;
89 public static boolean HARDWARE_SKINNING = false;
90 private static final Type[] TEXCOORD_TYPES =
100 private String meshName;
101 private String folderName;
102 private AssetManager assetManager;
103 private MaterialList materialList;
104 // Data per submesh/sharedgeom
105 private ShortBuffer sb;
106 private IntBuffer ib;
107 private FloatBuffer fb;
108 private VertexBuffer vb;
110 private Geometry geom;
111 private ByteBuffer indicesData;
112 private FloatBuffer weightsFloatData;
113 private int vertCount;
114 private boolean usesSharedVerts;
115 private boolean usesBigIndices;
117 private Mesh sharedMesh;
118 private int meshIndex = 0;
119 private int texCoordIndex = 0;
120 private String ignoreUntilEnd = null;
121 private List<Geometry> geoms = new ArrayList<Geometry>();
122 private IntMap<List<VertexBuffer>> lodLevels = new IntMap<List<VertexBuffer>>();
123 private AnimData animData;
125 public MeshLoader() {
130 public void startDocument() {
142 usesSharedVerts = false;
146 ignoreUntilEnd = null;
151 weightsFloatData = null;
155 public void endDocument() {
158 private void pushFace(String v1, String v2, String v3) throws SAXException {
159 int i1 = parseInt(v1);
161 // TODO: fan/strip support
162 int i2 = parseInt(v2);
163 int i3 = parseInt(v3);
165 ib.put(i1).put(i2).put(i3);
167 sb.put((short) i1).put((short) i2).put((short) i3);
171 private boolean isUsingSharedVerts(Geometry geom) {
172 return geom.getUserData(UserData.JME_SHAREDMESH) != null;
175 private void startFaces(String count) throws SAXException {
176 int numFaces = parseInt(count);
179 if (mesh.getMode() == Mesh.Mode.Triangles) {
180 numIndices = numFaces * 3;
182 throw new SAXException("Triangle strip or fan not supported!");
185 vb = new VertexBuffer(VertexBuffer.Type.Index);
186 if (!usesBigIndices) {
187 sb = BufferUtils.createShortBuffer(numIndices);
189 vb.setupData(Usage.Static, 3, Format.UnsignedShort, sb);
191 ib = BufferUtils.createIntBuffer(numIndices);
193 vb.setupData(Usage.Static, 3, Format.UnsignedInt, ib);
198 private void applyMaterial(Geometry geom, String matName) {
200 if (matName.endsWith(".j3m")) {
201 // load as native jme3 material instance
202 mat = assetManager.loadMaterial(matName);
204 if (materialList != null) {
205 mat = materialList.get(matName);
208 logger.log(Level.WARNING, "Material {0} not found. Applying default material", matName);
209 mat = (Material) assetManager.loadMaterial("Common/Materials/RedColor.j3m");
214 throw new RuntimeException("Cannot locate material named " + matName);
217 if (mat.isTransparent()) {
218 geom.setQueueBucket(Bucket.Transparent);
221 geom.setMaterial(mat);
224 private void startSubMesh(String matName, String usesharedvertices, String use32bitIndices, String opType) throws SAXException {
226 if (opType == null || opType.equals("triangle_list")) {
227 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);
234 usesBigIndices = parseBool(use32bitIndices, false);
235 usesSharedVerts = parseBool(usesharedvertices, false);
236 if (usesSharedVerts) {
237 // import vertexbuffers from shared geom
238 IntMap<VertexBuffer> sharedBufs = sharedMesh.getBuffers();
239 for (Entry<VertexBuffer> entry : sharedBufs) {
240 mesh.setBuffer(entry.getValue());
244 if (meshName == null) {
245 geom = new Geometry("OgreSubmesh-" + (++meshIndex), mesh);
247 geom = new Geometry(meshName + "-geom-" + (++meshIndex), mesh);
250 if (usesSharedVerts) {
251 // this mesh is shared!
252 geom.setUserData(UserData.JME_SHAREDMESH, sharedMesh);
255 applyMaterial(geom, matName);
259 private void startSharedGeom(String vertexcount) throws SAXException {
260 sharedMesh = new Mesh();
261 vertCount = parseInt(vertexcount);
262 usesSharedVerts = false;
268 private void startGeometry(String vertexcount) throws SAXException {
269 vertCount = parseInt(vertexcount);
273 * Normalizes weights if needed and finds largest amount of weights used
274 * for all vertices in the buffer.
276 private void endBoneAssigns() {
277 if (mesh != sharedMesh && isUsingSharedVerts(geom)) {
281 //int vertCount = mesh.getVertexCount();
282 int maxWeightsPerVert = 0;
283 weightsFloatData.rewind();
284 for (int v = 0; v < vertCount; v++) {
285 float w0 = weightsFloatData.get(),
286 w1 = weightsFloatData.get(),
287 w2 = weightsFloatData.get(),
288 w3 = weightsFloatData.get();
291 maxWeightsPerVert = Math.max(maxWeightsPerVert, 4);
292 } else if (w2 != 0) {
293 maxWeightsPerVert = Math.max(maxWeightsPerVert, 3);
294 } else if (w1 != 0) {
295 maxWeightsPerVert = Math.max(maxWeightsPerVert, 2);
296 } else if (w0 != 0) {
297 maxWeightsPerVert = Math.max(maxWeightsPerVert, 1);
300 float sum = w0 + w1 + w2 + w3;
302 weightsFloatData.position(weightsFloatData.position() - 4);
303 // compute new vals based on sum
304 float sumToB = 1f / sum;
305 weightsFloatData.put(w0 * sumToB);
306 weightsFloatData.put(w1 * sumToB);
307 weightsFloatData.put(w2 * sumToB);
308 weightsFloatData.put(w3 * sumToB);
311 weightsFloatData.rewind();
313 weightsFloatData = null;
316 mesh.setMaxNumWeights(maxWeightsPerVert);
319 private void startBoneAssigns() {
320 if (mesh != sharedMesh && usesSharedVerts) {
321 // will use bone assignments from shared mesh (?)
325 // current mesh will have bone assigns
326 //int vertCount = mesh.getVertexCount();
330 if (HARDWARE_SKINNING) {
331 weightsFloatData = BufferUtils.createFloatBuffer(vertCount * 4);
332 indicesData = BufferUtils.createByteBuffer(vertCount * 4);
334 // create array-backed buffers if software skinning for access speed
335 weightsFloatData = FloatBuffer.allocate(vertCount * 4);
336 indicesData = ByteBuffer.allocate(vertCount * 4);
339 VertexBuffer weights = new VertexBuffer(Type.BoneWeight);
340 VertexBuffer indices = new VertexBuffer(Type.BoneIndex);
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);
346 mesh.setBuffer(weights);
347 mesh.setBuffer(indices);
350 private void startVertexBuffer(Attributes attribs) throws SAXException {
351 if (parseBool(attribs.getValue("positions"), false)) {
352 vb = new VertexBuffer(Type.Position);
353 fb = BufferUtils.createFloatBuffer(vertCount * 3);
354 vb.setupData(Usage.Static, 3, Format.Float, fb);
357 if (parseBool(attribs.getValue("normals"), false)) {
358 vb = new VertexBuffer(Type.Normal);
359 fb = BufferUtils.createFloatBuffer(vertCount * 3);
360 vb.setupData(Usage.Static, 3, Format.Float, fb);
363 if (parseBool(attribs.getValue("colours_diffuse"), false)) {
364 vb = new VertexBuffer(Type.Color);
365 fb = BufferUtils.createFloatBuffer(vertCount * 4);
366 vb.setupData(Usage.Static, 4, Format.Float, fb);
369 if (parseBool(attribs.getValue("tangents"), false)) {
370 int dimensions = parseInt(attribs.getValue("tangent_dimensions"), 3);
371 vb = new VertexBuffer(Type.Tangent);
372 fb = BufferUtils.createFloatBuffer(vertCount * dimensions);
373 vb.setupData(Usage.Static, dimensions, Format.Float, fb);
376 if (parseBool(attribs.getValue("binormals"), false)) {
377 vb = new VertexBuffer(Type.Binormal);
378 fb = BufferUtils.createFloatBuffer(vertCount * 3);
379 vb.setupData(Usage.Static, 3, Format.Float, fb);
383 int texCoords = parseInt(attribs.getValue("texture_coords"), 0);
384 for (int i = 0; i < texCoords; i++) {
385 int dims = parseInt(attribs.getValue("texture_coord_dimensions_" + i), 2);
386 if (dims < 1 || dims > 4) {
387 throw new SAXException("Texture coord dimensions must be 1 <= dims <= 4");
391 vb = new VertexBuffer(TEXCOORD_TYPES[i]);
393 // more than 8 texture coordinates are not supported by ogre.
394 throw new SAXException("More than 8 texture coordinates not supported");
396 fb = BufferUtils.createFloatBuffer(vertCount * dims);
397 vb.setupData(Usage.Static, dims, Format.Float, fb);
402 private void startVertex() {
406 private void pushAttrib(Type type, Attributes attribs) throws SAXException {
408 FloatBuffer buf = (FloatBuffer) mesh.getBuffer(type).getData();
409 buf.put(parseFloat(attribs.getValue("x"))).put(parseFloat(attribs.getValue("y"))).put(parseFloat(attribs.getValue("z")));
410 } catch (Exception ex) {
411 throw new SAXException("Failed to push attrib", ex);
415 private void pushTangent(Attributes attribs) throws SAXException {
417 VertexBuffer tangentBuf = mesh.getBuffer(Type.Tangent);
418 FloatBuffer buf = (FloatBuffer) tangentBuf.getData();
419 buf.put(parseFloat(attribs.getValue("x"))).put(parseFloat(attribs.getValue("y"))).put(parseFloat(attribs.getValue("z")));
420 if (tangentBuf.getNumComponents() == 4) {
421 buf.put(parseFloat(attribs.getValue("w")));
423 } catch (Exception ex) {
424 throw new SAXException("Failed to push attrib", ex);
428 private void pushTexCoord(Attributes attribs) throws SAXException {
429 if (texCoordIndex >= 8) {
430 return; // More than 8 not supported by ogre.
432 Type type = TEXCOORD_TYPES[texCoordIndex];
434 VertexBuffer tcvb = mesh.getBuffer(type);
435 FloatBuffer buf = (FloatBuffer) tcvb.getData();
437 buf.put(parseFloat(attribs.getValue("u")));
438 if (tcvb.getNumComponents() >= 2) {
439 buf.put(parseFloat(attribs.getValue("v")));
440 if (tcvb.getNumComponents() >= 3) {
441 buf.put(parseFloat(attribs.getValue("w")));
442 if (tcvb.getNumComponents() == 4) {
443 buf.put(parseFloat(attribs.getValue("x")));
451 private void pushColor(Attributes attribs) throws SAXException {
452 FloatBuffer buf = (FloatBuffer) mesh.getBuffer(Type.Color).getData();
453 String value = parseString(attribs.getValue("value"));
454 String[] vals = value.split(" ");
455 if (vals.length != 3 && vals.length != 4) {
456 throw new SAXException("Color value must contain 3 or 4 components");
459 ColorRGBA color = new ColorRGBA();
460 color.r = parseFloat(vals[0]);
461 color.g = parseFloat(vals[1]);
462 color.b = parseFloat(vals[2]);
463 if (vals.length == 3) {
466 color.a = parseFloat(vals[3]);
469 buf.put(color.r).put(color.g).put(color.b).put(color.a);
472 private void startLodFaceList(String submeshindex, String numfaces) {
473 int index = Integer.parseInt(submeshindex);
474 int faceCount = Integer.parseInt(numfaces);
476 vb = new VertexBuffer(VertexBuffer.Type.Index);
477 sb = BufferUtils.createShortBuffer(faceCount * 3);
479 vb.setupData(Usage.Static, 3, Format.UnsignedShort, sb);
481 List<VertexBuffer> levels = lodLevels.get(index);
482 if (levels == null) {
483 levels = new ArrayList<VertexBuffer>();
484 Mesh submesh = geoms.get(index).getMesh();
485 levels.add(submesh.getBuffer(Type.Index));
486 lodLevels.put(index, levels);
492 private void startLevelOfDetail(String numlevels) {
493 // numLevels = Integer.parseInt(numlevels);
496 private void endLevelOfDetail() {
497 // set the lod data for each mesh
498 for (Entry<List<VertexBuffer>> entry : lodLevels) {
499 Mesh m = geoms.get(entry.getKey()).getMesh();
500 List<VertexBuffer> levels = entry.getValue();
501 VertexBuffer[] levelArray = new VertexBuffer[levels.size()];
502 levels.toArray(levelArray);
503 m.setLodLevels(levelArray);
507 private void startLodGenerated(String depthsqr) {
510 private void pushBoneAssign(String vertIndex, String boneIndex, String weight) throws SAXException {
511 int vert = parseInt(vertIndex);
512 float w = parseFloat(weight);
513 byte bone = (byte) parseInt(boneIndex);
516 assert vert >= 0 && vert < mesh.getVertexCount();
520 // see which weights are unused for a given bone
521 for (i = vert * 4; i < vert * 4 + 4; i++) {
522 v = weightsFloatData.get(i);
528 logger.log(Level.WARNING, "Vertex {0} has more than 4 weights per vertex! Ignoring..", vert);
532 weightsFloatData.put(i, w);
533 indicesData.put(i, bone);
536 private void startSkeleton(String name) {
537 animData = (AnimData) assetManager.loadAsset(folderName + name + ".xml");
540 private void startSubmeshName(String indexStr, String nameStr) {
541 int index = Integer.parseInt(indexStr);
542 geoms.get(index).setName(nameStr);
546 public void startElement(String uri, String localName, String qName, Attributes attribs) throws SAXException {
547 if (ignoreUntilEnd != null) {
551 if (qName.equals("texcoord")) {
552 pushTexCoord(attribs);
553 } else if (qName.equals("vertexboneassignment")) {
554 pushBoneAssign(attribs.getValue("vertexindex"),
555 attribs.getValue("boneindex"),
556 attribs.getValue("weight"));
557 } else if (qName.equals("face")) {
558 pushFace(attribs.getValue("v1"),
559 attribs.getValue("v2"),
560 attribs.getValue("v3"));
561 } else if (qName.equals("position")) {
562 pushAttrib(Type.Position, attribs);
563 } else if (qName.equals("normal")) {
564 pushAttrib(Type.Normal, attribs);
565 } else if (qName.equals("tangent")) {
566 pushTangent(attribs);
567 } else if (qName.equals("binormal")) {
568 pushAttrib(Type.Binormal, attribs);
569 } else if (qName.equals("colour_diffuse")) {
571 } else if (qName.equals("vertex")) {
573 } else if (qName.equals("faces")) {
574 startFaces(attribs.getValue("count"));
575 } else if (qName.equals("geometry")) {
576 String count = attribs.getValue("vertexcount");
578 count = attribs.getValue("count");
580 startGeometry(count);
581 } else if (qName.equals("vertexbuffer")) {
582 startVertexBuffer(attribs);
583 } else if (qName.equals("lodfacelist")) {
584 startLodFaceList(attribs.getValue("submeshindex"),
585 attribs.getValue("numfaces"));
586 } else if (qName.equals("lodgenerated")) {
587 startLodGenerated(attribs.getValue("fromdepthsquared"));
588 } else if (qName.equals("levelofdetail")) {
589 startLevelOfDetail(attribs.getValue("numlevels"));
590 } else if (qName.equals("boneassignments")) {
592 } else if (qName.equals("submesh")) {
593 startSubMesh(attribs.getValue("material"),
594 attribs.getValue("usesharedvertices"),
595 attribs.getValue("use32bitindexes"),
596 attribs.getValue("operationtype"));
597 } else if (qName.equals("sharedgeometry")) {
598 String count = attribs.getValue("vertexcount");
600 count = attribs.getValue("count");
603 if (count != null && !count.equals("0")) {
604 startSharedGeom(count);
606 } else if (qName.equals("submeshes")) {
608 } else if (qName.equals("skeletonlink")) {
609 startSkeleton(attribs.getValue("name"));
610 } else if (qName.equals("submeshnames")) {
612 } else if (qName.equals("submeshname")) {
613 startSubmeshName(attribs.getValue("index"), attribs.getValue("name"));
614 } else if (qName.equals("mesh")) {
617 logger.log(Level.WARNING, "Unknown tag: {0}. Ignoring.", qName);
618 ignoreUntilEnd = qName;
623 public void endElement(String uri, String name, String qName) {
624 if (ignoreUntilEnd != null) {
625 if (ignoreUntilEnd.equals(qName)) {
626 ignoreUntilEnd = null;
631 if (qName.equals("submesh")) {
632 usesBigIndices = false;
635 } else if (qName.equals("submeshes")) {
636 // IMPORTANT: restore sharedmesh, for use with shared boneweights
639 usesSharedVerts = false;
640 } else if (qName.equals("faces")) {
650 } else if (qName.equals("vertexbuffer")) {
653 } else if (qName.equals("geometry")
654 || qName.equals("sharedgeometry")) {
655 // finish writing to buffers
656 IntMap<VertexBuffer> bufs = mesh.getBuffers();
657 for (Entry<VertexBuffer> entry : bufs) {
658 Buffer data = entry.getValue().getData();
659 if (data.position() != 0) {
666 if (qName.equals("sharedgeometry")) {
670 } else if (qName.equals("lodfacelist")) {
674 } else if (qName.equals("levelofdetail")) {
676 } else if (qName.equals("boneassignments")) {
682 public void characters(char ch[], int start, int length) {
685 private Node compileModel() {
686 Node model = new Node(meshName + "-ogremesh");
688 for (int i = 0; i < geoms.size(); i++) {
689 Geometry g = geoms.get(i);
690 Mesh m = g.getMesh();
691 if (sharedMesh != null && isUsingSharedVerts(g)) {
692 m.setBound(sharedMesh.getBound().clone());
694 model.attachChild(geoms.get(i));
697 // Do not attach shared geometry to the node!
699 if (animData != null) {
700 // This model uses animation
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);
709 for (int i = 0; i < geoms.size(); i++) {
710 Geometry g = geoms.get(i);
711 Mesh m = geoms.get(i).getMesh();
712 boolean useShared = isUsingSharedVerts(g);
717 m.generateBindPose(!HARDWARE_SKINNING);
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);
725 // if (bindPos != null) {
726 // m.setBuffer(bindPos);
729 // if (bindNorm != null) {
730 // m.setBuffer(bindNorm);
733 // if (boneIndex != null) {
734 // m.setBuffer(boneIndex);
737 // if (boneWeight != null) {
738 // m.setBuffer(boneWeight);
743 // Put the animations in the AnimControl
744 HashMap<String, Animation> anims = new HashMap<String, Animation>();
745 ArrayList<Animation> animList = animData.anims;
746 for (int i = 0; i < animList.size(); i++) {
747 Animation anim = animList.get(i);
748 anims.put(anim.getName(), anim);
751 AnimControl ctrl = new AnimControl(animData.skeleton);
752 ctrl.setAnimations(anims);
753 model.addControl(ctrl);
755 // Put the skeleton in the skeleton control
756 SkeletonControl skeletonControl = new SkeletonControl(animData.skeleton);
758 // This will acquire the targets from the node
759 model.addControl(skeletonControl);
765 public Object load(AssetInfo info) throws IOException {
767 AssetKey key = info.getKey();
768 meshName = key.getName();
769 folderName = key.getFolder();
770 String ext = key.getExtension();
771 meshName = meshName.substring(0, meshName.length() - ext.length() - 1);
772 if (folderName != null && folderName.length() > 0) {
773 meshName = meshName.substring(folderName.length());
775 assetManager = info.getManager();
777 OgreMeshKey meshKey = null;
778 if (key instanceof OgreMeshKey) {
779 meshKey = (OgreMeshKey) key;
780 materialList = meshKey.getMaterialList();
781 String materialName = meshKey.getMaterialName();
782 if (materialList == null) {
783 if (materialName != null) {
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);
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);
801 materialList = (MaterialList) assetManager.loadAsset(new OgreMaterialKey(folderName + meshName + ".material"));
802 } 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);
809 // Added by larynx 25.06.2011
810 // Android needs the namespace aware flag set to true
812 // Now, hack is applied for both desktop and android to avoid
813 // checking with JmeSystem.
814 SAXParserFactory factory = SAXParserFactory.newInstance();
815 factory.setNamespaceAware(true);
817 XMLReader xr = factory.newSAXParser().getXMLReader();
818 xr.setContentHandler(this);
819 xr.setErrorHandler(this);
821 InputStreamReader r = null;
823 r = new InputStreamReader(info.openStream());
824 xr.parse(new InputSource(r));
831 return compileModel();
832 } catch (SAXException ex) {
833 IOException ioEx = new IOException("Error while parsing Ogre3D mesh.xml");
836 } catch (ParserConfigurationException ex) {
837 IOException ioEx = new IOException("Error while parsing Ogre3D mesh.xml");