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:
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user