Rejiggered the iso scene rendering yet again to address objects in the

scene that may partition two items, and to address the lack of a proper
total ordering of dirty items in the non-partitioned comparison case.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@792 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Walter Korman
2001-12-16 05:42:16 +00:00
parent 3c4c71b3c7
commit 1e1b5e1d4a
2 changed files with 346 additions and 44 deletions
@@ -1,10 +1,18 @@
// //
// $Id: DirtyItemList.java,v 1.6 2001/11/18 04:09:22 mdb Exp $ // $Id: DirtyItemList.java,v 1.7 2001/12/16 05:42:16 shaper Exp $
package com.threerings.miso.scene; package com.threerings.miso.scene;
import java.awt.*; import java.awt.Graphics2D;
import java.util.*; import java.awt.Rectangle;
import java.awt.Shape;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Comparator;
import com.samskivert.util.HashIntMap;
import com.samskivert.util.StringUtil;
import com.threerings.media.sprite.Sprite; import com.threerings.media.sprite.Sprite;
import com.threerings.media.tile.ObjectTile; import com.threerings.media.tile.ObjectTile;
@@ -15,7 +23,7 @@ import com.threerings.media.Log;
* The dirty item list keeps track of dirty sprites and object tiles * The dirty item list keeps track of dirty sprites and object tiles
* in a scene. * in a scene.
*/ */
public class DirtyItemList extends ArrayList public class DirtyItemList
{ {
/** /**
* Appends the dirty sprite at the given coordinates to the dirty * Appends the dirty sprite at the given coordinates to the dirty
@@ -24,7 +32,7 @@ public class DirtyItemList extends ArrayList
public void appendDirtySprite ( public void appendDirtySprite (
MisoCharacterSprite sprite, int x, int y, Rectangle dirtyRect) MisoCharacterSprite sprite, int x, int y, Rectangle dirtyRect)
{ {
add(new DirtyItem(sprite, null, x, y, dirtyRect)); _items.add(new DirtyItem(sprite, null, x, y, dirtyRect));
} }
/** /**
@@ -34,19 +42,105 @@ public class DirtyItemList extends ArrayList
public void appendDirtyObject ( public void appendDirtyObject (
ObjectTile tile, Shape bounds, int x, int y, Rectangle dirtyRect) ObjectTile tile, Shape bounds, int x, int y, Rectangle dirtyRect)
{ {
add(new DirtyItem(tile, bounds, x, y, dirtyRect)); _items.add(new DirtyItem(tile, bounds, x, y, dirtyRect));
}
/**
* Returns the dirty item at the given index in the list.
*/
public DirtyItem get (int idx)
{
return (DirtyItem)_items.get(idx);
} }
/** /**
* Returns an array of the {@link DirtyItem} objects in the list * Returns an array of the {@link DirtyItem} objects in the list
* sorted with the {@link DirtyItemComparator}. * sorted in proper rendering order.
*/ */
public DirtyItem[] sort () public DirtyItem[] sort ()
{ {
DirtyItem items[] = new DirtyItem[size()]; int size = size();
toArray(items);
Arrays.sort(items, DIRTY_COMP); if (DEBUG_SORT) {
return items; Log.info("Sorting dirty item list [size=" + size + "].");
}
// get items sorted by increasing origin x-coordinate
DirtyItem[] xitems = new DirtyItem[size];
_items.toArray(xitems);
Arrays.sort(xitems, ORIGIN_X_COMP);
if (DEBUG_SORT) {
Log.info("Sorted by x-origin " +
"[items=" + DirtyItemList.toString(xitems) + "].");
}
// get items sorted by increasing origin y-coordinate
DirtyItem[] yitems = new DirtyItem[size];
_items.toArray(yitems);
Arrays.sort(yitems, ORIGIN_Y_COMP);
if (DEBUG_SORT) {
Log.info("Sorted by y-origin " +
"[items=" + DirtyItemList.toString(yitems) + "].");
}
// sort items into proper render order
DirtyItem[] ritems = new DirtyItem[size];
_items.toArray(ritems);
Arrays.sort(ritems, new RenderComparator(xitems, yitems));
if (DEBUG_SORT) {
Log.info("Sorted for render " +
"[items=" + toString(ritems) + "].");
}
return ritems;
}
/**
* Returns the number of items in the dirty item list.
*/
public int size ()
{
return _items.size();
}
/**
* Clears all items in the list.
*/
public void clear ()
{
_items.clear();
}
/**
* Returns an abbreviated string representation of the two given dirty
* items describing each by only its origin coordinates. Intended for
* debugging purposes.
*/
protected static String toString (DirtyItem a, DirtyItem b)
{
return toString(new DirtyItem[] { a, b });
}
/**
* Returns an abbreviated string representation of the given dirty
* items describing each by only its origin coordinates. Intended for
* debugging purposes.
*/
protected static String toString (DirtyItem[] items)
{
StringBuffer buf = new StringBuffer();
buf.append("[");
for (int ii = 0; ii < items.length; ii++) {
buf.append("(ox=").append(items[ii].ox);
buf.append(", oy=").append(items[ii].oy).append(")");
if (ii < (items.length - 1)) {
buf.append(", ");
}
}
return buf.append("]").toString();
} }
/** /**
@@ -54,7 +148,7 @@ public class DirtyItemList extends ArrayList
* all of the information necessary to render their dirty regions * all of the information necessary to render their dirty regions
* to the target graphics context when the time comes to do so. * to the target graphics context when the time comes to do so.
*/ */
public class DirtyItem public static class DirtyItem
{ {
/** The dirtied object; one of either a sprite or an object tile. */ /** The dirtied object; one of either a sprite or an object tile. */
public Object obj; public Object obj;
@@ -121,6 +215,7 @@ public class DirtyItemList extends ArrayList
gfx.setClip(oclip); gfx.setClip(oclip);
} }
// documentation inherited
public boolean equals (Object other) public boolean equals (Object other)
{ {
// we're never equal to something that's not our kind // we're never equal to something that's not our kind
@@ -140,16 +235,56 @@ public class DirtyItemList extends ArrayList
return (ox == b.ox && oy == b.oy); return (ox == b.ox && oy == b.oy);
} }
/**
* Returns a string representation of the dirty item.
*/
public String toString () public String toString ()
{ {
return "[obj=" + obj + ", ox=" + ox + ", oy=" + oy + StringBuffer buf = new StringBuffer();
", lx=" + lx + ", ly=" + ly + ", rx=" + rx + buf.append("[obj=").append(obj);
", ry=" + ry + "]"; buf.append(", ox=").append(ox);
buf.append(", oy=").append(oy);
buf.append(", lx=").append(lx);
buf.append(", ly=").append(ly);
buf.append(", rx=").append(rx);
buf.append(", ry=").append(ry);
return buf.append("]").toString();
} }
} }
/** The dirty item comparator used to sort dirty items back to front. */ /**
protected static final Comparator DIRTY_COMP = new DirtyItemComparator(); * A comparator class for use in sorting dirty items in ascending
* origin x- or y-axis coordinate order.
*/
protected static class OriginComparator implements Comparator
{
/**
* Constructs an origin comparator that sorts dirty items in
* ascending order based on their origin coordinate on the given
* axis.
*/
public OriginComparator (int axis)
{
_axis = axis;
}
// documentation inherited
public int compare (Object a, Object b)
{
DirtyItem da = (DirtyItem)a;
DirtyItem db = (DirtyItem)b;
return (_axis == X_AXIS) ? (da.ox - db.ox) : (da.oy - db.oy);
}
// documentation inherited
public boolean equals (Object obj)
{
return (obj == this);
}
/** The axis this comparator sorts on. */
protected int _axis;
}
/** /**
* A comparator class for use in sorting the dirty sprites and * A comparator class for use in sorting the dirty sprites and
@@ -157,16 +292,167 @@ public class DirtyItemList extends ArrayList
* suitable for rendering in the isometric view with proper visual * suitable for rendering in the isometric view with proper visual
* results. * results.
*/ */
protected static class DirtyItemComparator implements Comparator protected static class RenderComparator implements Comparator
{ {
/**
* Constructs a render comparator with the given list of
* pre-sorted dirty item lists for each axis.
*/
public RenderComparator (DirtyItem[] xitems, DirtyItem[] yitems)
{
_xitems = xitems;
_yitems = yitems;
}
// documentation inherited
public int compare (Object a, Object b) public int compare (Object a, Object b)
{ {
DirtyItem da = (DirtyItem)a; DirtyItem da = (DirtyItem)a;
DirtyItem db = (DirtyItem)b; DirtyItem db = (DirtyItem)b;
// check for partitioning objects on the y-axis
int result = comparePartitioned(Y_AXIS, da, db);
if (result != 0) {
if (DEBUG_COMPARE) {
Log.info("compare: Y-partitioned " +
"[result=" + result +
", items=" + DirtyItemList.toString(da, db) + "].");
}
return result;
}
// check for partitioning objects on the x-axis
result = comparePartitioned(X_AXIS, da, db);
if (result != 0) {
if (DEBUG_COMPARE) {
Log.info("compare: X-partitioned " +
"[result=" + result +
", items=" + DirtyItemList.toString(da, db) + "].");
}
return result;
}
// use normal iso-ordering check
result = compareNonPartitioned(da, db);
if (DEBUG_COMPARE) {
Log.info("compare: non-partitioned " +
"[result=" + result +
", items=" + DirtyItemList.toString(da, db) + "].");
}
return result;
}
// documentation inherited
public boolean equals (Object obj)
{
return (obj == this);
}
/**
* Returns whether two dirty items have a partitioning object
* between them on the given axis.
*/
protected int comparePartitioned (
int axis, DirtyItem da, DirtyItem db)
{
// prepare for the partitioning check
DirtyItem[] sitems;
Comparator comp;
boolean swapped = false;
switch (axis) {
case X_AXIS:
if (da.ox == db.ox) {
// can't be partitioned if there's no space between
return 0;
}
// order items for proper comparison
if (da.ox > db.ox) {
DirtyItem temp = da;
da = db;
db = temp;
swapped = true;
}
// use the axis-specific sorted array
sitems = _xitems;
comp = ORIGIN_X_COMP;
break;
case Y_AXIS:
default:
if (da.oy == db.oy) {
// can't be partitioned if there's no space between
return 0;
}
// order items for proper comparison
if (da.oy > db.oy) {
DirtyItem temp = da;
da = db;
db = temp;
swapped = true;
}
// use the axis-specific sorted array
sitems = _yitems;
comp = ORIGIN_Y_COMP;
break;
}
// get the bounding item indices and the number of
// potentially-partitioning dirty items
int aidx = Arrays.binarySearch(sitems, da, comp);
int bidx = Arrays.binarySearch(sitems, db, comp);
int size = bidx - aidx - 1;
// check each potentially partitioning item
int startidx = aidx + 1, endidx = startidx + size;
for (int pidx = startidx; pidx < endidx; pidx++) {
DirtyItem dp = sitems[pidx];
if (dp.obj instanceof MisoCharacterSprite) {
// character sprites can't partition things
continue;
} else if ((dp.obj == da.obj) ||
(dp.obj == db.obj)) {
// can't be partitioned by ourselves
continue;
}
// perform the actual partition check for this object
switch (axis) {
case X_AXIS:
if (dp.ly >= da.ry &&
dp.ry <= db.ly &&
dp.lx > da.rx &&
dp.rx < db.lx) {
return (swapped) ? 1 : -1;
}
case Y_AXIS:
default:
if (dp.lx <= db.ox &&
dp.rx >= da.lx &&
dp.ry > da.oy &&
dp.oy < db.ry) {
return (swapped) ? 1 : -1;
}
}
}
// no partitioning object found
return 0;
}
/**
* Compares the two dirty items assuming there are no partitioning
* objects between them.
*/
protected int compareNonPartitioned (DirtyItem da, DirtyItem db)
{
if (da.ox == db.ox && if (da.ox == db.ox &&
da.oy == db.oy) { da.oy == db.oy) {
if (da.equals(db)) { if (da.equals(db)) {
// render level is equal if we're the same sprite // render level is equal if we're the same sprite
// or an object at the same location // or an object at the same location
@@ -176,41 +462,57 @@ public class DirtyItemList extends ArrayList
if ((da.obj instanceof MisoCharacterSprite) && if ((da.obj instanceof MisoCharacterSprite) &&
(db.obj instanceof MisoCharacterSprite)) { (db.obj instanceof MisoCharacterSprite)) {
// we're comparing two sprites co-existing on the same // we're comparing two sprites co-existing on the same
// tile, so study their fine coordinates to determine // tile, so study their fine coordinates
// rendering order
MisoCharacterSprite as = (MisoCharacterSprite)da.obj; MisoCharacterSprite as = (MisoCharacterSprite)da.obj;
MisoCharacterSprite bs = (MisoCharacterSprite)db.obj; MisoCharacterSprite bs = (MisoCharacterSprite)db.obj;
int ahei = as.getFineX() + as.getFineY(); int ahei = as.getFineX() + as.getFineY();
int bhei = bs.getFineX() + bs.getFineY(); int bhei = bs.getFineX() + bs.getFineY();
int diff = ahei - bhei;
if (ahei < bhei) { // if they're at the same vertical row of intra-tile
// item b is in front of item a // tiles, just use hashCode() for consistency
return -1; return (diff != 0) ? diff : (as.hashCode() - bs.hashCode());
} else if (ahei > bhei) {
// item a is in front of item b
return 1;
} else {
// if they're at the same vertical row of
// intra-tile tiles, just use something consistent
return as.hashCode() - bs.hashCode();
}
} }
} }
if (da.lx > db.ox || // establish a consistent ordering between objects. see the
da.ry > db.oy) { // diagram at "narya/docs/miso/render_sort_diagram.png" for
// item a is in front of item b // more information.
if (da.lx > db.ox) {
return 1; return 1;
} else if (da.ry > db.oy) {
return (db.lx > da.ox) ? -1 : 1;
} }
// item b is in front of item a
return -1; return -1;
} }
public boolean equals (Object obj) /** The items in the dirty item list sorted by ascending x origin. */
{ protected DirtyItem[] _xitems;
return (obj == this);
} /** The items in the dirty item list sorted by ascending y origin. */
protected DirtyItem[] _yitems;
} }
/** Whether to log debug info when comparing pairs of dirty items. */
protected static final boolean DEBUG_COMPARE = false;
/** Whether to log debug info for the overall dirty item sorting algorithm. */
protected static final boolean DEBUG_SORT = false;
/** Constants used to denote axis sorting constraints. */
protected static final int X_AXIS = 0;
protected static final int Y_AXIS = 1;
/** The comparator used to sort dirty items in ascending origin
* x-coordinate order. */
protected static final Comparator ORIGIN_X_COMP =
new OriginComparator(X_AXIS);
/** The comparator used to sort dirty items in ascending origin
* y-coordinate order. */
protected static final Comparator ORIGIN_Y_COMP =
new OriginComparator(Y_AXIS);
/** The list of dirty items. */
protected ArrayList _items = new ArrayList();
} }
@@ -1,5 +1,5 @@
// //
// $Id: IsoSceneView.java,v 1.78 2001/12/15 04:20:55 mdb Exp $ // $Id: IsoSceneView.java,v 1.79 2001/12/16 05:42:16 shaper Exp $
package com.threerings.miso.scene; package com.threerings.miso.scene;
@@ -194,7 +194,7 @@ public class IsoSceneView implements SceneView
gfx.setColor(Color.yellow); gfx.setColor(Color.yellow);
size = _dirtyItems.size(); size = _dirtyItems.size();
for (int ii = 0; ii < size; ii++) { for (int ii = 0; ii < size; ii++) {
Rectangle rect = ((DirtyItem)_dirtyItems.get(ii)).dirtyRect; Rectangle rect = _dirtyItems.get(ii).dirtyRect;
gfx.draw(rect); gfx.draw(rect);
} }
} }