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:
@@ -106,6 +106,9 @@ public class Model extends ModelNode
|
||||
/** The animation transforms (one transform per target per frame). */
|
||||
public transient Transform[][] transforms;
|
||||
|
||||
/** Uniquely identifies this animation within the model. */
|
||||
public transient int animId;
|
||||
|
||||
/**
|
||||
* Returns this animation's duration in seconds.
|
||||
*/
|
||||
@@ -130,6 +133,7 @@ public class Model extends ModelNode
|
||||
anim.transformTargets[ii] =
|
||||
(Spatial)pnodes.get(transformTargets[ii]);
|
||||
}
|
||||
anim.animId = animId;
|
||||
return anim;
|
||||
}
|
||||
|
||||
@@ -353,6 +357,13 @@ public class Model extends ModelNode
|
||||
*/
|
||||
public void initPrototype ()
|
||||
{
|
||||
// assign identifiers to the animations
|
||||
if (_anims != null) {
|
||||
int nextId = 1;
|
||||
for (Animation anim : _anims.values()) {
|
||||
anim.animId = nextId++;
|
||||
}
|
||||
}
|
||||
setReferenceTransforms();
|
||||
cullInvisibleNodes();
|
||||
initInstance();
|
||||
@@ -396,6 +407,28 @@ public class Model extends ModelNode
|
||||
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.
|
||||
*/
|
||||
@@ -654,6 +687,7 @@ public class Model extends ModelNode
|
||||
mstore._anims = new HashMap<String, Animation>();
|
||||
}
|
||||
mstore._pnodes = (HashMap)properties.originalToCopy.clone();
|
||||
mstore._animResolution = _animResolution;
|
||||
return mstore;
|
||||
}
|
||||
|
||||
@@ -731,19 +765,35 @@ public class Model extends ModelNode
|
||||
*/
|
||||
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
|
||||
while (_elapsed > 1f) {
|
||||
advanceFrameCounter();
|
||||
_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) {
|
||||
Spatial[] targets = _anim.transformTargets;
|
||||
Transform[] xforms = _anim.transforms[_fidx],
|
||||
nxforms = _anim.transforms[_nidx];
|
||||
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. */
|
||||
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
|
||||
* nodes. */
|
||||
protected HashMap _pnodes;
|
||||
@@ -927,8 +980,6 @@ public class Model extends ModelNode
|
||||
/** The name of the animation cancelled. */
|
||||
protected String _name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
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
|
||||
public void writeBuffers (FileChannel out)
|
||||
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
|
||||
public void writeBuffers (FileChannel out)
|
||||
throws IOException
|
||||
|
||||
@@ -63,6 +63,17 @@ public interface ModelSpatial
|
||||
*/
|
||||
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
|
||||
* clone properties.
|
||||
|
||||
@@ -40,9 +40,15 @@ import com.jme.math.Matrix4f;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.renderer.Renderer;
|
||||
import com.jme.scene.Spatial;
|
||||
import com.jme.scene.TriMesh;
|
||||
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.samskivert.util.HashIntMap;
|
||||
|
||||
import com.threerings.jme.Log;
|
||||
|
||||
/**
|
||||
@@ -171,6 +177,9 @@ public class SkinMesh extends ModelMesh
|
||||
|
||||
// store the current buffers as the originals
|
||||
storeOriginalBuffers();
|
||||
|
||||
// initialize the quantized frame table
|
||||
_frames = new HashIntMap<TriangleBatch>();
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
@@ -198,6 +207,7 @@ public class SkinMesh extends ModelMesh
|
||||
properties.addProperty("vertices");
|
||||
properties.addProperty("normals");
|
||||
properties.addProperty("displaylistid");
|
||||
mstore._frames = _frames;
|
||||
mstore._bones = new Bone[_bones.length];
|
||||
HashMap<Bone, Bone> bmap = new HashMap<Bone, Bone>();
|
||||
for (int ii = 0; ii < _bones.length; ii++) {
|
||||
@@ -263,6 +273,14 @@ public class SkinMesh extends ModelMesh
|
||||
vboinfo.setVBOIndexEnabled(true);
|
||||
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
|
||||
@@ -287,6 +305,51 @@ public class SkinMesh extends ModelMesh
|
||||
if (_weightGroups == null) {
|
||||
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
|
||||
_modelTransform.invert(_transform);
|
||||
for (Bone bone : _bones) {
|
||||
@@ -341,6 +404,12 @@ public class SkinMesh extends ModelMesh
|
||||
vbuf.put(_vbuf);
|
||||
nbuf.rewind();
|
||||
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];
|
||||
}
|
||||
|
||||
/** 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. */
|
||||
protected WeightGroup[] _weightGroups;
|
||||
|
||||
@@ -367,11 +440,21 @@ public class SkinMesh extends ModelMesh
|
||||
* versions. */
|
||||
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. */
|
||||
protected Matrix4f _modelTransform = new Matrix4f();
|
||||
|
||||
/** Working transform. */
|
||||
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user