OSDN Git Service

GeometryBatchFactory now retain original Lod informations thanks to Rickard
authorremy.bouquet@gmail.com <remy.bouquet@gmail.com@75d07b2b-3a1a-0410-a2c5-0572b91ccdca>
Sat, 11 Jun 2011 08:49:37 +0000 (08:49 +0000)
committerremy.bouquet@gmail.com <remy.bouquet@gmail.com@75d07b2b-3a1a-0410-a2c5-0572b91ccdca>
Sat, 11 Jun 2011 08:49:37 +0000 (08:49 +0000)
see post : http://jmonkeyengine.org/groups/contribution-depot-jme3/forum/topic/lods-for-geometrybatchfactory/

git-svn-id: http://jmonkeyengine.googlecode.com/svn/trunk@7579 75d07b2b-3a1a-0410-a2c5-0572b91ccdca

engine/src/test/jme3test/stress/TestBatchLod.java [new file with mode: 0644]
engine/src/tools/jme3tools/optimize/GeometryBatchFactory.java

diff --git a/engine/src/test/jme3test/stress/TestBatchLod.java b/engine/src/test/jme3test/stress/TestBatchLod.java
new file mode 100644 (file)
index 0000000..bd1ff5c
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.stress;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.KeyInput;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.control.LodControl;
+import jme3tools.optimize.GeometryBatchFactory;
+
+public class TestBatchLod extends SimpleApplication {
+
+    private boolean lod = false;
+
+    public static void main(String[] args) {
+        TestBatchLod app = new TestBatchLod();
+        app.start();
+    }
+
+    public void simpleInitApp() {
+//        inputManager.registerKeyBinding("USELOD", KeyInput.KEY_L);
+
+        DirectionalLight dl = new DirectionalLight();
+        dl.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
+        rootNode.addLight(dl);
+
+        Node teapotNode = (Node) assetManager.loadModel("Models/Teapot/Teapot.mesh.xml");
+        Geometry teapot = (Geometry) teapotNode.getChild(0);
+
+        Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+        mat.setFloat("Shininess", 16f);
+        mat.setBoolean("VertexLighting", true);
+        teapot.setMaterial(mat);
+
+        // show normals as material
+        //Material mat = new Material(assetManager, "Common/MatDefs/Misc/ShowNormals.j3md");
+        flyCam.setMoveSpeed(5);
+        for (int y = -5; y < 5; y++) {
+            for (int x = -5; x < 5; x++) {
+                Geometry clonePot = teapot.clone();
+
+                //clonePot.setMaterial(mat);
+                clonePot.setLocalTranslation(x * .5f, 0, y * .5f);
+                clonePot.setLocalScale(.15f);
+                clonePot.setMaterial(mat);
+                rootNode.attachChild(clonePot);
+            }
+        }
+        GeometryBatchFactory.optimize(rootNode, true);
+        LodControl control = new LodControl();
+        rootNode.getChild(0).addControl(control);
+        cam.setLocation(new Vector3f(-1.0748308f, 1.35778f, -1.5380064f));
+        cam.setRotation(new Quaternion(0.18343268f, 0.34531063f, -0.069015436f, 0.9177962f));
+
+    }
+}
index 3d613ee..2eb4623 100644 (file)
@@ -1,8 +1,6 @@
 package jme3tools.optimize;
 
-import com.jme3.font.BitmapText;
 import com.jme3.material.Material;
-import com.jme3.math.FastMath;
 import com.jme3.math.Matrix4f;
 import com.jme3.math.Vector3f;
 import com.jme3.scene.Geometry;
@@ -17,10 +15,11 @@ import com.jme3.scene.VertexBuffer.Usage;
 import com.jme3.scene.mesh.IndexBuffer;
 import com.jme3.scene.mesh.VirtualIndexBuffer;
 import com.jme3.scene.mesh.WrappedIndexBuffer;
-import com.jme3.scene.shape.Quad;
+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.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
@@ -30,43 +29,43 @@ import java.util.Map;
 
 public class GeometryBatchFactory {
 
-    private static void doTransformVerts(FloatBuffer inBuf, int offset, FloatBuffer outBuf, Matrix4f transform){
+    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);
+        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);
+            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){
+    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);
+        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);
+            outBuf.put(offset + i * 3 + 0, norm.x);
+            outBuf.put(offset + i * 3 + 1, norm.y);
+            outBuf.put(offset + i * 3 + 2, norm.z);
         }
     }
 
@@ -77,23 +76,23 @@ public class GeometryBatchFactory {
      * @param geometries
      * @param outMesh
      */
-    public static void mergeGeometries(Collection<Geometry> geometries, Mesh 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 totalTris = 0;
         int totalLodLevels = 0;
 
         Mode mode = null;
-        for (Geometry geom : geometries){
+        for (Geometry geom : geometries) {
             totalVerts += geom.getVertexCount();
-            totalTris  += geom.getTriangleCount();
+            totalTris += geom.getTriangleCount();
             totalLodLevels = Math.min(totalLodLevels, geom.getMesh().getNumLodLevels());
 
             Mode listMode;
             int components;
-            switch (geom.getMesh().getMode()){
+            switch (geom.getMesh().getMode()) {
                 case Points:
                     listMode = Mode.Points;
                     components = 1;
@@ -113,38 +112,39 @@ public class GeometryBatchFactory {
                 default:
                     throw new UnsupportedOperationException();
             }
-            
-            for (Entry<VertexBuffer> entry : geom.getMesh().getBuffers()){
+
+            for (Entry<VertexBuffer> entry : geom.getMesh().getBuffers()) {
                 compsForBuf[entry.getKey()] = entry.getValue().getNumComponents();
                 formatForBuf[entry.getKey()] = entry.getValue().getFormat();
             }
 
-            if (mode != null && mode != listMode){
+            if (mode != null && mode != listMode) {
                 throw new UnsupportedOperationException("Cannot combine different"
-                                                      + " primitive types: " + mode + " != " + listMode);
+                        + " primitive types: " + mode + " != " + listMode);
             }
             mode = listMode;
             compsForBuf[Type.Index.ordinal()] = components;
         }
 
         outMesh.setMode(mode);
-        if (totalVerts >= 65536){
+        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{
+        } 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)
+        for (int i = 0; i < compsForBuf.length; i++) {
+            if (compsForBuf[i] == 0) {
                 continue;
+            }
 
             Buffer data;
-            if (i == Type.Index.ordinal()){
+            if (i == Type.Index.ordinal()) {
                 data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalTris);
-            }else{
+            } else {
                 data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalVerts);
             }
 
@@ -154,50 +154,51 @@ public class GeometryBatchFactory {
         }
 
         int globalVertIndex = 0;
-        int globalTriIndex  = 0;
+        int globalTriIndex = 0;
 
-        for (Geometry geom : geometries){
+        for (Geometry geom : geometries) {
             Mesh inMesh = geom.getMesh();
             geom.computeWorldMatrix();
             Matrix4f worldMatrix = geom.getWorldMatrix();
 
             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(Type.values()[bufType]);
                 VertexBuffer outBuf = outMesh.getBuffer(Type.values()[bufType]);
 
-                if (outBuf == null)
+                if (outBuf == null) {
                     continue;
+                }
 
-                if (Type.Index.ordinal() == bufType){
+                if (Type.Index.ordinal() == bufType) {
                     int components = compsForBuf[bufType];
 
                     IndexBuffer inIdx = inMesh.getIndexBuffer();
-                    if (inIdx == null){
+                    if (inIdx == null) {
                         inIdx = new VirtualIndexBuffer(geomVertCount, inMesh.getMode());
-                    }else if (inMesh.getMode() != mode){
+                    } else if (inMesh.getMode() != mode) {
                         inIdx = new WrappedIndexBuffer(inMesh);
                     }
                     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;
+                    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){
+                } else if (Type.Position.ordinal() == bufType) {
                     FloatBuffer inPos = (FloatBuffer) inBuf.getData();
                     FloatBuffer outPos = (FloatBuffer) outBuf.getData();
                     doTransformVerts(inPos, globalVertIndex, outPos, worldMatrix);
-                }else if (Type.Normal.ordinal() == bufType || Type.Tangent.ordinal() == bufType){
+                } else if (Type.Normal.ordinal() == bufType || Type.Tangent.ordinal() == bufType) {
                     FloatBuffer inPos = (FloatBuffer) inBuf.getData();
                     FloatBuffer outPos = (FloatBuffer) outBuf.getData();
                     doTransformNorms(inPos, globalVertIndex, outPos, worldMatrix);
-                }else{
-                    for (int vert = 0; vert < geomVertCount; vert++){
+                } else {
+                    for (int vert = 0; vert < geomVertCount; vert++) {
                         int curGlobalVertIndex = globalVertIndex + vert;
                         inBuf.copyElement(vert, outBuf, curGlobalVertIndex);
                     }
@@ -205,23 +206,76 @@ public class GeometryBatchFactory {
             }
 
             globalVertIndex += geomVertCount;
-            globalTriIndex  += geomTriCount;
+            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).getData();
+                buffer.rewind();
+                //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){
+    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){
+        for (Geometry geom : geometries) {
             List<Geometry> outList = matToGeom.get(geom.getMaterial());
-            if (outList == null){
+            if (outList == null) {
                 outList = new ArrayList<Geometry>();
                 matToGeom.put(geom.getMaterial(), outList);
             }
@@ -229,11 +283,15 @@ public class GeometryBatchFactory {
         }
 
         int batchNum = 0;
-        for (Map.Entry<Material, List<Geometry>> entry : matToGeom.entrySet()){
+        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();
 
@@ -245,14 +303,14 @@ public class GeometryBatchFactory {
         return retVal;
     }
 
-    private static void gatherGeoms(Spatial scene, List<Geometry> geoms){
-        if (scene instanceof Node){
+    private static void gatherGeoms(Spatial scene, List<Geometry> geoms) {
+        if (scene instanceof Node) {
             Node node = (Node) scene;
-            for (Spatial child : node.getChildren()){
+            for (Spatial child : node.getChildren()) {
                 gatherGeoms(child, geoms);
             }
-        }else if (scene instanceof Geometry){
-            geoms.add((Geometry)scene);
+        } else if (scene instanceof Geometry) {
+            geoms.add((Geometry) scene);
         }
     }
 
@@ -263,12 +321,25 @@ public class GeometryBatchFactory {
      * @param scene The scene to optimize
      * @return The newly created optimized geometries attached to a node
      */
-    public static Node optimize(Node scene){
+    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);
-        for (Geometry geom : batchedGeoms){
+        List<Geometry> batchedGeoms = makeBatches(geoms, useLods);
+        for (Geometry geom : batchedGeoms) {
             scene.attachChild(geom);
         }
 
@@ -280,22 +351,24 @@ public class GeometryBatchFactory {
         return scene;
     }
 
-    public static void printMesh(Mesh mesh){
-        for (int bufType = 0; bufType < Type.values().length; bufType++){
+    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)
+            if (outBuf == null) {
                 continue;
+            }
 
             System.out.println(outBuf.getBufferType() + ": ");
-            for (int vert = 0; vert < outBuf.getNumElements(); vert++){
+            for (int vert = 0; vert < outBuf.getNumElements(); vert++) {
                 String str = "[";
-                for (int comp = 0; comp < outBuf.getNumComponents(); comp++){
+                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)
+                    if (comp != outBuf.getNumComponents() - 1) {
                         str += ", ";
+                    }
                 }
                 str += "]";
                 System.out.println(str);
@@ -304,20 +377,20 @@ public class GeometryBatchFactory {
         }
     }
 
-    public static void main(String[] args){
+    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
-        });
+                    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
-        });
+                    0, 1,
+                    1, 2,
+                    2, 3,
+                    3, 0
+                });
 
         Geometry g1 = new Geometry("g1", mesh);