Where possible, merge meshes with identical attributes.
git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@201 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
@@ -29,6 +29,7 @@ import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Properties;
|
||||
|
||||
import com.jme.bounding.BoundingBox;
|
||||
@@ -141,6 +142,51 @@ public class ModelMesh extends TriMesh
|
||||
Boolean.parseBoolean(tprops.getProperty("translucent"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an opaque object representing this mesh's attributes. The
|
||||
* object will implement {@link Object#hashCode} and {@link Object#equals}
|
||||
* so that meshes with the same attributes will have identical keys.
|
||||
*/
|
||||
public Object getAttributeKey ()
|
||||
{
|
||||
return new AttributeKey(_textureKey, _textures, _sphereMapped,
|
||||
_filterMode, _mipMapMode, _emissiveMap, _emissive, _additive,
|
||||
_solid, _transparent, _alphaThreshold, _translucent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges another mesh into this one. The mesh is assumed to be in the same coordinate system
|
||||
* and have the same attributes.
|
||||
*/
|
||||
public void merge (ModelMesh mesh)
|
||||
{
|
||||
TriangleBatch batch = (TriangleBatch)getBatch(0);
|
||||
TriangleBatch mbatch = (TriangleBatch)mesh.getBatch(0);
|
||||
|
||||
int vcount = batch.getVertexCount();
|
||||
batch.setVertexBuffer(concatenate(batch.getVertexBuffer(), mbatch.getVertexBuffer()));
|
||||
batch.setNormalBuffer(concatenate(batch.getNormalBuffer(), mbatch.getNormalBuffer()));
|
||||
if (batch.getColorBuffer() != null && mbatch.getColorBuffer() != null) {
|
||||
batch.setColorBuffer(concatenate(batch.getColorBuffer(), mbatch.getColorBuffer()));
|
||||
}
|
||||
batch.setTextureBuffer(concatenate(
|
||||
batch.getTextureBuffer(0), mbatch.getTextureBuffer(0)), 0);
|
||||
for (int ii = 1, nn = getTextureCount(); ii < nn; ii++) {
|
||||
batch.setTextureBuffer(batch.getTextureBuffer(0), ii);
|
||||
}
|
||||
|
||||
IntBuffer ibuf = batch.getIndexBuffer(), mibuf = mbatch.getIndexBuffer();
|
||||
IntBuffer nibuf = BufferUtils.createIntBuffer(ibuf.capacity() + mibuf.capacity());
|
||||
ibuf.clear();
|
||||
nibuf.put(ibuf);
|
||||
mibuf.clear();
|
||||
while (mibuf.hasRemaining()) {
|
||||
nibuf.put(mibuf.get() + vcount);
|
||||
}
|
||||
nibuf.clear();
|
||||
batch.setIndexBuffer(nibuf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the vertices and the transform of the mesh so that the mesh's
|
||||
* position lies at the center of its bounding volume.
|
||||
@@ -588,6 +634,20 @@ public class ModelMesh extends TriMesh
|
||||
return astate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates the two provided buffers.
|
||||
*/
|
||||
protected static FloatBuffer concatenate (FloatBuffer b1, FloatBuffer b2)
|
||||
{
|
||||
FloatBuffer nb = BufferUtils.createFloatBuffer(b1.capacity() + b2.capacity());
|
||||
b1.clear();
|
||||
nb.put(b1);
|
||||
b2.clear();
|
||||
nb.put(b2);
|
||||
nb.clear();
|
||||
return nb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the encoded triangle index/distance pairs in {@link #_tcodes}
|
||||
* using a two-pass (16 bit) radix sort (as described by
|
||||
@@ -745,8 +805,29 @@ public class ModelMesh extends TriMesh
|
||||
new RenderState[RenderState.RS_MAX_STATE];
|
||||
}
|
||||
|
||||
/** The type of bounding volume that this mesh should use. */
|
||||
protected int _boundingType;
|
||||
/** A wrapper around an array of attributes that uses {@link Arrays#deepHashCode} and
|
||||
* {@link Arrays#deepEquals} for hashing/comparison. */
|
||||
protected static class AttributeKey
|
||||
{
|
||||
public AttributeKey (Object... attrs)
|
||||
{
|
||||
_attrs = attrs;
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public int hashCode ()
|
||||
{
|
||||
return Arrays.deepHashCode(_attrs);
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
public boolean equals (Object other)
|
||||
{
|
||||
return Arrays.deepEquals(_attrs, ((AttributeKey)other)._attrs);
|
||||
}
|
||||
|
||||
protected Object[] _attrs;
|
||||
}
|
||||
|
||||
/** The name of the texture specified in the model file, which acts as a
|
||||
* property key and a default value. */
|
||||
|
||||
@@ -39,7 +39,9 @@ import java.util.Set;
|
||||
import com.jme.bounding.BoundingBox;
|
||||
import com.jme.bounding.BoundingSphere;
|
||||
import com.jme.math.FastMath;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.scene.Spatial;
|
||||
import com.jme.scene.batch.GeomBatch;
|
||||
import com.jme.util.geom.BufferUtils;
|
||||
|
||||
import com.samskivert.util.PropertiesUtil;
|
||||
@@ -51,6 +53,7 @@ import com.threerings.jme.model.ModelController;
|
||||
import com.threerings.jme.model.ModelMesh;
|
||||
import com.threerings.jme.model.ModelNode;
|
||||
import com.threerings.jme.model.SkinMesh;
|
||||
import com.threerings.jme.util.SpatialVisitor;
|
||||
|
||||
/**
|
||||
* An intermediate representation for models used to store data parsed from
|
||||
@@ -435,9 +438,6 @@ public class ModelDef
|
||||
{
|
||||
Model model = new Model(props.getProperty("name", "model"), props);
|
||||
|
||||
// set the overall scale
|
||||
model.setLocalScale(Float.parseFloat(props.getProperty("scale", "1")));
|
||||
|
||||
// start by creating the spatials and mapping them to their names
|
||||
for (int ii = 0, nn = spatials.size(); ii < nn; ii++) {
|
||||
Spatial spatial = spatials.get(ii).getSpatial(props);
|
||||
@@ -471,12 +471,21 @@ public class ModelDef
|
||||
ModelController ctrl = createController(subProps, target);
|
||||
if (ctrl != null) {
|
||||
model.addController(ctrl);
|
||||
referenced.add(target);
|
||||
}
|
||||
}
|
||||
|
||||
// in non-animated models, merge meshes with the same attributes
|
||||
if (StringUtil.isBlank(props.getProperty("animations"))) {
|
||||
mergeEquivalentMeshes(model, nodes, referenced);
|
||||
}
|
||||
|
||||
// get rid of any nodes that serve no purpose
|
||||
pruneUnusedNodes(model, nodes, referenced);
|
||||
|
||||
// set the overall scale
|
||||
model.setLocalScale(Float.parseFloat(props.getProperty("scale", "1")));
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
@@ -517,6 +526,65 @@ public class ModelDef
|
||||
return referenced.contains(node) || hasValidChildren;
|
||||
}
|
||||
|
||||
/** Merges meshes with the same attributes. */
|
||||
protected void mergeEquivalentMeshes (
|
||||
Model model, HashMap<String, Spatial> nodes, final HashSet<Spatial> referenced)
|
||||
{
|
||||
final HashMap<Object, ArrayList<ModelMesh>> meshes =
|
||||
new HashMap<Object, ArrayList<ModelMesh>>();
|
||||
new SpatialVisitor<ModelMesh>(ModelMesh.class) {
|
||||
public void traverse (Spatial spatial) {
|
||||
// cut out when we encounter referenced nodes
|
||||
if (!referenced.contains(spatial)) {
|
||||
spatial.updateWorldVectors();
|
||||
super.traverse(spatial);
|
||||
}
|
||||
}
|
||||
protected void visit (ModelMesh mesh) {
|
||||
// make sure we don't try this with skinned meshes
|
||||
if (mesh instanceof SkinMesh) {
|
||||
return;
|
||||
}
|
||||
Object key = mesh.getAttributeKey();
|
||||
ArrayList<ModelMesh> list = meshes.get(key);
|
||||
if (list == null) {
|
||||
meshes.put(key, list = new ArrayList<ModelMesh>());
|
||||
}
|
||||
list.add(mesh);
|
||||
}
|
||||
}.traverse(model);
|
||||
|
||||
for (ArrayList<ModelMesh> list : meshes.values()) {
|
||||
// the first in the list becomes the base
|
||||
ModelMesh base = list.get(0);
|
||||
flattenTransforms(base);
|
||||
model.attachChild(base);
|
||||
|
||||
// the rest are merged with it
|
||||
for (int ii = 1, nn = list.size(); ii < nn; ii++) {
|
||||
ModelMesh mesh = list.get(ii);
|
||||
flattenTransforms(mesh);
|
||||
mesh.getParent().detachChild(mesh);
|
||||
base.merge(mesh);
|
||||
}
|
||||
|
||||
// update the model bound and recenter
|
||||
base.updateModelBound();
|
||||
base.centerVertices();
|
||||
}
|
||||
}
|
||||
|
||||
/** Transforms the mesh's vertices and normals into model coordinates. */
|
||||
protected static void flattenTransforms (ModelMesh mesh)
|
||||
{
|
||||
GeomBatch batch = mesh.getBatch(0);
|
||||
batch.getWorldCoords(batch.getVertexBuffer());
|
||||
batch.getWorldNormals(batch.getNormalBuffer());
|
||||
mesh.getLocalTranslation().zero();
|
||||
mesh.getLocalRotation().loadIdentity();
|
||||
mesh.getLocalScale().set(Vector3f.UNIT_XYZ);
|
||||
}
|
||||
|
||||
/** Converts a boxed Integer list to an unboxed int array. */
|
||||
protected static int[] toArray (ArrayList<Integer> list)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user