Added support for model variants: subconfigurations with different
textures, etc., that share the same mesh and animations. git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@49 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
@@ -40,6 +40,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import com.jme.bounding.BoundingVolume;
|
||||
@@ -53,9 +54,12 @@ import com.jme.scene.Node;
|
||||
import com.jme.scene.Spatial;
|
||||
|
||||
import com.samskivert.util.ObserverList;
|
||||
import com.samskivert.util.PropertiesUtil;
|
||||
import com.samskivert.util.RandomUtil;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.jme.Log;
|
||||
import com.threerings.jme.util.SpatialVisitor;
|
||||
|
||||
/**
|
||||
* The base node for models.
|
||||
@@ -400,6 +404,14 @@ public class Model extends ModelNode
|
||||
initInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the names of the model's variant configurations.
|
||||
*/
|
||||
public String[] getVariantNames ()
|
||||
{
|
||||
return StringUtil.parseStringArray(_props.getProperty("variants", ""));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an animation to the model's library. This should only be called by
|
||||
* the model compiler.
|
||||
@@ -716,6 +728,39 @@ public class Model extends ModelNode
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new prototype using the given variant configuration.
|
||||
* Use {@link #createInstance} on the returned prototype to create
|
||||
* additional instances of the variant.
|
||||
*/
|
||||
public Model createPrototype (String variant)
|
||||
{
|
||||
if (_prototype != null) {
|
||||
return _prototype.createPrototype(variant);
|
||||
}
|
||||
// create an instance and rebind all animations
|
||||
final Model prototype = createInstance();
|
||||
for (Map.Entry<String, Animation> entry : _anims.entrySet()) {
|
||||
prototype._anims.put(entry.getKey(),
|
||||
entry.getValue().rebind(prototype._pnodes));
|
||||
}
|
||||
prototype._prototype = null;
|
||||
prototype._pnodes = null;
|
||||
|
||||
// reconfigure meshes with new variant type
|
||||
if (variant != null) {
|
||||
prototype._props = PropertiesUtil.getFilteredProperties(
|
||||
_props, variant);
|
||||
new SpatialVisitor<ModelMesh>(ModelMesh.class) {
|
||||
public void visit (ModelMesh mesh) {
|
||||
mesh.reconfigure(PropertiesUtil.getFilteredProperties(
|
||||
prototype._props, mesh.getParent().getName()));
|
||||
}
|
||||
}.traverse(prototype);
|
||||
}
|
||||
return prototype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns a new instance of this model.
|
||||
*/
|
||||
|
||||
@@ -60,6 +60,7 @@ import com.jme.scene.state.ZBufferState;
|
||||
import com.jme.system.DisplaySystem;
|
||||
import com.jme.util.geom.BufferUtils;
|
||||
|
||||
import com.samskivert.util.PropertiesUtil;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.jme.Log;
|
||||
@@ -86,6 +87,16 @@ public class ModelMesh extends TriMesh
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconfigures this model with a new set of (sub-)properties. Textures
|
||||
* must be (re-)resolved after calling this method.
|
||||
*/
|
||||
public void reconfigure (Properties props)
|
||||
{
|
||||
configure(_solid, _textureKey, _transparent, props);
|
||||
setRenderStates();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures this mesh based on the given parameters and (sub-)properties.
|
||||
*
|
||||
@@ -97,23 +108,23 @@ public class ModelMesh extends TriMesh
|
||||
public void configure (
|
||||
boolean solid, String texture, boolean transparent, Properties props)
|
||||
{
|
||||
_textureKey = texture;
|
||||
_textures = (texture == null) ? null : StringUtil.parseStringArray(
|
||||
props.getProperty(texture, texture));
|
||||
_sphereMapped = Boolean.parseBoolean(
|
||||
getSubProperty(props, texture, "sphere_map"));
|
||||
_filterMode = "nearest".equals(
|
||||
getSubProperty(props, texture, "filter")) ?
|
||||
Properties tprops = PropertiesUtil.getFilteredProperties(
|
||||
props, texture);
|
||||
_sphereMapped = Boolean.parseBoolean(tprops.getProperty("sphere_map"));
|
||||
_filterMode = "nearest".equals(tprops.getProperty("filter")) ?
|
||||
Texture.FM_NEAREST : Texture.FM_LINEAR;
|
||||
_mipMapMode = getMipMapMode(
|
||||
getSubProperty(props, texture, "mipmap"));
|
||||
_emissiveMap = getSubProperty(props, texture, "emissive");
|
||||
_mipMapMode = getMipMapMode(tprops.getProperty("mipmap"));
|
||||
_emissiveMap = tprops.getProperty("emissive");
|
||||
_solid = solid;
|
||||
_transparent = transparent;
|
||||
String threshold = getSubProperty(props, texture, "alpha_threshold");
|
||||
String threshold = tprops.getProperty("alpha_threshold");
|
||||
_alphaThreshold = (threshold == null) ?
|
||||
DEFAULT_ALPHA_THRESHOLD : Float.parseFloat(threshold);
|
||||
_translucent = _transparent && Boolean.parseBoolean(
|
||||
getSubProperty(props, texture, "translucent"));
|
||||
_translucent = _transparent &&
|
||||
Boolean.parseBoolean(tprops.getProperty("translucent"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,26 +155,7 @@ public class ModelMesh extends TriMesh
|
||||
storeOriginalBuffers();
|
||||
|
||||
// initialize the model if we're displaying
|
||||
if (DisplaySystem.getDisplaySystem() == null) {
|
||||
return;
|
||||
}
|
||||
if (_backCull == null) {
|
||||
initSharedStates();
|
||||
}
|
||||
if (_solid) {
|
||||
setRenderState(_backCull);
|
||||
}
|
||||
if (_transparent) {
|
||||
setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);
|
||||
if (_translucent) {
|
||||
setRenderState(_blendAlpha);
|
||||
setRenderState(_overlayZBuffer);
|
||||
} else if (_alphaThreshold == DEFAULT_ALPHA_THRESHOLD) {
|
||||
setRenderState(_defaultTestAlpha);
|
||||
} else {
|
||||
setRenderState(createTestAlpha(_alphaThreshold));
|
||||
}
|
||||
}
|
||||
setRenderStates();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -308,6 +300,7 @@ public class ModelMesh extends TriMesh
|
||||
mbatch.setModelBound(properties.isSet("bound") ?
|
||||
batch.getModelBound() : batch.getModelBound().clone(null));
|
||||
}
|
||||
mstore._textureKey = _textureKey;
|
||||
if (_textures != null && _textures.length > 1) {
|
||||
int tidx = properties.random % _textures.length;
|
||||
mstore._textures = new String[] { _textures[tidx] };
|
||||
@@ -352,6 +345,7 @@ public class ModelMesh extends TriMesh
|
||||
out.writeInt(_colorBufferSize);
|
||||
out.writeInt(_textureBufferSize);
|
||||
out.writeInt(_indexBufferSize);
|
||||
out.writeObject(_textureKey);
|
||||
out.writeObject(_textures);
|
||||
out.writeBoolean(_sphereMapped);
|
||||
out.writeInt(_filterMode);
|
||||
@@ -377,6 +371,7 @@ public class ModelMesh extends TriMesh
|
||||
_colorBufferSize = in.readInt();
|
||||
_textureBufferSize = in.readInt();
|
||||
_indexBufferSize = in.readInt();
|
||||
_textureKey = (String)in.readObject();
|
||||
_textures = (String[])in.readObject();
|
||||
_sphereMapped = in.readBoolean();
|
||||
_filterMode = in.readInt();
|
||||
@@ -445,6 +440,8 @@ public class ModelMesh extends TriMesh
|
||||
}
|
||||
if (_tstates[0] != null) {
|
||||
setRenderState(_tstates[0]);
|
||||
} else {
|
||||
clearRenderState(RenderState.RS_TEXTURE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -617,6 +614,34 @@ public class ModelMesh extends TriMesh
|
||||
FloatBuffer.wrap(_vbuf = new float[vbuf.capacity()]).put(vbuf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the model's render states (excluding the texture state, which is
|
||||
* set by {@link #resolveTextures}) according to its configuration.
|
||||
*/
|
||||
protected void setRenderStates ()
|
||||
{
|
||||
if (DisplaySystem.getDisplaySystem() == null) {
|
||||
return;
|
||||
}
|
||||
if (_backCull == null) {
|
||||
initSharedStates();
|
||||
}
|
||||
if (_solid) {
|
||||
setRenderState(_backCull);
|
||||
}
|
||||
if (_transparent) {
|
||||
setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);
|
||||
if (_translucent) {
|
||||
setRenderState(_blendAlpha);
|
||||
setRenderState(_overlayZBuffer);
|
||||
} else if (_alphaThreshold == DEFAULT_ALPHA_THRESHOLD) {
|
||||
setRenderState(_defaultTestAlpha);
|
||||
} else {
|
||||
setRenderState(createTestAlpha(_alphaThreshold));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks the transform and bounds of this mesh on the assumption that its
|
||||
* position will not change.
|
||||
@@ -627,17 +652,6 @@ public class ModelMesh extends TriMesh
|
||||
_transformLocked = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a sub-property from the given set, falling back on the main
|
||||
* property if the sub-property is not specified.
|
||||
*/
|
||||
protected static String getSubProperty (
|
||||
Properties props, String prefix, String property)
|
||||
{
|
||||
return props.getProperty(prefix + "." + property,
|
||||
props.getProperty(property));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mip-map mode corresponding to the given string
|
||||
* (defaulting to {@link Texture#MM_LINEAR_LINEAR}).
|
||||
@@ -876,6 +890,10 @@ public class ModelMesh extends TriMesh
|
||||
/** The type of bounding volume that this mesh should use. */
|
||||
protected int _boundingType;
|
||||
|
||||
/** The name of the texture specified in the model file, which acts as a
|
||||
* property key and a default value. */
|
||||
protected String _textureKey;
|
||||
|
||||
/** The name of this model's textures, or <code>null</code> for none. */
|
||||
protected String[] _textures;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user