Added ability to quantize animations to a set of preskinned meshes.

git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4172 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Andrzej Kapolka
2006-06-06 01:37:34 +00:00
parent cd8c665cf5
commit e3f54a52bb
5 changed files with 167 additions and 5 deletions
+56 -5
View File
@@ -106,6 +106,9 @@ public class Model extends ModelNode
/** The animation transforms (one transform per target per frame). */ /** The animation transforms (one transform per target per frame). */
public transient Transform[][] transforms; public transient Transform[][] transforms;
/** Uniquely identifies this animation within the model. */
public transient int animId;
/** /**
* Returns this animation's duration in seconds. * Returns this animation's duration in seconds.
*/ */
@@ -130,6 +133,7 @@ public class Model extends ModelNode
anim.transformTargets[ii] = anim.transformTargets[ii] =
(Spatial)pnodes.get(transformTargets[ii]); (Spatial)pnodes.get(transformTargets[ii]);
} }
anim.animId = animId;
return anim; return anim;
} }
@@ -353,6 +357,13 @@ public class Model extends ModelNode
*/ */
public void initPrototype () public void initPrototype ()
{ {
// assign identifiers to the animations
if (_anims != null) {
int nextId = 1;
for (Animation anim : _anims.values()) {
anim.animId = nextId++;
}
}
setReferenceTransforms(); setReferenceTransforms();
cullInvisibleNodes(); cullInvisibleNodes();
initInstance(); initInstance();
@@ -396,6 +407,28 @@ public class Model extends ModelNode
updateWorldData(0f); updateWorldData(0f);
} }
/**
* Sets the resolution at which to quantize animations over time.
* This should be set on the prototype before any animations are
* started or any instances are created.
*
* @param the temporal quantization rate in frames per second, or
* <code>0</code> to disable quantization
*/
public void setAnimationResolution (float resolution)
{
_animResolution = resolution;
}
/**
* Returns the resolution at which animations are quantized over time,
* or <code>0</code> if quantization is disabled.
*/
public float getAnimationResolution ()
{
return _animResolution;
}
/** /**
* Returns the names of the model's animations. * Returns the names of the model's animations.
*/ */
@@ -654,6 +687,7 @@ public class Model extends ModelNode
mstore._anims = new HashMap<String, Animation>(); mstore._anims = new HashMap<String, Animation>();
} }
mstore._pnodes = (HashMap)properties.originalToCopy.clone(); mstore._pnodes = (HashMap)properties.originalToCopy.clone();
mstore._animResolution = _animResolution;
return mstore; return mstore;
} }
@@ -731,19 +765,35 @@ public class Model extends ModelNode
*/ */
protected void updateAnimation (float time) protected void updateAnimation (float time)
{ {
float res = (_animResolution == 0f) ?
0f : (_anim.frameRate / _animResolution);
if (_elapsed < res && _elapsed > 0f) {
_elapsed += (time * _anim.frameRate);
return;
}
// advance the frame counter if necessary // advance the frame counter if necessary
while (_elapsed > 1f) { while (_elapsed > 1f) {
advanceFrameCounter(); advanceFrameCounter();
_elapsed -= 1f; _elapsed -= 1f;
} }
float qelapsed = (res == 0f) ?
_elapsed : (res * (int)(_elapsed / res));
// update the target transforms if not outside the view frustum // update the target transforms and animation frame if not outside the
// view frustum
if (!_outside) { if (!_outside) {
Spatial[] targets = _anim.transformTargets; Spatial[] targets = _anim.transformTargets;
Transform[] xforms = _anim.transforms[_fidx], Transform[] xforms = _anim.transforms[_fidx],
nxforms = _anim.transforms[_nidx]; nxforms = _anim.transforms[_nidx];
for (int ii = 0; ii < targets.length; ii++) { for (int ii = 0; ii < targets.length; ii++) {
xforms[ii].blend(nxforms[ii], _elapsed, targets[ii]); xforms[ii].blend(nxforms[ii], qelapsed, targets[ii]);
}
if (res != 0f) {
int frameId = (_anim.animId << 16) |
(int)((_fidx + _elapsed)/res);
setAnimationFrame(frameId);
} }
} }
@@ -822,6 +872,9 @@ public class Model extends ModelNode
* instances. */ * instances. */
protected CloneCreator _ccreator; protected CloneCreator _ccreator;
/** The resolution of the model's animations in frames per second. */
protected float _animResolution;
/** For instances, maps prototype nodes to their corresponding instance /** For instances, maps prototype nodes to their corresponding instance
* nodes. */ * nodes. */
protected HashMap _pnodes; protected HashMap _pnodes;
@@ -927,8 +980,6 @@ public class Model extends ModelNode
/** The name of the animation cancelled. */ /** The name of the animation cancelled. */
protected String _name; protected String _name;
} }
private static final long serialVersionUID = 1; private static final long serialVersionUID = 1;
} }
@@ -342,6 +342,12 @@ public class ModelMesh extends TriMesh
} }
} }
// documentation inherited from interface ModelSpatial
public void setAnimationFrame (int frameId)
{
// no-op
}
// documentation inherited from interface ModelSpatial // documentation inherited from interface ModelSpatial
public void writeBuffers (FileChannel out) public void writeBuffers (FileChannel out)
throws IOException throws IOException
@@ -248,6 +248,17 @@ public class ModelNode extends Node
} }
} }
// documentation inherited from interface ModelSpatial
public void setAnimationFrame (int frameId)
{
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
Spatial child = getChild(ii);
if (child instanceof ModelSpatial) {
((ModelSpatial)child).setAnimationFrame(frameId);
}
}
}
// documentation inherited from interface ModelSpatial // documentation inherited from interface ModelSpatial
public void writeBuffers (FileChannel out) public void writeBuffers (FileChannel out)
throws IOException throws IOException
@@ -63,6 +63,17 @@ public interface ModelSpatial
*/ */
public void resolveTextures (TextureProvider tprov); public void resolveTextures (TextureProvider tprov);
/**
* Recursively sets the quantized animation frame. For skinned meshes,
* the first time the frame is set causes the current skin to be stored
* in a table shared by all instances. At subsequent times, the stored
* skin is displayed.
*
* @param frameId the frame id, which uniquely identifies one frame of
* one animation
*/
public void setAnimationFrame (int frameId);
/** /**
* Creates or populates and returns a clone of this object using the given * Creates or populates and returns a clone of this object using the given
* clone properties. * clone properties.
@@ -40,9 +40,15 @@ import com.jme.math.Matrix4f;
import com.jme.math.Vector3f; import com.jme.math.Vector3f;
import com.jme.renderer.Renderer; import com.jme.renderer.Renderer;
import com.jme.scene.Spatial; import com.jme.scene.Spatial;
import com.jme.scene.TriMesh;
import com.jme.scene.VBOInfo; import com.jme.scene.VBOInfo;
import com.jme.scene.batch.SharedBatch;
import com.jme.scene.batch.TriangleBatch;
import com.jme.system.DisplaySystem;
import com.jme.util.geom.BufferUtils; import com.jme.util.geom.BufferUtils;
import com.samskivert.util.HashIntMap;
import com.threerings.jme.Log; import com.threerings.jme.Log;
/** /**
@@ -171,6 +177,9 @@ public class SkinMesh extends ModelMesh
// store the current buffers as the originals // store the current buffers as the originals
storeOriginalBuffers(); storeOriginalBuffers();
// initialize the quantized frame table
_frames = new HashIntMap<TriangleBatch>();
} }
@Override // documentation inherited @Override // documentation inherited
@@ -198,6 +207,7 @@ public class SkinMesh extends ModelMesh
properties.addProperty("vertices"); properties.addProperty("vertices");
properties.addProperty("normals"); properties.addProperty("normals");
properties.addProperty("displaylistid"); properties.addProperty("displaylistid");
mstore._frames = _frames;
mstore._bones = new Bone[_bones.length]; mstore._bones = new Bone[_bones.length];
HashMap<Bone, Bone> bmap = new HashMap<Bone, Bone>(); HashMap<Bone, Bone> bmap = new HashMap<Bone, Bone>();
for (int ii = 0; ii < _bones.length; ii++) { for (int ii = 0; ii < _bones.length; ii++) {
@@ -263,6 +273,14 @@ public class SkinMesh extends ModelMesh
vboinfo.setVBOIndexEnabled(true); vboinfo.setVBOIndexEnabled(true);
setVBOInfo(vboinfo); setVBOInfo(vboinfo);
} }
_useDisplayLists = useDisplayLists;
}
@Override // documentation inherited
public void setAnimationFrame (int frameId)
{
// switch to the specified frame id on next update
_updateFrameId = frameId;
} }
@Override // documentation inherited @Override // documentation inherited
@@ -287,6 +305,51 @@ public class SkinMesh extends ModelMesh
if (_weightGroups == null) { if (_weightGroups == null) {
return; return;
} }
// determine if we are using/updating a quantized frame
TriangleBatch lockBatch = null;
if (_updateFrameId > 0) {
if (_currentFrameId == _updateFrameId) {
return;
}
TriangleBatch batch = getBatch(0),
tbatch = _frames.get(_updateFrameId);
boolean reskin = false;
if (tbatch == null) {
_frames.put(_updateFrameId, tbatch = new TriangleBatch());
tbatch.setParentGeom(DUMMY_MESH);
tbatch.setColorBuffer(batch.getColorBuffer());
tbatch.setTextureBuffer(batch.getTextureBuffer(0), 0);
tbatch.setIndexBuffer(batch.getIndexBuffer());
tbatch.setVertexBuffer(
BufferUtils.createFloatBuffer(_ovbuf.length));
tbatch.setNormalBuffer(
BufferUtils.createFloatBuffer(_onbuf.length));
VBOInfo ovboinfo = batch.getVBOInfo();
if (ovboinfo != null) {
VBOInfo vboinfo = new VBOInfo(true);
vboinfo.setVBOIndexEnabled(true);
vboinfo.setVBOColorID(ovboinfo.getVBOColorID());
vboinfo.setVBOTextureID(0, ovboinfo.getVBOTextureID(0));
vboinfo.setVBOIndexID(ovboinfo.getVBOIndexID());
tbatch.setVBOInfo(vboinfo);
} else if (_useDisplayLists) {
lockBatch = tbatch;
}
reskin = true;
}
if (batch instanceof SharedBatch) {
((SharedBatch)batch).setTarget(tbatch);
} else {
clearBatches();
addBatch(new SharedBatch(tbatch));
getBatch(0).updateRenderState();
}
_currentFrameId = _updateFrameId;
if (!reskin) {
return;
}
}
// update the bone transforms // update the bone transforms
_modelTransform.invert(_transform); _modelTransform.invert(_transform);
for (Bone bone : _bones) { for (Bone bone : _bones) {
@@ -341,6 +404,12 @@ public class SkinMesh extends ModelMesh
vbuf.put(_vbuf); vbuf.put(_vbuf);
nbuf.rewind(); nbuf.rewind();
nbuf.put(_nbuf); nbuf.put(_nbuf);
// compile the target batch to a display list if requested
if (lockBatch != null) {
lockBatch.lockMeshes(
DisplaySystem.getDisplaySystem().getRenderer());
}
} }
/** /**
@@ -357,6 +426,10 @@ public class SkinMesh extends ModelMesh
_nbuf = new float[_onbuf.length]; _nbuf = new float[_onbuf.length];
} }
/** Pre-skinned meshes shared between all instances corresponding to
* frame ids from {@link #setAnimationFrame}. */
protected HashIntMap<TriangleBatch> _frames;
/** The groups of vertices influenced by different sets of bones. */ /** The groups of vertices influenced by different sets of bones. */
protected WeightGroup[] _weightGroups; protected WeightGroup[] _weightGroups;
@@ -367,11 +440,21 @@ public class SkinMesh extends ModelMesh
* versions. */ * versions. */
protected float[] _ovbuf, _onbuf, _vbuf, _nbuf; protected float[] _ovbuf, _onbuf, _vbuf, _nbuf;
/** The desired and current frame id, or -1 for continuous animation. */
protected int _updateFrameId = -1, _currentFrameId;
/** Whether or to use display lists if VBOs are unavailable for quantized
* meshes. */
protected boolean _useDisplayLists;
/** The node's transform in model space. */ /** The node's transform in model space. */
protected Matrix4f _modelTransform = new Matrix4f(); protected Matrix4f _modelTransform = new Matrix4f();
/** Working transform. */ /** Working transform. */
protected Matrix4f _transform = new Matrix4f(); protected Matrix4f _transform = new Matrix4f();
/** A dummy mesh that simply hold transformation values. */
protected static final TriMesh DUMMY_MESH = new TriMesh();
private static final long serialVersionUID = 1; private static final long serialVersionUID = 1;
} }