Added support for animations to the animation manager. Moved animation
classes into their own package. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@849 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -1,120 +0,0 @@
|
||||
//
|
||||
// $Id: AnimatedPanel.java,v 1.6 2002/01/08 22:16:58 shaper Exp $
|
||||
|
||||
package com.threerings.media.sprite;
|
||||
|
||||
import java.awt.AWTException;
|
||||
import java.awt.BufferCapabilities;
|
||||
import java.awt.Canvas;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.ImageCapabilities;
|
||||
import java.awt.Rectangle;
|
||||
|
||||
import java.awt.image.BufferStrategy;
|
||||
|
||||
import com.threerings.media.Log;
|
||||
|
||||
/**
|
||||
* The animated panel provides a useful extensible implementation of a
|
||||
* {@link Canvas} that implements the {@link AnimatedView} interface.
|
||||
* Sub-classes should override {@link #render} to draw their
|
||||
* panel-specific contents, and may choose to override {@link
|
||||
* #invalidateRects} and {@link #invalidateRect} to optimize their
|
||||
* internal rendering.
|
||||
*/
|
||||
public class AnimatedPanel extends Canvas implements AnimatedView
|
||||
{
|
||||
/**
|
||||
* Constructs an animated panel.
|
||||
*/
|
||||
public AnimatedPanel ()
|
||||
{
|
||||
// set our attributes for optimal display performance
|
||||
// setIgnoreRepaint(true);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void paint (Graphics g)
|
||||
{
|
||||
update(g);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void update (Graphics g)
|
||||
{
|
||||
paintImmediately();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the panel to the given graphics object. Sub-classes
|
||||
* should override this method to paint their panel-specific
|
||||
* contents.
|
||||
*/
|
||||
protected void render (Graphics g)
|
||||
{
|
||||
// nothing for now
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void invalidateRects (DirtyRectList rects)
|
||||
{
|
||||
// nothing for now
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void invalidateRect (Rectangle rect)
|
||||
{
|
||||
// nothing for now
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void paintImmediately ()
|
||||
{
|
||||
if (!isValid() || !isShowing()) {
|
||||
Log.warning("Attempt to paint unprepared panel " +
|
||||
"[valid=" + isValid() +
|
||||
", showing=" + isShowing() + "].");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_strategy == null) {
|
||||
// create and obtain a reference to the buffer strategy
|
||||
createBufferStrategy(BUFFER_COUNT);
|
||||
_strategy = getBufferStrategy();
|
||||
Log.info("Created buffer strategy [strategy=" + _strategy + "].");
|
||||
}
|
||||
|
||||
// render the panel
|
||||
Graphics g = null;
|
||||
try {
|
||||
g = _strategy.getDrawGraphics();
|
||||
render(g);
|
||||
} finally {
|
||||
if (g != null) {
|
||||
g.dispose();
|
||||
}
|
||||
}
|
||||
_strategy.show();
|
||||
}
|
||||
|
||||
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.
|
||||
BufferCapabilities bufferCaps = new BufferCapabilities(
|
||||
new ImageCapabilities(false), new ImageCapabilities(false), null);
|
||||
try {
|
||||
createBufferStrategy(numBuffers, bufferCaps);
|
||||
} catch (AWTException e) {
|
||||
throw new InternalError("Could not create a buffer strategy");
|
||||
}
|
||||
}
|
||||
|
||||
/** The number of buffers to use when rendering. */
|
||||
protected static final int BUFFER_COUNT = 2;
|
||||
|
||||
/** The buffer strategy used for optimal animation rendering. */
|
||||
protected BufferStrategy _strategy;
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
//
|
||||
// $Id: AnimatedView.java,v 1.6 2002/01/08 22:16:58 shaper Exp $
|
||||
|
||||
package com.threerings.media.sprite;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import javax.swing.JComponent;
|
||||
|
||||
/**
|
||||
* A view that wishes to interact with the animation manager needs to
|
||||
* implement this interface to give the animation manager a means by which
|
||||
* to communicate the regions of the view that need to be repainted
|
||||
* because of the process of animating on top of the view.
|
||||
*/
|
||||
public interface AnimatedView
|
||||
{
|
||||
/**
|
||||
* Invalidate a list of rectangles in screen pixel coordinates in the
|
||||
* scene view for later repainting.
|
||||
*
|
||||
* @param rects the list of {@link java.awt.Rectangle} objects.
|
||||
*/
|
||||
public void invalidateRects (DirtyRectList rects);
|
||||
|
||||
/**
|
||||
* Invalidates a rectangle in screen pixel coordinates in the scene
|
||||
* view for later repainting.
|
||||
*
|
||||
* @param rect the {@link java.awt.Rectangle} to dirty.
|
||||
*/
|
||||
public void invalidateRect (Rectangle rect);
|
||||
|
||||
/**
|
||||
* Requests that the animated view paint itself immediately (that it
|
||||
* complete the painting process before returning from this function).
|
||||
* This will only be called on the AWT thread and when it is safe to
|
||||
* paint.
|
||||
*/
|
||||
public void paintImmediately ();
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
//
|
||||
// $Id: AnimationManager.java,v 1.21 2001/12/18 09:46:07 mdb Exp $
|
||||
|
||||
package com.threerings.media.sprite;
|
||||
|
||||
import java.awt.Graphics;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.AncestorEvent;
|
||||
|
||||
import com.samskivert.swing.event.AncestorAdapter;
|
||||
import com.samskivert.util.Interval;
|
||||
import com.samskivert.util.IntervalManager;
|
||||
|
||||
import com.threerings.media.Log;
|
||||
import com.threerings.media.util.PerformanceMonitor;
|
||||
import com.threerings.media.util.PerformanceObserver;
|
||||
|
||||
/**
|
||||
* The animation manager handles the regular refreshing of the scene view
|
||||
* to allow for animation. It also may someday manage special scene-wide
|
||||
* animations, such as rain, fog, or earthquakes.
|
||||
*/
|
||||
public class AnimationManager
|
||||
implements Interval, PerformanceObserver
|
||||
{
|
||||
/**
|
||||
* Construct and initialize the animation manager with a sprite
|
||||
* manager and the view in which the animations will take place. The
|
||||
* animation manager will automatically start itself up, but must be
|
||||
* explicitly shutdown when the animated view is no longer in
|
||||
* operation via a call to {@link #stop}.
|
||||
*/
|
||||
public AnimationManager (SpriteManager spritemgr, AnimatedView view)
|
||||
{
|
||||
// save off references to the objects we care about
|
||||
_spritemgr = spritemgr;
|
||||
_view = view;
|
||||
|
||||
// register to monitor the refresh action
|
||||
PerformanceMonitor.register(this, "refresh", 1000);
|
||||
|
||||
// start ourselves up
|
||||
start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the animation manager to doing its business.
|
||||
*/
|
||||
public synchronized void start ()
|
||||
{
|
||||
if (_ticker == null) {
|
||||
// create ticker for queueing up tick requests on AWT thread
|
||||
_ticker = new Runnable() {
|
||||
public void run () {
|
||||
tick();
|
||||
}
|
||||
};
|
||||
// register the refresh interval
|
||||
_iid = IntervalManager.register(this, REFRESH_INTERVAL, null, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs the animation manager to stop doing its business.
|
||||
*/
|
||||
public synchronized void stop ()
|
||||
{
|
||||
if (_ticker != null) {
|
||||
_ticker = null;
|
||||
// un-register the refresh interval since we're now hidden
|
||||
IntervalManager.remove(_iid);
|
||||
_iid = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by our interval when we'd like to begin a tick. Returns
|
||||
* whether we're already ticking, and notes that we've requested
|
||||
* another tick.
|
||||
*/
|
||||
protected synchronized boolean requestTick ()
|
||||
{
|
||||
return !(_ticking++ > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the tick task when it's finished with a tick.
|
||||
* Returns whether a new tick task should be created immediately.
|
||||
*/
|
||||
protected synchronized boolean finishedTick ()
|
||||
{
|
||||
if (--_ticking > 0) {
|
||||
_ticking = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The <code>IntervalManager</code> calls this method as often as
|
||||
* we've requested to obtain our desired frame rate. Since we'd
|
||||
* like to avoid messy thread-synchronization between the AWT
|
||||
* thread and other threads, here we just add the tick task to the
|
||||
* AWT thread for later execution.
|
||||
*/
|
||||
public void intervalExpired (int id, Object arg)
|
||||
{
|
||||
if (requestTick()) {
|
||||
// throw the tick task on the AWT thread task queue
|
||||
queueTick();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The <code>tick</code> method handles updating sprites and
|
||||
* repainting the target display.
|
||||
*/
|
||||
protected void tick ()
|
||||
{
|
||||
synchronized (this) {
|
||||
// see if we were shutdown since we were last queued up
|
||||
if (_ticker == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// every tick should have a timestamp associated with it
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
// call tick on all sprites
|
||||
_spritemgr.tick(now);
|
||||
|
||||
// invalidate screen-rects dirtied by sprites
|
||||
DirtyRectList rects = _spritemgr.getDirtyRects();
|
||||
if (rects.size() > 0) {
|
||||
// pass the dirty-rects on to the scene view
|
||||
_view.invalidateRects(rects);
|
||||
|
||||
// refresh the display
|
||||
_view.paintImmediately();
|
||||
}
|
||||
|
||||
// update refresh-rate information
|
||||
// PerformanceMonitor.tick(AnimationManager.this, "refresh");
|
||||
|
||||
if (finishedTick()) {
|
||||
// finishedTick returning true means there's been a
|
||||
// request for at least one more tick since we started
|
||||
// this tick, so we want to queue up another tick
|
||||
// immediately
|
||||
queueTick();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues up a tick on the AWT event handler thread, iff we are still
|
||||
* operating.
|
||||
*/
|
||||
protected synchronized void queueTick ()
|
||||
{
|
||||
if (_ticker != null) {
|
||||
SwingUtilities.invokeLater(_ticker);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void checkpoint (String name, int ticks)
|
||||
{
|
||||
Log.info(name + " [ticks=" + ticks + "].");
|
||||
}
|
||||
|
||||
/** The ticker runnable that we put on the AWT thread periodically. */
|
||||
protected Runnable _ticker;
|
||||
|
||||
/** The number of outstanding tick requests. */
|
||||
protected int _ticking = 0;
|
||||
|
||||
/** The refresh interval id. */
|
||||
protected int _iid = -1;
|
||||
|
||||
/** The sprite manager. */
|
||||
protected SpriteManager _spritemgr;
|
||||
|
||||
/** The view on which we are animating. */
|
||||
protected AnimatedView _view;
|
||||
|
||||
/** The desired number of refresh operations per second. */
|
||||
protected static final int FRAME_RATE = 70;
|
||||
|
||||
/** The milliseconds to sleep to obtain desired frame rate. */
|
||||
protected static final long REFRESH_INTERVAL = 1000 / FRAME_RATE;
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user