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). */
|
/** 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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user