diff --git a/src/java/com/threerings/cast/builder/SpritePanel.java b/src/java/com/threerings/cast/builder/SpritePanel.java index eb4fa6a64..f3383804f 100644 --- a/src/java/com/threerings/cast/builder/SpritePanel.java +++ b/src/java/com/threerings/cast/builder/SpritePanel.java @@ -1,14 +1,24 @@ // -// $Id: SpritePanel.java,v 1.9 2002/01/08 22:16:58 shaper Exp $ +// $Id: SpritePanel.java,v 1.10 2002/01/11 16:17:33 shaper Exp $ package com.threerings.cast.builder; -import java.awt.*; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Rectangle; -import com.threerings.media.sprite.*; +import com.threerings.media.animation.AnimatedPanel; +import com.threerings.media.animation.AnimationManager; +import com.threerings.media.sprite.Sprite; +import com.threerings.media.sprite.SpriteManager; import com.threerings.cast.Log; -import com.threerings.cast.*; +import com.threerings.cast.CharacterDescriptor; +import com.threerings.cast.CharacterManager; +import com.threerings.cast.CharacterSprite; +import com.threerings.cast.StandardActions; /** * The sprite panel displays a character sprite centered in the panel diff --git a/src/java/com/threerings/media/sprite/AnimatedPanel.java b/src/java/com/threerings/media/animation/AnimatedPanel.java similarity index 74% rename from src/java/com/threerings/media/sprite/AnimatedPanel.java rename to src/java/com/threerings/media/animation/AnimatedPanel.java index 307d93624..47b98a7c3 100644 --- a/src/java/com/threerings/media/sprite/AnimatedPanel.java +++ b/src/java/com/threerings/media/animation/AnimatedPanel.java @@ -1,7 +1,7 @@ // -// $Id: AnimatedPanel.java,v 1.6 2002/01/08 22:16:58 shaper Exp $ +// $Id: AnimatedPanel.java,v 1.1 2002/01/11 16:17:33 shaper Exp $ -package com.threerings.media.sprite; +package com.threerings.media.animation; import java.awt.AWTException; import java.awt.BufferCapabilities; @@ -12,6 +12,8 @@ import java.awt.Rectangle; import java.awt.image.BufferStrategy; +import java.util.List; + import com.threerings.media.Log; /** @@ -36,12 +38,14 @@ public class AnimatedPanel extends Canvas implements AnimatedView // documentation inherited public void paint (Graphics g) { + // Log.info("AnimatedPanel paint"); update(g); } // documentation inherited public void update (Graphics g) { + // Log.info("AnimatedPanel update"); paintImmediately(); } @@ -56,7 +60,7 @@ public class AnimatedPanel extends Canvas implements AnimatedView } // documentation inherited - public void invalidateRects (DirtyRectList rects) + public void invalidateRects (List rects) { // nothing for now } @@ -97,13 +101,27 @@ public class AnimatedPanel extends Canvas implements AnimatedView _strategy.show(); } + // documentation inherited public void createBufferStrategy (int numBuffers) { - // for now, always use un-accelerated blitting. page-flipping - // seems to result in artifacts in certain conditions, and the - // buffer strategy's volatile images are irretrievably lost when - // the panel is hidden. + // explicitly avoid trying to create a page-flipping strategy, as + // page-flipping seems to result in artifacts in certain + // conditions, and the buffer strategy's volatile images are + // irretrievably lost when the panel is hidden. + + // try an accelerated blitting strategy BufferCapabilities bufferCaps = new BufferCapabilities( + new ImageCapabilities(true), new ImageCapabilities(true), null); + try { + createBufferStrategy(numBuffers, bufferCaps); + Log.info("Created accelerated blitting strategy."); + return; + } catch (AWTException e) { + // failed, fall through to the next potential strategy + } + + // try an un-accelerated blitting strategy + bufferCaps = new BufferCapabilities( new ImageCapabilities(false), new ImageCapabilities(false), null); try { createBufferStrategy(numBuffers, bufferCaps); diff --git a/src/java/com/threerings/media/sprite/AnimatedView.java b/src/java/com/threerings/media/animation/AnimatedView.java similarity index 85% rename from src/java/com/threerings/media/sprite/AnimatedView.java rename to src/java/com/threerings/media/animation/AnimatedView.java index 210f2d163..71adb6445 100644 --- a/src/java/com/threerings/media/sprite/AnimatedView.java +++ b/src/java/com/threerings/media/animation/AnimatedView.java @@ -1,10 +1,10 @@ // -// $Id: AnimatedView.java,v 1.6 2002/01/08 22:16:58 shaper Exp $ +// $Id: AnimatedView.java,v 1.1 2002/01/11 16:17:33 shaper Exp $ -package com.threerings.media.sprite; +package com.threerings.media.animation; import java.awt.Rectangle; -import javax.swing.JComponent; +import java.util.List; /** * A view that wishes to interact with the animation manager needs to @@ -20,7 +20,7 @@ public interface AnimatedView * * @param rects the list of {@link java.awt.Rectangle} objects. */ - public void invalidateRects (DirtyRectList rects); + public void invalidateRects (List rects); /** * Invalidates a rectangle in screen pixel coordinates in the scene diff --git a/src/java/com/threerings/media/animation/Animation.java b/src/java/com/threerings/media/animation/Animation.java new file mode 100644 index 000000000..2458f50bd --- /dev/null +++ b/src/java/com/threerings/media/animation/Animation.java @@ -0,0 +1,136 @@ +// +// $Id: Animation.java,v 1.1 2002/01/11 16:17:33 shaper Exp $ + +package com.threerings.media.animation; + +import java.awt.Graphics2D; +import java.awt.Rectangle; + +import java.util.ArrayList; + +import com.threerings.media.Log; + +/** + * The animation class is an abstract class that should be extended to + * provide animation functionality for use with the {@link + * AnimationManager} and an {@link AnimatedView}. + */ +public abstract class Animation +{ + /** + * Constructs an animation. + * + * @param bounds the animation rendering bounds. + */ + public Animation (Rectangle bounds) + { + _bounds = bounds; + } + + /** + * Called by the {@link AnimationManager} to allow the animation to + * render itself to the given graphics context. + */ + public abstract void paint (Graphics2D gfx); + + /** + * Called periodically by the {@link AnimationManager} to give the + * animation a chance to do its thing. + */ + public abstract void tick (long timestamp); + + /** + * Invalidates the animation's bounds for later re-rendering by the + * {@link AnimationManager}. + */ + public void invalidate () + { + _animmgr.addDirtyRect(_bounds); + } + + /** + * Returns whether the animation has finished all of its business. + */ + public boolean isFinished () + { + return _finished; + } + + /** + * Adds an animation observer to this animation's list of observers. + */ + public void addAnimationObserver (AnimationObserver obs) + { + // create the observer list if it doesn't yet exist + if (_observers == null) { + _observers = new ArrayList(); + } + + // make sure each observer observes only once + if (_observers.contains(obs)) { + Log.info("Attempt to observe animation already observing " + + "[anim=" + this + ", obs=" + obs + "]."); + return; + } + + // add the observer + _observers.add(obs); + } + + /** + * Notifies any animation observers that the given animation is + * finished. + */ + public void notifyObservers (AnimationEvent e) + { + int size = (_observers == null) ? 0 : _observers.size(); + for (int ii = 0; ii < size; ii++) { + ((AnimationObserver)_observers.get(ii)).handleEvent(e); + } + } + + /** + * Return a string representation of the animation. + */ + public String toString () + { + StringBuffer buf = new StringBuffer(); + buf.append("["); + toString(buf); + return buf.append("]").toString(); + } + + /** + * This should be overridden by derived classes (which should be sure + * to call super.toString()) to append the derived class + * specific animation information to the string buffer. + */ + protected void toString (StringBuffer buf) + { + buf.append("bounds=").append(_bounds); + } + + /** + * Called by the animation manager when an animation is added to said + * manager for management. + * + * @param animmgr the animation manager. + * @param view the animated view. + */ + protected void setAnimationManager (AnimationManager animmgr) + { + _animmgr = animmgr; + } + + /** Whether the animation is finished. */ + protected boolean _finished = false; + + /** The animation bounds. */ + protected Rectangle _bounds; + + /** The list of animation observers. */ + protected ArrayList _observers; + + /** The animation manager. */ + protected AnimationManager _animmgr; +} diff --git a/src/java/com/threerings/media/animation/AnimationCompletedEvent.java b/src/java/com/threerings/media/animation/AnimationCompletedEvent.java new file mode 100644 index 000000000..215ef52f0 --- /dev/null +++ b/src/java/com/threerings/media/animation/AnimationCompletedEvent.java @@ -0,0 +1,16 @@ +// +// $Id: AnimationCompletedEvent.java,v 1.1 2002/01/11 16:17:33 shaper Exp $ + +package com.threerings.media.animation; + +/** + * An animation completed event is dispatched when an animation has + * completed all of its business. + */ +public class AnimationCompletedEvent extends AnimationEvent +{ + public AnimationCompletedEvent (Animation anim) + { + super(anim); + } +} diff --git a/src/java/com/threerings/media/animation/AnimationEvent.java b/src/java/com/threerings/media/animation/AnimationEvent.java new file mode 100644 index 000000000..8f643460a --- /dev/null +++ b/src/java/com/threerings/media/animation/AnimationEvent.java @@ -0,0 +1,32 @@ +// +// $Id: AnimationEvent.java,v 1.1 2002/01/11 16:17:33 shaper Exp $ + +package com.threerings.media.animation; + +/** + * An animation event is sent to all of an animation's observers whenever + * one of the various possible animation events takes place. + */ +public class AnimationEvent +{ + /** + * Create an animation event. + * + * @param animation the involved animation. + */ + public AnimationEvent (Animation anim) + { + _anim = anim; + } + + /** + * Returns the animation involved in the event. + */ + public Animation getAnimation () + { + return _anim; + } + + /** The animation associated with this event. */ + protected Animation _anim; +} diff --git a/src/java/com/threerings/media/sprite/AnimationManager.java b/src/java/com/threerings/media/animation/AnimationManager.java similarity index 52% rename from src/java/com/threerings/media/sprite/AnimationManager.java rename to src/java/com/threerings/media/animation/AnimationManager.java index 9a75680a9..abd13c8e7 100644 --- a/src/java/com/threerings/media/sprite/AnimationManager.java +++ b/src/java/com/threerings/media/animation/AnimationManager.java @@ -1,9 +1,14 @@ // -// $Id: AnimationManager.java,v 1.21 2001/12/18 09:46:07 mdb Exp $ +// $Id: AnimationManager.java,v 1.1 2002/01/11 16:17:33 shaper Exp $ -package com.threerings.media.sprite; +package com.threerings.media.animation; + +import java.awt.Graphics2D; +import java.awt.Rectangle; + +import java.util.ArrayList; +import java.util.List; -import java.awt.Graphics; import javax.swing.JComponent; import javax.swing.SwingUtilities; import javax.swing.event.AncestorEvent; @@ -13,6 +18,7 @@ import com.samskivert.util.Interval; import com.samskivert.util.IntervalManager; import com.threerings.media.Log; +import com.threerings.media.sprite.SpriteManager; import com.threerings.media.util.PerformanceMonitor; import com.threerings.media.util.PerformanceObserver; @@ -74,6 +80,64 @@ public class AnimationManager } } + /** + * Adds a rectangle to the dirty rectangle list. Note that the + * rectangle may be destructively modified by the animation manager at + * some later date. + * + * @param rect the rectangle to add. + */ + public void addDirtyRect (Rectangle rect) + { + _dirty.add(rect); + } + + /** + * Registers the given {@link Animation} with the animation manager + * for ticking and painting. + */ + public void registerAnimation (Animation anim) + { + if (_anims.contains(anim)) { + Log.warning("Attempt to register animation more than once " + + "[anim=" + anim + "]."); + return; + } + + anim.setAnimationManager(this); + _anims.add(anim); + // Log.info("Registered animation [anim=" + anim + "]."); + } + + /** + * Un-registers the given {@link Animation} from the animation + * manager. + */ + public void unregisterAnimation (Animation anim) + { + // un-register the animation + if (!_anims.remove(anim)) { + Log.warning("Attempt to un-register animation that isn't " + + "registered [anim=" + anim + "]."); + } + + // dirty the animation bounds + anim.invalidate(); + // Log.info("Un-registered animation [anim=" + anim + "]."); + } + + /** + * Renders all registered animations to the given graphics context. + */ + public void renderAnimations (Graphics2D gfx) + { + int size = _anims.size(); + for (int ii = 0; ii < size; ii++) { + Animation anim = (Animation)_anims.get(ii); + anim.paint(gfx); + } + } + /** * Called by our interval when we'd like to begin a tick. Returns * whether we're already ticking, and notes that we've requested @@ -130,10 +194,17 @@ public class AnimationManager long now = System.currentTimeMillis(); // call tick on all sprites - _spritemgr.tick(now); + _spritemgr.tick(now, _dirty); - // invalidate screen-rects dirtied by sprites - DirtyRectList rects = _spritemgr.getDirtyRects(); + // call tick on all animations + tickAnimations(now); + + // perform a single pass merging overlapping rectangles. note + // that this will also clear out the contents of our internal + // dirty rectangle list. + List rects = mergeDirtyRects(_dirty); + + // invalidate screen-rects dirtied by sprites and/or animations if (rects.size() > 0) { // pass the dirty-rects on to the scene view _view.invalidateRects(rects); @@ -142,8 +213,11 @@ public class AnimationManager _view.paintImmediately(); } + // remove any finished animations + removeFinishedAnimations(); + // update refresh-rate information - // PerformanceMonitor.tick(AnimationManager.this, "refresh"); + PerformanceMonitor.tick(AnimationManager.this, "refresh"); if (finishedTick()) { // finishedTick returning true means there's been a @@ -154,6 +228,40 @@ public class AnimationManager } } + /** + * Calls tick on all animations currently registered with the + * animation manager. + */ + protected void tickAnimations (long timestamp) + { + int size = _anims.size(); + for (int ii = 0; ii < size; ii++) { + ((Animation)_anims.get(ii)).tick(timestamp); +// if (anim.isFinished()) { +// anim.notifyObservers(new AnimationWillCompleteEvent(anim)); +// } + } + } + + /** + * Removes any finished animations from the list of animations and + * notifies their respective animation observers, if any. + */ + protected void removeFinishedAnimations () + { + int size = _anims.size(); + for (int ii = size - 1; ii >= 0; ii--) { + Animation anim = (Animation)_anims.get(ii); + if (anim.isFinished()) { + // let any animation observers know that we're done + anim.notifyObservers(new AnimationCompletedEvent(anim)); + // un-register the animation + unregisterAnimation(anim); + Log.info("Removed finished animation [anim=" + anim + "]."); + } + } + } + /** * Queues up a tick on the AWT event handler thread, iff we are still * operating. @@ -165,12 +273,51 @@ public class AnimationManager } } + /** + * Returns a new list of dirty rectangles representing the given list + * with any intersecting rectangles merged. The given list is + * destructively modified and cleared of all contents. + * + * @return the list of merged dirty rects. + */ + protected List mergeDirtyRects (List rects) + { + ArrayList merged = new ArrayList(); + + while (rects.size() > 0) { + // pop the next rectangle from the dirty list + Rectangle mr = (Rectangle)rects.remove(0); + + // merge in any overlapping rectangles + for (int ii = 0; ii < rects.size(); ii++) { + Rectangle r = (Rectangle)rects.get(ii); + if (mr.intersects(r)) { + // remove the overlapping rectangle from the list + rects.remove(ii--); + // grow the merged dirty rectangle + mr.add(r); + } + } + + // add the merged rectangle to the list + merged.add(mr); + } + + return merged; + } + // documentation inherited public void checkpoint (String name, int ticks) { Log.info(name + " [ticks=" + ticks + "]."); } + /** The list of animations. */ + protected ArrayList _anims = new ArrayList(); + + /** The dirty rectangles. */ + protected ArrayList _dirty = new ArrayList(); + /** The ticker runnable that we put on the AWT thread periodically. */ protected Runnable _ticker; diff --git a/src/java/com/threerings/media/animation/AnimationObserver.java b/src/java/com/threerings/media/animation/AnimationObserver.java new file mode 100644 index 000000000..069e8e3be --- /dev/null +++ b/src/java/com/threerings/media/animation/AnimationObserver.java @@ -0,0 +1,19 @@ +// +// $Id: AnimationObserver.java,v 1.1 2002/01/11 16:17:33 shaper Exp $ + +package com.threerings.media.animation; + +/** + * An interface to be implemented by classes that would like to observe an + * {@link Animation} and be notified of interesting events relating to it. + */ +public interface AnimationObserver +{ + /** + * This method is called by the {@link AnimationManager} when + * something interesting involving the animation happens. + * + * @param event the animation event. + */ + public void handleEvent (AnimationEvent event); +} diff --git a/src/java/com/threerings/media/animation/BobbleAnimation.java b/src/java/com/threerings/media/animation/BobbleAnimation.java new file mode 100644 index 000000000..5cb673123 --- /dev/null +++ b/src/java/com/threerings/media/animation/BobbleAnimation.java @@ -0,0 +1,97 @@ +// +// $Id: BobbleAnimation.java,v 1.1 2002/01/11 16:17:33 shaper Exp $ + +package com.threerings.media.animation; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Rectangle; + +import com.threerings.media.Log; +import com.threerings.media.util.RandomUtil; + +/** + * An animation that bobbles an image around within a specific horizontal + * and vertical pixel range for a given period of time. + */ +public class BobbleAnimation extends Animation +{ + /** + * Constructs a bobble animation. + * + * @param image the image to animate. + * @param sx the starting x-position. + * @param sy the starting y-position. + * @param rx the horizontal bobble range. + * @param ry the vertical bobble range. + * @param length the time to animate in milliseconds. + */ + public BobbleAnimation ( + Image image, int sx, int sy, int rx, int ry, int length) + { + super(new Rectangle(sx - rx, sy - ry, sx + image.getWidth(null) + rx, + sy + image.getHeight(null) + ry)); + + // save things off + _image = image; + _sx = sx; + _sy = sy; + _rx = rx; + _ry = ry; + + // calculate animation ending time + _end = System.currentTimeMillis() + length; + + // calculate the dirty rectangle bounds + int wid = image.getWidth(null), hei = image.getHeight(null); + // _bounds = new Rectangle(sx - rx, sy - ry, sx + wid + rx, sy + hei + ry); + } + + // documentation inherited + public void tick (long timestamp) + { + _finished = (timestamp >= _end); + invalidate(); + + // calculate the latest position + int dx = RandomUtil.getInt(_rx); + int dy = RandomUtil.getInt(_ry); + _x = (_sx + dx) * ((RandomUtil.getInt(2) == 0) ? -1 : 1); + _y = (_sy + dy) * ((RandomUtil.getInt(2) == 0) ? -1 : 1); + } + + // documentation inherited + public void paint (Graphics2D gfx) + { + gfx.drawImage(_image, _x, _y, null); + } + + // documentation inherited + protected void toString (StringBuffer buf) + { + super.toString(buf); + + buf.append(", x=").append(_x); + buf.append(", y=").append(_y); + buf.append(", rx=").append(_rx); + buf.append(", ry=").append(_ry); + buf.append(", sx=").append(_sx); + buf.append(", sy=").append(_sy); + } + + /** The current position of the image. */ + protected int _x, _y; + + /** The horizontal and vertical bobbling range. */ + protected int _rx, _ry; + + /** The starting position. */ + protected int _sx, _sy; + + /** The image to animate. */ + protected Image _image; + + /** The ending animation time. */ + protected long _end; +} diff --git a/src/java/com/threerings/media/animation/ExplodeAnimation.java b/src/java/com/threerings/media/animation/ExplodeAnimation.java new file mode 100644 index 000000000..d72d0a12c --- /dev/null +++ b/src/java/com/threerings/media/animation/ExplodeAnimation.java @@ -0,0 +1,165 @@ +// +// $Id: ExplodeAnimation.java,v 1.1 2002/01/11 16:17:33 shaper Exp $ + +package com.threerings.media.animation; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.Shape; + +import com.threerings.media.Log; + +/** + * An animation that displays an image exploding into chunks. The + * animation ends when all image chunks have exited the animation bounds. + */ +public class ExplodeAnimation extends Animation +{ + /** + * Constructs an explode animation. + * + * @param bounds the bounds within which to animate. + * @param image the image to animate. + * @param sx the starting x-position. + * @param sy the starting y-position. + * @param vel the initial chunk velocity. + */ + public ExplodeAnimation ( + Rectangle bounds, Image image, int sx, int sy, float vel) + { + super(bounds); + + // save things off + _image = image; + _sx = sx; + _sy = sy; + _vel = vel; + + // save the animation starting time + _start = System.currentTimeMillis(); + + // determine chunk dimensions + _cwid = image.getWidth(null) / X_CHUNK_COUNT; + _chei = image.getHeight(null) / Y_CHUNK_COUNT; + + // initialize the chunk positions + int cx = sx + (image.getWidth(null) / 2); + int cy = sy + (image.getHeight(null) / 2); + int cpos = (cx << 16 | cy); + for (int ii = 0; ii < NUM_CHUNKS; ii++) { + int xpos = ii % X_CHUNK_COUNT; + int ypos = ii / X_CHUNK_COUNT; + _cpos[ii] = ((sx + xpos) << 16 | (sy + ypos)); + } + } + + // documentation inherited + public void tick (long timestamp) + { + // figure out the distance the chunks have travelled + long msecs = timestamp - _start; + int travpix = (int)(msecs * _vel); + + // move all chunks and check whether any remain to be animated + int inside = 0; + for (int ii = 0; ii < NUM_CHUNKS; ii++) { + // retrieve the current chunk position + int x = _cpos[ii] >> 16; + int y = _cpos[ii] & 0xFFFF; + + // determine the chunk movement direction + int xpos = ii % X_CHUNK_COUNT; + int ypos = ii / X_CHUNK_COUNT; + int mx, my; + switch (xpos) { + case 0: mx = -1; break; + case X_CHUNK_COUNT - 1: mx = 1; break; + default: mx = 0; break; + } + switch (ypos) { + case 0: my = -1; break; + case Y_CHUNK_COUNT - 1: my = 1; break; + default: my = 0; + } + // deal with the center point for odd dimensions + if (mx == 0 && my == 0) { + my = 1; + } + + // update the chunk position + x = _sx + (travpix * mx); + y = _sy + (travpix * my); + _cpos[ii] = (x << 16 | y); + + // note whether this chunk is still within our bounds + if (_bounds.contains(x, y)) { + inside++; + } + } + + _finished = (inside == 0); + invalidate(); + } + + // documentation inherited + public void paint (Graphics2D gfx) + { + for (int ii = 0; ii < NUM_CHUNKS; ii++) { + // get the chunk location on-screen + int x = _cpos[ii] >> 16; + int y = _cpos[ii] & 0xFFFF; + + // get the chunk position within the image + int xpos = ii % X_CHUNK_COUNT; + int ypos = ii / X_CHUNK_COUNT; + + // calculate image chunk offset + int xoff = -(xpos * _cwid); + int yoff = -(ypos * _chei); + + // draw the chunk + Shape oclip = gfx.getClip(); + gfx.clipRect(x, y, _cwid, _chei); + gfx.drawImage(_image, x + xoff, y + yoff, null); + gfx.setClip(oclip); + } + } + + // documentation inherited + protected void toString (StringBuffer buf) + { + super.toString(buf); + + buf.append(", sx=").append(_sx); + buf.append(", sy=").append(_sy); + } + + /** The number of horizontal chunks from the image to animate. */ + protected static final int X_CHUNK_COUNT = 3; + + /** The number of vertical chunks from the image to animate. */ + protected static final int Y_CHUNK_COUNT = 3; + + /** The total number of chunks. */ + protected static final int NUM_CHUNKS = X_CHUNK_COUNT * Y_CHUNK_COUNT; + + /** The current position of each chunk. */ + protected int[] _cpos = new int[NUM_CHUNKS]; + + /** The individual chunk dimensions in pixels. */ + protected int _cwid, _chei; + + /** The chunk velocity. */ + protected float _vel; + + /** The starting position. */ + protected int _sx, _sy; + + /** The image to animate. */ + protected Image _image; + + /** The starting animation time. */ + protected long _start; +} diff --git a/src/java/com/threerings/media/animation/FadeAnimation.java b/src/java/com/threerings/media/animation/FadeAnimation.java new file mode 100644 index 000000000..198b20361 --- /dev/null +++ b/src/java/com/threerings/media/animation/FadeAnimation.java @@ -0,0 +1,117 @@ +// +// $Id: FadeAnimation.java,v 1.1 2002/01/11 16:17:33 shaper Exp $ + +package com.threerings.media.animation; + +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Composite; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Rectangle; + +import com.threerings.media.Log; + +/** + * An animation that displays an image fading from invisibility to full + * opacity, or vice-versa. The animation is finished when the target + * opacity is reached. + */ +public class FadeAnimation extends Animation +{ + /** + * Constructs a fade animation. + * + * @param image the image to animate. + * @param x the image x-position. + * @param y the image y-position. + * @param alpha the starting alpha. + * @param step the alpha amount to step by each millisecond. + * @param target the target alpha level. + */ + public FadeAnimation ( + Image image, int x, int y, float alpha, float step, float target) + { + super(new Rectangle(x, y, image.getWidth(null), image.getHeight(null))); + + // save things off + _image = image; + _x = x; + _y = y; + _startAlpha = _alpha = alpha; + _step = step; + _target = target; + + // create the initial composite + _comp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, _alpha); + + // save the animation starting time + _start = System.currentTimeMillis(); + } + + // documentation inherited + public void tick (long timestamp) + { + // figure out the current alpha + long msecs = timestamp - _start; + _alpha = Math.min(_startAlpha + (msecs * _step), 1.0f); + _comp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, _alpha); + + // check whether we're done and dirty ourselves + _finished = ((_startAlpha < _target) ? (_alpha >= _target) : + (_alpha <= _target)); + invalidate(); + + // Log.info("Ticked fade [anim=" + this + "]."); + } + + // documentation inherited + public void paint (Graphics2D gfx) + { + Composite ocomp = gfx.getComposite(); + if (_comp == null) { + Log.warning("Fade anim has null composite [anim=" + this + "]."); + } else { + gfx.setComposite(_comp); + } + gfx.drawImage(_image, _x, _y, null); + gfx.setComposite(ocomp); + } + + // documentation inherited + protected void toString (StringBuffer buf) + { + super.toString(buf); + + buf.append(", x=").append(_x); + buf.append(", y=").append(_y); + buf.append(", alpha=").append(_alpha); + buf.append(", startAlpha=").append(_startAlpha); + buf.append(", step=").append(_step); + buf.append(", target=").append(_target); + } + + /** The composite object used to render the image with desired opacity. */ + protected Composite _comp; + + /** The current alpha of the image. */ + protected float _alpha; + + /** The target alpha. */ + protected float _target; + + /** The alpha step per millisecond. */ + protected float _step; + + /** The starting alpha. */ + protected float _startAlpha; + + /** The image position. */ + protected int _x, _y; + + /** The image to animate. */ + protected Image _image; + + /** The starting animation time. */ + protected long _start; +} diff --git a/src/java/com/threerings/media/sprite/DirtyRectList.java b/src/java/com/threerings/media/sprite/DirtyRectList.java deleted file mode 100644 index 1e22a8445..000000000 --- a/src/java/com/threerings/media/sprite/DirtyRectList.java +++ /dev/null @@ -1,41 +0,0 @@ -// -// $Id: DirtyRectList.java,v 1.3 2001/12/16 08:05:20 mdb Exp $ - -package com.threerings.media.sprite; - -import java.awt.Rectangle; -import java.util.ArrayList; - -import com.samskivert.util.StringUtil; - -/** - * The dirty rect list is used to maintain a list of dirty rectangles. It - * differs from a plain old list only in that it provides a convenient - * mechanism for appending a dirty rectangle to the list if and only if - * that rectangle is not already on the list. - */ -public class DirtyRectList extends ArrayList -{ - /** - * Appends the specified dirty rectangle to the list only if a - * rectangle of the same position and size is not already in the - * list. - * - * @return true if the rectangle were appended, false if she weren't. - * Har! - */ - public boolean appendDirtyRect (Rectangle rect) - { - if (contains(rect)) { - return false; - } else { - add(rect); - return true; - } - } - - public String toString () - { - return StringUtil.toString(iterator()); - } -} diff --git a/src/java/com/threerings/media/sprite/SpriteManager.java b/src/java/com/threerings/media/sprite/SpriteManager.java index e1cd1b511..1149b612d 100644 --- a/src/java/com/threerings/media/sprite/SpriteManager.java +++ b/src/java/com/threerings/media/sprite/SpriteManager.java @@ -1,13 +1,23 @@ // -// $Id: SpriteManager.java,v 1.16 2001/10/25 01:40:26 shaper Exp $ +// $Id: SpriteManager.java,v 1.17 2002/01/11 16:17:33 shaper Exp $ package com.threerings.media.sprite; -import java.awt.*; -import java.util.*; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.Shape; + +import java.util.Arrays; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Iterator; import java.util.List; +import com.samskivert.util.CollectionUtil; import com.samskivert.util.Tuple; + import com.threerings.media.Log; /** @@ -22,7 +32,7 @@ public class SpriteManager { _sprites = new ArrayList(); _notify = new ArrayList(); - _dirty = new DirtyRectList(); + _dirty = new ArrayList(); } /** @@ -89,39 +99,6 @@ public class SpriteManager sprite.setSpriteManager(null); } - /** - * Return the list of dirty rects in screen pixel coordinates that - * have been created by any sprites since the last time the dirty - * rects were requested. - * - * @return the list of dirty rects. - */ - public DirtyRectList getDirtyRects () - { - DirtyRectList merged = new DirtyRectList(); - - while (_dirty.size() > 0) { - // pop the next rectangle from the dirty list - Rectangle mr = (Rectangle)_dirty.remove(0); - - // merge in any overlapping rectangles - for (int ii = 0; ii < _dirty.size(); ii++) { - Rectangle r = (Rectangle)_dirty.get(ii); - if (mr.intersects(r)) { - // remove the overlapping rectangle from the list - _dirty.remove(ii--); - // grow the merged dirty rectangle - mr.add(r); - } - } - - // add the merged rectangle to the list - merged.add(mr); - } - - return merged; - } - /** * Render the sprites residing within the given polygon to the given * graphics context. @@ -163,7 +140,7 @@ public class SpriteManager * queue by the {@link AnimationManager}. Handles moving about of * sprites and reporting of sprite collisions. */ - public void tick (long timestamp) + public void tick (long timestamp, List rects) { // tick all sprites tickSprites(timestamp); @@ -181,6 +158,13 @@ public class SpriteManager // observers may take, such as sprite removal, won't screw us // up elsewhere. handleSpriteEvents(); + + // add all generated dirty rectangles to the passed-in dirty + // rectangle list + CollectionUtil.addAll(rects, _dirty.iterator()); + + // clear out our internal dirty rectangle list + _dirty.clear(); } /** @@ -312,7 +296,7 @@ public class SpriteManager protected ArrayList _sprites; /** The dirty rectangles created by sprites. */ - protected DirtyRectList _dirty; + protected ArrayList _dirty; /** The list of pending sprite notifications. */ protected ArrayList _notify; diff --git a/src/java/com/threerings/media/sprite/SpriteObserver.java b/src/java/com/threerings/media/sprite/SpriteObserver.java index 658c9ef65..e2ca19895 100644 --- a/src/java/com/threerings/media/sprite/SpriteObserver.java +++ b/src/java/com/threerings/media/sprite/SpriteObserver.java @@ -1,5 +1,5 @@ // -// $Id: SpriteObserver.java,v 1.3 2001/09/13 19:36:20 mdb Exp $ +// $Id: SpriteObserver.java,v 1.4 2002/01/11 16:17:33 shaper Exp $ package com.threerings.media.sprite; @@ -14,7 +14,6 @@ public interface SpriteObserver * This method is called by the {@link SpriteManager} when something * interesting is accomplished by or happens to the sprite. * - * @param sprite the sprite involved. * @param event the sprite event. */ public void handleEvent (SpriteEvent event); diff --git a/src/java/com/threerings/miso/client/IsoSceneView.java b/src/java/com/threerings/miso/client/IsoSceneView.java index 153f31d28..5cfaef017 100644 --- a/src/java/com/threerings/miso/client/IsoSceneView.java +++ b/src/java/com/threerings/miso/client/IsoSceneView.java @@ -1,19 +1,19 @@ // -// $Id: IsoSceneView.java,v 1.82 2002/01/08 22:16:59 shaper Exp $ +// $Id: IsoSceneView.java,v 1.83 2002/01/11 16:17:34 shaper Exp $ package com.threerings.miso.scene; +import java.awt.BasicStroke; import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; +import java.awt.Point; import java.awt.Polygon; +import java.awt.Rectangle; import java.awt.Shape; import java.awt.Stroke; -import java.awt.Rectangle; -import java.awt.FontMetrics; -import java.awt.Point; -import java.awt.Font; -import java.awt.BasicStroke; import java.util.ArrayList; import java.util.Iterator; @@ -21,7 +21,7 @@ import java.util.List; import com.samskivert.util.HashIntMap; -import com.threerings.media.sprite.DirtyRectList; +import com.threerings.media.animation.AnimationManager; import com.threerings.media.sprite.Path; import com.threerings.media.sprite.SpriteManager; @@ -45,14 +45,19 @@ public class IsoSceneView implements SceneView /** * Constructs an iso scene view. * + * @param animmgr the animation manager. * @param spritemgr the sprite manager. * @param model the data model. */ - public IsoSceneView (SpriteManager spritemgr, IsoSceneViewModel model) + public IsoSceneView (AnimationManager animmgr, SpriteManager spritemgr, + IsoSceneViewModel model) { + // save off references + _animmgr = animmgr; _spritemgr = spritemgr; - _model = model; + + // give the model a chance to pre-calculate various things _model.precalculate(); // create our polygon arrays and create polygons for each of the @@ -106,6 +111,9 @@ public class IsoSceneView implements SceneView // render the scene to the graphics context renderScene(gfx); + // render any animations + _animmgr.renderAnimations(gfx); + // draw frames of dirty tiles and rectangles // drawDirtyRegions(gfx); @@ -366,7 +374,7 @@ public class IsoSceneView implements SceneView } // documentation inherited - public void invalidateRects (DirtyRectList rects) + public void invalidateRects (List rects) { int size = rects.size(); for (int ii = 0; ii < size; ii++) { @@ -625,4 +633,7 @@ public class IsoSceneView implements SceneView /** The sprite manager. */ protected SpriteManager _spritemgr; + + /** The animation manager. */ + protected AnimationManager _animmgr; } diff --git a/src/java/com/threerings/miso/client/SceneView.java b/src/java/com/threerings/miso/client/SceneView.java index 4a4cb00ad..0877fa5a4 100644 --- a/src/java/com/threerings/miso/client/SceneView.java +++ b/src/java/com/threerings/miso/client/SceneView.java @@ -1,5 +1,5 @@ // -// $Id: SceneView.java,v 1.21 2002/01/08 22:16:59 shaper Exp $ +// $Id: SceneView.java,v 1.22 2002/01/11 16:17:34 shaper Exp $ package com.threerings.miso.scene; @@ -8,7 +8,6 @@ import java.awt.Point; import java.awt.Rectangle; import java.util.List; -import com.threerings.media.sprite.DirtyRectList; import com.threerings.media.sprite.Path; /** @@ -24,7 +23,7 @@ public interface SceneView * * @param rects the list of {@link java.awt.Rectangle} objects. */ - public void invalidateRects (DirtyRectList rects); + public void invalidateRects (List rects); /** * Invalidate a rectangle in screen pixel coordinates in the scene diff --git a/src/java/com/threerings/miso/client/SceneViewPanel.java b/src/java/com/threerings/miso/client/SceneViewPanel.java index 7af40c56d..32ebfc11f 100644 --- a/src/java/com/threerings/miso/client/SceneViewPanel.java +++ b/src/java/com/threerings/miso/client/SceneViewPanel.java @@ -1,5 +1,5 @@ // -// $Id: SceneViewPanel.java,v 1.22 2002/01/08 22:16:59 shaper Exp $ +// $Id: SceneViewPanel.java,v 1.23 2002/01/11 16:17:34 shaper Exp $ package com.threerings.miso.scene; @@ -7,10 +7,12 @@ import java.awt.Dimension; import java.awt.Graphics; import java.awt.Rectangle; +import java.util.List; + import com.samskivert.util.Config; -import com.threerings.media.sprite.AnimatedPanel; -import com.threerings.media.sprite.DirtyRectList; +import com.threerings.media.animation.AnimationManager; +import com.threerings.media.animation.AnimatedPanel; import com.threerings.media.sprite.SpriteManager; import com.threerings.miso.util.MisoUtil; @@ -27,6 +29,12 @@ public class SceneViewPanel extends AnimatedPanel */ public SceneViewPanel (Config config, SpriteManager spritemgr) { + // save off references + _spritemgr = spritemgr; + + // create an animation manager for this panel + _animmgr = new AnimationManager(_spritemgr, this); + // create the data model for the scene view _viewmodel = new IsoSceneViewModel(config); @@ -35,7 +43,7 @@ public class SceneViewPanel extends AnimatedPanel _viewmodel.addListener(this); // create the scene view - _view = newSceneView(spritemgr, _viewmodel); + _view = newSceneView(_animmgr, spritemgr, _viewmodel); } /** @@ -50,9 +58,9 @@ public class SceneViewPanel extends AnimatedPanel * Constructs the underlying scene view implementation. */ protected IsoSceneView newSceneView ( - SpriteManager smgr, IsoSceneViewModel model) + AnimationManager amgr, SpriteManager smgr, IsoSceneViewModel model) { - return new IsoSceneView(smgr, model); + return new IsoSceneView(amgr, smgr, model); } /** @@ -78,7 +86,7 @@ public class SceneViewPanel extends AnimatedPanel } // documentation inherited - public void invalidateRects (DirtyRectList rects) + public void invalidateRects (List rects) { // pass the invalid rects on to our scene view _view.invalidateRects(rects); @@ -113,4 +121,10 @@ public class SceneViewPanel extends AnimatedPanel /** The scene view we're managing. */ protected SceneView _view; + + /** A reference to the active sprite manager. */ + protected SpriteManager _spritemgr; + + /** A reference to the active animation manager. */ + protected AnimationManager _animmgr; } diff --git a/src/java/com/threerings/miso/client/SwingSceneViewPanel.java b/src/java/com/threerings/miso/client/SwingSceneViewPanel.java index d92bbe7e4..a5dbff467 100644 --- a/src/java/com/threerings/miso/client/SwingSceneViewPanel.java +++ b/src/java/com/threerings/miso/client/SwingSceneViewPanel.java @@ -1,5 +1,5 @@ // -// $Id: SwingSceneViewPanel.java,v 1.1 2002/01/08 22:19:29 shaper Exp $ +// $Id: SwingSceneViewPanel.java,v 1.2 2002/01/11 16:17:34 shaper Exp $ package com.threerings.miso.scene; @@ -10,11 +10,14 @@ import java.awt.Rectangle; import javax.swing.JComponent; +import java.util.List; + import com.samskivert.util.Config; -import com.threerings.media.sprite.AnimatedPanel; -import com.threerings.media.sprite.DirtyRectList; +import com.threerings.media.animation.AnimatedView; +import com.threerings.media.animation.AnimationManager; import com.threerings.media.sprite.SpriteManager; + import com.threerings.miso.util.MisoUtil; /** @@ -30,7 +33,7 @@ import com.threerings.miso.util.MisoUtil; * do not depend on use of the animation manager to refresh the display. */ public class SwingSceneViewPanel extends JComponent - implements IsoSceneViewModelListener + implements AnimatedView, IsoSceneViewModelListener { /** * Constructs the scene view panel. @@ -44,8 +47,11 @@ public class SwingSceneViewPanel extends JComponent // the scene display has changed and needs must be repainted _viewmodel.addListener(this); + // create an animation manager for this panel + AnimationManager animmgr = new AnimationManager(spritemgr, this); + // create the scene view - _view = newSceneView(spritemgr, _viewmodel); + _view = newSceneView(animmgr, spritemgr, _viewmodel); } /** @@ -60,9 +66,9 @@ public class SwingSceneViewPanel extends JComponent * Constructs the underlying scene view implementation. */ protected IsoSceneView newSceneView ( - SpriteManager smgr, IsoSceneViewModel model) + AnimationManager amgr, SpriteManager smgr, IsoSceneViewModel model) { - return new IsoSceneView(smgr, model); + return new IsoSceneView(amgr, smgr, model); } /** @@ -89,7 +95,7 @@ public class SwingSceneViewPanel extends JComponent } // documentation inherited - public void invalidateRects (DirtyRectList rects) + public void invalidateRects (List rects) { // pass the invalid rects on to our scene view _view.invalidateRects(rects); @@ -101,6 +107,12 @@ public class SwingSceneViewPanel extends JComponent _view.invalidateRect(rect); } + // documentation inherited + public void paintImmediately () + { + repaint(); + } + /** * Returns the desired size for the panel based on the requested * and calculated bounds of the scene view. diff --git a/tests/src/java/com/threerings/miso/viewer/ViewerSceneViewPanel.java b/tests/src/java/com/threerings/miso/viewer/ViewerSceneViewPanel.java index d8c903abf..5efffddec 100644 --- a/tests/src/java/com/threerings/miso/viewer/ViewerSceneViewPanel.java +++ b/tests/src/java/com/threerings/miso/viewer/ViewerSceneViewPanel.java @@ -1,5 +1,5 @@ // -// $Id: ViewerSceneViewPanel.java,v 1.37 2002/01/08 22:16:59 shaper Exp $ +// $Id: ViewerSceneViewPanel.java,v 1.38 2002/01/11 16:17:34 shaper Exp $ package com.threerings.miso.viewer; @@ -15,7 +15,7 @@ import com.threerings.cast.CharacterManager; import com.threerings.cast.ComponentRepository; import com.threerings.cast.util.CastUtil; -import com.threerings.media.sprite.AnimationManager; +import com.threerings.media.animation.AnimationManager; import com.threerings.media.sprite.LineSegmentPath; import com.threerings.media.sprite.PathCompletedEvent; import com.threerings.media.sprite.SpriteEvent; @@ -45,9 +45,6 @@ public class ViewerSceneViewPanel extends SceneViewPanel { super(ctx.getConfig(), spritemgr); - // create an animation manager for this panel - _animmgr = new AnimationManager(spritemgr, this); - // create the character descriptors _descUser = CastUtil.getRandomDescriptor(crepo); _descDecoy = CastUtil.getRandomDescriptor(crepo); @@ -213,9 +210,6 @@ public class ViewerSceneViewPanel extends SceneViewPanel /** The character descriptor for the decoy characters. */ protected CharacterDescriptor _descDecoy; - /** The animation manager. */ - protected AnimationManager _animmgr; - /** The sprite we're manipulating within the view. */ protected MisoCharacterSprite _sprite;