2 * Copyright (c) 2009-2012 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.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;
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;
69 * Loads Ogre3D mesh.xml files.
71 public class MeshLoader extends DefaultHandler implements AssetLoader {
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 =
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;
93 private FloatBuffer fb;
94 private VertexBuffer vb;
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;
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;
114 public MeshLoader() {
119 public void startDocument() {
131 usesSharedMesh.clear();
132 usesSharedVerts = false;
136 ignoreUntilEnd = null;
140 actuallyHasWeights = false;
141 submeshNamesHack = false;
143 weightsFloatData = null;
147 public void endDocument() {
150 private void pushIndex(int index) {
154 sb.put((short) index);
158 private void pushFace(String v1, String v2, String v3) throws SAXException {
159 // TODO: fan/strip support
160 switch (mesh.getMode()) {
162 pushIndex(parseInt(v1));
163 pushIndex(parseInt(v2));
164 pushIndex(parseInt(v3));
167 pushIndex(parseInt(v1));
168 pushIndex(parseInt(v2));
171 pushIndex(parseInt(v1));
176 // private boolean isUsingSharedVerts(Geometry geom) {
177 // Old code for buffer sharer
178 //return geom.getUserData(UserData.JME_SHAREDMESH) != null;
180 private void startFaces(String count) throws SAXException {
181 int numFaces = parseInt(count);
182 int indicesPerFace = 0;
184 switch (mesh.getMode()) {
195 throw new SAXException("Strips or fans not supported!");
198 int numIndices = indicesPerFace * numFaces;
200 vb = new VertexBuffer(VertexBuffer.Type.Index);
201 if (!usesBigIndices) {
202 sb = BufferUtils.createShortBuffer(numIndices);
204 vb.setupData(Usage.Static, indicesPerFace, Format.UnsignedShort, sb);
206 ib = BufferUtils.createIntBuffer(numIndices);
208 vb.setupData(Usage.Static, indicesPerFace, Format.UnsignedInt, ib);
213 private void applyMaterial(Geometry geom, String matName) {
215 if (matName.endsWith(".j3m")) {
216 // load as native jme3 material instance
218 mat = assetManager.loadMaterial(matName);
219 } catch (AssetNotFoundException ex) {
220 // Warning will be raised (see below)
221 if (!ex.getMessage().equals(matName)) {
226 if (materialList != null) {
227 mat = materialList.get(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));
237 if (mat.isTransparent()) {
238 geom.setQueueBucket(Bucket.Transparent);
241 geom.setMaterial(mat);
244 private void startSubMesh(String matName, String usesharedvertices, String use32bitIndices, String opType) throws SAXException {
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);
255 throw new SAXException("Unsupported operation type: " + opType);
258 usesBigIndices = parseBool(use32bitIndices, false);
259 usesSharedVerts = parseBool(usesharedvertices, false);
260 if (usesSharedVerts) {
261 usesSharedMesh.add(true);
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());
270 usesSharedMesh.add(false);
273 if (meshName == null) {
274 geom = new Geometry("OgreSubmesh-" + (++meshIndex), mesh);
276 geom = new Geometry(meshName + "-geom-" + (++meshIndex), mesh);
279 if (usesSharedVerts) {
280 // Old code for buffer sharer
281 // this mesh is shared!
282 //geom.setUserData(UserData.JME_SHAREDMESH, sharedMesh);
285 applyMaterial(geom, matName);
289 private void startSharedGeom(String vertexcount) throws SAXException {
290 sharedMesh = new Mesh();
291 vertCount = parseInt(vertexcount);
292 usesSharedVerts = false;
298 private void startGeometry(String vertexcount) throws SAXException {
299 vertCount = parseInt(vertexcount);
303 * Normalizes weights if needed and finds largest amount of weights used for
304 * all vertices in the buffer.
306 private void endBoneAssigns() {
307 // if (mesh != sharedMesh && isUsingSharedVerts(geom)) {
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);
317 weightsFloatData = null;
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();
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);
342 float sum = w0 + w1 + w2 + w3;
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);
353 weightsFloatData.rewind();
355 actuallyHasWeights = false;
356 weightsFloatData = null;
359 mesh.setMaxNumWeights(maxWeightsPerVert);
362 private void startBoneAssigns() {
363 if (mesh != sharedMesh && usesSharedVerts) {
364 // will use bone assignments from shared mesh (?)
368 // current mesh will have bone assigns
369 //int vertCount = mesh.getVertexCount();
373 // create array-backed buffers for software skinning for access speed
374 weightsFloatData = FloatBuffer.allocate(vertCount * 4);
375 indicesData = ByteBuffer.allocate(vertCount * 4);
377 VertexBuffer weights = new VertexBuffer(Type.BoneWeight);
378 VertexBuffer indices = new VertexBuffer(Type.BoneIndex);
380 weights.setupData(Usage.CpuOnly, 4, Format.Float, weightsFloatData);
381 indices.setupData(Usage.CpuOnly, 4, Format.UnsignedByte, indicesData);
383 mesh.setBuffer(weights);
384 mesh.setBuffer(indices);
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);
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);
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);
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);
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);
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);
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");
438 vb = new VertexBuffer(TEXCOORD_TYPES[i]);
440 // more than 8 texture coordinates are not supported by ogre.
441 throw new SAXException("More than 8 texture coordinates not supported");
443 fb = BufferUtils.createFloatBuffer(vertCount * dims);
444 vb.setupData(Usage.Static, dims, Format.Float, fb);
449 private void startVertex() {
453 private void pushAttrib(Type type, Attributes attribs) throws SAXException {
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);
462 private void pushTangent(Attributes attribs) throws SAXException {
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")));
470 } catch (Exception ex) {
471 throw new SAXException("Failed to push attrib", ex);
475 private void pushTexCoord(Attributes attribs) throws SAXException {
476 if (texCoordIndex >= 8) {
477 return; // More than 8 not supported by ogre.
479 Type type = TEXCOORD_TYPES[texCoordIndex];
481 VertexBuffer tcvb = mesh.getBuffer(type);
482 FloatBuffer buf = (FloatBuffer) tcvb.getData();
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")));
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");
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) {
513 color.a = parseFloat(vals[3]);
516 buf.put(color.r).put(color.g).put(color.b).put(color.a);
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);
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);
530 vb.setupData(Usage.Static, 3, Format.UnsignedInt, ib);
532 sb = BufferUtils.createShortBuffer(faceCount * 3);
534 vb.setupData(Usage.Static, 3, Format.UnsignedShort, sb);
537 List<VertexBuffer> levels = lodLevels.get(index);
538 if (levels == null) {
539 // Create the LOD levels list
540 levels = new ArrayList<VertexBuffer>();
542 // Add the first LOD level (always the original index buffer)
543 levels.add(originalIndexBuffer);
544 lodLevels.put(index, levels);
549 private void startLevelOfDetail(String numlevels) {
550 // numLevels = Integer.parseInt(numlevels);
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);
564 private void startLodGenerated(String depthsqr) {
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);
573 assert vert >= 0 && vert < mesh.getVertexCount();
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);
585 logger.log(Level.WARNING, "Vertex {0} has more than 4 weights per vertex! Ignoring..", vert);
589 weightsFloatData.put(i, w);
590 indicesData.put(i, bone);
591 actuallyHasWeights = true;
594 private void startSkeleton(String name) {
595 AssetKey assetKey = new AssetKey(folderName + name + ".xml");
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});
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()});
610 geoms.get(index).setName(nameStr);
615 public void startElement(String uri, String localName, String qName, Attributes attribs) throws SAXException {
616 if (ignoreUntilEnd != null) {
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")) {
640 } else if (qName.equals("vertex")) {
642 } else if (qName.equals("faces")) {
643 startFaces(attribs.getValue("count"));
644 } else if (qName.equals("geometry")) {
645 String count = attribs.getValue("vertexcount");
647 count = attribs.getValue("count");
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")) {
661 } else if (qName.equals("submesh")) {
662 if (submeshNamesHack) {
663 // Hack for blender2ogre only
664 startSubmeshName(attribs.getValue("index"), attribs.getValue("name"));
666 startSubMesh(attribs.getValue("material"),
667 attribs.getValue("usesharedvertices"),
668 attribs.getValue("use32bitindexes"),
669 attribs.getValue("operationtype"));
671 } else if (qName.equals("sharedgeometry")) {
672 String count = attribs.getValue("vertexcount");
674 count = attribs.getValue("count");
677 if (count != null && !count.equals("0")) {
678 startSharedGeom(count);
680 } else if (qName.equals("submeshes")) {
682 } else if (qName.equals("skeletonlink")) {
683 startSkeleton(attribs.getValue("name"));
684 } else if (qName.equals("submeshnames")) {
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")) {
694 logger.log(Level.WARNING, "Unknown tag: {0}. Ignoring.", qName);
695 ignoreUntilEnd = qName;
700 public void endElement(String uri, String name, String qName) {
701 if (ignoreUntilEnd != null) {
702 if (ignoreUntilEnd.equals(qName)) {
703 ignoreUntilEnd = null;
709 // If submesh hack is enabled, ignore any submesh/submeshes
711 if (qName.equals("submesh") && !submeshNamesHack) {
712 usesBigIndices = false;
715 } else if (qName.equals("submeshes") && !submeshNamesHack) {
716 // IMPORTANT: restore sharedmesh, for use with shared boneweights
719 usesSharedVerts = false;
720 } else if (qName.equals("faces")) {
730 } else if (qName.equals("vertexbuffer")) {
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) {
745 if (qName.equals("sharedgeometry")) {
749 } else if (qName.equals("lodfacelist")) {
753 } else if (qName.equals("levelofdetail")) {
755 } else if (qName.equals("boneassignments")) {
757 } else if (qName.equals("submeshnames")) {
758 // Restore default handling for "submesh" tag.
759 submeshNamesHack = false;
764 public void characters(char ch[], int start, int length) {
767 private Node compileModel() {
768 Node model = new Node(meshName + "-ogremesh");
770 for (int i = 0; i < geoms.size(); i++) {
771 Geometry g = geoms.get(i);
772 Mesh m = g.getMesh();
774 // New code for buffer extract
775 if (sharedMesh != null && usesSharedMesh.get(i)) {
776 m.extractVertexData(sharedMesh);
779 model.attachChild(geoms.get(i));
782 // Do not attach shared geometry to the node!
784 if (animData != null) {
785 // This model uses animation
787 for (int i = 0; i < geoms.size(); i++) {
788 Geometry g = geoms.get(i);
789 Mesh m = geoms.get(i).getMesh();
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);
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);
810 AnimControl ctrl = new AnimControl(animData.skeleton);
811 ctrl.setAnimations(anims);
812 model.addControl(ctrl);
814 // Put the skeleton in the skeleton control
815 SkeletonControl skeletonControl = new SkeletonControl(animData.skeleton);
817 // This will acquire the targets from the node
818 model.addControl(skeletonControl);
824 public Object load(AssetInfo info) throws IOException {
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());
834 assetManager = info.getManager();
836 if (key instanceof OgreMeshKey) {
837 // OgreMeshKey is being used, try getting the material list
839 OgreMeshKey meshKey = (OgreMeshKey) key;
840 materialList = meshKey.getMaterialList();
841 String materialName = meshKey.getMaterialName();
843 // Material list not set but material name is available
844 if (materialList == null && materialName != null) {
845 OgreMaterialKey materialKey = new OgreMaterialKey(folderName + materialName + ".material");
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});
853 // Make sure to reset it to null so that previous state
854 // doesn't leak onto this one
858 // If for some reason material list could not be found through
859 // OgreMeshKey, or if regular ModelKey specified, load using
861 if (materialList == null) {
862 OgreMaterialKey materialKey = new OgreMaterialKey(folderName + meshName + ".material");
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});
870 // Added by larynx 25.06.2011
871 // Android needs the namespace aware flag set to true
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);
878 XMLReader xr = factory.newSAXParser().getXMLReader();
879 xr.setContentHandler(this);
880 xr.setErrorHandler(this);
882 InputStreamReader r = null;
884 r = new InputStreamReader(info.openStream());
885 xr.parse(new InputSource(r));
892 return compileModel();
893 } catch (SAXException ex) {
894 IOException ioEx = new IOException("Error while parsing Ogre3D mesh.xml");
897 } catch (ParserConfigurationException ex) {
898 IOException ioEx = new IOException("Error while parsing Ogre3D mesh.xml");