Major low-level rendering rethink. There will be much follow-on cleanup.

git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@1286 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2002-04-23 01:16:28 +00:00
parent 849eaec47b
commit cc6a9d00ef
18 changed files with 1596 additions and 1093 deletions
@@ -1,5 +1,5 @@
//
// $Id: ImageSprite.java,v 1.3 2002/04/17 15:52:49 mdb Exp $
// $Id: ImageSprite.java,v 1.4 2002/04/23 01:16:28 mdb Exp $
package com.threerings.media.sprite;
@@ -175,8 +175,10 @@ public class ImageSprite extends Sprite
updateRenderOffset();
updateRenderOrigin();
// now invalidate the dirtied region
invalidate(dirty);
// give the dirty rectangle to the region manager
if (_spritemgr != null) {
_spritemgr.getRegionManager().addDirtyRegion(dirty);
}
}
/**
@@ -252,14 +254,6 @@ public class ImageSprite extends Sprite
}
}
// documentation inherited
protected void invalidate (Rectangle r)
{
if (_frame != null) {
super.invalidate(r);
}
}
// documentation inherited
protected void toString (StringBuffer buf)
{
@@ -1,15 +1,13 @@
//
// $Id: Sprite.java,v 1.41 2002/04/16 02:28:50 mdb Exp $
// $Id: Sprite.java,v 1.42 2002/04/23 01:16:28 mdb Exp $
package com.threerings.media.sprite;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Iterator;
import com.threerings.util.DirectionCodes;
@@ -17,7 +15,7 @@ import com.threerings.media.Log;
/**
* The sprite class represents a single moveable object in an animated
* view. A sprite has a position and orientation within the view, and can
* view. A sprite has a position and orientation within the view, and can
* be moved along a path.
*/
public abstract class Sprite
@@ -182,8 +180,8 @@ public abstract class Sprite
*/
public void setLocation (int x, int y)
{
// create a starting dirty rectangle with our current position
Rectangle dirty = new Rectangle(_bounds);
// dirty our current bounds
invalidate();
// move ourselves
_x = x;
@@ -193,19 +191,8 @@ public abstract class Sprite
// size of our current bounds
updateRenderOrigin();
if (dirty.intersects(_bounds)) {
// grow the dirty rectangle to reflect our new location
dirty.add(_bounds);
} else {
// dirty the our new bounds rectangle separately from the old
// to avoid potentially creating a large dirty rectangle if
// the sprite warps from place to place
invalidate(null);
}
// invalidate the potentially-grown starting dirty rectangle
invalidate(dirty);
// dirty our new bounds
invalidate();
}
/**
@@ -327,25 +314,9 @@ public abstract class Sprite
* Invalidate the sprite's bounding rectangle for later repainting.
*/
public void invalidate ()
{
invalidate(null);
}
/**
* Invalidate the given display rectangle for later repainting.
* Passing <code>null</code> will simply invalidate the sprite's
* entire rendered bounds. Note that the given rectangle may be
* destructively modified at some later time, e.g., by {@link
* com.threerings.media.animation.AnimationManager#mergeDirtyRects}.
*/
protected void invalidate (Rectangle r)
{
if (_spritemgr != null) {
_spritemgr.addDirtyRect((r != null) ? r : new Rectangle(_bounds));
} else {
// Log.warning("Was invalidated but have no sprite manager " +
// this + ".");
_spritemgr.getRegionManager().invalidateRegion(_bounds);
}
}
@@ -357,11 +328,11 @@ public abstract class Sprite
* the timestamp information to compute elapsed progress if it wishes
* to handle heavy loads gracefully.
*/
public void tick (long timestamp)
public void tick (long tickStamp)
{
// if we've a path, move the sprite along toward its destination
if (_path != null) {
_path.updatePosition(this, timestamp);
_path.updatePosition(this, tickStamp);
}
}
@@ -411,13 +382,12 @@ public abstract class Sprite
/**
* Called by the sprite manager to let us know that the view we occupy
* is scrolling by the specified amount.
*
* @return a dirty rectangle that will be added to the list of
* rectangles dirtied by the scrolling or null if the sprite scrolled
* along with the view and didn't create any dirty region.
* is scrolling by the specified amount. A sprite anchored to the view
* will simply update its coordinates; a fixed sprite will remain in
* the same position but invalidate its bounds and its scrolled
* bounds.
*/
protected Rectangle viewWillScroll (int dx, int dy)
protected void viewWillScroll (int dx, int dy)
{
if (_scrollsWithView) {
// update our coordinates
@@ -431,12 +401,9 @@ public abstract class Sprite
_path.viewWillScroll(dx, dy);
}
return null;
} else {
// if we're not scrolling, then we need to dirty our bounds
// and the part of our bounds that were just scrolled out from
// under us
// if we're not scrolling, we need to dirty our bounds and the
// part that was just scrolled out from under us
Rectangle dirty = new Rectangle(_bounds);
// expand the rectangle to contain the scrolled regions
@@ -449,7 +416,8 @@ public abstract class Sprite
}
dirty.add(ex, ey);
return dirty;
// give the dirty rectangle to the region manager
_spritemgr.getRegionManager().addDirtyRegion(dirty);
}
}
@@ -1,5 +1,5 @@
//
// $Id: SpriteManager.java,v 1.28 2002/04/15 23:10:24 mdb Exp $
// $Id: SpriteManager.java,v 1.29 2002/04/23 01:16:28 mdb Exp $
package com.threerings.media.sprite;
@@ -19,29 +19,23 @@ import com.samskivert.util.SortableArrayList;
import com.samskivert.util.Tuple;
import com.threerings.media.Log;
import com.threerings.media.MediaConstants;
import com.threerings.media.RegionManager;
/**
* The sprite manager manages the sprites running about in the game.
*/
public class SpriteManager
implements MediaConstants
{
/** Constant for the front layer of sprites. */
public static final int FRONT = 0;
/** Constant for the back layer of sprites. */
public static final int BACK = 1;
/** Constant for all layers of sprites. */
public static final int ALL = 2;
/**
* Construct and initialize the SpriteManager object.
* Construct and initialize the sprite manager.
*/
public SpriteManager ()
public SpriteManager (RegionManager remgr)
{
_sprites = new SortableArrayList();
_notify = new ArrayList();
_dirty = new ArrayList();
_remgr = remgr;
}
/**
@@ -49,33 +43,18 @@ public class SpriteManager
* the specified offsets. It can update the positions of its sprites
* if they are tracking the scrolled view, or generate dirty regions
* for the sprites that remain in place (meaning they move relative to
* the scrolling view). Regions invalidated by the scrolled sprites
* should be appended to the supplied invalid rectangles list.
* the scrolling view).
*/
public void viewWillScroll (int dx, int dy, List invalidRects)
public void viewWillScroll (int dx, int dy)
{
// let the sprites know that the view is scrolling
int size = _sprites.size();
for (int i = 0; i < size; i++) {
Sprite sprite = (Sprite)_sprites.get(i);
Rectangle dirty = sprite.viewWillScroll(dx, dy);
if (dirty != null) {
invalidRects.add(dirty);
}
sprite.viewWillScroll(dx, dy);
}
}
/**
* Add a rectangle to the dirty rectangle list.
*
* @param rect the rectangle to add.
*/
public void addDirtyRect (Rectangle rect)
{
// translate the rectangle according to our viewport offset
_dirty.add(rect);
}
/**
* When an animated view processes its dirty rectangles, it may
* require an expansion of the dirty region which may in turn
@@ -153,20 +132,29 @@ public class SpriteManager
}
/**
* Render the sprites residing within the given shape and layer to the
* given graphics context.
* Provides access to the region manager that the sprite manager is
* using to collect invalid regions every frame. This should generally
* only be used by sprites that want to invalidate themselves.
*/
public RegionManager getRegionManager ()
{
return _remgr;
}
/**
* Render to the given graphics context the sprites intersecting the
* given shape and residing in the specified layer.
*
* @param gfx the graphics context.
* @param bounds the bounding shape.
* @param layer the layer to render; one of {@link #FRONT}, {@link
* #BACK}, or {@link #ALL}. The front layer contains all sprites with
* a positive render order; the back layer contains all sprites with a
* negative render order; all, both.
* @param bounds the bounding shape.
*/
public void renderSprites (Graphics2D gfx, Shape bounds, int layer)
public void renderSprites (Graphics2D gfx, int layer, Shape bounds)
{
// TODO: optimize to store sprites based on quadrants they're
// in (or somesuch), and sorted, so that we can more quickly
// TODO: optimize to store sprites based on quadrants they're in
// (or somesuch), and sorted, so that we can more quickly
// determine which sprites to draw.
int size = _sprites.size();
@@ -197,14 +185,14 @@ public class SpriteManager
}
/**
* Called periodically by the tick tasks put on the AWT event queue by
* the {@link com.threerings.media.animation.AnimationManager}.
* Handles moving about of sprites and reporting of sprite collisions.
* Must be called every frame so that the sprites can be properly
* updated. Normally a sprite manager is used in conjunction with an
* animated panel which case this is called automatically.
*/
public void tick (long timestamp, List rects)
public void tick (long tickStamp)
{
// tick all sprites
tickSprites(timestamp);
tickSprites(tickStamp);
// re-sort the sprite list to account for potential new positions
_sprites.sort(SPRITE_COMP);
@@ -219,26 +207,19 @@ 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();
}
/**
* Call <code>tick()</code> on all sprite objects to give them a
* Call {@link Sprite#tick} on all sprite objects to give them a
* chance to move themselves about, change their display image,
* and so forth.
* generate dirty regions and so forth.
*/
protected void tickSprites (long timestamp)
protected void tickSprites (long tickStamp)
{
int size = _sprites.size();
for (int ii = 0; ii < size; ii++) {
Sprite sprite = (Sprite)_sprites.get(ii);
sprite.tick(timestamp);
sprite.tick(tickStamp);
}
}
@@ -302,8 +283,8 @@ public class SpriteManager
}
/**
* Notify all sprite observers of any sprite events that took
* place during our most recent <code>tick()</code>.
* Notify all sprite observers of any sprite events that took place
* during our most recent <code>tick()</code>.
*/
protected void handleSpriteEvents ()
{
@@ -336,18 +317,19 @@ public class SpriteManager
_notify.add(new Tuple(observers, event));
}
/** The comparator used to sort sprites by horizontal position. */
protected static final Comparator SPRITE_COMP = new SpriteComparator();
/** The sprite objects we're managing. */
protected SortableArrayList _sprites;
/** The dirty rectangles created by sprites. */
protected ArrayList _dirty;
/** The list of pending sprite notifications. */
protected ArrayList _notify;
/** Used to accumulate dirty regions. */
protected RegionManager _remgr;
/** The comparator used to sort sprites by horizontal position. */
protected static final Comparator SPRITE_COMP = new SpriteComparator();
/** Used to sort sprites. */
protected static class SpriteComparator implements Comparator
{
public int compare (Object o1, Object o2)