Kaynağa Gözat

used dynamicEqual instead of dynamicHashCode in BatchNode and GeometryBatchFactory

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9362 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
rem..om 13 yıl önce
ebeveyn
işleme
c9d5011555

+ 74 - 74
engine/src/core/com/jme3/scene/BatchNode.java

@@ -66,7 +66,7 @@ import java.util.logging.Logger;
  * @author Nehon
  * @author Nehon
  */
  */
 public class BatchNode extends Node implements Savable {
 public class BatchNode extends Node implements Savable {
-    
+
     private static final Logger logger = Logger.getLogger(BatchNode.class.getName());
     private static final Logger logger = Logger.getLogger(BatchNode.class.getName());
     /**
     /**
      * the list of geometry holding the batched meshes
      * the list of geometry holding the batched meshes
@@ -92,23 +92,23 @@ public class BatchNode extends Node implements Savable {
     public BatchNode() {
     public BatchNode() {
         super();
         super();
     }
     }
-    
+
     public BatchNode(String name) {
     public BatchNode(String name) {
         super(name);
         super(name);
     }
     }
-    
+
     @Override
     @Override
     public void updateGeometricState() {
     public void updateGeometricState() {
         if ((refreshFlags & RF_LIGHTLIST) != 0) {
         if ((refreshFlags & RF_LIGHTLIST) != 0) {
             updateWorldLightList();
             updateWorldLightList();
         }
         }
-        
+
         if ((refreshFlags & RF_TRANSFORM) != 0) {
         if ((refreshFlags & RF_TRANSFORM) != 0) {
             // combine with parent transforms- same for all spatial
             // combine with parent transforms- same for all spatial
             // subclasses.
             // subclasses.
             updateWorldTransforms();
             updateWorldTransforms();
         }
         }
-        
+
         if (!children.isEmpty()) {
         if (!children.isEmpty()) {
             // the important part- make sure child geometric state is refreshed
             // the important part- make sure child geometric state is refreshed
             // first before updating own world bound. This saves
             // first before updating own world bound. This saves
@@ -119,42 +119,42 @@ public class BatchNode extends Node implements Savable {
             for (Spatial child : children.getArray()) {
             for (Spatial child : children.getArray()) {
                 child.updateGeometricState();
                 child.updateGeometricState();
             }
             }
-            
+
             for (Batch batch : batches.getArray()) {
             for (Batch batch : batches.getArray()) {
                 if (batch.needMeshUpdate) {
                 if (batch.needMeshUpdate) {
                     batch.geometry.getMesh().updateBound();
                     batch.geometry.getMesh().updateBound();
                     batch.geometry.updateWorldBound();
                     batch.geometry.updateWorldBound();
                     batch.needMeshUpdate = false;
                     batch.needMeshUpdate = false;
-                    
+
                 }
                 }
             }
             }
-            
-            
+
+
         }
         }
-        
+
         if ((refreshFlags & RF_BOUND) != 0) {
         if ((refreshFlags & RF_BOUND) != 0) {
             updateWorldBound();
             updateWorldBound();
         }
         }
-        
+
         assert refreshFlags == 0;
         assert refreshFlags == 0;
     }
     }
-    
+
     protected Transform getTransforms(Geometry geom) {
     protected Transform getTransforms(Geometry geom) {
         return geom.getWorldTransform();
         return geom.getWorldTransform();
     }
     }
-    
+
     protected void updateSubBatch(Geometry bg) {
     protected void updateSubBatch(Geometry bg) {
         Batch batch = batchesByGeom.get(bg);
         Batch batch = batchesByGeom.get(bg);
         if (batch != null) {
         if (batch != null) {
             Mesh mesh = batch.geometry.getMesh();
             Mesh mesh = batch.geometry.getMesh();
-            
+
             VertexBuffer pvb = mesh.getBuffer(VertexBuffer.Type.Position);
             VertexBuffer pvb = mesh.getBuffer(VertexBuffer.Type.Position);
             FloatBuffer posBuf = (FloatBuffer) pvb.getData();
             FloatBuffer posBuf = (FloatBuffer) pvb.getData();
             VertexBuffer nvb = mesh.getBuffer(VertexBuffer.Type.Normal);
             VertexBuffer nvb = mesh.getBuffer(VertexBuffer.Type.Normal);
             FloatBuffer normBuf = (FloatBuffer) nvb.getData();
             FloatBuffer normBuf = (FloatBuffer) nvb.getData();
-            
+
             if (mesh.getBuffer(VertexBuffer.Type.Tangent) != null) {
             if (mesh.getBuffer(VertexBuffer.Type.Tangent) != null) {
-                
+
                 VertexBuffer tvb = mesh.getBuffer(VertexBuffer.Type.Tangent);
                 VertexBuffer tvb = mesh.getBuffer(VertexBuffer.Type.Tangent);
                 FloatBuffer tanBuf = (FloatBuffer) tvb.getData();
                 FloatBuffer tanBuf = (FloatBuffer) tvb.getData();
                 doTransformsTangents(posBuf, normBuf, tanBuf, bg.startIndex, bg.startIndex + bg.getVertexCount(), bg.cachedOffsetMat);
                 doTransformsTangents(posBuf, normBuf, tanBuf, bg.startIndex, bg.startIndex + bg.getVertexCount(), bg.cachedOffsetMat);
@@ -164,8 +164,8 @@ public class BatchNode extends Node implements Savable {
             }
             }
             pvb.updateData(posBuf);
             pvb.updateData(posBuf);
             nvb.updateData(normBuf);
             nvb.updateData(normBuf);
-            
-            
+
+
             batch.needMeshUpdate = true;
             batch.needMeshUpdate = true;
         }
         }
     }
     }
@@ -181,12 +181,12 @@ public class BatchNode extends Node implements Savable {
             batch.geometry.setIgnoreTransform(true);
             batch.geometry.setIgnoreTransform(true);
         }
         }
     }
     }
-    
+
     protected void doBatch() {
     protected void doBatch() {
         Map<Material, List<Geometry>> matMap = new HashMap<Material, List<Geometry>>();
         Map<Material, List<Geometry>> matMap = new HashMap<Material, List<Geometry>>();
         maxVertCount = 0;
         maxVertCount = 0;
         int nbGeoms = 0;
         int nbGeoms = 0;
-        
+
         gatherGeomerties(matMap, this, needsFullRebatch);
         gatherGeomerties(matMap, this, needsFullRebatch);
         if (needsFullRebatch) {
         if (needsFullRebatch) {
             for (Batch batch : batches.getArray()) {
             for (Batch batch : batches.getArray()) {
@@ -216,14 +216,14 @@ public class BatchNode extends Node implements Savable {
             }
             }
             mergeGeometries(m, list);
             mergeGeometries(m, list);
             m.setDynamic();
             m.setDynamic();
-            
+
             batch.updateGeomList(list);
             batch.updateGeomList(list);
-            
+
             batch.geometry = new Geometry(batchName);
             batch.geometry = new Geometry(batchName);
             batch.geometry.setMaterial(material);
             batch.geometry.setMaterial(material);
             this.attachChild(batch.geometry);
             this.attachChild(batch.geometry);
-            
-            
+
+
             batch.geometry.setMesh(m);
             batch.geometry.setMesh(m);
             batch.geometry.getMesh().updateCounts();
             batch.geometry.getMesh().updateCounts();
             batch.geometry.getMesh().updateBound();
             batch.geometry.getMesh().updateBound();
@@ -232,8 +232,8 @@ public class BatchNode extends Node implements Savable {
         if (batches.size() > 0) {
         if (batches.size() > 0) {
             needsFullRebatch = false;
             needsFullRebatch = false;
         }
         }
-        
-        
+
+
         logger.log(Level.INFO, "Batched {0} geometries in {1} batches.", new Object[]{nbGeoms, batches.size()});
         logger.log(Level.INFO, "Batched {0} geometries in {1} batches.", new Object[]{nbGeoms, batches.size()});
 
 
 
 
@@ -244,11 +244,11 @@ public class BatchNode extends Node implements Savable {
             tmpFloatT = new float[maxVertCount * 4];
             tmpFloatT = new float[maxVertCount * 4];
         }
         }
     }
     }
-    
+
     private void gatherGeomerties(Map<Material, List<Geometry>> map, Spatial n, boolean rebatch) {
     private void gatherGeomerties(Map<Material, List<Geometry>> map, Spatial n, boolean rebatch) {
-        
+
         if (n.getClass() == Geometry.class) {
         if (n.getClass() == Geometry.class) {
-            
+
             if (!isBatch(n) && n.getBatchHint() != BatchHint.Never) {
             if (!isBatch(n) && n.getBatchHint() != BatchHint.Never) {
                 Geometry g = (Geometry) n;
                 Geometry g = (Geometry) n;
                 if (!g.isBatched() || rebatch) {
                 if (!g.isBatched() || rebatch) {
@@ -259,7 +259,7 @@ public class BatchNode extends Node implements Savable {
                     if (list == null) {
                     if (list == null) {
                         //trying to compare materials with the isEqual method 
                         //trying to compare materials with the isEqual method 
                         for (Map.Entry<Material, List<Geometry>> mat : map.entrySet()) {
                         for (Map.Entry<Material, List<Geometry>> mat : map.entrySet()) {
-                            if (g.getMaterial().dynamicHashCode() == mat.getKey().dynamicHashCode()) {
+                            if (g.getMaterial().dynamicEquals(mat)) {
                                 list = mat.getValue();
                                 list = mat.getValue();
                             }
                             }
                         }
                         }
@@ -272,7 +272,7 @@ public class BatchNode extends Node implements Savable {
                     list.add(g);
                     list.add(g);
                 }
                 }
             }
             }
-            
+
         } else if (n instanceof Node) {
         } else if (n instanceof Node) {
             for (Spatial child : ((Node) n).getChildren()) {
             for (Spatial child : ((Node) n).getChildren()) {
                 if (child instanceof BatchNode) {
                 if (child instanceof BatchNode) {
@@ -281,18 +281,18 @@ public class BatchNode extends Node implements Savable {
                 gatherGeomerties(map, child, rebatch);
                 gatherGeomerties(map, child, rebatch);
             }
             }
         }
         }
-        
+
     }
     }
-    
+
     private Batch findBatchByMaterial(Material m) {
     private Batch findBatchByMaterial(Material m) {
         for (Batch batch : batches.getArray()) {
         for (Batch batch : batches.getArray()) {
-            if (batch.geometry.getMaterial().dynamicHashCode() == m.dynamicHashCode()) {
+            if (batch.geometry.getMaterial().dynamicEquals(m)) {
                 return batch;
                 return batch;
             }
             }
         }
         }
         return null;
         return null;
     }
     }
-    
+
     private boolean isBatch(Spatial s) {
     private boolean isBatch(Spatial s) {
         for (Batch batch : batches.getArray()) {
         for (Batch batch : batches.getArray()) {
             if (batch.geometry == s) {
             if (batch.geometry == s) {
@@ -373,7 +373,7 @@ public class BatchNode extends Node implements Savable {
 //        oc.write(material, "material", null);
 //        oc.write(material, "material", null);
 
 
     }
     }
-    
+
     @Override
     @Override
     public void read(JmeImporter im) throws IOException {
     public void read(JmeImporter im) throws IOException {
         super.read(im);
         super.read(im);
@@ -410,11 +410,11 @@ public class BatchNode extends Node implements Savable {
     private void mergeGeometries(Mesh outMesh, List<Geometry> geometries) {
     private void mergeGeometries(Mesh outMesh, List<Geometry> geometries) {
         int[] compsForBuf = new int[VertexBuffer.Type.values().length];
         int[] compsForBuf = new int[VertexBuffer.Type.values().length];
         VertexBuffer.Format[] formatForBuf = new VertexBuffer.Format[compsForBuf.length];
         VertexBuffer.Format[] formatForBuf = new VertexBuffer.Format[compsForBuf.length];
-        
+
         int totalVerts = 0;
         int totalVerts = 0;
         int totalTris = 0;
         int totalTris = 0;
         int totalLodLevels = 0;
         int totalLodLevels = 0;
-        
+
         Mesh.Mode mode = null;
         Mesh.Mode mode = null;
         for (Geometry geom : geometries) {
         for (Geometry geom : geometries) {
             totalVerts += geom.getVertexCount();
             totalVerts += geom.getVertexCount();
@@ -445,12 +445,12 @@ public class BatchNode extends Node implements Savable {
                 default:
                 default:
                     throw new UnsupportedOperationException();
                     throw new UnsupportedOperationException();
             }
             }
-            
+
             for (VertexBuffer vb : geom.getMesh().getBufferList().getArray()) {
             for (VertexBuffer vb : geom.getMesh().getBufferList().getArray()) {
                 compsForBuf[vb.getBufferType().ordinal()] = vb.getNumComponents();
                 compsForBuf[vb.getBufferType().ordinal()] = vb.getNumComponents();
                 formatForBuf[vb.getBufferType().ordinal()] = vb.getFormat();
                 formatForBuf[vb.getBufferType().ordinal()] = vb.getFormat();
             }
             }
-            
+
             if (mode != null && mode != listMode) {
             if (mode != null && mode != listMode) {
                 throw new UnsupportedOperationException("Cannot combine different"
                 throw new UnsupportedOperationException("Cannot combine different"
                         + " primitive types: " + mode + " != " + listMode);
                         + " primitive types: " + mode + " != " + listMode);
@@ -458,7 +458,7 @@ public class BatchNode extends Node implements Savable {
             mode = listMode;
             mode = listMode;
             compsForBuf[VertexBuffer.Type.Index.ordinal()] = components;
             compsForBuf[VertexBuffer.Type.Index.ordinal()] = components;
         }
         }
-        
+
         outMesh.setMode(mode);
         outMesh.setMode(mode);
         if (totalVerts >= 65536) {
         if (totalVerts >= 65536) {
             // make sure we create an UnsignedInt buffer so
             // make sure we create an UnsignedInt buffer so
@@ -473,46 +473,46 @@ public class BatchNode extends Node implements Savable {
             if (compsForBuf[i] == 0) {
             if (compsForBuf[i] == 0) {
                 continue;
                 continue;
             }
             }
-            
+
             Buffer data;
             Buffer data;
             if (i == VertexBuffer.Type.Index.ordinal()) {
             if (i == VertexBuffer.Type.Index.ordinal()) {
                 data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalTris);
                 data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalTris);
             } else {
             } else {
                 data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalVerts);
                 data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalVerts);
             }
             }
-            
+
             VertexBuffer vb = new VertexBuffer(VertexBuffer.Type.values()[i]);
             VertexBuffer vb = new VertexBuffer(VertexBuffer.Type.values()[i]);
             vb.setupData(VertexBuffer.Usage.Dynamic, compsForBuf[i], formatForBuf[i], data);
             vb.setupData(VertexBuffer.Usage.Dynamic, compsForBuf[i], formatForBuf[i], data);
             outMesh.setBuffer(vb);
             outMesh.setBuffer(vb);
         }
         }
-        
+
         int globalVertIndex = 0;
         int globalVertIndex = 0;
         int globalTriIndex = 0;
         int globalTriIndex = 0;
-        
+
         for (Geometry geom : geometries) {
         for (Geometry geom : geometries) {
             Mesh inMesh = geom.getMesh();
             Mesh inMesh = geom.getMesh();
             if (!isBatch(geom)) {
             if (!isBatch(geom)) {
                 geom.batch(this, globalVertIndex);
                 geom.batch(this, globalVertIndex);
             }
             }
-            
+
             int geomVertCount = inMesh.getVertexCount();
             int geomVertCount = inMesh.getVertexCount();
             int geomTriCount = inMesh.getTriangleCount();
             int geomTriCount = inMesh.getTriangleCount();
-            
+
             for (int bufType = 0; bufType < compsForBuf.length; bufType++) {
             for (int bufType = 0; bufType < compsForBuf.length; bufType++) {
                 VertexBuffer inBuf = inMesh.getBuffer(VertexBuffer.Type.values()[bufType]);
                 VertexBuffer inBuf = inMesh.getBuffer(VertexBuffer.Type.values()[bufType]);
-                
+
                 VertexBuffer outBuf = outMesh.getBuffer(VertexBuffer.Type.values()[bufType]);
                 VertexBuffer outBuf = outMesh.getBuffer(VertexBuffer.Type.values()[bufType]);
-                
+
                 if (outBuf == null) {
                 if (outBuf == null) {
                     continue;
                     continue;
                 }
                 }
-                
+
                 if (VertexBuffer.Type.Index.ordinal() == bufType) {
                 if (VertexBuffer.Type.Index.ordinal() == bufType) {
                     int components = compsForBuf[bufType];
                     int components = compsForBuf[bufType];
-                    
+
                     IndexBuffer inIdx = inMesh.getIndicesAsList();
                     IndexBuffer inIdx = inMesh.getIndicesAsList();
                     IndexBuffer outIdx = outMesh.getIndexBuffer();
                     IndexBuffer outIdx = outMesh.getIndexBuffer();
-                    
+
                     for (int tri = 0; tri < geomTriCount; tri++) {
                     for (int tri = 0; tri < geomTriCount; tri++) {
                         for (int comp = 0; comp < components; comp++) {
                         for (int comp = 0; comp < components; comp++) {
                             int idx = inIdx.get(tri * components + comp) + globalVertIndex;
                             int idx = inIdx.get(tri * components + comp) + globalVertIndex;
@@ -538,17 +538,17 @@ public class BatchNode extends Node implements Savable {
 //                    }
 //                    }
                 }
                 }
             }
             }
-            
+
             globalVertIndex += geomVertCount;
             globalVertIndex += geomVertCount;
             globalTriIndex += geomTriCount;
             globalTriIndex += geomTriCount;
         }
         }
     }
     }
-    
+
     private void doTransforms(FloatBuffer bufPos, FloatBuffer bufNorm, int start, int end, Matrix4f transform) {
     private void doTransforms(FloatBuffer bufPos, FloatBuffer bufNorm, int start, int end, Matrix4f transform) {
         TempVars vars = TempVars.get();
         TempVars vars = TempVars.get();
         Vector3f pos = vars.vect1;
         Vector3f pos = vars.vect1;
         Vector3f norm = vars.vect2;
         Vector3f norm = vars.vect2;
-        
+
         int length = (end - start) * 3;
         int length = (end - start) * 3;
 
 
         // offset is given in element units
         // offset is given in element units
@@ -566,10 +566,10 @@ public class BatchNode extends Node implements Savable {
             norm.y = tmpFloatN[index++];
             norm.y = tmpFloatN[index++];
             pos.z = tmpFloat[index];
             pos.z = tmpFloat[index];
             norm.z = tmpFloatN[index];
             norm.z = tmpFloatN[index];
-            
+
             transform.mult(pos, pos);
             transform.mult(pos, pos);
             transform.multNormal(norm, norm);
             transform.multNormal(norm, norm);
-            
+
             index -= 2;
             index -= 2;
             tmpFloat[index] = pos.x;
             tmpFloat[index] = pos.x;
             tmpFloatN[index++] = norm.x;
             tmpFloatN[index++] = norm.x;
@@ -577,7 +577,7 @@ public class BatchNode extends Node implements Savable {
             tmpFloatN[index++] = norm.y;
             tmpFloatN[index++] = norm.y;
             tmpFloat[index] = pos.z;
             tmpFloat[index] = pos.z;
             tmpFloatN[index++] = norm.z;
             tmpFloatN[index++] = norm.z;
-            
+
         }
         }
         vars.release();
         vars.release();
         bufPos.position(offset);
         bufPos.position(offset);
@@ -587,13 +587,13 @@ public class BatchNode extends Node implements Savable {
         //using bulk put as it's faster
         //using bulk put as it's faster
         bufNorm.put(tmpFloatN, 0, length);
         bufNorm.put(tmpFloatN, 0, length);
     }
     }
-    
+
     private void doTransformsTangents(FloatBuffer bufPos, FloatBuffer bufNorm, FloatBuffer bufTangents, int start, int end, Matrix4f transform) {
     private void doTransformsTangents(FloatBuffer bufPos, FloatBuffer bufNorm, FloatBuffer bufTangents, int start, int end, Matrix4f transform) {
         TempVars vars = TempVars.get();
         TempVars vars = TempVars.get();
         Vector3f pos = vars.vect1;
         Vector3f pos = vars.vect1;
         Vector3f norm = vars.vect2;
         Vector3f norm = vars.vect2;
         Vector3f tan = vars.vect3;
         Vector3f tan = vars.vect3;
-        
+
         int length = (end - start) * 3;
         int length = (end - start) * 3;
         int tanLength = (end - start) * 4;
         int tanLength = (end - start) * 4;
 
 
@@ -601,14 +601,14 @@ public class BatchNode extends Node implements Savable {
         // convert to be in component units
         // convert to be in component units
         int offset = start * 3;
         int offset = start * 3;
         int tanOffset = start * 4;
         int tanOffset = start * 4;
-        
+
         bufPos.position(offset);
         bufPos.position(offset);
         bufNorm.position(offset);
         bufNorm.position(offset);
         bufTangents.position(tanOffset);
         bufTangents.position(tanOffset);
         bufPos.get(tmpFloat, 0, length);
         bufPos.get(tmpFloat, 0, length);
         bufNorm.get(tmpFloatN, 0, length);
         bufNorm.get(tmpFloatN, 0, length);
         bufTangents.get(tmpFloatT, 0, tanLength);
         bufTangents.get(tmpFloatT, 0, tanLength);
-        
+
         int index = 0;
         int index = 0;
         int tanIndex = 0;
         int tanIndex = 0;
         while (index < length) {
         while (index < length) {
@@ -618,32 +618,32 @@ public class BatchNode extends Node implements Savable {
             norm.y = tmpFloatN[index++];
             norm.y = tmpFloatN[index++];
             pos.z = tmpFloat[index];
             pos.z = tmpFloat[index];
             norm.z = tmpFloatN[index];
             norm.z = tmpFloatN[index];
-            
+
             tan.x = tmpFloatT[tanIndex++];
             tan.x = tmpFloatT[tanIndex++];
             tan.y = tmpFloatT[tanIndex++];
             tan.y = tmpFloatT[tanIndex++];
             tan.z = tmpFloatT[tanIndex++];
             tan.z = tmpFloatT[tanIndex++];
-            
+
             transform.mult(pos, pos);
             transform.mult(pos, pos);
             transform.multNormal(norm, norm);
             transform.multNormal(norm, norm);
             transform.multNormal(tan, tan);
             transform.multNormal(tan, tan);
-            
+
             index -= 2;
             index -= 2;
             tanIndex -= 3;
             tanIndex -= 3;
-            
+
             tmpFloat[index] = pos.x;
             tmpFloat[index] = pos.x;
             tmpFloatN[index++] = norm.x;
             tmpFloatN[index++] = norm.x;
             tmpFloat[index] = pos.y;
             tmpFloat[index] = pos.y;
             tmpFloatN[index++] = norm.y;
             tmpFloatN[index++] = norm.y;
             tmpFloat[index] = pos.z;
             tmpFloat[index] = pos.z;
             tmpFloatN[index++] = norm.z;
             tmpFloatN[index++] = norm.z;
-            
+
             tmpFloatT[tanIndex++] = tan.x;
             tmpFloatT[tanIndex++] = tan.x;
             tmpFloatT[tanIndex++] = tan.y;
             tmpFloatT[tanIndex++] = tan.y;
             tmpFloatT[tanIndex++] = tan.z;
             tmpFloatT[tanIndex++] = tan.z;
 
 
             //Skipping 4th element of tangent buffer (handedness)
             //Skipping 4th element of tangent buffer (handedness)
             tanIndex++;
             tanIndex++;
-            
+
         }
         }
         vars.release();
         vars.release();
         bufPos.position(offset);
         bufPos.position(offset);
@@ -656,7 +656,7 @@ public class BatchNode extends Node implements Savable {
         //using bulk put as it's faster
         //using bulk put as it's faster
         bufTangents.put(tmpFloatT, 0, tanLength);
         bufTangents.put(tmpFloatT, 0, tanLength);
     }
     }
-    
+
     private void doCopyBuffer(FloatBuffer inBuf, int offset, FloatBuffer outBuf, int componentSize) {
     private void doCopyBuffer(FloatBuffer inBuf, int offset, FloatBuffer outBuf, int componentSize) {
         TempVars vars = TempVars.get();
         TempVars vars = TempVars.get();
         Vector3f pos = vars.vect1;
         Vector3f pos = vars.vect1;
@@ -664,19 +664,19 @@ public class BatchNode extends Node implements Savable {
         // offset is given in element units
         // offset is given in element units
         // convert to be in component units
         // convert to be in component units
         offset *= componentSize;
         offset *= componentSize;
-        
+
         for (int i = 0; i < inBuf.capacity() / componentSize; i++) {
         for (int i = 0; i < inBuf.capacity() / componentSize; i++) {
             pos.x = inBuf.get(i * componentSize + 0);
             pos.x = inBuf.get(i * componentSize + 0);
             pos.y = inBuf.get(i * componentSize + 1);
             pos.y = inBuf.get(i * componentSize + 1);
             pos.z = inBuf.get(i * componentSize + 2);
             pos.z = inBuf.get(i * componentSize + 2);
-            
+
             outBuf.put(offset + i * componentSize + 0, pos.x);
             outBuf.put(offset + i * componentSize + 0, pos.x);
             outBuf.put(offset + i * componentSize + 1, pos.y);
             outBuf.put(offset + i * componentSize + 1, pos.y);
             outBuf.put(offset + i * componentSize + 2, pos.z);
             outBuf.put(offset + i * componentSize + 2, pos.z);
         }
         }
         vars.release();
         vars.release();
     }
     }
-    
+
     protected class Batch {
     protected class Batch {
 
 
         /**
         /**
@@ -693,11 +693,11 @@ public class BatchNode extends Node implements Savable {
         Geometry geometry;
         Geometry geometry;
         boolean needMeshUpdate = false;
         boolean needMeshUpdate = false;
     }
     }
-    
+
     protected void setNeedsFullRebatch(boolean needsFullRebatch) {
     protected void setNeedsFullRebatch(boolean needsFullRebatch) {
         this.needsFullRebatch = needsFullRebatch;
         this.needsFullRebatch = needsFullRebatch;
     }
     }
-    
+
     public int getOffsetIndex(Geometry batchedGeometry) {
     public int getOffsetIndex(Geometry batchedGeometry) {
         return batchedGeometry.startIndex;
         return batchedGeometry.startIndex;
     }
     }

+ 428 - 428
engine/src/tools/jme3tools/optimize/GeometryBatchFactory.java

@@ -1,428 +1,428 @@
-package jme3tools.optimize;
-
-import com.jme3.material.Material;
-import com.jme3.math.Matrix4f;
-import com.jme3.math.Transform;
-import com.jme3.math.Vector3f;
-import com.jme3.scene.Mesh.Mode;
-import com.jme3.scene.*;
-import com.jme3.scene.VertexBuffer.Format;
-import com.jme3.scene.VertexBuffer.Type;
-import com.jme3.scene.VertexBuffer.Usage;
-import com.jme3.scene.mesh.IndexBuffer;
-import com.jme3.util.BufferUtils;
-import com.jme3.util.IntMap.Entry;
-import java.nio.Buffer;
-import java.nio.FloatBuffer;
-import java.nio.ShortBuffer;
-import java.util.*;
-import java.util.logging.Logger;
-
-public class GeometryBatchFactory {
-
-    private static final Logger logger = Logger.getLogger(GeometryBatchFactory.class.getName());
-
-    private static void doTransformVerts(FloatBuffer inBuf, int offset, FloatBuffer outBuf, Matrix4f transform) {
-        Vector3f pos = new Vector3f();
-
-        // offset is given in element units
-        // convert to be in component units
-        offset *= 3;
-
-        for (int i = 0; i < inBuf.capacity() / 3; i++) {
-            pos.x = inBuf.get(i * 3 + 0);
-            pos.y = inBuf.get(i * 3 + 1);
-            pos.z = inBuf.get(i * 3 + 2);
-
-            transform.mult(pos, pos);
-
-            outBuf.put(offset + i * 3 + 0, pos.x);
-            outBuf.put(offset + i * 3 + 1, pos.y);
-            outBuf.put(offset + i * 3 + 2, pos.z);
-        }
-    }
-
-    private static void doTransformNorms(FloatBuffer inBuf, int offset, FloatBuffer outBuf, Matrix4f transform) {
-        Vector3f norm = new Vector3f();
-
-        // offset is given in element units
-        // convert to be in component units
-        offset *= 3;
-
-        for (int i = 0; i < inBuf.capacity() / 3; i++) {
-            norm.x = inBuf.get(i * 3 + 0);
-            norm.y = inBuf.get(i * 3 + 1);
-            norm.z = inBuf.get(i * 3 + 2);
-
-            transform.multNormal(norm, norm);
-
-            outBuf.put(offset + i * 3 + 0, norm.x);
-            outBuf.put(offset + i * 3 + 1, norm.y);
-            outBuf.put(offset + i * 3 + 2, norm.z);
-        }
-    }
-
-    private static void doTransformTangents(FloatBuffer inBuf, int offset, int components, FloatBuffer outBuf, Matrix4f transform) {
-        Vector3f tan = new Vector3f();
-
-        // offset is given in element units
-        // convert to be in component units
-        offset *= components;
-
-        for (int i = 0; i < inBuf.capacity() / components; i++) {
-            tan.x = inBuf.get(i * components + 0);
-            tan.y = inBuf.get(i * components + 1);
-            tan.z = inBuf.get(i * components + 2);
-
-            transform.multNormal(tan, tan);
-
-            outBuf.put(offset + i * components + 0, tan.x);
-            outBuf.put(offset + i * components + 1, tan.y);
-            outBuf.put(offset + i * components + 2, tan.z);
-
-            if (components == 4) {
-                outBuf.put(offset + i * components + 3, inBuf.get(i * components + 3));
-            }
-        }
-    }
-
-    /**
-     * Merges all geometries in the collection into
-     * the output mesh. Creates a new material using the TextureAtlas.
-     * 
-     * @param geometries
-     * @param outMesh
-     */
-    public static void mergeGeometries(Collection<Geometry> geometries, Mesh outMesh) {
-        int[] compsForBuf = new int[VertexBuffer.Type.values().length];
-        Format[] formatForBuf = new Format[compsForBuf.length];
-
-        int totalVerts = 0;
-        int totalTris = 0;
-        int totalLodLevels = 0;
-
-        Mode mode = null;
-        for (Geometry geom : geometries) {
-            totalVerts += geom.getVertexCount();
-            totalTris += geom.getTriangleCount();
-            totalLodLevels = Math.min(totalLodLevels, geom.getMesh().getNumLodLevels());
-
-            Mode listMode;
-            int components;
-            switch (geom.getMesh().getMode()) {
-                case Points:
-                    listMode = Mode.Points;
-                    components = 1;
-                    break;
-                case LineLoop:
-                case LineStrip:
-                case Lines:
-                    listMode = Mode.Lines;
-                    components = 2;
-                    break;
-                case TriangleFan:
-                case TriangleStrip:
-                case Triangles:
-                    listMode = Mode.Triangles;
-                    components = 3;
-                    break;
-                default:
-                    throw new UnsupportedOperationException();
-            }
-
-            for (VertexBuffer vb : geom.getMesh().getBufferList().getArray()) {
-                compsForBuf[vb.getBufferType().ordinal()] = vb.getNumComponents();
-                formatForBuf[vb.getBufferType().ordinal()] = vb.getFormat();
-            }
-
-            if (mode != null && mode != listMode) {
-                throw new UnsupportedOperationException("Cannot combine different"
-                        + " primitive types: " + mode + " != " + listMode);
-            }
-            mode = listMode;
-            compsForBuf[Type.Index.ordinal()] = components;
-        }
-
-        outMesh.setMode(mode);
-        if (totalVerts >= 65536) {
-            // make sure we create an UnsignedInt buffer so
-            // we can fit all of the meshes
-            formatForBuf[Type.Index.ordinal()] = Format.UnsignedInt;
-        } else {
-            formatForBuf[Type.Index.ordinal()] = Format.UnsignedShort;
-        }
-
-        // generate output buffers based on retrieved info
-        for (int i = 0; i < compsForBuf.length; i++) {
-            if (compsForBuf[i] == 0) {
-                continue;
-            }
-
-            Buffer data;
-            if (i == Type.Index.ordinal()) {
-                data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalTris);
-            } else {
-                data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalVerts);
-            }
-
-            VertexBuffer vb = new VertexBuffer(Type.values()[i]);
-            vb.setupData(Usage.Static, compsForBuf[i], formatForBuf[i], data);
-            outMesh.setBuffer(vb);
-        }
-
-        int globalVertIndex = 0;
-        int globalTriIndex = 0;
-
-        for (Geometry geom : geometries) {
-            Mesh inMesh = geom.getMesh();
-            geom.computeWorldMatrix();
-            Matrix4f worldMatrix = geom.getWorldMatrix();
-
-            int geomVertCount = inMesh.getVertexCount();
-            int geomTriCount = inMesh.getTriangleCount();
-
-            for (int bufType = 0; bufType < compsForBuf.length; bufType++) {
-                VertexBuffer inBuf = inMesh.getBuffer(Type.values()[bufType]);
-                VertexBuffer outBuf = outMesh.getBuffer(Type.values()[bufType]);
-
-                if (inBuf == null || outBuf == null) {
-                    continue;
-                }
-
-                if (Type.Index.ordinal() == bufType) {
-                    int components = compsForBuf[bufType];
-
-                    IndexBuffer inIdx = inMesh.getIndicesAsList();
-                    IndexBuffer outIdx = outMesh.getIndexBuffer();
-
-                    for (int tri = 0; tri < geomTriCount; tri++) {
-                        for (int comp = 0; comp < components; comp++) {
-                            int idx = inIdx.get(tri * components + comp) + globalVertIndex;
-                            outIdx.put((globalTriIndex + tri) * components + comp, idx);
-                        }
-                    }
-                } else if (Type.Position.ordinal() == bufType) {
-                    FloatBuffer inPos = (FloatBuffer) inBuf.getDataReadOnly();
-                    FloatBuffer outPos = (FloatBuffer) outBuf.getData();
-                    doTransformVerts(inPos, globalVertIndex, outPos, worldMatrix);
-                } else if (Type.Normal.ordinal() == bufType) {
-                    FloatBuffer inPos = (FloatBuffer) inBuf.getDataReadOnly();
-                    FloatBuffer outPos = (FloatBuffer) outBuf.getData();
-                    doTransformNorms(inPos, globalVertIndex, outPos, worldMatrix);
-                } else if (Type.Tangent.ordinal() == bufType) {
-                    FloatBuffer inPos = (FloatBuffer) inBuf.getDataReadOnly();
-                    FloatBuffer outPos = (FloatBuffer) outBuf.getData();
-                    int components = inBuf.getNumComponents();
-                    doTransformTangents(inPos, globalVertIndex, components, outPos, worldMatrix);
-                } else {
-                    inBuf.copyElements(0, outBuf, globalVertIndex, geomVertCount);
-                }
-            }
-
-            globalVertIndex += geomVertCount;
-            globalTriIndex += geomTriCount;
-        }
-    }
-
-    public static void makeLods(Collection<Geometry> geometries, Mesh outMesh) {
-        int lodLevels = 0;
-        int[] lodSize = null;
-        int index = 0;
-        for (Geometry g : geometries) {
-            if (lodLevels == 0) {
-                lodLevels = g.getMesh().getNumLodLevels();
-            }
-            if (lodSize == null) {
-                lodSize = new int[lodLevels];
-            }
-            for (int i = 0; i < lodLevels; i++) {
-                lodSize[i] += g.getMesh().getLodLevel(i).getData().capacity();
-                //if( i == 0) System.out.println(index + " " +lodSize[i]);
-            }
-            index++;
-        }
-        int[][] lodData = new int[lodLevels][];
-        for (int i = 0; i < lodLevels; i++) {
-            lodData[i] = new int[lodSize[i]];
-        }
-        VertexBuffer[] lods = new VertexBuffer[lodLevels];
-        int bufferPos[] = new int[lodLevels];
-        //int index = 0;
-        int numOfVertices = 0;
-        int curGeom = 0;
-        for (Geometry g : geometries) {
-            if (numOfVertices == 0) {
-                numOfVertices = g.getVertexCount();
-            }
-            for (int i = 0; i < lodLevels; i++) {
-                ShortBuffer buffer = (ShortBuffer) g.getMesh().getLodLevel(i).getDataReadOnly();
-                //System.out.println("buffer: " + buffer.capacity() + " limit: " + lodSize[i] + " " + index);
-                for (int j = 0; j < buffer.capacity(); j++) {
-                    lodData[i][bufferPos[i] + j] = buffer.get() + numOfVertices * curGeom;
-                    //bufferPos[i]++;
-                }
-                bufferPos[i] += buffer.capacity();
-            }
-            curGeom++;
-        }
-        for (int i = 0; i < lodLevels; i++) {
-            lods[i] = new VertexBuffer(Type.Index);
-            lods[i].setupData(Usage.Dynamic, 1, Format.UnsignedInt, BufferUtils.createIntBuffer(lodData[i]));
-        }
-        System.out.println(lods.length);
-        outMesh.setLodLevels(lods);
-    }
-
-    public static List<Geometry> makeBatches(Collection<Geometry> geometries) {
-        return makeBatches(geometries, false);
-    }
-
-    /**
-     * Batches a collection of Geometries so that all with the same material get combined.
-     * @param geometries The Geometries to combine
-     * @return A List of newly created Geometries, each with a  distinct material
-     */
-    public static List<Geometry> makeBatches(Collection<Geometry> geometries, boolean useLods) {
-        ArrayList<Geometry> retVal = new ArrayList<Geometry>();
-        HashMap<Material, List<Geometry>> matToGeom = new HashMap<Material, List<Geometry>>();
-
-        for (Geometry geom : geometries) {
-            List<Geometry> outList = matToGeom.get(geom.getMaterial());
-            if (outList == null) {
-                //trying to compare materials with the contentEquals method 
-                for (Material mat : matToGeom.keySet()) {
-                    if (geom.getMaterial().dynamicHashCode() == mat.dynamicHashCode()) {
-                        outList = matToGeom.get(mat);
-                    }
-                }
-            }
-            if (outList == null) {
-                outList = new ArrayList<Geometry>();
-                matToGeom.put(geom.getMaterial(), outList);
-            }
-            outList.add(geom);
-        }
-
-        int batchNum = 0;
-        for (Map.Entry<Material, List<Geometry>> entry : matToGeom.entrySet()) {
-            Material mat = entry.getKey();
-            List<Geometry> geomsForMat = entry.getValue();
-            Mesh mesh = new Mesh();
-            mergeGeometries(geomsForMat, mesh);
-            // lods
-            if (useLods) {
-                makeLods(geomsForMat, mesh);
-            }
-            mesh.updateCounts();
-            mesh.updateBound();
-
-            Geometry out = new Geometry("batch[" + (batchNum++) + "]", mesh);
-            out.setMaterial(mat);
-            retVal.add(out);
-        }
-
-        return retVal;
-    }
-
-    public static void gatherGeoms(Spatial scene, List<Geometry> geoms) {
-        if (scene instanceof Node) {
-            Node node = (Node) scene;
-            for (Spatial child : node.getChildren()) {
-                gatherGeoms(child, geoms);
-            }
-        } else if (scene instanceof Geometry) {
-            geoms.add((Geometry) scene);
-        }
-    }
-
-    /**
-     * Optimizes a scene by combining Geometry with the same material.
-     * All Geometries found in the scene are detached from their parent and
-     * a new Node containing the optimized Geometries is attached.
-     * @param scene The scene to optimize
-     * @return The newly created optimized geometries attached to a node
-     */
-    public static Spatial optimize(Node scene) {
-        return optimize(scene, false);
-    }
-
-    /**
-     * Optimizes a scene by combining Geometry with the same material.
-     * All Geometries found in the scene are detached from their parent and
-     * a new Node containing the optimized Geometries is attached.
-     * @param scene The scene to optimize
-     * @param useLods true if you want the resulting geometry to keep lod information
-     * @return The newly created optimized geometries attached to a node
-     */
-    public static Node optimize(Node scene, boolean useLods) {
-        ArrayList<Geometry> geoms = new ArrayList<Geometry>();
-
-        gatherGeoms(scene, geoms);
-
-        List<Geometry> batchedGeoms = makeBatches(geoms, useLods);
-        for (Geometry geom : batchedGeoms) {
-            scene.attachChild(geom);
-        }
-
-        for (Iterator<Geometry> it = geoms.iterator(); it.hasNext();) {
-            Geometry geometry = it.next();
-            geometry.removeFromParent();
-        }
-
-        // Since the scene is returned unaltered the transform must be reset
-        scene.setLocalTransform(Transform.IDENTITY);
-
-        return scene;
-    }
-
-    public static void printMesh(Mesh mesh) {
-        for (int bufType = 0; bufType < Type.values().length; bufType++) {
-            VertexBuffer outBuf = mesh.getBuffer(Type.values()[bufType]);
-            if (outBuf == null) {
-                continue;
-            }
-
-            System.out.println(outBuf.getBufferType() + ": ");
-            for (int vert = 0; vert < outBuf.getNumElements(); vert++) {
-                String str = "[";
-                for (int comp = 0; comp < outBuf.getNumComponents(); comp++) {
-                    Object val = outBuf.getElementComponent(vert, comp);
-                    outBuf.setElementComponent(vert, comp, val);
-                    val = outBuf.getElementComponent(vert, comp);
-                    str += val;
-                    if (comp != outBuf.getNumComponents() - 1) {
-                        str += ", ";
-                    }
-                }
-                str += "]";
-                System.out.println(str);
-            }
-            System.out.println("------");
-        }
-    }
-
-    public static void main(String[] args) {
-        Mesh mesh = new Mesh();
-        mesh.setBuffer(Type.Position, 3, new float[]{
-                    0, 0, 0,
-                    1, 0, 0,
-                    1, 1, 0,
-                    0, 1, 0
-                });
-        mesh.setBuffer(Type.Index, 2, new short[]{
-                    0, 1,
-                    1, 2,
-                    2, 3,
-                    3, 0
-                });
-
-        Geometry g1 = new Geometry("g1", mesh);
-
-        ArrayList<Geometry> geoms = new ArrayList<Geometry>();
-        geoms.add(g1);
-
-        Mesh outMesh = new Mesh();
-        mergeGeometries(geoms, outMesh);
-        printMesh(outMesh);
-    }
-}
+package jme3tools.optimize;
+
+import com.jme3.material.Material;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Transform;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Mesh.Mode;
+import com.jme3.scene.*;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.VertexBuffer.Usage;
+import com.jme3.scene.mesh.IndexBuffer;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.IntMap.Entry;
+import java.nio.Buffer;
+import java.nio.FloatBuffer;
+import java.nio.ShortBuffer;
+import java.util.*;
+import java.util.logging.Logger;
+
+public class GeometryBatchFactory {
+
+    private static final Logger logger = Logger.getLogger(GeometryBatchFactory.class.getName());
+
+    private static void doTransformVerts(FloatBuffer inBuf, int offset, FloatBuffer outBuf, Matrix4f transform) {
+        Vector3f pos = new Vector3f();
+
+        // offset is given in element units
+        // convert to be in component units
+        offset *= 3;
+
+        for (int i = 0; i < inBuf.capacity() / 3; i++) {
+            pos.x = inBuf.get(i * 3 + 0);
+            pos.y = inBuf.get(i * 3 + 1);
+            pos.z = inBuf.get(i * 3 + 2);
+
+            transform.mult(pos, pos);
+
+            outBuf.put(offset + i * 3 + 0, pos.x);
+            outBuf.put(offset + i * 3 + 1, pos.y);
+            outBuf.put(offset + i * 3 + 2, pos.z);
+        }
+    }
+
+    private static void doTransformNorms(FloatBuffer inBuf, int offset, FloatBuffer outBuf, Matrix4f transform) {
+        Vector3f norm = new Vector3f();
+
+        // offset is given in element units
+        // convert to be in component units
+        offset *= 3;
+
+        for (int i = 0; i < inBuf.capacity() / 3; i++) {
+            norm.x = inBuf.get(i * 3 + 0);
+            norm.y = inBuf.get(i * 3 + 1);
+            norm.z = inBuf.get(i * 3 + 2);
+
+            transform.multNormal(norm, norm);
+
+            outBuf.put(offset + i * 3 + 0, norm.x);
+            outBuf.put(offset + i * 3 + 1, norm.y);
+            outBuf.put(offset + i * 3 + 2, norm.z);
+        }
+    }
+
+    private static void doTransformTangents(FloatBuffer inBuf, int offset, int components, FloatBuffer outBuf, Matrix4f transform) {
+        Vector3f tan = new Vector3f();
+
+        // offset is given in element units
+        // convert to be in component units
+        offset *= components;
+
+        for (int i = 0; i < inBuf.capacity() / components; i++) {
+            tan.x = inBuf.get(i * components + 0);
+            tan.y = inBuf.get(i * components + 1);
+            tan.z = inBuf.get(i * components + 2);
+
+            transform.multNormal(tan, tan);
+
+            outBuf.put(offset + i * components + 0, tan.x);
+            outBuf.put(offset + i * components + 1, tan.y);
+            outBuf.put(offset + i * components + 2, tan.z);
+
+            if (components == 4) {
+                outBuf.put(offset + i * components + 3, inBuf.get(i * components + 3));
+            }
+        }
+    }
+
+    /**
+     * Merges all geometries in the collection into
+     * the output mesh. Creates a new material using the TextureAtlas.
+     * 
+     * @param geometries
+     * @param outMesh
+     */
+    public static void mergeGeometries(Collection<Geometry> geometries, Mesh outMesh) {
+        int[] compsForBuf = new int[VertexBuffer.Type.values().length];
+        Format[] formatForBuf = new Format[compsForBuf.length];
+
+        int totalVerts = 0;
+        int totalTris = 0;
+        int totalLodLevels = 0;
+
+        Mode mode = null;
+        for (Geometry geom : geometries) {
+            totalVerts += geom.getVertexCount();
+            totalTris += geom.getTriangleCount();
+            totalLodLevels = Math.min(totalLodLevels, geom.getMesh().getNumLodLevels());
+
+            Mode listMode;
+            int components;
+            switch (geom.getMesh().getMode()) {
+                case Points:
+                    listMode = Mode.Points;
+                    components = 1;
+                    break;
+                case LineLoop:
+                case LineStrip:
+                case Lines:
+                    listMode = Mode.Lines;
+                    components = 2;
+                    break;
+                case TriangleFan:
+                case TriangleStrip:
+                case Triangles:
+                    listMode = Mode.Triangles;
+                    components = 3;
+                    break;
+                default:
+                    throw new UnsupportedOperationException();
+            }
+
+            for (VertexBuffer vb : geom.getMesh().getBufferList().getArray()) {
+                compsForBuf[vb.getBufferType().ordinal()] = vb.getNumComponents();
+                formatForBuf[vb.getBufferType().ordinal()] = vb.getFormat();
+            }
+
+            if (mode != null && mode != listMode) {
+                throw new UnsupportedOperationException("Cannot combine different"
+                        + " primitive types: " + mode + " != " + listMode);
+            }
+            mode = listMode;
+            compsForBuf[Type.Index.ordinal()] = components;
+        }
+
+        outMesh.setMode(mode);
+        if (totalVerts >= 65536) {
+            // make sure we create an UnsignedInt buffer so
+            // we can fit all of the meshes
+            formatForBuf[Type.Index.ordinal()] = Format.UnsignedInt;
+        } else {
+            formatForBuf[Type.Index.ordinal()] = Format.UnsignedShort;
+        }
+
+        // generate output buffers based on retrieved info
+        for (int i = 0; i < compsForBuf.length; i++) {
+            if (compsForBuf[i] == 0) {
+                continue;
+            }
+
+            Buffer data;
+            if (i == Type.Index.ordinal()) {
+                data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalTris);
+            } else {
+                data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalVerts);
+            }
+
+            VertexBuffer vb = new VertexBuffer(Type.values()[i]);
+            vb.setupData(Usage.Static, compsForBuf[i], formatForBuf[i], data);
+            outMesh.setBuffer(vb);
+        }
+
+        int globalVertIndex = 0;
+        int globalTriIndex = 0;
+
+        for (Geometry geom : geometries) {
+            Mesh inMesh = geom.getMesh();
+            geom.computeWorldMatrix();
+            Matrix4f worldMatrix = geom.getWorldMatrix();
+
+            int geomVertCount = inMesh.getVertexCount();
+            int geomTriCount = inMesh.getTriangleCount();
+
+            for (int bufType = 0; bufType < compsForBuf.length; bufType++) {
+                VertexBuffer inBuf = inMesh.getBuffer(Type.values()[bufType]);
+                VertexBuffer outBuf = outMesh.getBuffer(Type.values()[bufType]);
+
+                if (inBuf == null || outBuf == null) {
+                    continue;
+                }
+
+                if (Type.Index.ordinal() == bufType) {
+                    int components = compsForBuf[bufType];
+
+                    IndexBuffer inIdx = inMesh.getIndicesAsList();
+                    IndexBuffer outIdx = outMesh.getIndexBuffer();
+
+                    for (int tri = 0; tri < geomTriCount; tri++) {
+                        for (int comp = 0; comp < components; comp++) {
+                            int idx = inIdx.get(tri * components + comp) + globalVertIndex;
+                            outIdx.put((globalTriIndex + tri) * components + comp, idx);
+                        }
+                    }
+                } else if (Type.Position.ordinal() == bufType) {
+                    FloatBuffer inPos = (FloatBuffer) inBuf.getDataReadOnly();
+                    FloatBuffer outPos = (FloatBuffer) outBuf.getData();
+                    doTransformVerts(inPos, globalVertIndex, outPos, worldMatrix);
+                } else if (Type.Normal.ordinal() == bufType) {
+                    FloatBuffer inPos = (FloatBuffer) inBuf.getDataReadOnly();
+                    FloatBuffer outPos = (FloatBuffer) outBuf.getData();
+                    doTransformNorms(inPos, globalVertIndex, outPos, worldMatrix);
+                } else if (Type.Tangent.ordinal() == bufType) {
+                    FloatBuffer inPos = (FloatBuffer) inBuf.getDataReadOnly();
+                    FloatBuffer outPos = (FloatBuffer) outBuf.getData();
+                    int components = inBuf.getNumComponents();
+                    doTransformTangents(inPos, globalVertIndex, components, outPos, worldMatrix);
+                } else {
+                    inBuf.copyElements(0, outBuf, globalVertIndex, geomVertCount);
+                }
+            }
+
+            globalVertIndex += geomVertCount;
+            globalTriIndex += geomTriCount;
+        }
+    }
+
+    public static void makeLods(Collection<Geometry> geometries, Mesh outMesh) {
+        int lodLevels = 0;
+        int[] lodSize = null;
+        int index = 0;
+        for (Geometry g : geometries) {
+            if (lodLevels == 0) {
+                lodLevels = g.getMesh().getNumLodLevels();
+            }
+            if (lodSize == null) {
+                lodSize = new int[lodLevels];
+            }
+            for (int i = 0; i < lodLevels; i++) {
+                lodSize[i] += g.getMesh().getLodLevel(i).getData().capacity();
+                //if( i == 0) System.out.println(index + " " +lodSize[i]);
+            }
+            index++;
+        }
+        int[][] lodData = new int[lodLevels][];
+        for (int i = 0; i < lodLevels; i++) {
+            lodData[i] = new int[lodSize[i]];
+        }
+        VertexBuffer[] lods = new VertexBuffer[lodLevels];
+        int bufferPos[] = new int[lodLevels];
+        //int index = 0;
+        int numOfVertices = 0;
+        int curGeom = 0;
+        for (Geometry g : geometries) {
+            if (numOfVertices == 0) {
+                numOfVertices = g.getVertexCount();
+            }
+            for (int i = 0; i < lodLevels; i++) {
+                ShortBuffer buffer = (ShortBuffer) g.getMesh().getLodLevel(i).getDataReadOnly();
+                //System.out.println("buffer: " + buffer.capacity() + " limit: " + lodSize[i] + " " + index);
+                for (int j = 0; j < buffer.capacity(); j++) {
+                    lodData[i][bufferPos[i] + j] = buffer.get() + numOfVertices * curGeom;
+                    //bufferPos[i]++;
+                }
+                bufferPos[i] += buffer.capacity();
+            }
+            curGeom++;
+        }
+        for (int i = 0; i < lodLevels; i++) {
+            lods[i] = new VertexBuffer(Type.Index);
+            lods[i].setupData(Usage.Dynamic, 1, Format.UnsignedInt, BufferUtils.createIntBuffer(lodData[i]));
+        }
+        System.out.println(lods.length);
+        outMesh.setLodLevels(lods);
+    }
+
+    public static List<Geometry> makeBatches(Collection<Geometry> geometries) {
+        return makeBatches(geometries, false);
+    }
+
+    /**
+     * Batches a collection of Geometries so that all with the same material get combined.
+     * @param geometries The Geometries to combine
+     * @return A List of newly created Geometries, each with a  distinct material
+     */
+    public static List<Geometry> makeBatches(Collection<Geometry> geometries, boolean useLods) {
+        ArrayList<Geometry> retVal = new ArrayList<Geometry>();
+        HashMap<Material, List<Geometry>> matToGeom = new HashMap<Material, List<Geometry>>();
+
+        for (Geometry geom : geometries) {
+            List<Geometry> outList = matToGeom.get(geom.getMaterial());
+            if (outList == null) {
+                //trying to compare materials with the contentEquals method 
+                for (Material mat : matToGeom.keySet()) {
+                    if (geom.getMaterial().dynamicEquals(mat)){
+                        outList = matToGeom.get(mat);
+                    }
+                }
+            }
+            if (outList == null) {
+                outList = new ArrayList<Geometry>();
+                matToGeom.put(geom.getMaterial(), outList);
+            }
+            outList.add(geom);
+        }
+
+        int batchNum = 0;
+        for (Map.Entry<Material, List<Geometry>> entry : matToGeom.entrySet()) {
+            Material mat = entry.getKey();
+            List<Geometry> geomsForMat = entry.getValue();
+            Mesh mesh = new Mesh();
+            mergeGeometries(geomsForMat, mesh);
+            // lods
+            if (useLods) {
+                makeLods(geomsForMat, mesh);
+            }
+            mesh.updateCounts();
+            mesh.updateBound();
+
+            Geometry out = new Geometry("batch[" + (batchNum++) + "]", mesh);
+            out.setMaterial(mat);
+            retVal.add(out);
+        }
+
+        return retVal;
+    }
+
+    public static void gatherGeoms(Spatial scene, List<Geometry> geoms) {
+        if (scene instanceof Node) {
+            Node node = (Node) scene;
+            for (Spatial child : node.getChildren()) {
+                gatherGeoms(child, geoms);
+            }
+        } else if (scene instanceof Geometry) {
+            geoms.add((Geometry) scene);
+        }
+    }
+
+    /**
+     * Optimizes a scene by combining Geometry with the same material.
+     * All Geometries found in the scene are detached from their parent and
+     * a new Node containing the optimized Geometries is attached.
+     * @param scene The scene to optimize
+     * @return The newly created optimized geometries attached to a node
+     */
+    public static Spatial optimize(Node scene) {
+        return optimize(scene, false);
+    }
+
+    /**
+     * Optimizes a scene by combining Geometry with the same material.
+     * All Geometries found in the scene are detached from their parent and
+     * a new Node containing the optimized Geometries is attached.
+     * @param scene The scene to optimize
+     * @param useLods true if you want the resulting geometry to keep lod information
+     * @return The newly created optimized geometries attached to a node
+     */
+    public static Node optimize(Node scene, boolean useLods) {
+        ArrayList<Geometry> geoms = new ArrayList<Geometry>();
+
+        gatherGeoms(scene, geoms);
+
+        List<Geometry> batchedGeoms = makeBatches(geoms, useLods);
+        for (Geometry geom : batchedGeoms) {
+            scene.attachChild(geom);
+        }
+
+        for (Iterator<Geometry> it = geoms.iterator(); it.hasNext();) {
+            Geometry geometry = it.next();
+            geometry.removeFromParent();
+        }
+
+        // Since the scene is returned unaltered the transform must be reset
+        scene.setLocalTransform(Transform.IDENTITY);
+
+        return scene;
+    }
+
+    public static void printMesh(Mesh mesh) {
+        for (int bufType = 0; bufType < Type.values().length; bufType++) {
+            VertexBuffer outBuf = mesh.getBuffer(Type.values()[bufType]);
+            if (outBuf == null) {
+                continue;
+            }
+
+            System.out.println(outBuf.getBufferType() + ": ");
+            for (int vert = 0; vert < outBuf.getNumElements(); vert++) {
+                String str = "[";
+                for (int comp = 0; comp < outBuf.getNumComponents(); comp++) {
+                    Object val = outBuf.getElementComponent(vert, comp);
+                    outBuf.setElementComponent(vert, comp, val);
+                    val = outBuf.getElementComponent(vert, comp);
+                    str += val;
+                    if (comp != outBuf.getNumComponents() - 1) {
+                        str += ", ";
+                    }
+                }
+                str += "]";
+                System.out.println(str);
+            }
+            System.out.println("------");
+        }
+    }
+
+    public static void main(String[] args) {
+        Mesh mesh = new Mesh();
+        mesh.setBuffer(Type.Position, 3, new float[]{
+                    0, 0, 0,
+                    1, 0, 0,
+                    1, 1, 0,
+                    0, 1, 0
+                });
+        mesh.setBuffer(Type.Index, 2, new short[]{
+                    0, 1,
+                    1, 2,
+                    2, 3,
+                    3, 0
+                });
+
+        Geometry g1 = new Geometry("g1", mesh);
+
+        ArrayList<Geometry> geoms = new ArrayList<Geometry>();
+        geoms.add(g1);
+
+        Mesh outMesh = new Mesh();
+        mergeGeometries(geoms, outMesh);
+        printMesh(outMesh);
+    }
+}