Various fixes, added support for model instancing.

git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4035 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Andrzej Kapolka
2006-04-18 23:22:45 +00:00
parent ba72823bcc
commit f703f1aab3
7 changed files with 288 additions and 36 deletions
+112 -10
View File
@@ -43,6 +43,7 @@ import com.samskivert.util.ObserverList;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.renderer.CloneCreator;
import com.jme.scene.Controller;
import com.jme.scene.Spatial;
@@ -89,6 +90,25 @@ public class Model extends ModelNode
/** The animation transforms (one transform per target per frame). */
public transient Transform[][] transforms;
/**
* Rebinds this animation for a prototype instance.
*
* @param pnodes a mapping from prototype nodes to instance nodes
*/
public Animation rebind (HashMap pnodes)
{
Animation anim = new Animation();
anim.frameRate = frameRate;
anim.repeatType = repeatType;
anim.transforms = transforms;
anim.transformTargets = new Spatial[transformTargets.length];
for (int ii = 0; ii < transformTargets.length; ii++) {
anim.transformTargets[ii] =
(Spatial)pnodes.get(transformTargets[ii]);
}
return anim;
}
private void writeObject (ObjectOutputStream out)
throws IOException
{
@@ -234,7 +254,8 @@ public class Model extends ModelNode
}
/**
* Adds an animation to the model's library.
* Adds an animation to the model's library. This should only be called by
* the model compiler.
*/
public void addAnimation (String name, Animation anim)
{
@@ -249,6 +270,9 @@ public class Model extends ModelNode
*/
public String[] getAnimations ()
{
if (_prototype != null) {
return _prototype.getAnimations();
}
return (_anims == null) ? new String[0] :
_anims.keySet().toArray(new String[_anims.size()]);
}
@@ -261,17 +285,21 @@ public class Model extends ModelNode
if (_anim != null) {
stopAnimation();
}
_anim = _anims.get(name);
if (_anim == null) {
Log.warning("Requested unknown animation [name=" +
name + "].");
Animation anim = _anims.get(name);
if (anim != null) {
startAnimation(name, anim);
return;
}
_animName = name;
_fidx = 0;
_nidx = 1;
_fdir = +1;
_elapsed = 0f;
if (_prototype != null) {
Animation panim = _prototype._anims.get(name);
if (panim != null) {
_anims.put(name, anim = panim.rebind(_pnodes));
startAnimation(name, anim);
return;
}
}
Log.warning("Requested unknown animation [name=" +
name + "].");
}
/**
@@ -353,6 +381,55 @@ public class Model extends ModelNode
_props = (Properties)in.readObject();
_anims = (HashMap<String, Animation>)in.readObject();
}
/**
* Creates and returns a new instance of this model.
*/
public Model createInstance ()
{
if (_prototype != null) {
return _prototype.createInstance();
}
if (_ccreator == null) {
// allow adding and removing properties at any time
_ccreator = new CloneCreator(this) {
public void addProperty (String name) {
props.put(name, Boolean.TRUE);
}
public void removeProperty (String name) {
props.remove(name);
}
};
_ccreator.addProperty("vertices");
_ccreator.addProperty("colors");
_ccreator.addProperty("normals");
_ccreator.addProperty("texcoords");
_ccreator.addProperty("vboinfo");
_ccreator.addProperty("indices");
_ccreator.addProperty("obbtree");
_ccreator.addProperty("displaylistid");
_ccreator.addProperty("bound");
}
return (Model)_ccreator.createCopy();
}
@Override // documentation inherited
public Spatial putClone (Spatial store, CloneCreator properties)
{
Model mstore;
if (store == null) {
mstore = new Model(getName(), _props);
} else {
mstore = (Model)store;
}
super.putClone(mstore, properties);
mstore._prototype = this;
if (_anims != null) {
mstore._anims = new HashMap<String, Animation>();
}
mstore._pnodes = properties.originalToCopy;
return mstore;
}
@Override // documentation inherited
public void updateWorldData (float time)
@@ -364,6 +441,19 @@ public class Model extends ModelNode
// update children
super.updateWorldData(time);
}
/**
* Starts the supplied animation.
*/
protected void startAnimation (String name, Animation anim)
{
_anim = anim;
_animName = name;
_fidx = 0;
_nidx = 1;
_fdir = +1;
_elapsed = 0f;
}
/**
* Updates the model's state according to the current animation.
@@ -415,6 +505,18 @@ public class Model extends ModelNode
}
}
/** A reference to the prototype, or <code>null</code> if this is a
* prototype. */
protected Model _prototype;
/** For prototype models, a customized clone creator used to generate
* instances. */
protected CloneCreator _ccreator;
/** For instances, maps prototype nodes to their corresponding instance
* nodes. */
protected HashMap _pnodes;
/** The model properties. */
protected Properties _props;
@@ -39,8 +39,12 @@ import com.jme.bounding.BoundingBox;
import com.jme.bounding.BoundingSphere;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.renderer.CloneCreator;
import com.jme.renderer.Renderer;
import com.jme.scene.SharedMesh;
import com.jme.scene.Spatial;
import com.jme.scene.TriMesh;
import com.jme.scene.VBOInfo;
import com.jme.scene.state.AlphaState;
import com.jme.scene.state.CullState;
import com.jme.scene.state.TextureState;
@@ -155,6 +159,26 @@ public class ModelMesh extends TriMesh
_textureBufferSize = (textures == null) ? 0 : textures.capacity();
}
@Override // documentation inherited
public Spatial putClone (Spatial store, CloneCreator properties)
{
ModelMesh mstore;
if (store == null) {
mstore = new ModelMesh(getName());
} else {
mstore = (ModelMesh)store;
}
super.putClone(mstore, properties);
if (properties.isSet("displaylistid")) {
mstore.batch.setDisplayListID(getDisplayListID());
}
if (properties.isSet("bound")) {
mstore.setModelBound(getModelBound());
}
mstore._texture = _texture;
return mstore;
}
// documentation inherited from interface Externalizable
public void writeExternal (ObjectOutput out)
throws IOException
@@ -199,6 +223,20 @@ public class ModelMesh extends TriMesh
// no-op
}
// documentation inherited from interface ModelSpatial
public void lockStaticMeshes (
Renderer renderer, boolean useVBOs, boolean useDisplayLists)
{
if (useVBOs && renderer.supportsVBO()) {
VBOInfo vboinfo = new VBOInfo(true);
vboinfo.setVBOIndexEnabled(true);
setVBOInfo(vboinfo);
} else if (useDisplayLists) {
lockMeshes(renderer);
}
}
// documentation inherited from interface ModelSpatial
public void resolveTextures (TextureProvider tprov)
{
@@ -35,6 +35,8 @@ import java.util.ArrayList;
import com.jme.math.Matrix4f;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.renderer.CloneCreator;
import com.jme.renderer.Renderer;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
@@ -70,17 +72,6 @@ public class ModelNode extends Node
return _modelTransform;
}
// documentation inherited from interface ModelSpatial
public void setReferenceTransforms ()
{
updateWorldVectors();
for (Object child : getChildren()) {
if (child instanceof ModelSpatial) {
((ModelSpatial)child).setReferenceTransforms();
}
}
}
@Override // documentation inherited
public void updateWorldVectors ()
{
@@ -96,6 +87,19 @@ public class ModelNode extends Node
}
}
@Override // documentation inherited
public Spatial putClone (Spatial store, CloneCreator properties)
{
ModelNode mstore;
if (store == null) {
mstore = new ModelNode(getName());
} else {
mstore = (ModelNode)store;
}
super.putClone(mstore, properties);
return mstore;
}
// documentation inherited from interface Externalizable
public void writeExternal (ObjectOutput out)
throws IOException
@@ -121,6 +125,29 @@ public class ModelNode extends Node
}
}
// documentation inherited from interface ModelSpatial
public void setReferenceTransforms ()
{
updateWorldVectors();
for (Object child : getChildren()) {
if (child instanceof ModelSpatial) {
((ModelSpatial)child).setReferenceTransforms();
}
}
}
// documentation inherited from interface ModelSpatial
public void lockStaticMeshes (
Renderer renderer, boolean useVBOs, boolean useDisplayLists)
{
for (Object child : getChildren()) {
if (child instanceof ModelSpatial) {
((ModelSpatial)child).lockStaticMeshes(renderer, useVBOs,
useDisplayLists);
}
}
}
// documentation inherited from interface ModelSpatial
public void resolveTextures (TextureProvider tprov)
{
@@ -27,7 +27,11 @@ import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import com.jme.math.Matrix4f;
import com.jme.renderer.Renderer;
import com.jme.scene.Spatial;
/**
* Contains method common to both {@link ModelNode}s and {@link ModelMesh}es.
@@ -39,6 +43,17 @@ public interface ModelSpatial
*/
public void setReferenceTransforms ();
/**
* Recursively compiles any static meshes to vertex buffer objects (VBOs)
* or display lists.
*
* @param useVBOs if true, use VBOs if the graphics card supports them
* @param useDisplayLists if true and not using VBOs, compile static
* objects to display lists
*/
public void lockStaticMeshes (
Renderer renderer, boolean useVBOs, boolean useDisplayLists);
/**
* Recursively resolves texture references using the given provider.
*/
@@ -30,8 +30,14 @@ import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.HashMap;
import com.jme.math.Matrix4f;
import com.jme.math.Vector3f;
import com.jme.renderer.CloneCreator;
import com.jme.renderer.Renderer;
import com.jme.scene.Spatial;
import com.jme.scene.VBOInfo;
import com.jme.util.geom.BufferUtils;
import com.threerings.jme.Log;
@@ -60,6 +66,24 @@ public class SkinMesh extends ModelMesh
/** The inverses of the bones' mesh space reference transforms. */
public transient Matrix4f[] invRefTransforms;
/**
* Rebinds this weight group for a prototype instance.
*
* @param pnodes a mapping from prototype nodes to instance nodes
*/
public WeightGroup rebind (HashMap pnodes)
{
WeightGroup group = new WeightGroup();
group.indices = indices;
group.weights = weights;
group.invRefTransforms = invRefTransforms;
group.bones = new ModelNode[bones.length];
for (int ii = 0; ii < bones.length; ii++) {
group.bones[ii] = (ModelNode)pnodes.get(bones[ii]);
}
return group;
}
private static final long serialVersionUID = 1;
}
@@ -107,6 +131,32 @@ public class SkinMesh extends ModelMesh
setNormalBuffer(BufferUtils.clone(_onbuf = getNormalBuffer()));
}
@Override // documentation inherited
public Spatial putClone (Spatial store, CloneCreator properties)
{
SkinMesh mstore;
if (store == null) {
mstore = new SkinMesh(getName());
} else {
mstore = (SkinMesh)store;
}
properties.removeProperty("vertices");
properties.removeProperty("normals");
properties.removeProperty("displaylistid");
super.putClone(mstore, properties);
properties.addProperty("vertices");
properties.addProperty("normals");
properties.addProperty("displaylistid");
mstore._weightGroups = new WeightGroup[_weightGroups.length];
for (int ii = 0; ii < _weightGroups.length; ii++) {
mstore._weightGroups[ii] =
_weightGroups[ii].rebind(properties.originalToCopy);
}
mstore._ovbuf = _ovbuf;
mstore._onbuf = _onbuf;
return mstore;
}
@Override // documentation inherited
public void writeExternal (ObjectOutput out)
throws IOException
@@ -138,6 +188,20 @@ public class SkinMesh extends ModelMesh
}
}
@Override // documentation inherited
public void lockStaticMeshes (
Renderer renderer, boolean useVBOs, boolean useDisplayLists)
{
// we can use VBOs for color, texture, and indices
if (useVBOs && renderer.supportsVBO()) {
VBOInfo vboinfo = new VBOInfo(false);
vboinfo.setVBOColorEnabled(true);
vboinfo.setVBOTextureEnabled(true);
vboinfo.setVBOIndexEnabled(true);
setVBOInfo(vboinfo);
}
}
@Override // documentation inherited
public void updateWorldVectors ()
{
@@ -197,9 +261,9 @@ public class SkinMesh extends ModelMesh
xform = _transforms[kk];
weight = weights[ww++];
_vertex.addLocal(
xform.mult(_overtex, _tmp).mult(weight));
xform.mult(_overtex, _tmp).multLocal(weight));
_normal.addLocal(
xform.multAcross(_onormal, _tmp).mult(weight));
xform.multAcross(_onormal, _tmp).multLocal(weight));
}
BufferUtils.setInBuffer(_vertex, vbuf, idx);
BufferUtils.setInBuffer(_normal, nbuf, idx);
+18 -13
View File
@@ -258,13 +258,20 @@ public class ModelDef
/** A vertex influenced by a number of bones. */
public static class SkinVertex extends Vertex
{
/** The bones influencing the vertex. */
public ArrayList<BoneWeight> boneWeights = new ArrayList<BoneWeight>();
/** The bones influencing the vertex, mapped by name. */
public HashMap<String, BoneWeight> boneWeights =
new HashMap<String, BoneWeight>();
public void addBoneWeight (BoneWeight weight)
{
if (weight.weight > 0f) {
boneWeights.add(weight);
if (weight.weight == 0f) {
return;
}
BoneWeight bweight = boneWeights.get(weight.bone);
if (bweight != null) {
bweight.weight += weight.weight;
} else {
boneWeights.put(weight.bone, weight);
}
}
@@ -272,10 +279,13 @@ public class ModelDef
public HashSet<ModelNode> getBones (HashMap<String, Spatial> nodes)
{
HashSet<ModelNode> bones = new HashSet<ModelNode>();
for (BoneWeight bweight : boneWeights) {
Spatial node = nodes.get(bweight.bone);
for (String bone : boneWeights.keySet()) {
Spatial node = nodes.get(bone);
if (node instanceof ModelNode) {
bones.add((ModelNode)node);
} else {
Log.warning("Missing or invalid bone for bone weight " +
"[bone=" + bone + "].");
}
}
return bones;
@@ -284,13 +294,8 @@ public class ModelDef
/** Returns the weight of the given bone. */
public float getWeight (ModelNode bone)
{
String name = bone.getName();
for (BoneWeight bweight : boneWeights) {
if (bweight.bone.equals(name)) {
return bweight.weight;
}
}
return 0f;
BoneWeight bweight = boneWeights.get(bone.getName());
return (bweight == null) ? 0f : bweight.weight;
}
}
@@ -360,6 +360,7 @@ public class ModelViewer extends JmeCanvasApp
_ctx.getGeometry().detachChild(_model);
}
_ctx.getGeometry().attachChild(_model = model);
_model.lockStaticMeshes(_ctx.getRenderer(), true, true);
// resolve the textures from the file's directory
final File dir = file.getParentFile();