diff --git a/src/java/com/threerings/jme/model/Rotator.java b/src/java/com/threerings/jme/model/Rotator.java index 4054f8e0..96b81ac5 100644 --- a/src/java/com/threerings/jme/model/Rotator.java +++ b/src/java/com/threerings/jme/model/Rotator.java @@ -37,6 +37,7 @@ import com.jme.util.export.OutputCapsule; import com.samskivert.util.StringUtil; import com.threerings.jme.Log; +import com.threerings.jme.util.JmeUtil; /** * A procedural animation that rotates a node around at a constant angular @@ -48,29 +49,8 @@ public class Rotator extends ModelController public void configure (Properties props, Spatial target) { super.configure(props, target); - String axisstr = props.getProperty("axis", "x"), - velstr = props.getProperty("velocity", "3.14"); - if (axisstr.equalsIgnoreCase("x")) { - _axis = Vector3f.UNIT_X; - } else if (axisstr.equalsIgnoreCase("y")) { - _axis = Vector3f.UNIT_Y; - } else if (axisstr.equalsIgnoreCase("z")) { - _axis = Vector3f.UNIT_Z; - } else { - float[] axis = StringUtil.parseFloatArray(axisstr); - if (axis != null && axis.length == 3) { - _axis = new Vector3f(axis[0], axis[1], - axis[2]).normalizeLocal(); - - } else { - Log.warning("Invalid rotation axis [axis=" + axisstr + "]."); - } - } - try { - _velocity = Float.parseFloat(velstr); - } catch (NumberFormatException e) { - Log.warning("Invalid angular velocity [velocity=" + velstr + "]."); - } + _axis = JmeUtil.parseAxis(props.getProperty("axis", "x")); + _velocity = Float.parseFloat(props.getProperty("velocity", "3.14")); } // documentation inherited diff --git a/src/java/com/threerings/jme/model/TextureAnimator.java b/src/java/com/threerings/jme/model/TextureAnimator.java index 8fb88645..036352ae 100644 --- a/src/java/com/threerings/jme/model/TextureAnimator.java +++ b/src/java/com/threerings/jme/model/TextureAnimator.java @@ -38,6 +38,8 @@ import com.jme.util.export.OutputCapsule; import com.samskivert.util.StringUtil; import com.threerings.jme.Log; +import com.threerings.jme.util.JmeUtil; +import com.threerings.jme.util.JmeUtil.FrameState; import com.threerings.jme.util.SpatialVisitor; /** @@ -53,48 +55,20 @@ public class TextureAnimator extends TextureController _frameHeight = Float.valueOf(props.getProperty("frame_height", "0.5")); _frameCount = Integer.valueOf(props.getProperty("frame_count", "4")); _frameRate = Float.valueOf(props.getProperty("frame_rate", "1")); - String rtype = props.getProperty("repeat_type", "wrap"); - if (rtype.equals("clamp")) { - _repeatType = Controller.RT_CLAMP; - } else if (rtype.equals("cycle")) { - _repeatType = Controller.RT_CYCLE; - } else if (rtype.equals("wrap")) { - _repeatType = Controller.RT_WRAP; - } else { - Log.warning("Invalid repeat type [type=" + rtype + "]."); - _repeatType = Controller.RT_WRAP; - } - } - - @Override // documentation inherited - public void resolveTextures (TextureProvider tprov) - { - super.resolveTextures(tprov); - - // compute derived values - _hframes = (int)Math.floor(1f / _frameWidth); - _vframes = (int)Math.floor(1f / _frameHeight); - - // use the same translation vector for all textures - _translation = new Vector3f(); - for (Texture texture : _textures) { - texture.setTranslation(_translation); - } + _repeatType = JmeUtil.parseRepeatType(props.getProperty("repeat_type")); } // documentation inherited public void update (float time) { + super.update(time); if (!isActive()) { return; } - float spf = 1f / _frameRate; - for (_faccum += time; _faccum >= spf; _faccum -= spf) { - advanceFrame(); - } + _fstate.update(time, _frameRate, _frameCount, _repeatType); _translation.set( - (_fidx % _hframes) * _frameWidth, - -(_fidx / _hframes) * _frameHeight, + (_fstate.idx % _hframes) * _frameWidth, + -(_fstate.idx / _hframes) * _frameHeight, 0f); } @@ -143,24 +117,19 @@ public class TextureAnimator extends TextureController capsule.write(_repeatType, "repeatType", Controller.RT_WRAP); } - /** - * Advances by one frame. - */ - protected void advanceFrame () + @Override // documentation inherited + protected void initTextures () { - if ((_fidx += _fdir) >= _frameCount) { - if (_repeatType == Controller.RT_CLAMP) { - _fidx = _frameCount - 1; - _fdir = 0; - } else if (_repeatType == Controller.RT_WRAP) { - _fidx = 0; - } else { // repeatType == Controller.RT_CYCLE - _fidx = _frameCount - 2; - _fdir = -1; - } - } else if (_fidx < 0) { - _fidx = 1; - _fdir = +1; + super.initTextures(); + + // compute derived values + _hframes = (int)Math.floor(1f / _frameWidth); + _vframes = (int)Math.floor(1f / _frameHeight); + + // use the same translation vector for all textures + _translation = new Vector3f(); + for (Texture texture : _textures) { + texture.setTranslation(_translation); } } @@ -182,11 +151,8 @@ public class TextureAnimator extends TextureController /** The number of frames on the horizontal and vertical axes. */ protected transient int _hframes, _vframes; - /** The current animation frame and direction. */ - protected transient int _fidx, _fdir = +1; - - /** The time accumulated towards the next frame. */ - protected transient float _faccum; + /** The animation position. */ + protected transient FrameState _fstate = new FrameState(); /** The shared texture translation. */ protected transient Vector3f _translation; diff --git a/src/java/com/threerings/jme/model/TextureController.java b/src/java/com/threerings/jme/model/TextureController.java index 19637262..72ee0faf 100644 --- a/src/java/com/threerings/jme/model/TextureController.java +++ b/src/java/com/threerings/jme/model/TextureController.java @@ -23,9 +23,12 @@ package com.threerings.jme.model; import java.util.HashMap; +import org.lwjgl.opengl.Display; + import com.jme.image.Texture; import com.jme.scene.state.RenderState; import com.jme.scene.state.TextureState; +import com.jme.system.DisplaySystem; import com.threerings.jme.util.SpatialVisitor; @@ -37,28 +40,49 @@ public abstract class TextureController extends ModelController @Override // documentation inherited public void resolveTextures (TextureProvider tprov) { + // reinitialize the cloned textures if we re-resolve super.resolveTextures(tprov); - + _textures = null; + } + + // documentation inherited + public void update (float time) + { + // initialize the textures before the first update + if (_textures == null) { + initTextures(); + } + } + + /** + * Performs any per-instance initialization required for textures. + */ + protected void initTextures () + { // find and clone all textures under the target final HashMap clones = new HashMap(); new SpatialVisitor(ModelMesh.class) { protected void visit (ModelMesh mesh) { - TextureState tstate = (TextureState)mesh.getRenderState(RenderState.RS_TEXTURE); - if (tstate == null) { + TextureState otstate = (TextureState)mesh.getRenderState(RenderState.RS_TEXTURE); + if (otstate == null) { return; } - for (int ii = 0, nn = tstate.getNumberOfSetTextures(); ii < nn; ii++) { - Texture tex = tstate.getTexture(ii), ctex = clones.get(tex); + TextureState ntstate = + DisplaySystem.getDisplaySystem().getRenderer().createTextureState(); + for (int ii = 0, nn = otstate.getNumberOfSetTextures(); ii < nn; ii++) { + Texture tex = otstate.getTexture(ii), ctex = clones.get(tex); if (ctex == null) { if (tex.getTextureId() == 0) { - tstate.load(ii); + otstate.load(ii); } clones.put(tex, ctex = tex.createSimpleClone()); } - tstate.setTexture(ctex, ii); + ntstate.setTexture(ctex, ii); } + mesh.setRenderState(ntstate); } }.traverse(_target); + _target.updateRenderState(); // remember them for updates _textures = clones.values().toArray(new Texture[0]); diff --git a/src/java/com/threerings/jme/model/TextureTranslator.java b/src/java/com/threerings/jme/model/TextureTranslator.java index 75b19982..1037953d 100644 --- a/src/java/com/threerings/jme/model/TextureTranslator.java +++ b/src/java/com/threerings/jme/model/TextureTranslator.java @@ -58,21 +58,10 @@ public class TextureTranslator extends TextureController } } - @Override // documentation inherited - public void resolveTextures (TextureProvider tprov) - { - super.resolveTextures(tprov); - - // use the same translation vector for all textures - _translation = new Vector3f(); - for (Texture texture : _textures) { - texture.setTranslation(_translation); - } - } - // documentation inherited public void update (float time) { + super.update(time); if (!isActive()) { return; } @@ -112,6 +101,18 @@ public class TextureTranslator extends TextureController capsule.write(_velocity, "velocity", null); } + @Override // documentation inherited + protected void initTextures () + { + super.initTextures(); + + // use the same translation vector for all textures + _translation = new Vector3f(); + for (Texture texture : _textures) { + texture.setTranslation(_translation); + } + } + /** The velocity at which to translate the texture. */ protected Vector2f _velocity; diff --git a/src/java/com/threerings/jme/tools/AnimationDef.java b/src/java/com/threerings/jme/tools/AnimationDef.java index 709e9117..a096de44 100644 --- a/src/java/com/threerings/jme/tools/AnimationDef.java +++ b/src/java/com/threerings/jme/tools/AnimationDef.java @@ -33,6 +33,7 @@ import com.jme.scene.Spatial; import com.threerings.jme.Log; import com.threerings.jme.model.Model; +import com.threerings.jme.util.JmeUtil; /** * A basic representation for keyframe animations. @@ -144,14 +145,7 @@ public class AnimationDef // create and configure the animation Model.Animation anim = new Model.Animation(); anim.frameRate = frameRate; - String rtype = props.getProperty("repeat_type", "clamp"); - if (rtype.equals("cycle")) { - anim.repeatType = Controller.RT_CYCLE; - } else if (rtype.equals("wrap")) { - anim.repeatType = Controller.RT_WRAP; - } else { - anim.repeatType = Controller.RT_CLAMP; - } + anim.repeatType = JmeUtil.parseRepeatType(props.getProperty("repeat_type")); // collect all transforms anim.transformTargets = targets.toArray(new Spatial[targets.size()]); diff --git a/src/java/com/threerings/jme/util/JmeUtil.java b/src/java/com/threerings/jme/util/JmeUtil.java new file mode 100644 index 00000000..cbac38d0 --- /dev/null +++ b/src/java/com/threerings/jme/util/JmeUtil.java @@ -0,0 +1,127 @@ +// +// $Id: SpatialVisitor.java 119 2007-01-24 00:22:12Z dhoover $ + +package com.threerings.jme.util; + +import com.jme.math.Vector3f; +import com.jme.scene.Controller; + +import com.samskivert.util.StringUtil; + +import com.threerings.jme.Log; + +/** + * Some static classes and methods of general utility to applications using JME. + */ +public class JmeUtil +{ + /** + * Represents the current position and direction in an animation composed of a fixed number of + * discrete frames using one of the repeat types defined in {@link Controller}. + */ + public static class FrameState + { + /** The index of the current frame. */ + public int idx; + + /** The current direction of animation. */ + public int dir = +1; + + /** The fractional progress towards the next frame. */ + public float accum; + + /** + * Resets the state back to the beginning. + */ + public void reset () + { + set(0, +1, 0f); + } + + /** + * Sets the entire frame state. + */ + public void set (int idx, int dir, float accum) + { + this.idx = idx; + this.dir = dir; + this.accum = accum; + } + + /** + * Updates the frame state after some amount of time has elapsed. + */ + public void update (float elapsed, float frameRate, int frameCount, int repeatType) + { + float frames = elapsed * frameRate; + for (accum += frames; accum >= 1f; accum -= 1f) { + advance(frameCount, repeatType); + } + } + + /** + * Advances the position by one frame. + */ + public void advance (int frameCount, int repeatType) + { + if ((idx += dir) >= frameCount) { + if (repeatType == Controller.RT_CLAMP) { + idx = frameCount - 1; + dir = 0; + } else if (repeatType == Controller.RT_WRAP) { + idx = 0; + } else { // repeatType == Controller.RT_CYCLE + idx = frameCount - 2; + dir = -1; + } + } else if (idx < 0) { + idx = 1; + dir = +1; + } + } + } + + /** + * Attempts to parse a string containing an axis: either "x", "y", or "z", or three + * comma-delimited values representing an axis vector. The value returned may be one of + * JME's "constant" vectors (for example, {@link Vector3f#UNIT_X}), so don't modify it. + */ + public static Vector3f parseAxis (String axis) + { + if (axis == null) { + return null; + } + if (axis.equalsIgnoreCase("x")) { + return Vector3f.UNIT_X; + } else if (axis.equalsIgnoreCase("y")) { + return Vector3f.UNIT_Y; + } else if (axis.equalsIgnoreCase("z")) { + return Vector3f.UNIT_Z; + } + float[] vals = StringUtil.parseFloatArray(axis); + if (vals != null && vals.length == 3) { + return new Vector3f(vals[0], vals[1], vals[2]).normalizeLocal(); + } else { + Log.warning("Invalid axis [axis=" + axis + "]."); + return null; + } + } + + /** + * Attempts to parse a string describing one of the repeat types defined in {@link Controller}: + * "clamp", "cycle", or "wrap". Will return {@link Controller#RT_WRAP} if the type is + * null or invalid. + */ + public static int parseRepeatType (String type) + { + if ("clamp".equals(type)) { + return Controller.RT_CLAMP; + } else if ("cycle".equals(type)) { + return Controller.RT_CYCLE; + } + if (type != null && !"wrap".equals(type)) { + Log.warning("Invalid repeat type [type=" + type + "]."); + } + return Controller.RT_WRAP; + } +}