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;
import java.awt.*;
import java.util.*;
import java.awt.Graphics2D;
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.tile.ObjectTile;
@@ -15,7 +23,7 @@ import com.threerings.media.Log;
* The dirty item list keeps track of dirty sprites and object tiles
* in a scene.
*/
public class DirtyItemList extends ArrayList
public class DirtyItemList
{
/**
* Appends the dirty sprite at the given coordinates to the dirty
@@ -24,7 +32,7 @@ public class DirtyItemList extends ArrayList
public void appendDirtySprite (
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 (
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
* sorted with the {@link DirtyItemComparator}.
* sorted in proper rendering order.
*/
public DirtyItem[] sort ()
{
DirtyItem items[] = new DirtyItem[size()];
toArray(items);
Arrays.sort(items, DIRTY_COMP);
return items;
int size = size();
if (DEBUG_SORT) {
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
* 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. */
public Object obj;
@@ -121,6 +215,7 @@ public class DirtyItemList extends ArrayList
gfx.setClip(oclip);
}
// documentation inherited
public boolean equals (Object other)
{
// 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);
}
/**
* Returns a string representation of the dirty item.
*/
public String toString ()
{
return "[obj=" + obj + ", ox=" + ox + ", oy=" + oy +
", lx=" + lx + ", ly=" + ly + ", rx=" + rx +
", ry=" + ry + "]";
StringBuffer buf = new StringBuffer();
buf.append("[obj=").append(obj);
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
@@ -157,16 +292,167 @@ public class DirtyItemList extends ArrayList
* suitable for rendering in the isometric view with proper visual
* 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)
{
DirtyItem da = (DirtyItem)a;
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 &&
da.oy == db.oy) {
if (da.equals(db)) {
// render level is equal if we're the same sprite
// or an object at the same location
@@ -176,41 +462,57 @@ public class DirtyItemList extends ArrayList
if ((da.obj instanceof MisoCharacterSprite) &&
(db.obj instanceof MisoCharacterSprite)) {
// we're comparing two sprites co-existing on the same
// tile, so study their fine coordinates to determine
// rendering order
// tile, so study their fine coordinates
MisoCharacterSprite as = (MisoCharacterSprite)da.obj;
MisoCharacterSprite bs = (MisoCharacterSprite)db.obj;
int ahei = as.getFineX() + as.getFineY();
int bhei = bs.getFineX() + bs.getFineY();
if (ahei < bhei) {
// item b is in front of item a
return -1;
} 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();
}
int diff = ahei - bhei;
// if they're at the same vertical row of intra-tile
// tiles, just use hashCode() for consistency
return (diff != 0) ? diff : (as.hashCode() - bs.hashCode());
}
}
if (da.lx > db.ox ||
da.ry > db.oy) {
// item a is in front of item b
// establish a consistent ordering between objects. see the
// diagram at "narya/docs/miso/render_sort_diagram.png" for
// more information.
if (da.lx > db.ox) {
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;
}
public boolean equals (Object obj)
{
return (obj == this);
}
/** The items in the dirty item list sorted by ascending x origin. */
protected DirtyItem[] _xitems;
/** 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;
@@ -194,7 +194,7 @@ public class IsoSceneView implements SceneView
gfx.setColor(Color.yellow);
size = _dirtyItems.size();
for (int ii = 0; ii < size; ii++) {
Rectangle rect = ((DirtyItem)_dirtyItems.get(ii)).dirtyRect;
Rectangle rect = _dirtyItems.get(ii).dirtyRect;
gfx.draw(rect);
}
}