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 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 public static boolean HARDWARE_SKINNING = false; 76 private static final Type[] TEXCOORD_TYPES = 77 new Type[]{ 78 Type.TexCoord, 79 Type.TexCoord2, 80 Type.TexCoord3, 81 Type.TexCoord4, 82 Type.TexCoord5, 83 Type.TexCoord6, 84 Type.TexCoord7, 85 Type.TexCoord8,}; 86 private AssetKey key; 87 private String meshName; 88 private String folderName; 89 private AssetManager assetManager; 90 private MaterialList materialList; 91 // Data per submesh/sharedgeom 92 private ShortBuffer sb; 93 private IntBuffer ib; 94 private FloatBuffer fb; 95 private VertexBuffer vb; 96 private Mesh mesh; 97 private Geometry geom; 98 private ByteBuffer indicesData; 99 private FloatBuffer weightsFloatData; 100 private boolean actuallyHasWeights = false; 101 private int vertCount; 102 private boolean usesSharedVerts; 103 private boolean usesBigIndices; 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 indicesData = null; 142 weightsFloatData = null; 143 } 144 145 @Override 146 public void endDocument() { 147 } 148 149 private void pushIndex(int index){ 150 if (ib != null){ 151 ib.put(index); 152 }else{ 153 sb.put((short)index); 154 } 155 } 156 157 private void pushFace(String v1, String v2, String v3) throws SAXException { 158 // TODO: fan/strip support 159 switch (mesh.getMode()){ 160 case Triangles: 161 pushIndex(parseInt(v1)); 162 pushIndex(parseInt(v2)); 163 pushIndex(parseInt(v3)); 164 break; 165 case Lines: 166 pushIndex(parseInt(v1)); 167 pushIndex(parseInt(v2)); 168 break; 169 case Points: 170 pushIndex(parseInt(v1)); 171 break; 172 } 173 } 174 175 // private boolean isUsingSharedVerts(Geometry geom) { 176 // Old code for buffer sharer 177 //return geom.getUserData(UserData.JME_SHAREDMESH) != null; 178 // } 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 } 235 236 if (mat.isTransparent()) { 237 geom.setQueueBucket(Bucket.Transparent); 238 } 239 240 geom.setMaterial(mat); 241 } 242 243 private void startSubMesh(String matName, String usesharedvertices, String use32bitIndices, String opType) throws SAXException { 244 mesh = new Mesh(); 245 if (opType == null || opType.equals("triangle_list")) { 246 mesh.setMode(Mesh.Mode.Triangles); 247 //} else if (opType.equals("triangle_strip")) { 248 // mesh.setMode(Mesh.Mode.TriangleStrip); 249 //} else if (opType.equals("triangle_fan")) { 250 // mesh.setMode(Mesh.Mode.TriangleFan); 251 } else if (opType.equals("line_list")) { 252 mesh.setMode(Mesh.Mode.Lines); 253 } else { 254 throw new SAXException("Unsupported operation type: " + opType); 255 } 256 257 usesBigIndices = parseBool(use32bitIndices, false); 258 usesSharedVerts = parseBool(usesharedvertices, false); 259 if (usesSharedVerts) { 260 usesSharedMesh.add(true); 261 262 // Old code for buffer sharer 263 // import vertexbuffers from shared geom 264 // IntMap<VertexBuffer> sharedBufs = sharedMesh.getBuffers(); 265 // for (Entry<VertexBuffer> entry : sharedBufs) { 266 // mesh.setBuffer(entry.getValue()); 267 // } 268 }else{ 269 usesSharedMesh.add(false); 270 } 271 272 if (meshName == null) { 273 geom = new Geometry("OgreSubmesh-" + (++meshIndex), mesh); 274 } else { 275 geom = new Geometry(meshName + "-geom-" + (++meshIndex), mesh); 276 } 277 278 if (usesSharedVerts) { 279 // Old code for buffer sharer 280 // this mesh is shared! 281 //geom.setUserData(UserData.JME_SHAREDMESH, sharedMesh); 282 } 283 284 applyMaterial(geom, matName); 285 geoms.add(geom); 286 } 287 288 private void startSharedGeom(String vertexcount) throws SAXException { 289 sharedMesh = new Mesh(); 290 vertCount = parseInt(vertexcount); 291 usesSharedVerts = false; 292 293 geom = null; 294 mesh = sharedMesh; 295 } 296 297 private void startGeometry(String vertexcount) throws SAXException { 298 vertCount = parseInt(vertexcount); 299 } 300 301 /** 302 * Normalizes weights if needed and finds largest amount of weights used 303 * for all vertices in the buffer. 304 */ 305 private void endBoneAssigns() { 306 // if (mesh != sharedMesh && isUsingSharedVerts(geom)) { 307 // return; 308 // } 309 310 if (!actuallyHasWeights){ 311 // No weights were actually written (the tag didn't have any entries) 312 // remove those buffers 313 mesh.clearBuffer(Type.BoneIndex); 314 mesh.clearBuffer(Type.BoneWeight); 315 316 weightsFloatData = null; 317 indicesData = null; 318 319 return; 320 } 321 322 //int vertCount = mesh.getVertexCount(); 323 int maxWeightsPerVert = 0; 324 weightsFloatData.rewind(); 325 for (int v = 0; v < vertCount; v++) { 326 float w0 = weightsFloatData.get(), 327 w1 = weightsFloatData.get(), 328 w2 = weightsFloatData.get(), 329 w3 = weightsFloatData.get(); 330 331 if (w3 != 0) { 332 maxWeightsPerVert = Math.max(maxWeightsPerVert, 4); 333 } else if (w2 != 0) { 334 maxWeightsPerVert = Math.max(maxWeightsPerVert, 3); 335 } else if (w1 != 0) { 336 maxWeightsPerVert = Math.max(maxWeightsPerVert, 2); 337 } else if (w0 != 0) { 338 maxWeightsPerVert = Math.max(maxWeightsPerVert, 1); 339 } 340 341 float sum = w0 + w1 + w2 + w3; 342 if (sum != 1f) { 343 weightsFloatData.position(weightsFloatData.position() - 4); 344 // compute new vals based on sum 345 float sumToB = 1f / sum; 346 weightsFloatData.put(w0 * sumToB); 347 weightsFloatData.put(w1 * sumToB); 348 weightsFloatData.put(w2 * sumToB); 349 weightsFloatData.put(w3 * sumToB); 350 } 351 } 352 weightsFloatData.rewind(); 353 354 actuallyHasWeights = false; 355 weightsFloatData = null; 356 indicesData = null; 357 358 mesh.setMaxNumWeights(maxWeightsPerVert); 359 } 360 361 private void startBoneAssigns() { 362 if (mesh != sharedMesh && usesSharedVerts) { 363 // will use bone assignments from shared mesh (?) 364 return; 365 } 366 367 // current mesh will have bone assigns 368 //int vertCount = mesh.getVertexCount(); 369 // each vertex has 370 // - 4 bone weights 371 // - 4 bone indices 372 if (HARDWARE_SKINNING) { 373 weightsFloatData = BufferUtils.createFloatBuffer(vertCount * 4); 374 indicesData = BufferUtils.createByteBuffer(vertCount * 4); 375 } else { 376 // create array-backed buffers if software skinning for access speed 377 weightsFloatData = FloatBuffer.allocate(vertCount * 4); 378 indicesData = ByteBuffer.allocate(vertCount * 4); 379 } 380 381 VertexBuffer weights = new VertexBuffer(Type.BoneWeight); 382 VertexBuffer indices = new VertexBuffer(Type.BoneIndex); 383 384 Usage usage = HARDWARE_SKINNING ? Usage.Static : Usage.CpuOnly; 385 weights.setupData(usage, 4, Format.Float, weightsFloatData); 386 indices.setupData(usage, 4, Format.UnsignedByte, indicesData); 387 388 mesh.setBuffer(weights); 389 mesh.setBuffer(indices); 390 } 391 392 private void startVertexBuffer(Attributes attribs) throws SAXException { 393 if (parseBool(attribs.getValue("positions"), false)) { 394 vb = new VertexBuffer(Type.Position); 395 fb = BufferUtils.createFloatBuffer(vertCount * 3); 396 vb.setupData(Usage.Static, 3, Format.Float, fb); 397 mesh.setBuffer(vb); 398 } 399 if (parseBool(attribs.getValue("normals"), false)) { 400 vb = new VertexBuffer(Type.Normal); 401 fb = BufferUtils.createFloatBuffer(vertCount * 3); 402 vb.setupData(Usage.Static, 3, Format.Float, fb); 403 mesh.setBuffer(vb); 404 } 405 if (parseBool(attribs.getValue("colours_diffuse"), false)) { 406 vb = new VertexBuffer(Type.Color); 407 fb = BufferUtils.createFloatBuffer(vertCount * 4); 408 vb.setupData(Usage.Static, 4, Format.Float, fb); 409 mesh.setBuffer(vb); 410 } 411 if (parseBool(attribs.getValue("tangents"), false)) { 412 int dimensions = parseInt(attribs.getValue("tangent_dimensions"), 3); 413 vb = new VertexBuffer(Type.Tangent); 414 fb = BufferUtils.createFloatBuffer(vertCount * dimensions); 415 vb.setupData(Usage.Static, dimensions, Format.Float, fb); 416 mesh.setBuffer(vb); 417 } 418 if (parseBool(attribs.getValue("binormals"), false)) { 419 vb = new VertexBuffer(Type.Binormal); 420 fb = BufferUtils.createFloatBuffer(vertCount * 3); 421 vb.setupData(Usage.Static, 3, Format.Float, fb); 422 mesh.setBuffer(vb); 423 } 424 425 int texCoords = parseInt(attribs.getValue("texture_coords"), 0); 426 for (int i = 0; i < texCoords; i++) { 427 int dims = parseInt(attribs.getValue("texture_coord_dimensions_" + i), 2); 428 if (dims < 1 || dims > 4) { 429 throw new SAXException("Texture coord dimensions must be 1 <= dims <= 4"); 430 } 431 432 if (i <= 7) { 433 vb = new VertexBuffer(TEXCOORD_TYPES[i]); 434 } else { 435 // more than 8 texture coordinates are not supported by ogre. 436 throw new SAXException("More than 8 texture coordinates not supported"); 437 } 438 fb = BufferUtils.createFloatBuffer(vertCount * dims); 439 vb.setupData(Usage.Static, dims, Format.Float, fb); 440 mesh.setBuffer(vb); 441 } 442 } 443 444 private void startVertex() { 445 texCoordIndex = 0; 446 } 447 448 private void pushAttrib(Type type, Attributes attribs) throws SAXException { 449 try { 450 FloatBuffer buf = (FloatBuffer) mesh.getBuffer(type).getData(); 451 buf.put(parseFloat(attribs.getValue("x"))).put(parseFloat(attribs.getValue("y"))).put(parseFloat(attribs.getValue("z"))); 452 } catch (Exception ex) { 453 throw new SAXException("Failed to push attrib", ex); 454 } 455 } 456 457 private void pushTangent(Attributes attribs) throws SAXException { 458 try { 459 VertexBuffer tangentBuf = mesh.getBuffer(Type.Tangent); 460 FloatBuffer buf = (FloatBuffer) tangentBuf.getData(); 461 buf.put(parseFloat(attribs.getValue("x"))).put(parseFloat(attribs.getValue("y"))).put(parseFloat(attribs.getValue("z"))); 462 if (tangentBuf.getNumComponents() == 4) { 463 buf.put(parseFloat(attribs.getValue("w"))); 464 } 465 } catch (Exception ex) { 466 throw new SAXException("Failed to push attrib", ex); 467 } 468 } 469 470 private void pushTexCoord(Attributes attribs) throws SAXException { 471 if (texCoordIndex >= 8) { 472 return; // More than 8 not supported by ogre. 473 } 474 Type type = TEXCOORD_TYPES[texCoordIndex]; 475 476 VertexBuffer tcvb = mesh.getBuffer(type); 477 FloatBuffer buf = (FloatBuffer) tcvb.getData(); 478 479 buf.put(parseFloat(attribs.getValue("u"))); 480 if (tcvb.getNumComponents() >= 2) { 481 buf.put(parseFloat(attribs.getValue("v"))); 482 if (tcvb.getNumComponents() >= 3) { 483 buf.put(parseFloat(attribs.getValue("w"))); 484 if (tcvb.getNumComponents() == 4) { 485 buf.put(parseFloat(attribs.getValue("x"))); 486 } 487 } 488 } 489 490 texCoordIndex++; 491 } 492 493 private void pushColor(Attributes attribs) throws SAXException { 494 FloatBuffer buf = (FloatBuffer) mesh.getBuffer(Type.Color).getData(); 495 String value = parseString(attribs.getValue("value")); 496 String[] vals = value.split("\\s"); 497 if (vals.length != 3 && vals.length != 4) { 498 throw new SAXException("Color value must contain 3 or 4 components"); 499 } 500 501 ColorRGBA color = new ColorRGBA(); 502 color.r = parseFloat(vals[0]); 503 color.g = parseFloat(vals[1]); 504 color.b = parseFloat(vals[2]); 505 if (vals.length == 3) { 506 color.a = 1f; 507 } else { 508 color.a = parseFloat(vals[3]); 509 } 510 511 buf.put(color.r).put(color.g).put(color.b).put(color.a); 512 } 513 514 private void startLodFaceList(String submeshindex, String numfaces) { 515 int index = Integer.parseInt(submeshindex); 516 mesh = geoms.get(index).getMesh(); 517 int faceCount = Integer.parseInt(numfaces); 518 519 VertexBuffer originalIndexBuffer = mesh.getBuffer(Type.Index); 520 vb = new VertexBuffer(VertexBuffer.Type.Index); 521 if (originalIndexBuffer.getFormat() == Format.UnsignedInt){ 522 // LOD buffer should also be integer 523 ib = BufferUtils.createIntBuffer(faceCount * 3); 524 sb = null; 525 vb.setupData(Usage.Static, 3, Format.UnsignedInt, ib); 526 }else{ 527 sb = BufferUtils.createShortBuffer(faceCount * 3); 528 ib = null; 529 vb.setupData(Usage.Static, 3, Format.UnsignedShort, sb); 530 } 531 532 List<VertexBuffer> levels = lodLevels.get(index); 533 if (levels == null) { 534 // Create the LOD levels list 535 levels = new ArrayList<VertexBuffer>(); 536 537 // Add the first LOD level (always the original index buffer) 538 levels.add(originalIndexBuffer); 539 lodLevels.put(index, levels); 540 } 541 levels.add(vb); 542 } 543 544 private void startLevelOfDetail(String numlevels) { 545 // numLevels = Integer.parseInt(numlevels); 546 } 547 548 private void endLevelOfDetail() { 549 // set the lod data for each mesh 550 for (Entry<List<VertexBuffer>> entry : lodLevels) { 551 Mesh m = geoms.get(entry.getKey()).getMesh(); 552 List<VertexBuffer> levels = entry.getValue(); 553 VertexBuffer[] levelArray = new VertexBuffer[levels.size()]; 554 levels.toArray(levelArray); 555 m.setLodLevels(levelArray); 556 } 557 } 558 559 private void startLodGenerated(String depthsqr) { 560 } 561 562 private void pushBoneAssign(String vertIndex, String boneIndex, String weight) throws SAXException { 563 int vert = parseInt(vertIndex); 564 float w = parseFloat(weight); 565 byte bone = (byte) parseInt(boneIndex); 566 567 assert bone >= 0; 568 assert vert >= 0 && vert < mesh.getVertexCount(); 569 570 int i; 571 float v = 0; 572 // see which weights are unused for a given bone 573 for (i = vert * 4; i < vert * 4 + 4; i++) { 574 v = weightsFloatData.get(i); 575 if (v == 0) { 576 break; 577 } 578 } 579 if (v != 0) { 580 logger.log(Level.WARNING, "Vertex {0} has more than 4 weights per vertex! Ignoring..", vert); 581 return; 582 } 583 584 weightsFloatData.put(i, w); 585 indicesData.put(i, bone); 586 actuallyHasWeights = true; 587 } 588 589 private void startSkeleton(String name) { 590 AssetKey assetKey = new AssetKey(folderName + name + ".xml"); 591 try { 592 animData = (AnimData) assetManager.loadAsset(assetKey); 593 } catch (AssetNotFoundException ex){ 594 logger.log(Level.WARNING, "Cannot locate {0} for model {1}", new Object[]{assetKey, key}); 595 animData = null; 596 } 597 } 598 599 private void startSubmeshName(String indexStr, String nameStr) { 600 int index = Integer.parseInt(indexStr); 601 geoms.get(index).setName(nameStr); 602 } 603 604 @Override 605 public void startElement(String uri, String localName, String qName, Attributes attribs) throws SAXException { 606 if (ignoreUntilEnd != null) { 607 return; 608 } 609 610 if (qName.equals("texcoord")) { 611 pushTexCoord(attribs); 612 } else if (qName.equals("vertexboneassignment")) { 613 pushBoneAssign(attribs.getValue("vertexindex"), 614 attribs.getValue("boneindex"), 615 attribs.getValue("weight")); 616 } else if (qName.equals("face")) { 617 pushFace(attribs.getValue("v1"), 618 attribs.getValue("v2"), 619 attribs.getValue("v3")); 620 } else if (qName.equals("position")) { 621 pushAttrib(Type.Position, attribs); 622 } else if (qName.equals("normal")) { 623 pushAttrib(Type.Normal, attribs); 624 } else if (qName.equals("tangent")) { 625 pushTangent(attribs); 626 } else if (qName.equals("binormal")) { 627 pushAttrib(Type.Binormal, attribs); 628 } else if (qName.equals("colour_diffuse")) { 629 pushColor(attribs); 630 } else if (qName.equals("vertex")) { 631 startVertex(); 632 } else if (qName.equals("faces")) { 633 startFaces(attribs.getValue("count")); 634 } else if (qName.equals("geometry")) { 635 String count = attribs.getValue("vertexcount"); 636 if (count == null) { 637 count = attribs.getValue("count"); 638 } 639 startGeometry(count); 640 } else if (qName.equals("vertexbuffer")) { 641 startVertexBuffer(attribs); 642 } else if (qName.equals("lodfacelist")) { 643 startLodFaceList(attribs.getValue("submeshindex"), 644 attribs.getValue("numfaces")); 645 } else if (qName.equals("lodgenerated")) { 646 startLodGenerated(attribs.getValue("fromdepthsquared")); 647 } else if (qName.equals("levelofdetail")) { 648 startLevelOfDetail(attribs.getValue("numlevels")); 649 } else if (qName.equals("boneassignments")) { 650 startBoneAssigns(); 651 } else if (qName.equals("submesh")) { 652 startSubMesh(attribs.getValue("material"), 653 attribs.getValue("usesharedvertices"), 654 attribs.getValue("use32bitindexes"), 655 attribs.getValue("operationtype")); 656 } else if (qName.equals("sharedgeometry")) { 657 String count = attribs.getValue("vertexcount"); 658 if (count == null) { 659 count = attribs.getValue("count"); 660 } 661 662 if (count != null && !count.equals("0")) { 663 startSharedGeom(count); 664 } 665 } else if (qName.equals("submeshes")) { 666 // ok 667 } else if (qName.equals("skeletonlink")) { 668 startSkeleton(attribs.getValue("name")); 669 } else if (qName.equals("submeshnames")) { 670 // ok 671 } else if (qName.equals("submeshname")) { 672 startSubmeshName(attribs.getValue("index"), attribs.getValue("name")); 673 } else if (qName.equals("mesh")) { 674 // ok 675 } else { 676 logger.log(Level.WARNING, "Unknown tag: {0}. Ignoring.", qName); 677 ignoreUntilEnd = qName; 678 } 679 } 680 681 @Override 682 public void endElement(String uri, String name, String qName) { 683 if (ignoreUntilEnd != null) { 684 if (ignoreUntilEnd.equals(qName)) { 685 ignoreUntilEnd = null; 686 } 687 return; 688 } 689 690 if (qName.equals("submesh")) { 691 usesBigIndices = false; 692 geom = null; 693 mesh = null; 694 } else if (qName.equals("submeshes")) { 695 // IMPORTANT: restore sharedmesh, for use with shared boneweights 696 geom = null; 697 mesh = sharedMesh; 698 usesSharedVerts = false; 699 } else if (qName.equals("faces")) { 700 if (ib != null) { 701 ib.flip(); 702 } else { 703 sb.flip(); 704 } 705 706 vb = null; 707 ib = null; 708 sb = null; 709 } else if (qName.equals("vertexbuffer")) { 710 fb = null; 711 vb = null; 712 } else if (qName.equals("geometry") 713 || qName.equals("sharedgeometry")) { 714 // finish writing to buffers 715 for (VertexBuffer buf : mesh.getBufferList().getArray()) { 716 Buffer data = buf.getData(); 717 if (data.position() != 0) { 718 data.flip(); 719 } 720 } 721 mesh.updateBound(); 722 mesh.setStatic(); 723 724 if (qName.equals("sharedgeometry")) { 725 geom = null; 726 mesh = null; 727 } 728 } else if (qName.equals("lodfacelist")) { 729 sb.flip(); 730 vb = null; 731 sb = null; 732 } else if (qName.equals("levelofdetail")) { 733 endLevelOfDetail(); 734 } else if (qName.equals("boneassignments")) { 735 endBoneAssigns(); 736 } 737 } 738 739 @Override 740 public void characters(char ch[], int start, int length) { 741 } 742 743 private Node compileModel() { 744 Node model = new Node(meshName + "-ogremesh"); 745 746 for (int i = 0; i < geoms.size(); i++) { 747 Geometry g = geoms.get(i); 748 Mesh m = g.getMesh(); 749 750 // New code for buffer extract 751 if (sharedMesh != null && usesSharedMesh.get(i)) { 752 m.extractVertexData(sharedMesh); 753 } 754 755 // Old code for buffer sharer 756 //if (sharedMesh != null && isUsingSharedVerts(g)) { 757 // m.setBound(sharedMesh.getBound().clone()); 758 //} 759 model.attachChild(geoms.get(i)); 760 } 761 762 // Do not attach shared geometry to the node! 763 764 if (animData != null) { 765 // This model uses animation 766 767 // Old code for buffer sharer 768 // generate bind pose for mesh 769 // ONLY if not using shared geometry 770 // This includes the shared geoemtry itself actually 771 //if (sharedMesh != null) { 772 // sharedMesh.generateBindPose(!HARDWARE_SKINNING); 773 //} 774 775 for (int i = 0; i < geoms.size(); i++) { 776 Geometry g = geoms.get(i); 777 Mesh m = geoms.get(i).getMesh(); 778 779 m.generateBindPose(!HARDWARE_SKINNING); 780 781 // Old code for buffer sharer 782 //boolean useShared = isUsingSharedVerts(g); 783 //if (!useShared) { 784 // create bind pose 785 //m.generateBindPose(!HARDWARE_SKINNING); 786 //} 787 } 788 789 // Put the animations in the AnimControl 790 HashMap<String, Animation> anims = new HashMap<String, Animation>(); 791 ArrayList<Animation> animList = animData.anims; 792 for (int i = 0; i < animList.size(); i++) { 793 Animation anim = animList.get(i); 794 anims.put(anim.getName(), anim); 795 } 796 797 AnimControl ctrl = new AnimControl(animData.skeleton); 798 ctrl.setAnimations(anims); 799 model.addControl(ctrl); 800 801 // Put the skeleton in the skeleton control 802 SkeletonControl skeletonControl = new SkeletonControl(animData.skeleton); 803 804 // This will acquire the targets from the node 805 model.addControl(skeletonControl); 806 } 807 808 return model; 809 } 810 811 public Object load(AssetInfo info) throws IOException { 812 try { 813 key = info.getKey(); 814 meshName = key.getName(); 815 folderName = key.getFolder(); 816 String ext = key.getExtension(); 817 meshName = meshName.substring(0, meshName.length() - ext.length() - 1); 818 if (folderName != null && folderName.length() > 0) { 819 meshName = meshName.substring(folderName.length()); 820 } 821 assetManager = info.getManager(); 822 823 if (key instanceof OgreMeshKey) { 824 // OgreMeshKey is being used, try getting the material list 825 // from it 826 OgreMeshKey meshKey = (OgreMeshKey) key; 827 materialList = meshKey.getMaterialList(); 828 String materialName = meshKey.getMaterialName(); 829 830 // Material list not set but material name is available 831 if (materialList == null && materialName != null) { 832 OgreMaterialKey materialKey = new OgreMaterialKey(folderName + materialName + ".material"); 833 try { 834 materialList = (MaterialList) assetManager.loadAsset(materialKey); 835 } catch (AssetNotFoundException e) { 836 logger.log(Level.WARNING, "Cannot locate {0} for model {1}", new Object[]{materialKey, key}); 837 } 838 } 839 }else{ 840 // Make sure to reset it to null so that previous state 841 // doesn't leak onto this one 842 materialList = null; 843 } 844 845 // If for some reason material list could not be found through 846 // OgreMeshKey, or if regular ModelKey specified, load using 847 // default method. 848 if (materialList == null){ 849 OgreMaterialKey materialKey = new OgreMaterialKey(folderName + meshName + ".material"); 850 try { 851 materialList = (MaterialList) assetManager.loadAsset(materialKey); 852 } catch (AssetNotFoundException e) { 853 logger.log(Level.WARNING, "Cannot locate {0} for model {1}", new Object[]{ materialKey, key }); 854 } 855 } 856 857 // Added by larynx 25.06.2011 858 // Android needs the namespace aware flag set to true 859 // Kirill 30.06.2011 860 // Now, hack is applied for both desktop and android to avoid 861 // checking with JmeSystem. 862 SAXParserFactory factory = SAXParserFactory.newInstance(); 863 factory.setNamespaceAware(true); 864 865 XMLReader xr = factory.newSAXParser().getXMLReader(); 866 xr.setContentHandler(this); 867 xr.setErrorHandler(this); 868 869 InputStreamReader r = null; 870 try { 871 r = new InputStreamReader(info.openStream()); 872 xr.parse(new InputSource(r)); 873 } finally { 874 if (r != null){ 875 r.close(); 876 } 877 } 878 879 return compileModel(); 880 } catch (SAXException ex) { 881 IOException ioEx = new IOException("Error while parsing Ogre3D mesh.xml"); 882 ioEx.initCause(ex); 883 throw ioEx; 884 } catch (ParserConfigurationException ex) { 885 IOException ioEx = new IOException("Error while parsing Ogre3D mesh.xml"); 886 ioEx.initCause(ex); 887 throw ioEx; 888 } 889 890 } 891 } 892