Home | History | Annotate | Download | only in ogre
      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