Further progress: texturing, compiling animations.

git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4020 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Andrzej Kapolka
2006-04-14 03:26:50 +00:00
parent fc2870f7e5
commit 3c6ac7ac67
11 changed files with 560 additions and 35 deletions
+3
View File
@@ -9,6 +9,9 @@ m.file_menu = File
m.file_load = Load Model...
m.file_quit = Quit
m.anim_menu = Animations
m.anim_stop = Stop
m.load_title = Select Model to Load
m.load_filter = Model Files (*.properties, *.dat)
@@ -21,6 +21,7 @@
package com.threerings.jme.model;
import java.io.Externalizable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -29,13 +30,19 @@ import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.nio.ByteOrder;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import java.util.Properties;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.scene.Spatial;
import com.threerings.jme.Log;
/**
@@ -43,6 +50,97 @@ import com.threerings.jme.Log;
*/
public class Model extends ModelNode
{
/** An animation for the model. */
public static class Animation
implements Serializable
{
/** The transformation targets of the animation. */
public Spatial[] transformTargets;
/** The animation transforms (one transform per target per frame). */
public transient Transform[][] transforms;
private void writeObject (ObjectOutputStream out)
throws IOException
{
out.defaultWriteObject();
out.writeInt(transforms.length);
for (int ii = 0; ii < transforms.length; ii++) {
for (int jj = 0; jj < transformTargets.length; jj++) {
transforms[ii][jj].writeExternal(out);
}
}
}
private void readObject (ObjectInputStream in)
throws IOException, ClassNotFoundException
{
in.defaultReadObject();
transforms = new Transform[in.readInt()][transformTargets.length];
for (int ii = 0; ii < transforms.length; ii++) {
for (int jj = 0; jj < transformTargets.length; jj++) {
transforms[ii][jj] = new Transform(new Vector3f(),
new Quaternion(), new Vector3f());
transforms[ii][jj].readExternal(in);
}
}
}
private static final long serialVersionUID = 1;
}
/** A frame element that manipulates the target's transform. */
public static final class Transform
implements Externalizable
{
public Transform (
Vector3f translation, Quaternion rotation, Vector3f scale)
{
_translation = translation;
_rotation = rotation;
_scale = scale;
}
/**
* Blends between this transform and the next, applying the result to
* the given target.
*
* @param alpha the blend factor: 0.0 for entirely this frame, 1.0 for
* entirely the next
*/
public void blend (Transform next, float alpha, Spatial target)
{
target.getLocalTranslation().interpolate(_translation,
next._translation, alpha);
target.getLocalRotation().slerp(_rotation, next._rotation, alpha);
target.getLocalScale().interpolate(_scale, next._scale, alpha);
}
// documentation inherited from interface Externalizable
public void writeExternal (ObjectOutput out)
throws IOException
{
_translation.writeExternal(out);
_rotation.writeExternal(out);
_scale.writeExternal(out);
}
// documentation inherited from interface Externalizable
public void readExternal (ObjectInput in)
throws IOException, ClassNotFoundException
{
_translation.readExternal(in);
_rotation.readExternal(in);
_scale.readExternal(in);
}
/** The transform at this frame. */
protected Vector3f _translation, _scale;
protected Quaternion _rotation;
private static final long serialVersionUID = 1;
}
/**
* Attempts to read a model from the specified file.
*
@@ -102,6 +200,48 @@ public class Model extends ModelNode
return _props;
}
/**
* Adds an animation to the model's library.
*/
public void addAnimation (String name, Animation anim)
{
if (_anims == null) {
_anims = new HashMap<String, Animation>();
}
_anims.put(name, anim);
}
/**
* Returns the names of the model's animations.
*/
public String[] getAnimations ()
{
return (_anims == null) ? new String[0] :
_anims.keySet().toArray(new String[_anims.size()]);
}
/**
* Starts the named animation.
*/
public void startAnimation (String name)
{
_anim = _anims.get(name);
if (_anim == null) {
Log.warning("Requested unknown animation [name=" +
name + "].");
return;
}
_fidx = -1;
}
/**
* Stops the currently running animation.
*/
public void stopAnimation ()
{
_anim = null;
}
/**
* Writes this model out to a file.
*/
@@ -125,6 +265,7 @@ public class Model extends ModelNode
{
super.writeExternal(out);
out.writeObject(_props);
out.writeObject(_anims);
}
@Override // documentation inherited
@@ -133,10 +274,45 @@ public class Model extends ModelNode
{
super.readExternal(in);
_props = (Properties)in.readObject();
_anims = (HashMap<String, Animation>)in.readObject();
}
@Override // documentation inherited
public void updateWorldData (float time)
{
if (_anim != null) {
updateAnimation(time);
}
// update children
super.updateWorldData(time);
}
/**
* Updates the model's state according to the current animation.
*/
protected void updateAnimation (float time)
{
}
/** The model properties. */
protected Properties _props;
/** The model animations. */
protected HashMap<String, Animation> _anims;
/** The currently running animation, or <code>null</code> for none. */
protected Animation _anim;
/** The last frame index. */
protected int _fidx;
/** The time corresponding to the last frame. */
protected float _ftime;
/** Identifies a transform frame element. */
protected static final byte TRANSFORM_ELEMENT = 0;
private static final long serialVersionUID = 1;
}
@@ -39,7 +39,13 @@ import com.jme.bounding.BoundingBox;
import com.jme.bounding.BoundingSphere;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.renderer.Renderer;
import com.jme.scene.TriMesh;
import com.jme.scene.state.AlphaState;
import com.jme.scene.state.CullState;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.ZBufferState;
import com.jme.system.DisplaySystem;
import com.threerings.jme.Log;
@@ -82,11 +88,15 @@ public class ModelMesh extends TriMesh
{
_boundingType = "sphere".equals(props.getProperty("bound")) ?
SPHERE_BOUND : BOX_BOUND;
_texture = props.getProperty("texture");
_solid = !"false".equals(props.getProperty("solid"));
_transparent = "true".equals(props.getProperty("transparent"));
}
/**
* Sets the buffers as {@link ByteBuffer}s, because we can't create byte
* views of non-byte buffers.
* views of non-byte buffers. This method is where the model is
* initialized after loading.
*/
public void reconstruct (
ByteBuffer vertices, ByteBuffer normals, ByteBuffer colors,
@@ -104,12 +114,28 @@ public class ModelMesh extends TriMesh
_textureByteBuffer = textures;
_indexByteBuffer = indices;
// initialize the model if we're displaying
if (DisplaySystem.getDisplaySystem() == null) {
return;
}
if (_boundingType == BOX_BOUND) {
setModelBound(new BoundingBox());
} else { // _boundingType == SPHERE_BOUND
setModelBound(new BoundingSphere());
}
updateModelBound();
if (_backCull == null) {
initSharedStates();
}
if (_solid) {
setRenderState(_backCull);
}
if (_transparent) {
setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);
setRenderState(_blendAlpha);
setRenderState(_overlayZBuffer);
}
}
// documentation inherited
@@ -153,6 +179,9 @@ public class ModelMesh extends TriMesh
out.writeInt(_textureBufferSize);
out.writeInt(_indexBufferSize);
out.writeInt(_boundingType);
out.writeObject(_texture);
out.writeBoolean(_solid);
out.writeBoolean(_transparent);
}
// documentation inherited from interface Externalizable
@@ -169,6 +198,20 @@ public class ModelMesh extends TriMesh
_textureBufferSize = in.readInt();
_indexBufferSize = in.readInt();
_boundingType = in.readInt();
_texture = (String)in.readObject();
_solid = in.readBoolean();
_transparent = in.readBoolean();
}
// documentation inherited from interface ModelSpatial
public void resolveTextures (TextureProvider tprov)
{
if (_texture != null) {
TextureState tstate = tprov.getTexture(_texture);
if (tstate != null) {
setRenderState(tstate);
}
}
}
// documentation inherited from interface ModelSpatial
@@ -289,7 +332,7 @@ public class ModelMesh extends TriMesh
/**
* Imposes the specified order on the given buffer of 32 bit values.
*/
protected void convertOrder (ByteBuffer buf, ByteOrder order)
protected static void convertOrder (ByteBuffer buf, ByteOrder order)
{
if (buf.order() == order) {
return;
@@ -301,6 +344,22 @@ public class ModelMesh extends TriMesh
}
}
/**
* Initializes the states shared between all models. Requires an active
* display.
*/
protected static void initSharedStates ()
{
Renderer renderer = DisplaySystem.getDisplaySystem().getRenderer();
_backCull = renderer.createCullState();
_backCull.setCullMode(CullState.CS_BACK);
_blendAlpha = renderer.createAlphaState();
_blendAlpha.setBlendEnabled(true);
_overlayZBuffer = renderer.createZBufferState();
_overlayZBuffer.setFunction(ZBufferState.CF_LEQUAL);
_overlayZBuffer.setWritable(false);
}
/** The sizes of the various buffers (zero for <code>null</code>). */
protected int _vertexBufferSize, _normalBufferSize, _colorBufferSize,
_textureBufferSize, _indexBufferSize;
@@ -312,6 +371,24 @@ public class ModelMesh extends TriMesh
/** The type of bounding volume that this mesh should use. */
protected int _boundingType;
/** The name of this model's texture, or <code>null</code> for none. */
protected String _texture;
/** Whether or not this mesh can enable back-face culling. */
protected boolean _solid;
/** Whether or not this mesh must be rendered as transparent. */
protected boolean _transparent;
/** The shared state for back-face culling. */
protected static CullState _backCull;
/** The shared state for alpha blending. */
protected static AlphaState _blendAlpha;
/** The shared state for checking, but not writing to, the z buffer. */
protected static ZBufferState _overlayZBuffer;
/** Indicates that this mesh should use a bounding box. */
protected static final int BOX_BOUND = 0;
@@ -98,7 +98,17 @@ public class ModelNode extends Node
attachChild((Spatial)children.get(ii));
}
}
// documentation inherited from interface ModelSpatial
public void resolveTextures (TextureProvider tprov)
{
for (Object child : getChildren()) {
if (child instanceof ModelSpatial) {
((ModelSpatial)child).resolveTextures(tprov);
}
}
}
// documentation inherited from interface ModelSpatial
public void writeBuffers (FileChannel out)
throws IOException
@@ -32,6 +32,11 @@ import java.nio.channels.FileChannel;
*/
public interface ModelSpatial
{
/**
* Recursively resolves texture references using the given provider.
*/
public void resolveTextures (TextureProvider tprov);
/**
* Recursively writes any data buffers to the output channel.
*/
@@ -0,0 +1,35 @@
//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.jme.model;
import com.jme.scene.state.TextureState;
/**
* Provides a means for models to resolve their texture references.
*/
public interface TextureProvider
{
/**
* Returns a texture state containing the named texture.
*/
public TextureState getTexture (String name);
}
@@ -22,6 +22,16 @@
package com.threerings.jme.tools;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Properties;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.scene.Spatial;
import com.threerings.jme.Log;
import com.threerings.jme.model.Model;
/**
* A basic representation for keyframe animations.
@@ -29,34 +39,112 @@ import java.util.ArrayList;
public class AnimationDef
{
/** A single frame of the animation. */
public static class Frame
public static class FrameDef
{
/** Transforms for affected nodes. */
public ArrayList<Transform> transforms = new ArrayList<Transform>();
/** Transform for affected nodes. */
public ArrayList<TransformDef> transforms =
new ArrayList<TransformDef>();
public void addTransform (Transform transform)
public void addTransform (TransformDef transform)
{
transforms.add(transform);
}
/** Adds all transform targets in this frame to the supplied set. */
public void addTransformTargets (
HashMap<String, Spatial> nodes, HashSet<Spatial> targets)
{
for (int ii = 0, nn = transforms.size(); ii < nn; ii++) {
String name = transforms.get(ii).name;
Spatial target = nodes.get(name);
if (target != null) {
targets.add(target);
} else {
Log.warning("Missing animation target [name=" + name +
"].");
}
}
}
/** Returns the array of transforms for this frame. */
public Model.Transform[] getTransforms (Spatial[] targets)
{
Model.Transform[] mtransforms =
new Model.Transform[targets.length];
for (int ii = 0; ii < targets.length; ii++) {
mtransforms[ii] = getTransform(targets[ii]);
}
return mtransforms;
}
/** Returns the transform for the supplied target. */
protected Model.Transform getTransform (Spatial target)
{
String name = target.getName();
for (int ii = 0, nn = transforms.size(); ii < nn; ii++) {
TransformDef transform = transforms.get(ii);
if (name.equals(transform.name)) {
return transform.getTransform();
}
}
return null;
}
}
/** A transform for a single node. */
public static class Transform
public static class TransformDef
{
/** The name of the node to transform. */
/** The name of the affected node. */
public String name;
/** The transformation parameters. */
public float[] translation;
public float[] rotation;
public float[] scale;
/** Returns the live transform object. */
public Model.Transform getTransform ()
{
return new Model.Transform(
new Vector3f(translation[0], translation[1], translation[2]),
new Quaternion(rotation[0], rotation[1], rotation[2],
rotation[3]),
new Vector3f(scale[0], scale[1], scale[2]));
}
}
/** The individual frames of the animation. */
public ArrayList<Frame> frames = new ArrayList<Frame>();
public ArrayList<FrameDef> frames = new ArrayList<FrameDef>();
public void addFrame (Frame frame)
public void addFrame (FrameDef frame)
{
frames.add(frame);
}
/**
* Creates the "live" animation object that will be serialized with the
* object.
*
* @param props the model properties
* @param nodes the nodes in the model, mapped by name
*/
public Model.Animation createAnimation (
Properties props, HashMap<String, Spatial> nodes)
{
// find all affected nodes
HashSet<Spatial> targets = new HashSet<Spatial>();
for (int ii = 0, nn = frames.size(); ii < nn; ii++) {
frames.get(ii).addTransformTargets(nodes, targets);
}
// collect all transforms
Model.Animation anim = new Model.Animation();
anim.transformTargets = targets.toArray(new Spatial[targets.size()]);
anim.transforms = new Model.Transform[frames.size()][targets.size()];
for (int ii = 0; ii < anim.transforms.length; ii++) {
anim.transforms[ii] =
frames.get(ii).getTransforms(anim.transformTargets);
}
return anim;
}
}
@@ -28,6 +28,7 @@ import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Properties;
import org.apache.tools.ant.BuildException;
@@ -35,11 +36,16 @@ import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import com.jme.scene.Spatial;
import com.samskivert.util.StringUtil;
import com.threerings.jme.model.BoneNode;
import com.threerings.jme.model.Model;
import com.threerings.jme.model.ModelMesh;
import com.threerings.jme.model.ModelNode;
import com.threerings.jme.model.SkinMesh;
import com.threerings.jme.tools.xml.AnimationParser;
import com.threerings.jme.tools.xml.ModelParser;
/**
@@ -63,21 +69,45 @@ public class CompileModelTask extends Task
String root = (didx == -1) ? spath : spath.substring(0, didx);
File content = new File(root + ".xml"),
target = new File(root + ".dat");
if (source.lastModified() < target.lastModified() &&
content.lastModified() < target.lastModified()) {
return null;
boolean needsUpdate = false;
if (source.lastModified() >= target.lastModified() ||
content.lastModified() >= target.lastModified()) {
needsUpdate = true;
}
System.out.println("Compiling " + source.getParent() + "...");
// load the model properties
Properties props = new Properties();
FileInputStream in = new FileInputStream(source);
props.load(in);
in.close();
// locate the animations, if any
String[] actions =
StringUtil.parseStringArray(props.getProperty("actions", ""));
File[] afiles = new File[actions.length];
File dir = source.getParentFile();
for (int ii = 0; ii < actions.length; ii++) {
afiles[ii] = new File(dir, actions[ii] + ".xml");
if (afiles[ii].lastModified() >= target.lastModified()) {
needsUpdate = true;
}
}
if (!needsUpdate) {
return null;
}
System.out.println("Compiling " + source.getParent() + "...");
// load the model content
ModelDef mdef = _mparser.parseModel(content.toString());
Model model = mdef.createModel(props);
HashMap<String, Spatial> nodes = new HashMap<String, Spatial>();
Model model = mdef.createModel(props, nodes);
// load the actions, if any
for (int ii = 0; ii < actions.length; ii++) {
System.out.println(" Adding " + afiles[ii] + "...");
AnimationDef adef = _aparser.parseAnimation(afiles[ii].toString());
model.addAnimation(actions[ii], adef.createAnimation(props, nodes));
}
// write and return the model
model.writeToFile(target);
@@ -114,4 +144,7 @@ public class CompileModelTask extends Task
/** A parser for the model definitions. */
protected static ModelParser _mparser = new ModelParser();
/** A parser for the animation definitions. */
protected static AnimationParser _aparser = new AnimationParser();
}
@@ -69,7 +69,7 @@ public class ModelDef
public Spatial getSpatial (Properties props)
{
if (_spatial == null) {
_spatial = createSpatial(props);
_spatial = createSpatial(new NodeProperties(props, name));
setTransform();
}
return _spatial;
@@ -137,8 +137,8 @@ public class ModelDef
/** Configures the new mesh and returns it. */
protected ModelMesh configure (ModelMesh mmesh, Properties props)
{
// configure using sub-properties
mmesh.configure(PropertiesUtil.getSubProperties(props, name, ""));
// configure using properties
mmesh.configure(props);
// set the various buffers
int vsize = vertices.size();
@@ -344,13 +344,15 @@ public class ModelDef
/**
* Creates the model node defined herein.
*
* @param props the properties of the model
* @param nodes a node map to populate
*/
public Model createModel (Properties props)
public Model createModel (Properties props, HashMap<String, Spatial> nodes)
{
Model model = new Model(props.getProperty("name", "model"), props);
// start by creating the spatials and mapping them to their names
HashMap<String, Spatial> nodes = new HashMap<String, Spatial>();
for (int ii = 0, nn = spatials.size(); ii < nn; ii++) {
Spatial spatial = spatials.get(ii).getSpatial(props);
nodes.put(spatial.getName(), spatial);
@@ -388,4 +390,34 @@ public class ModelDef
}
return array;
}
/** A wrapper for the model properties providing access to the properties
* of a node within the model. */
protected static class NodeProperties extends Properties
{
public NodeProperties (Properties mprops, String name)
{
_mprops = mprops;
_prefix = name + ".";
}
@Override // documentation inherited
public String getProperty (String key)
{
return getProperty(key, null);
}
@Override // documentation inherited
public String getProperty (String key, String defaultValue)
{
return _mprops.getProperty(_prefix + key,
_mprops.getProperty(key, defaultValue));
}
/** The properties of the model. */
protected Properties _mprops;
/** The node prefix. */
protected String _prefix;
}
}
@@ -37,10 +37,12 @@ import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.logging.Level;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
@@ -52,6 +54,7 @@ import javax.swing.JPopupMenu;
import javax.swing.KeyStroke;
import javax.swing.filechooser.FileFilter;
import com.jme.image.Texture;
import com.jme.light.DirectionalLight;
import com.jme.math.FastMath;
import com.jme.math.Vector3f;
@@ -59,7 +62,9 @@ import com.jme.renderer.ColorRGBA;
import com.jme.scene.Line;
import com.jme.scene.state.LightState;
import com.jme.scene.state.MaterialState;
import com.jme.scene.state.TextureState;
import com.jme.util.LoggingSystem;
import com.jme.util.TextureManager;
import com.samskivert.swing.GroupLayout;
import com.samskivert.util.Config;
@@ -68,7 +73,9 @@ import com.threerings.util.MessageBundle;
import com.threerings.util.MessageManager;
import com.threerings.jme.JmeCanvasApp;
import com.threerings.jme.Log;
import com.threerings.jme.model.Model;
import com.threerings.jme.model.TextureProvider;
/**
* A simple viewer application that allows users to examine models and their
@@ -104,6 +111,7 @@ public class ModelViewer extends JmeCanvasApp
_frame.setJMenuBar(menu);
JMenu file = new JMenu(_msg.get("m.file_menu"));
file.setMnemonic(KeyEvent.VK_F);
menu.add(file);
Action load = new AbstractAction(_msg.get("m.file_load")) {
public void actionPerformed (ActionEvent e) {
@@ -126,11 +134,20 @@ public class ModelViewer extends JmeCanvasApp
KeyStroke.getKeyStroke(KeyEvent.VK_Q, KeyEvent.CTRL_MASK));
file.add(quit);
_frame.getContentPane().add(getCanvas(), BorderLayout.CENTER);
_amenu = new JMenu(_msg.get("m.anim_menu"));
_amenu.setMnemonic(KeyEvent.VK_A);
menu.add(_amenu);
_amenu.setVisible(false);
_stop = new AbstractAction(_msg.get("m.anim_stop")) {
public void actionPerformed (ActionEvent e) {
_model.stopAnimation();
}
};
_stop.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_S);
_stop.putValue(Action.ACCELERATOR_KEY,
KeyStroke.getKeyStroke(KeyEvent.VK_S, KeyEvent.CTRL_MASK));
_controls = GroupLayout.makeVBox(GroupLayout.NONE, GroupLayout.TOP);
_controls.setPreferredSize(new Dimension(100, 100));
_frame.getContentPane().add(_controls, BorderLayout.EAST);
_frame.getContentPane().add(getCanvas(), BorderLayout.CENTER);
_status = new JLabel(" ");
_status.setHorizontalAlignment(JLabel.LEFT);
@@ -283,7 +300,7 @@ public class ModelViewer extends JmeCanvasApp
_status.setText(_msg.get("m.compiling_model", file));
Model model = CompileModelTask.compileModel(file);
if (model != null) {
setModel(model);
setModel(model, file);
return;
}
// if compileModel returned null, the .dat file is up-to-date
@@ -300,20 +317,65 @@ public class ModelViewer extends JmeCanvasApp
throws IOException
{
_status.setText(_msg.get("m.loading_model", file));
setModel(Model.readFromFile(file, false));
setModel(Model.readFromFile(file, false), file);
}
/**
* Sets the model once it's been loaded.
*
* @param file the file from which the model was loaded
*/
protected void setModel (Model model)
protected void setModel (Model model, File file)
{
if (_model != null) {
_ctx.getGeometry().detachChild(_model);
}
_ctx.getGeometry().attachChild(_model = model);
_model.setLocalScale(0.04f);
// resolve the textures from the file's directory
final File dir = file.getParentFile();
_model.resolveTextures(new TextureProvider() {
public TextureState getTexture (String name) {
TextureState tstate = _tstates.get(name);
if (tstate == null) {
File file = new File(dir, name);
Texture tex = TextureManager.loadTexture(file.toString(),
Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR);
if (tex == null) {
Log.warning("Couldn't find texture [path=" + file +
"].");
return null;
}
tstate = _ctx.getRenderer().createTextureState();
tstate.setTexture(tex);
_tstates.put(name, tstate);
}
return tstate;
}
protected HashMap<String, TextureState> _tstates =
new HashMap<String, TextureState>();
});
_model.updateRenderState();
// create buttons for the model's animations
String[] anims = _model.getAnimations();
_amenu.removeAll();
if (anims.length == 0) {
_amenu.setVisible(false);
return;
}
_amenu.setVisible(true);
for (int ii = 0; ii < anims.length; ii++) {
final String anim = anims[ii];
_amenu.add(new AbstractAction(anim) {
public void actionPerformed (ActionEvent e) {
_model.startAnimation(anim);
}
});
}
_amenu.addSeparator();
_amenu.add(_stop);
}
/** The translation bundle. */
@@ -325,8 +387,11 @@ public class ModelViewer extends JmeCanvasApp
/** The viewer frame. */
protected JFrame _frame;
/** The control panel. */
protected JPanel _controls;
/** The animation menu. */
protected JMenu _amenu;
/** The stop animation action. */
protected Action _stop;
/** The status bar. */
protected JLabel _status;
@@ -48,16 +48,17 @@ public class AnimationParser
AnimationDef.class.getName());
String frame = anim + "/frame";
_digester.addObjectCreate(frame, AnimationDef.Frame.class.getName());
_digester.addObjectCreate(frame,
AnimationDef.FrameDef.class.getName());
_digester.addSetNext(frame, "addFrame",
AnimationDef.Frame.class.getName());
AnimationDef.FrameDef.class.getName());
String xform = frame + "/transform";
_digester.addObjectCreate(xform,
AnimationDef.Transform.class.getName());
AnimationDef.TransformDef.class.getName());
_digester.addRule(xform, new SetPropertyFieldsRule());
_digester.addSetNext(xform, "addTransform",
AnimationDef.Transform.class.getName());
AnimationDef.TransformDef.class.getName());
}
/**