[SOLVED + PATCH] GeometryBatchFactory for skeleton animation done by anyone already?

before I would try reinventing the wheel, has anybody tried to extend the GeometryBatchFactory to be usable on skinned models (merging weights and indexes of bones)?

Ok, I had a closer look at the batch factory. Basically it is already all in place, as all buffers are merged. Just one small thing one would need, it is the maxNumWeights which is not set currently.



To use it load the model, grab all controls and remove them, optimize the model, than add the controls back. Here’s a hack showing this:



[patch]



Index: src/test/jme3test/model/anim/TestOgreComplexAnim.java

===================================================================

— src/test/jme3test/model/anim/TestOgreComplexAnim.java (revision 9678)

+++ src/test/jme3test/model/anim/TestOgreComplexAnim.java (working copy)

@@ -32,6 +32,10 @@



package jme3test.model.anim;



+import java.util.ArrayList;

+

+import jme3tools.optimize.GeometryBatchFactory;

+

import com.jme3.animation.AnimChannel;

import com.jme3.animation.AnimControl;

import com.jme3.animation.Bone;

@@ -44,6 +48,7 @@

import com.jme3.math.Quaternion;

import com.jme3.math.Vector3f;

import com.jme3.scene.Node;

+import com.jme3.scene.control.Control;

import com.jme3.scene.debug.SkeletonDebugger;



public class TestOgreComplexAnim extends SimpleApplication {

@@ -70,6 +75,18 @@

rootNode.addLight(dl);



Node model = (Node) assetManager.loadModel(“Models/Oto/Oto.mesh.xml”);

+

  •   ArrayList&lt;Control&gt; controls = new ArrayList&lt;Control&gt;();<br />
    
  •   for (int i = 0; i &lt; model.getNumControls(); i++) {<br />
    
  •   	Control c = model.getControl(i);<br />
    
  •   	controls.add(c);<br />
    
  •   }<br />
    
  •   for (Control c : controls) {<br />
    
  •   	model.removeControl(c);<br />
    
  •   }<br />
    
  •   model = GeometryBatchFactory.optimize(model, false);<br />
    
  •   for (Control c : controls)<br />
    
  •   	model.addControl(c);<br />
    

control = model.getControl(AnimControl.class);
[/patch]

And here's the patch for the factory

[patch]
Index: src/tools/jme3tools/optimize/GeometryBatchFactory.java
===================================================================
--- src/tools/jme3tools/optimize/GeometryBatchFactory.java (revision 9675)
+++ src/tools/jme3tools/optimize/GeometryBatchFactory.java (working copy)
@@ -1,21 +1,29 @@
package jme3tools.optimize;

+import java.nio.Buffer;
+import java.nio.FloatBuffer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
+
import com.jme3.material.Material;
import com.jme3.math.Matrix4f;
import com.jme3.math.Transform;
import com.jme3.math.Vector3f;
-import com.jme3.scene.*;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
import com.jme3.scene.Mesh.Mode;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.VertexBuffer;
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 java.nio.Buffer;
-import java.nio.FloatBuffer;
-import java.nio.ShortBuffer;
-import java.util.*;
-import java.util.logging.Logger;

public class GeometryBatchFactory {

@@ -99,6 +107,7 @@
int totalVerts = 0;
int totalTris = 0;
int totalLodLevels = 0;
+ int maxWeights = -1;

Mode mode = null;
for (Geometry geom : geometries) {
@@ -134,6 +143,12 @@
formatForBuf[vb language=".getBufferType().ordinal()"][/vb] = vb.getFormat();
}

+ int listWeights = geom.getMesh().getMaxNumWeights();
+ if (mode != null && maxWeights != listWeights) {
+ throw new UnsupportedOperationException("Cannot combine different"
+ + " weight types: " + maxWeights + " != " + listWeights);
+ }
+ maxWeights = listWeights;
if (mode != null && mode != listMode) {
throw new UnsupportedOperationException("Cannot combine different"
+ " primitive types: " + mode + " != " + listMode);
@@ -141,7 +156,7 @@
mode = listMode;
compsForBuf[Type.Index.ordinal()] = components;
}
-
+ outMesh.setMaxNumWeights(maxWeights);
outMesh.setMode(mode);
if (totalVerts >= 65536) {
// make sure we create an UnsignedInt buffer so
[/patch]
3 Likes

Hm, cool! So this works? That would be awesome for the BatchNode too… @nehon? :smiley:

@Momoko_Fan yes you are right, currently every module (blender armature, ogre, animation) asume there are up to four weights, so the weightbuffer would currently never be interleaved with different max weights for rendering

if someone would try to do some calculations based on maxweights and an interleaved buffer of say 2 and 3 component weights this could get wired results (but currently I do not see any usecase withing jme for doing calculations on weight buffers)

Any reason why you can’t combine meshes with different max weight values? It should just be the max() of the max weights values for all meshes …

This assumption is deeply ingrained in the engine so I think its okay to allow merging models with different max weights.

Thanks, it has been committed. As a bonus the fix was applied to BatchNode as well.

1 Like

mhhh… So…does this means we can batch 2000 sinbad and all is gonna work correctly?

I looked at the change in BatchNode and it just fixes the the weight calculation.

What about mesh updating from the skeleton control? Vertex indices are not the same.

Animation doesn’t depend on vertex indices, only bone indices and bone weight which are copied directly.

Awesome :slight_smile: Paul and me were guessing around about this and it turns out its like in the ideal version we came up with :wink:

@Momoko_Fan said:
Animation doesn't depend on vertex indices, only bone indices and bone weight which are copied directly.

Mhhh true..... this is huge then

You can batch 2000 sinbads but then they have to be playing the exact same animation at the exact same time, and the animation framework will have update the vertices of all 2000 sinbads. I am not sure what is so “huge” about this.

Animation in batches :wink:

@Momoko_Fan said:
You can batch 2000 sinbads but then they have to be playing the exact same animation at the exact same time, and the animation framework will have update the vertices of all 2000 sinbads. I am not sure what is so "huge" about this.

the simple fact that it works is huge...and what ever you do it will be faster than un-batched.

This plus hardware skinning and it's huger.

Its not gonna be useful at all, not unless you batch meshes within the same model like the guy in the other thread wanted to do.

I’m thinking that perhaps it can be used for scenery, 2000 windmills and a don Quixote - there’s a game already :slight_smile:

@Momoko_Fan the guy in the other thread was me too :slight_smile:



One usecase from a game called drakensang.de was to optimize the custom created character models for players in the game world to be drawable in one drawcall, which I would like to imitate for my mmo.

Being able to batch up a polygon soup for characters is the first step, creating a texture atlas for it the second. Therafter you can draw many more diverse looking player characters with a minimum of drawcalls (at least in theory).

Currently I doubt this is needed for the game, because the number of characters you see at once is not that big with a fixed viewpoint.

Basically my usecase is to draw 200 sinbads which all look different.



@nehon Using batching on oto for example will give you NO BOOST AT ALL…

@ghoust said:
@nehon Using batching on oto for example will give you NO BOOST AT ALL....

mhhh?? why?

Just to be sure we are talking about the same thing. When i say batching 200 Sinbad it's baching the 200 altogether.
Sinbad has 7 geometries but 3 materials so it would benefit from batching even with one sinbad.
Oto is one mesh so batching it makes no sense.
Batching 200 Oto altogether however should give you a boost compared to 200 unbatched Oto.
Or did I misunderstood something?
@jmaasing said:
I'm thinking that perhaps it can be used for scenery, 2000 windmills and a don Quixote - there's a game already :)

You would probably find that looks artificial - when did you ever see 2000 windmills in perfect step?

However it could potentially be used for things like armies marching with all the models in a squad....even then you would need to adjust them in the patch if they started going up/down hills or whatever though so the gains would probably not be worth the complexity.

Instead of having everything batched, you could have a few batches with either a delay or a different animation and spread them around, so they don’t look exact. Would be good for things like crowds in arenas as well.