More work on object tiles. Revamped isometric scene rendering to

gather dirty sprites and objects and render after base and fringe tile
layers.  Add shadow tiles in the footprint of objects to make them
impassable.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@460 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Walter Korman
2001-10-13 01:08:59 +00:00
parent eb3bc62083
commit 3773da79a6
10 changed files with 439 additions and 152 deletions
@@ -0,0 +1,90 @@
//
// $Id: DirtyItemList.java,v 1.1 2001/10/13 01:08:59 shaper Exp $
package com.threerings.media.sprite;
import java.util.*;
import com.threerings.media.sprite.Sprite;
import com.threerings.media.tile.ObjectTile;
import com.threerings.media.Log;
/**
* The dirty item list keeps track of dirty sprites and object tiles
* in a scene. Since scenes can only ever have one of either item at
* any given coordinate, dirty items are stored and checked for
* equality based solely on their coordinate, and each coordinate can
* exist in the list only once.
*/
public class DirtyItemList extends ArrayList
{
/**
* Append the dirty item at the given coordinates to the dirty
* item list.
*/
public boolean appendDirtyItem (Object item, int x, int y)
{
// only allow appending sprites and object tiles
if (!(item instanceof Sprite) && !(item instanceof ObjectTile)) {
return false;
}
// only add the item if there are no existing items
if (!contains(x, y)) {
add(new DirtyItem(item, x, y));
return true;
}
return false;
}
/**
* Sort the items in the list using the given comparator.
*/
public void sort (Comparator comp)
{
Object[] items = new Object[size()];
toArray(items);
Arrays.sort(items, comp);
clear();
for (int ii = 0; ii < items.length; ii++) {
add(items[ii]);
}
}
/**
* Returns whether the list contains a dirty item at the given
* coordinates.
*/
protected boolean contains (int x, int y)
{
int size = size();
for (int ii = 0; ii < size; ii++) {
Object o = get(ii);
if (o instanceof DirtyItem) {
DirtyItem di = (DirtyItem)o;
return (x == di.x && y == di.y);
}
}
return false;
}
/**
* A wrapper class to hold the items inserted in the dirty list
* along with their coordinates in the scene.
*/
public class DirtyItem
{
public Object obj;
public int x, y;
public DirtyItem (Object obj, int x, int y)
{
this.obj = obj;
this.x = x;
this.y = y;
}
}
}
@@ -1,5 +1,5 @@
// //
// $Id: DirtyRectList.java,v 1.1 2001/08/22 02:14:57 mdb Exp $ // $Id: DirtyRectList.java,v 1.2 2001/10/13 01:08:59 shaper Exp $
package com.threerings.media.sprite; package com.threerings.media.sprite;
@@ -16,7 +16,8 @@ public class DirtyRectList extends ArrayList
{ {
/** /**
* Appends the specified dirty rectangle to the list only if a * Appends the specified dirty rectangle to the list only if a
* rectangle of the same size is not already in the list. * 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. * @return true if the rectangle were appended, false if she weren't.
* Har! * Har!
@@ -1,5 +1,5 @@
// //
// $Id: Sprite.java,v 1.24 2001/10/12 00:36:03 shaper Exp $ // $Id: Sprite.java,v 1.25 2001/10/13 01:08:59 shaper Exp $
package com.threerings.media.sprite; package com.threerings.media.sprite;
@@ -209,29 +209,21 @@ public class Sprite
} }
/** /**
* Returns whether the sprite is inside the given polygon in * Returns whether the sprite is inside the given shape in
* pixel coordinates. * pixel coordinates.
*
* @param bounds the bounding polygon.
*
* @return whether the sprite is inside the polygon.
*/ */
public boolean inside (Polygon bounds) public boolean inside (Shape shape)
{ {
return bounds.contains(_x, _y); return shape.contains(_x, _y);
} }
/** /**
* Returns whether the sprite's drawn rectangle intersects the given * Returns whether the sprite's drawn rectangle intersects the given
* polygon in pixel coordinates. * shape in pixel coordinates.
*
* @param bounds the bounding polygon.
*
* @return whether the sprite intersects polygon.
*/ */
public boolean intersects (Polygon bounds) public boolean intersects (Shape shape)
{ {
return bounds.intersects(_rbounds); return shape.intersects(_rbounds);
} }
/** /**
@@ -1,10 +1,11 @@
// //
// $Id: SpriteManager.java,v 1.14 2001/09/13 19:36:20 mdb Exp $ // $Id: SpriteManager.java,v 1.15 2001/10/13 01:08:59 shaper Exp $
package com.threerings.media.sprite; package com.threerings.media.sprite;
import java.awt.*; import java.awt.*;
import java.util.*; import java.util.*;
import java.util.List;
import com.samskivert.util.Tuple; import com.samskivert.util.Tuple;
import com.threerings.media.Log; import com.threerings.media.Log;
@@ -36,29 +37,23 @@ public class SpriteManager
/** /**
* When an animated view processes its dirty rectangles, it may * When an animated view processes its dirty rectangles, it may
* require an expansion of the dirty region which may in turn require * require an expansion of the dirty region which may in turn
* the invalidation of more sprites than were originally invalid. In * require the invalidation of more sprites than were originally
* such cases, the animated view can call back to the sprite manager, * invalid. In such cases, the animated view can call back to the
* asking it to append the rectangles of the sprites that intersect a * sprite manager, asking it to append the sprites that intersect
* particular region to the dirty rectangle list that it's processing. * a particular region to the given list.
* A sprite's rectangle will only be appended if it's not already in
* the list.
* *
* @param rects the dirty rectangle list being processed by the * @param list the list to fill with any intersecting sprites.
* animated view.
* @param bounds the bounds the intersection of which we have * @param bounds the bounds the intersection of which we have
* interest. * interest.
*/ */
public void invalidateIntersectingSprites ( public void getIntersectingSprites (List list, Shape shape)
DirtyRectList rects, Polygon bounds)
{ {
int size = _sprites.size(); int size = _sprites.size();
for (int ii = 0; ii < size; ii++) { for (int ii = 0; ii < size; ii++) {
Sprite sprite = (Sprite)_sprites.get(ii); Sprite sprite = (Sprite)_sprites.get(ii);
if (sprite.intersects(bounds)) { if (sprite.intersects(shape)) {
if (rects.appendDirtyRect(sprite.getRenderedBounds())) { list.add(sprite);
Log.info("Expanded for: " + sprite);
}
} }
} }
} }
@@ -1,5 +1,5 @@
// //
// $Id: DisplayMisoSceneImpl.java,v 1.39 2001/10/11 00:41:27 shaper Exp $ // $Id: DisplayMisoSceneImpl.java,v 1.40 2001/10/13 01:08:59 shaper Exp $
package com.threerings.miso.scene; package com.threerings.miso.scene;
@@ -15,8 +15,8 @@ import com.threerings.whirled.data.Scene;
import com.threerings.miso.Log; import com.threerings.miso.Log;
import com.threerings.miso.scene.util.ClusterUtil; import com.threerings.miso.scene.util.ClusterUtil;
import com.threerings.miso.tile.MisoTile; import com.threerings.miso.tile.MisoTile;
import com.threerings.miso.tile.ShadowTile;
/** /**
* A scene object represents the data model corresponding to a single * A scene object represents the data model corresponding to a single
@@ -54,6 +54,14 @@ public class MisoSceneImpl implements EditableMisoScene
* initialized to contain tiles of the specified default tileset and * initialized to contain tiles of the specified default tileset and
* tile id. * tile id.
* *
* <em>Note:</em> Be sure to call {@link #prepareAllObjectTiles}
* before the scene is first used so that the base layer will be
* properly populated with shadow tiles in the footprint of all
* object tiles. Similarly, any addition of tiles to or removal
* of tiles from the object layer of the scene should be followed
* by a call to {@link #prepareObjectTile} or {@link
* #unprepareObjectTile}, respectively.
*
* @param model the iso scene view model. * @param model the iso scene view model.
* @param deftile the default tile. * @param deftile the default tile.
*/ */
@@ -62,11 +70,15 @@ public class MisoSceneImpl implements EditableMisoScene
_model = model; _model = model;
_deftile = deftile; _deftile = deftile;
// create the individual tile layer arrays
baseTiles = new MisoTile[_model.scenewid][_model.scenehei]; baseTiles = new MisoTile[_model.scenewid][_model.scenehei];
fringeTiles = new Tile[_model.scenewid][_model.scenehei]; fringeTiles = new Tile[_model.scenewid][_model.scenehei];
objectTiles = new ObjectTile[_model.scenewid][_model.scenehei]; objectTiles = new ObjectTile[_model.scenewid][_model.scenehei];
// create the conjoined array for purely utilitarian purposes
tiles = new Tile[][][] { baseTiles, fringeTiles, objectTiles }; tiles = new Tile[][][] { baseTiles, fringeTiles, objectTiles };
// initialize the always-fully-populated base layer
initBaseTiles(); initBaseTiles();
} }
@@ -76,6 +88,12 @@ public class MisoSceneImpl implements EditableMisoScene
return name; return name;
} }
// documentation inherited
public void setDefaultTile (MisoTile tile)
{
_deftile = tile;
}
// documentation inherited // documentation inherited
public int getId () public int getId ()
{ {
@@ -94,11 +112,13 @@ public class MisoSceneImpl implements EditableMisoScene
return null; return null;
} }
// documentation inherited
public Tile[][][] getTiles () public Tile[][][] getTiles ()
{ {
return tiles; return tiles;
} }
// documentation inherited
public Tile[][] getTiles (int lnum) public Tile[][] getTiles (int lnum)
{ {
return tiles[lnum]; return tiles[lnum];
@@ -267,6 +287,71 @@ public class MisoSceneImpl implements EditableMisoScene
} }
} }
/**
* Place shadow tiles in the footprint of all object tiles in the
* scene. This method should be called once the scene tiles are
* fully populated, but before the scene is used in any other
* meaningful capacity.
*/
public void prepareAllObjectTiles ()
{
for (int xx = 0; xx < _model.scenewid; xx++) {
for (int yy = 0; yy < _model.scenehei; yy++) {
if (objectTiles[xx][yy] != null) {
prepareObjectTile(xx, yy);
}
}
}
}
/**
* Place shadow tiles in the footprint of the object tile at the
* given coordinates in the scene. This method should be called
* when an object tile is added to the scene.
*
* @param x the tile x-coordinate.
* @param y the tile y-coordinate.
*/
public void prepareObjectTile (int x, int y)
{
setObjectTileFootprint(x, y, ShadowTile.TILE);
}
/**
* Remove shadow tiles from the footprint of the object tile at
* the given coordinates in the scene. This method should be
* called when an object tile is removed from the scene.
*
* @param x the tile x-coordinate.
* @param y the tile y-coordinate.
*/
public void unprepareObjectTile (int x, int y)
{
setObjectTileFootprint(x, y, _deftile);
}
/**
* Place the given tile in the footprint of the object tile at the
* given coordinates in the scene.
*
* @param x the tile x-coordinate.
* @param y the tile y-coordinate.
* @param stamp the tile to place in the object footprint.
*/
protected void setObjectTileFootprint (int x, int y, MisoTile stamp)
{
ObjectTile tile = objectTiles[x][y];
int endx = Math.max(0, (x - tile.baseWidth + 1));
int endy = Math.max(0, (y - tile.baseHeight + 1));
for (int xx = x; xx >= endx; xx--) {
for (int yy = y; yy >= endy; yy--) {
baseTiles[xx][yy] = stamp;
}
}
}
/** The default scene name. */ /** The default scene name. */
protected static final String DEF_SCENE_NAME = "Untitled Scene"; protected static final String DEF_SCENE_NAME = "Untitled Scene";
@@ -1,5 +1,5 @@
// //
// $Id: IsoSceneView.java,v 1.59 2001/10/12 00:42:08 shaper Exp $ // $Id: IsoSceneView.java,v 1.60 2001/10/13 01:08:59 shaper Exp $
package com.threerings.miso.scene; package com.threerings.miso.scene;
@@ -8,7 +8,7 @@ import java.awt.geom.*;
import java.awt.image.*; import java.awt.image.*;
import java.util.List; import java.util.List;
import java.util.ArrayList; import java.util.*;
import com.samskivert.util.HashIntMap; import com.samskivert.util.HashIntMap;
@@ -17,9 +17,7 @@ import com.threerings.media.tile.Tile;
import com.threerings.media.tile.ObjectTile; import com.threerings.media.tile.ObjectTile;
import com.threerings.miso.Log; import com.threerings.miso.Log;
import com.threerings.miso.scene.util.AStarPathUtil; import com.threerings.miso.scene.util.*;
import com.threerings.miso.scene.util.IsoUtil;
import com.threerings.miso.scene.util.MisoSceneUtil;
/** /**
* The <code>IsoSceneView</code> provides an isometric view of a * The <code>IsoSceneView</code> provides an isometric view of a
@@ -39,9 +37,6 @@ public class IsoSceneView implements SceneView
setModel(model); setModel(model);
// get the font used to render tile coordinates
_font = new Font("Arial", Font.PLAIN, 7);
// create our polygon arrays and create polygons for each of the // create our polygon arrays and create polygons for each of the
// tiles. we use these repeatedly, so we go ahead and make 'em all // tiles. we use these repeatedly, so we go ahead and make 'em all
// up front // up front
@@ -52,15 +47,9 @@ public class IsoSceneView implements SceneView
} }
} }
// create the hashtable used to store object bounds polygons
_objpolys = new HashIntMap();
// create the array used to mark dirty tiles // create the array used to mark dirty tiles
_dirty = new boolean[model.scenewid][model.tilehei]; _dirty = new boolean[model.scenewid][model.tilehei];
// create the list of dirty rectangles
_dirtyRects = new ArrayList();
clearDirtyRegions(); clearDirtyRegions();
} }
@@ -77,6 +66,7 @@ public class IsoSceneView implements SceneView
public void paint (Graphics g) public void paint (Graphics g)
{ {
if (_scene == null) { if (_scene == null) {
Log.info("Scene view painted with null scene.");
return; return;
} }
@@ -88,20 +78,19 @@ public class IsoSceneView implements SceneView
gfx.setClip(0, 0, _model.bounds.width, _model.bounds.height); gfx.setClip(0, 0, _model.bounds.width, _model.bounds.height);
if (_numDirty == 0) { if (_numDirty == 0) {
// render the full scene // invalidate the entire screen
renderScene(gfx); invalidate();
} else {
// render only dirty tiles
renderSceneInvalid(gfx);
// draw frames of dirty tiles and rectangles
// drawDirtyRegions(gfx);
// clear out the dirty tiles and rectangles
clearDirtyRegions();
} }
// render the scene to the graphics context
renderScene(gfx);
// draw frames of dirty tiles and rectangles
// drawDirtyRegions(gfx);
// clear out the dirty tiles and rectangles
clearDirtyRegions();
// draw sprite paths // draw sprite paths
if (_model.showPaths) { if (_model.showPaths) {
_spritemgr.renderSpritePaths(gfx); _spritemgr.renderSpritePaths(gfx);
@@ -127,9 +116,21 @@ public class IsoSceneView implements SceneView
{ {
} }
/**
* Invalidate the entire visible scene view.
*/
protected void invalidate ()
{
DirtyRectList rects = new DirtyRectList();
rects.add(new Rectangle(
0, 0, _model.bounds.width,_model.bounds.height));
invalidateRects(rects);
}
protected void clearDirtyRegions () protected void clearDirtyRegions ()
{ {
_dirtyRects.clear(); _dirtyRects.clear();
_dirtyItems.clear();
_numDirty = 0; _numDirty = 0;
for (int xx = 0; xx < _model.scenewid; xx++) { for (int xx = 0; xx < _model.scenewid; xx++) {
@@ -165,11 +166,22 @@ public class IsoSceneView implements SceneView
* *
* @param gfx the graphics context. * @param gfx the graphics context.
*/ */
protected void renderSceneInvalid (Graphics2D gfx) protected void renderScene (Graphics2D gfx)
{
renderTiles(gfx);
renderDirtyItems(gfx);
}
/**
* Renders the base and fringe layer tiles to the given graphics
* context.
*/
protected void renderTiles (Graphics2D gfx)
{ {
int numDrawn = 0; int numDrawn = 0;
Tile[][][] tiles = _scene.getTiles(); Tile[][][] tiles = _scene.getTiles();
// render the base and fringe layers
for (int yy = 0; yy < _model.scenehei; yy++) { for (int yy = 0; yy < _model.scenehei; yy++) {
for (int xx = 0; xx < _model.scenewid; xx++) { for (int xx = 0; xx < _model.scenewid; xx++) {
@@ -181,8 +193,9 @@ public class IsoSceneView implements SceneView
// get the tile's screen position // get the tile's screen position
Polygon poly = _polys[xx][yy]; Polygon poly = _polys[xx][yy];
// draw all layers at this tile position // draw both layers at this tile position
for (int kk = 0; kk < MisoScene.NUM_LAYERS; kk++) { for (int kk = MisoScene.LAYER_BASE;
kk < MisoScene.LAYER_FRINGE; kk++) {
// get the tile at these coordinates and layer // get the tile at these coordinates and layer
Tile tile = tiles[kk][xx][yy]; Tile tile = tiles[kk][xx][yy];
@@ -190,17 +203,10 @@ public class IsoSceneView implements SceneView
continue; continue;
} }
// offset the image y-position by the tile-specific height
int ypos = poly.ypoints[0] - _model.tilehhei -
(tile.height - _model.tilehei);
// draw the tile image // draw the tile image
renderTile(gfx, tile, xx, yy, poly); renderTile(gfx, tile, xx, yy, poly);
} }
// draw all sprites residing in the current tile
_spritemgr.renderSprites(gfx, poly);
// paint the tile coordinate if desired // paint the tile coordinate if desired
if (_model.showCoords) { if (_model.showCoords) {
paintCoords(gfx, xx, yy, poly.xpoints[0], paintCoords(gfx, xx, yy, poly.xpoints[0],
@@ -216,78 +222,33 @@ public class IsoSceneView implements SceneView
} }
/** /**
* Render the scene to the given graphics context. * Renders the dirty sprites and objects in the scene to the given
* * graphics context.
* @param gfx the graphics context.
*/ */
protected void renderScene (Graphics2D gfx) protected void renderDirtyItems (Graphics2D gfx)
{ {
Tile[][][] tiles = _scene.getTiles(); // sort the dirty sprites and objects visually back-to-front
int mx = 1; _dirtyItems.sort(IsoUtil.DIRTY_COMP);
int my = 0;
int screenY = _model.origin.y; // render dirty sprites and objects
int size = _dirtyItems.size();
for (int ii = 0; ii < size; ii++) {
DirtyItemList.DirtyItem di =
(DirtyItemList.DirtyItem)_dirtyItems.get(ii);
for (int ii = 0; ii < _model.tilerows; ii++) { if (di.obj instanceof Sprite) {
// determine starting tile coordinates ((Sprite)di.obj).paint(gfx);
int tx = (ii < _model.scenehei) ? 0 : mx++;
int ty = my;
// determine number of tiles in this row } else {
int length = (ty - tx) + 1; Polygon poly = (Polygon)
_objpolys.get(getCoordinateKey(di.x, di.y));
// determine starting screen x-position ((ObjectTile)di.obj).paint(gfx, poly);
int screenX = _model.origin.x - ((length) * _model.tilehwid);
for (int jj = 0; jj < length; jj++) {
for (int kk = 0; kk < MisoScene.NUM_LAYERS; kk++) {
// grab the tile we're rendering
Tile tile = tiles[kk][tx][ty];
if (tile == null) {
continue;
}
Polygon poly = _polys[tx][ty];
// determine screen y-position, accounting for
// tile image height
int ypos = screenY - (tile.height - _model.tilehei);
// draw the tile image at the appropriate screen position
renderTile(gfx, tile, tx, ty, poly);
// draw all sprites residing in the current tile
// TODO: simplify other tile positioning here to use poly
_spritemgr.renderSprites(gfx, poly);
}
// draw tile coordinates in each tile
if (_model.showCoords) {
paintCoords(gfx, tx, ty, screenX, screenY);
}
// each tile is one tile-width to the right of the previous
screenX += _model.tilewid;
// advance tile x and decrement tile y as we move to
// the right drawing the row
tx++;
ty--;
}
// each row is a half-tile-height away from the previous row
screenY += _model.tilehhei;
// advance starting y-axis coordinate unless we've hit bottom
if ((++my) > _model.scenehei - 1) {
my = _model.scenehei - 1;
} }
} }
} }
/** /**
* Render the given tile in the scene to the given graphics * Renders the given tile in the scene to the given graphics
* context. * context.
*/ */
protected void renderTile ( protected void renderTile (
@@ -301,8 +262,8 @@ public class IsoSceneView implements SceneView
} }
/** /**
* Pre-processes all object tiles in the scene to create shadow * Pre-processes all object tiles in the scene to create bounding
* tiles and bounding polygons. * polygons.
*/ */
protected void prepareAllObjectTiles () protected void prepareAllObjectTiles ()
{ {
@@ -320,17 +281,14 @@ public class IsoSceneView implements SceneView
} }
/** /**
* Pre-processes a single object tile. Creates shadow tiles in * Pre-processes a single object tile. Creates the bounding
* its footprint making the object fully impassable, and creates * polygon for the object which is used when invalidating dirty
* the bounding polygon for the object which is used when * rectangles or tiles, and when rendering the object to a
* invalidating dirty rectangles or tiles, and when rendering the * graphics context. This method should be called when an object
* object to a graphics context. This method should be called * tile is added to a scene.
* when an object tile is added to a scene.
*/ */
protected void prepareObjectTile (ObjectTile tile, int x, int y) protected void prepareObjectTile (ObjectTile tile, int x, int y)
{ {
// TODO: create shadow tiles in the footprint of this object
// create the bounding polygon for this object // create the bounding polygon for this object
int key = getCoordinateKey(x, y); int key = getCoordinateKey(x, y);
Polygon bounds = IsoUtil.getObjectBounds(_model, _polys[x][y], tile); Polygon bounds = IsoUtil.getObjectBounds(_model, _polys[x][y], tile);
@@ -454,6 +412,9 @@ public class IsoSceneView implements SceneView
// dirty the tiles impacted by this rectangle // dirty the tiles impacted by this rectangle
invalidateScreenRect(rects, r.x, r.y, r.width, r.height); invalidateScreenRect(rects, r.x, r.y, r.width, r.height);
// dirty any sprites or objects impacted by this rectangle
invalidateItems(r);
// save the rectangle for potential display later // save the rectangle for potential display later
_dirtyRects.add(r); _dirtyRects.add(r);
} }
@@ -557,6 +518,11 @@ public class IsoSceneView implements SceneView
} }
} }
/**
* Marks the tile at the given coordinates dirty and adds dirty
* rectangles to the dirty rectangle list for any sprites
* currently occupying the tile.
*/
protected void addDirtyTile (DirtyRectList rects, int x, int y) protected void addDirtyTile (DirtyRectList rects, int x, int y)
{ {
// constrain x-coordinate to a valid range // constrain x-coordinate to a valid range
@@ -584,7 +550,67 @@ public class IsoSceneView implements SceneView
// and add the dirty rectangles of any sprites that we've just // and add the dirty rectangles of any sprites that we've just
// inadvertently touched by dirtying this tile // inadvertently touched by dirtying this tile
_spritemgr.invalidateIntersectingSprites(rects, _polys[x][y]); invalidateIntersectingSprites(rects, _polys[x][y]);
}
/**
* Adds any sprites or objects in the scene whose bounds overlap
* with the given dirty rectangle to the dirty item list for later
* re-rendering.
*/
protected void invalidateItems (Rectangle r)
{
// add any sprites impacted by the dirty rectangle
_dirtySprites.clear();
_spritemgr.getIntersectingSprites(_dirtySprites, r);
int size = _dirtySprites.size();
for (int ii = 0; ii < size; ii++) {
Sprite sprite = (Sprite)_dirtySprites.get(ii);
Point tpos = new Point();
IsoUtil.screenToTile(_model, sprite.getX(), sprite.getY(), tpos);
if (_dirtyItems.appendDirtyItem(sprite, tpos.x, tpos.y)) {
// Log.info("Dirtied item: " + sprite);
}
}
// add any objects impacted by the dirty rectangle
ObjectTile tiles[][] = _scene.getObjectLayer();
Iterator iter = _objpolys.keys();
while (iter.hasNext()) {
// get the object's coordinates and bounding polygon
int coord = ((Integer)iter.next()).intValue();
Polygon poly = (Polygon)_objpolys.get(coord);
if (poly.intersects(r)) {
int tx = coord >> 16, ty = coord & 0x0000FFFF;
if (_dirtyItems.appendDirtyItem(tiles[tx][ty], tx, ty)) {
// Log.info("Dirtied item: Object(" + tx + ", " +
// ty + ")");
}
}
}
}
/**
* Adds dirty rectangles to the dirty rectangle list for any
* sprites in the scene whose bounds overlap with the given
* polygon.
*/
protected void invalidateIntersectingSprites (
DirtyRectList rects, Polygon bounds)
{
_dirtySprites.clear();
_spritemgr.getIntersectingSprites(_dirtySprites, bounds);
int size = _dirtySprites.size();
for (int ii = 0; ii < size; ii++) {
Sprite sprite = (Sprite)_dirtySprites.get(ii);
if (rects.appendDirtyRect(sprite.getRenderedBounds())) {
Log.info("Expanded for: " + sprite);
}
}
} }
public void setModel (IsoSceneViewModel model) public void setModel (IsoSceneViewModel model)
@@ -674,13 +700,13 @@ public class IsoSceneView implements SceneView
} }
/** The font to draw tile coordinates. */ /** The font to draw tile coordinates. */
protected Font _font; protected Font _font = new Font("Arial", Font.PLAIN, 7);
/** Polygon instances for all of our tiles. */ /** Polygon instances for all of our tiles. */
protected Polygon _polys[][]; protected Polygon _polys[][];
/** Bounding polygons for all of the object tiles. */ /** Bounding polygons for all of the object tiles. */
protected HashIntMap _objpolys; protected HashIntMap _objpolys = new HashIntMap();
/** The dirty tiles that need to be re-painted. */ /** The dirty tiles that need to be re-painted. */
protected boolean _dirty[][]; protected boolean _dirty[][];
@@ -689,7 +715,13 @@ public class IsoSceneView implements SceneView
protected int _numDirty; protected int _numDirty;
/** The dirty rectangles that need to be re-painted. */ /** The dirty rectangles that need to be re-painted. */
protected ArrayList _dirtyRects; protected ArrayList _dirtyRects = new ArrayList();
/** The dirty sprites and objects that need to be re-painted. */
protected DirtyItemList _dirtyItems = new DirtyItemList();
/** The working sprites list used when calculating dirty regions. */
protected ArrayList _dirtySprites = new ArrayList();
/** The scene view model data. */ /** The scene view model data. */
protected IsoSceneViewModel _model; protected IsoSceneViewModel _model;
@@ -1,11 +1,13 @@
// //
// $Id: IsoUtil.java,v 1.7 2001/10/12 00:42:08 shaper Exp $ // $Id: IsoUtil.java,v 1.8 2001/10/13 01:08:59 shaper Exp $
package com.threerings.miso.scene.util; package com.threerings.miso.scene.util;
import java.awt.*; import java.awt.*;
import java.util.Comparator;
import com.threerings.media.sprite.Sprite; import com.threerings.media.sprite.Sprite;
import com.threerings.media.sprite.DirtyItemList;
import com.threerings.media.tile.ObjectTile; import com.threerings.media.tile.ObjectTile;
import com.threerings.media.util.MathUtil; import com.threerings.media.util.MathUtil;
@@ -18,6 +20,20 @@ import com.threerings.miso.scene.*;
*/ */
public class IsoUtil public class IsoUtil
{ {
/** The comparator used to sort dirty items in ascending render order. */
public static final Comparator DIRTY_COMP = new DirtyItemComparator();
/**
* Returns a polygon bounding the given object tile display image,
* with the bottom of the polygon shaped to conform to the known
* object dimensions.
*
* @param model the scene view model.
* @param root the root tile at which the object is located.
* @param tile the object tile.
*
* @return the bounding polygon.
*/
public static Polygon getObjectBounds ( public static Polygon getObjectBounds (
IsoSceneViewModel model, Polygon root, ObjectTile tile) IsoSceneViewModel model, Polygon root, ObjectTile tile)
{ {
@@ -55,7 +71,7 @@ public class IsoUtil
return boundsPoly; return boundsPoly;
} }
/** /**
* Given two points in screen pixel coordinates, return the * Given two points in screen pixel coordinates, return the
* compass direction that point B lies in from point A from an * compass direction that point B lies in from point A from an
* isometric perspective. * isometric perspective.
@@ -327,4 +343,29 @@ public class IsoUtil
/** Multiplication factor to embed tile coords in full coords. */ /** Multiplication factor to embed tile coords in full coords. */
protected static final int FULL_TILE_FACTOR = 100; protected static final int FULL_TILE_FACTOR = 100;
/**
* A comparator class to facilitate sorting the dirty items in
* ascending x- and y-coordinate order suitable for rendering in
* the isometric view with proper visual results.
*/
protected static class DirtyItemComparator implements Comparator
{
public int compare (Object a, Object b)
{
DirtyItemList.DirtyItem da = (DirtyItemList.DirtyItem)a;
DirtyItemList.DirtyItem db = (DirtyItemList.DirtyItem)b;
if (da.y <= db.y && da.x <= db.x) {
return -1;
}
return 1;
}
public boolean equals (Object obj)
{
return (obj == this);
}
}
} }
@@ -0,0 +1,41 @@
//
// $Id: ShadowTile.java,v 1.1 2001/10/13 01:08:59 shaper Exp $
package com.threerings.miso.tile;
import java.awt.Graphics2D;
import java.awt.Shape;
/**
* The shadow tile extends miso tile to provide an always-impassable
* tile that has no display image. Shadow tiles are intended for
* placement in the footprint of {@link ObjectTile} objects.
*/
public class ShadowTile extends MisoTile
{
/** Single instantiation of shadow tile for re-use in scenes. */
public static ShadowTile TILE = new ShadowTile();
/**
* Constructs a shadow tile.
*/
public ShadowTile ()
{
super(SHADOW_TSID, SHADOW_TID);
// shadow tiles are always impassable
passable = false;
}
// documentation inherited
public void paint (Graphics2D gfx, Shape dest)
{
// paint nothing as we're naught but a measly shadow of a tile
}
/** The shadow tile set id. */
protected static final int SHADOW_TSID = -1;
/** The shadow tile id. */
protected static final int SHADOW_TID = -1;
}
@@ -1,8 +1,10 @@
// //
// $Id: EditableMisoScene.java,v 1.2 2001/10/05 23:58:36 mdb Exp $ // $Id: EditableMisoScene.java,v 1.3 2001/10/13 01:08:59 shaper Exp $
package com.threerings.miso.scene; package com.threerings.miso.scene;
import com.threerings.miso.tile.MisoTile;
/** /**
* The editable Miso scene interface provides the means for modifying a * The editable Miso scene interface provides the means for modifying a
* Miso scene which is needed by things like the scene editor. This is * Miso scene which is needed by things like the scene editor. This is
@@ -23,6 +25,11 @@ public interface EditableMisoScene
*/ */
public void setName (String name); public void setName (String name);
/**
* Updates the scene's default tile.
*/
public void setDefaultTile (MisoTile tile);
/** /**
* Set the default entrance portal for this scene. * Set the default entrance portal for this scene.
* *
@@ -1,5 +1,5 @@
// //
// $Id: XMLSceneParser.java,v 1.18 2001/10/11 00:41:27 shaper Exp $ // $Id: XMLSceneParser.java,v 1.19 2001/10/13 01:08:59 shaper Exp $
package com.threerings.miso.scene.xml; package com.threerings.miso.scene.xml;
@@ -311,6 +311,9 @@ public class XMLSceneParser extends DefaultHandler
// read the XML input stream and construct the scene object // read the XML input stream and construct the scene object
XMLUtil.parse(this, bis); XMLUtil.parse(this, bis);
// place shadow tiles for any objects in the scene
_info.scene.prepareAllObjectTiles();
// return the final scene object // return the final scene object
return _info.scene; return _info.scene;