diff --git a/src/java/com/threerings/jme/model/Model.java b/src/java/com/threerings/jme/model/Model.java
index cb3988b1a..f08be4d25 100644
--- a/src/java/com/threerings/jme/model/Model.java
+++ b/src/java/com/threerings/jme/model/Model.java
@@ -329,6 +329,7 @@ public class Model extends ModelNode
for (int ii = 0; ii < anim.transformTargets.length; ii++) {
oxforms[ii].apply(anim.transformTargets[ii]);
}
+ updateWorldData(0f);
}
/**
diff --git a/src/java/com/threerings/jme/model/SkinMesh.java b/src/java/com/threerings/jme/model/SkinMesh.java
index f233b4790..46f9dc6f5 100644
--- a/src/java/com/threerings/jme/model/SkinMesh.java
+++ b/src/java/com/threerings/jme/model/SkinMesh.java
@@ -23,6 +23,7 @@ package com.threerings.jme.model;
import java.io.IOException;
import java.io.ObjectInput;
+import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.Serializable;
@@ -30,7 +31,9 @@ import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import com.jme.bounding.BoundingVolume;
import com.jme.math.Matrix4f;
@@ -53,40 +56,81 @@ public class SkinMesh extends ModelMesh
public static class WeightGroup
implements Serializable
{
- /** The indices of the affected vertices. */
- public int[] indices;
+ /** The number of vertices in this weight group. */
+ public int vertexCount;
/** The bones influencing this group. */
- public ModelNode[] bones;
+ public Bone[] bones;
- /** The array of interleaved weights (of length indices.length *
- * bones.length): weights for first vertex, weights for second,
- * etc. */
+ /** The array of interleaved weights (of length vertexCount *
+ * boneIndices.length): weights for first vertex, weights for
+ * second, etc. */
public float[] weights;
-
- /** 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
+ * @param bmap the mapping from prototype to instance bones
*/
- public WeightGroup rebind (HashMap pnodes)
+ public WeightGroup rebind (HashMap bmap)
{
- WeightGroup group = new WeightGroup();
- group.indices = indices;
- group.weights = weights;
- group.invRefTransforms = invRefTransforms;
- group.bones = new ModelNode[bones.length];
+ WeightGroup wgroup = new WeightGroup();
+ wgroup.vertexCount = vertexCount;
+ wgroup.bones = new Bone[bones.length];
for (int ii = 0; ii < bones.length; ii++) {
- group.bones[ii] = (ModelNode)pnodes.get(bones[ii]);
+ wgroup.bones[ii] = bmap.get(bones[ii]);
}
- return group;
+ wgroup.weights = weights;
+ return wgroup;
}
private static final long serialVersionUID = 1;
}
+
+ /** Represents a bone that influences the mesh. */
+ public static class Bone
+ implements Serializable
+ {
+ /** The node that defines the bone's position. */
+ public ModelNode node;
+
+ /** The inverse of the bone's model space reference transform. */
+ public transient Matrix4f invRefTransform;
+
+ /** The bone's current transform in model space. */
+ public transient Matrix4f transform;
+
+ public Bone (ModelNode node)
+ {
+ this.node = node;
+ transform = new Matrix4f();
+ }
+
+ /**
+ * Rebinds this bone for a prototype instance.
+ *
+ * @param pnodes a mapping from prototype nodes to instance nodes
+ */
+ public Bone rebind (HashMap pnodes)
+ {
+ Bone bone = new Bone((ModelNode)pnodes.get(node));
+ bone.invRefTransform = invRefTransform;
+ bone.transform = new Matrix4f();
+ return bone;
+ }
+
+ /**
+ * Initializes the bone's transient state.
+ */
+ private void readObject (ObjectInputStream in)
+ throws IOException, ClassNotFoundException
+ {
+ in.defaultReadObject();
+ transform = new Matrix4f();
+ }
+
+ private static final long serialVersionUID = 1;
+ }
/**
* No-arg constructor for deserialization.
@@ -104,20 +148,19 @@ public class SkinMesh extends ModelMesh
}
/**
- * Sets the weight groups that determine how vertices are affected by
- * bones.
+ * Sets the array of weight groups that determine how bones affect
+ * each vertex.
*/
public void setWeightGroups (WeightGroup[] weightGroups)
{
_weightGroups = weightGroups;
- }
-
- /**
- * Returns a reference to the array of weight groups.
- */
- public WeightGroup[] getWeightGroups ()
- {
- return _weightGroups;
+
+ // compile a list of all referenced bones
+ HashSet bones = new HashSet();
+ for (WeightGroup group : weightGroups) {
+ Collections.addAll(bones, group.bones);
+ }
+ _bones = bones.toArray(new Bone[bones.size()]);
}
@Override // documentation inherited
@@ -127,18 +170,15 @@ public class SkinMesh extends ModelMesh
{
super.reconstruct(vertices, normals, colors, textures, indices);
- // replace the vertex and normal buffers with working buffers
- setVertexBuffer(BufferUtils.clone(_ovbuf = getVertexBuffer()));
- setNormalBuffer(BufferUtils.clone(_onbuf = getNormalBuffer()));
+ // store the current buffers as the originals
+ storeOriginalBuffers();
}
@Override // documentation inherited
public void centerVertices ()
{
super.centerVertices();
- _ovbuf.rewind();
- getVertexBuffer().rewind();
- _ovbuf.put(getVertexBuffer());
+ storeOriginalBuffers();
}
@Override // documentation inherited
@@ -159,10 +199,15 @@ public class SkinMesh extends ModelMesh
properties.addProperty("vertices");
properties.addProperty("normals");
properties.addProperty("displaylistid");
+ mstore._bones = new Bone[_bones.length];
+ HashMap bmap = new HashMap();
+ for (int ii = 0; ii < _bones.length; ii++) {
+ bmap.put(_bones[ii], mstore._bones[ii] =
+ _bones[ii].rebind(properties.originalToCopy));
+ }
mstore._weightGroups = new WeightGroup[_weightGroups.length];
for (int ii = 0; ii < _weightGroups.length; ii++) {
- mstore._weightGroups[ii] =
- _weightGroups[ii].rebind(properties.originalToCopy);
+ mstore._weightGroups[ii] = _weightGroups[ii].rebind(bmap);
}
mstore._ovbuf = _ovbuf;
mstore._onbuf = _onbuf;
@@ -182,7 +227,7 @@ public class SkinMesh extends ModelMesh
throws IOException, ClassNotFoundException
{
super.readExternal(in);
- _weightGroups = (WeightGroup[])in.readObject();
+ setWeightGroups((WeightGroup[])in.readObject());
}
@Override // documentation inherited
@@ -198,13 +243,9 @@ public class SkinMesh extends ModelMesh
{
updateWorldVectors();
_modelTransform.invert(_transform);
- for (int ii = 0; ii < _weightGroups.length; ii++) {
- WeightGroup group = _weightGroups[ii];
- group.invRefTransforms = new Matrix4f[group.bones.length];
- for (int jj = 0; jj < group.bones.length; jj++) {
- group.invRefTransforms[jj] = _transform.mult(
- group.bones[jj].getModelTransform()).invertLocal();
- }
+ for (Bone bone : _bones) {
+ bone.invRefTransform =
+ _transform.mult(bone.node.getModelTransform()).invert();
}
}
@@ -244,53 +285,66 @@ public class SkinMesh extends ModelMesh
if (_weightGroups == null) {
return;
}
+ // update the bone transforms
_modelTransform.invert(_transform);
+ for (Bone bone : _bones) {
+ _transform.mult(bone.node.getModelTransform(), bone.transform);
+ bone.transform.multLocal(bone.invRefTransform);
+ }
- // deform the mesh according to the positions of the bones
- ModelNode[] bones;
- int idx;
- int[] indices;
+ // deform the mesh according to the positions of the bones (this code
+ // is ugly as sin because it's optimized at a low level)
+ Bone[] bones;
+ int vertexCount;
float[] weights;
- Matrix4f xform;
- Matrix4f[] invRefXforms;
- float weight;
+ Matrix4f m;
+ float weight, ovx, ovy, ovz, onx, ony, onz, vx, vy, vz, nx, ny, nz;
FloatBuffer vbuf = getVertexBuffer(), nbuf = getNormalBuffer();
- for (int ii = 0; ii < _weightGroups.length; ii++) {
+ vbuf.rewind();
+ nbuf.rewind();
+ for (int ii = 0, bidx = 0; ii < _weightGroups.length; ii++) {
+ vertexCount = _weightGroups[ii].vertexCount;
bones = _weightGroups[ii].bones;
- invRefXforms = _weightGroups[ii].invRefTransforms;
- if (_transforms == null || _transforms.length < bones.length) {
- _transforms = new Matrix4f[bones.length];
- }
- for (int jj = 0; jj < bones.length; jj++) {
- if (_transforms[jj] == null) {
- _transforms[jj] = new Matrix4f();
- }
- _transform.mult(bones[jj].getModelTransform(),
- _transforms[jj]);
- _transforms[jj].multLocal(invRefXforms[jj]);
- }
- indices = _weightGroups[ii].indices;
weights = _weightGroups[ii].weights;
- for (int jj = 0, ww = 0; jj < indices.length; jj++) {
- idx = indices[jj];
- BufferUtils.populateFromBuffer(_overtex, _ovbuf, idx);
- BufferUtils.populateFromBuffer(_onormal, _onbuf, idx);
- _vertex.zero();
- _normal.zero();
+ for (int jj = 0, ww = 0; jj < vertexCount; jj++) {
+ ovx = _ovbuf[bidx];
+ ovy = _ovbuf[bidx + 1];
+ ovz = _ovbuf[bidx + 2];
+ onx = _onbuf[bidx++];
+ ony = _onbuf[bidx++];
+ onz = _onbuf[bidx++];
+ vx = vy = vz = 0f;
+ nx = ny = nz = 0f;
for (int kk = 0; kk < bones.length; kk++) {
- xform = _transforms[kk];
+ m = bones[kk].transform;
weight = weights[ww++];
- _vertex.addLocal(
- xform.mult(_overtex, _tmp).multLocal(weight));
- _normal.addLocal(
- multNormal(_onormal, xform, _tmp).multLocal(weight));
+
+ vx += (ovx*m.m00 + ovy*m.m01 + ovz*m.m02 + m.m03) * weight;
+ vy += (ovx*m.m10 + ovy*m.m11 + ovz*m.m12 + m.m13) * weight;
+ vz += (ovx*m.m20 + ovy*m.m21 + ovz*m.m22 + m.m23) * weight;
+
+ nx += (onx*m.m00 + ony*m.m01 + onz*m.m02) * weight;
+ ny += (onx*m.m10 + ony*m.m11 + onz*m.m12) * weight;
+ nz += (onx*m.m20 + ony*m.m21 + onz*m.m22) * weight;
}
- BufferUtils.setInBuffer(_vertex, vbuf, idx);
- BufferUtils.setInBuffer(_normal, nbuf, idx);
+ vbuf.put(vx); vbuf.put(vy); vbuf.put(vz);
+ nbuf.put(nx); nbuf.put(ny); nbuf.put(nz);
}
}
}
+ /**
+ * Stores the current vertex and normal buffers for later deformation.
+ */
+ protected void storeOriginalBuffers ()
+ {
+ FloatBuffer vbuf = getVertexBuffer(), nbuf = getNormalBuffer();
+ vbuf.rewind();
+ nbuf.rewind();
+ FloatBuffer.wrap(_ovbuf = new float[vbuf.capacity()]).put(vbuf);
+ FloatBuffer.wrap(_onbuf = new float[nbuf.capacity()]).put(nbuf);
+ }
+
/**
* Multiplies a normal vector by the 3x3 rotation submatrix of the given
* matrix.
@@ -311,20 +365,15 @@ public class SkinMesh extends ModelMesh
/** The groups of vertices influenced by different sets of bones. */
protected WeightGroup[] _weightGroups;
+ /** The bones referenced by the weight groups. */
+ protected Bone[] _bones;
+
/** The original (undeformed) vertex and normal buffers. */
- protected FloatBuffer _ovbuf, _onbuf;
+ protected float[] _ovbuf, _onbuf;
/** The node's transform in model space. */
protected Matrix4f _modelTransform = new Matrix4f();
-
- /** A working array for transforms. */
- protected Matrix4f[] _transforms;
- /** Working vectors. */
- protected Vector3f _overtex = new Vector3f(), _vertex = new Vector3f(),
- _onormal = new Vector3f(), _normal = new Vector3f(),
- _tmp = new Vector3f();
-
/** Working transform. */
protected Matrix4f _transform = new Matrix4f();
diff --git a/src/java/com/threerings/jme/tools/ModelDef.java b/src/java/com/threerings/jme/tools/ModelDef.java
index 628170c5f..bf6548458 100644
--- a/src/java/com/threerings/jme/tools/ModelDef.java
+++ b/src/java/com/threerings/jme/tools/ModelDef.java
@@ -28,11 +28,13 @@ import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
+import java.util.Set;
import com.jme.bounding.BoundingBox;
import com.jme.bounding.BoundingSphere;
@@ -244,38 +246,72 @@ public class ModelDef
return;
}
- // divide the vertices up by weight groups
- HashMap, WeightGroupDef> groups =
- new HashMap, WeightGroupDef>();
- for (int ii = 0, nn = vertices.size(); ii < nn; ii++) {
- SkinVertex svertex = (SkinVertex)vertices.get(ii);
- HashSet bones = svertex.getBones(nodes);
- WeightGroupDef group = groups.get(bones);
- if (group == null) {
- groups.put(bones, group = new WeightGroupDef());
- }
- group.indices.add(ii);
- for (ModelNode bone : bones) {
- group.weights.add(svertex.getWeight(bone));
- }
- }
-
- // resolve names and set in mesh
+ // create and set the final weight groups
SkinMesh.WeightGroup[] wgroups =
- new SkinMesh.WeightGroup[groups.size()];
+ new SkinMesh.WeightGroup[_groups.size()];
+ HashMap bones =
+ new HashMap();
int ii = 0;
- for (Map.Entry, WeightGroupDef> entry :
- groups.entrySet()) {
+ for (Map.Entry, WeightGroupDef> entry :
+ _groups.entrySet()) {
SkinMesh.WeightGroup wgroup = new SkinMesh.WeightGroup();
- wgroup.indices = toArray(entry.getValue().indices);
- HashSet bones = entry.getKey();
- referenced.addAll(bones);
- wgroup.bones = bones.toArray(new ModelNode[bones.size()]);
+ wgroup.vertexCount = entry.getValue().indices.size();
+ wgroup.bones = new SkinMesh.Bone[entry.getKey().size()];
+ int jj = 0;
+ for (String bname : entry.getKey()) {
+ SkinMesh.Bone bone = bones.get(bname);
+ if (bone == null) {
+ Spatial node = nodes.get(bname);
+ bones.put(bname,
+ bone = new SkinMesh.Bone((ModelNode)node));
+ referenced.add(node);
+ }
+ wgroup.bones[jj++] = bone;
+ }
wgroup.weights = toArray(entry.getValue().weights);
wgroups[ii++] = wgroup;
}
((SkinMesh)_mesh).setWeightGroups(wgroups);
}
+
+ @Override // documentation inherited
+ protected void configureMesh (Properties props)
+ {
+ // divide the vertices up by weight groups
+ _groups = new HashMap, WeightGroupDef>();
+ for (int ii = 0, nn = vertices.size(); ii < nn; ii++) {
+ SkinVertex svertex = (SkinVertex)vertices.get(ii);
+ Set bones = svertex.boneWeights.keySet();
+ WeightGroupDef group = _groups.get(bones);
+ if (group == null) {
+ _groups.put(bones, group = new WeightGroupDef());
+ }
+ group.indices.add(ii);
+ for (String bone : bones) {
+ group.weights.add(svertex.boneWeights.get(bone).weight);
+ }
+ }
+
+ // reorder the vertices by group
+ ArrayList overts = vertices;
+ vertices = new ArrayList();
+ int[] imap = new int[overts.size()];
+ for (Map.Entry, WeightGroupDef> entry :
+ _groups.entrySet()) {
+ for (int idx : entry.getValue().indices) {
+ imap[idx] = vertices.size();
+ vertices.add(overts.get(idx));
+ }
+ }
+ for (int ii = 0, nn = indices.size(); ii < nn; ii++) {
+ indices.set(ii, imap[indices.get(ii)]);
+ }
+
+ super.configureMesh(props);
+ }
+
+ /** The intermediate weight groups, mapped by bone names. */
+ protected HashMap, WeightGroupDef> _groups;
}
/** A generic node. */
diff --git a/src/ms/TRAnimationExporter.mcr b/src/ms/TRAnimationExporter.mcr
index 7c94d1d97..6113f918c 100644
--- a/src/ms/TRAnimationExporter.mcr
+++ b/src/ms/TRAnimationExporter.mcr
@@ -62,14 +62,8 @@ macroScript TRAnimationExporter category:"File" \
outFile = createfile fileName
format "\n\n" to:outFile
format "\n\n" frameRate to:outFile
- local nodes
- if selection.count > 0 then (
- nodes = selection
- ) else (
- nodes = objects
- )
for t = animationRange.start to animationRange.end do at time t (
- writeFrame nodes outFile
+ writeFrame objects outFile
)
format "\n" to:outFile
close outFile
diff --git a/src/ms/TRModelExporter.mcr b/src/ms/TRModelExporter.mcr
index a2d98fe7e..b4a40f576 100644
--- a/src/ms/TRModelExporter.mcr
+++ b/src/ms/TRModelExporter.mcr
@@ -149,17 +149,9 @@ macroScript TRModelExporter category:"File" \
outFile = createfile fileName
format "\n\n" to:outFile
format "\n\n" to:outFile
- local nodes
- oldsel = selection as array
- if selection.count > 0 then (
- nodes = selection
- ) else (
- nodes = objects
- )
- for node in nodes do in coordsys world (
+ for node in objects do in coordsys world (
writeNode node outFile
)
- select oldsel
format "\n" to:outFile
close outFile
)