Fixed scene rendering issues and improved performance.

git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@517 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Walter Korman
2001-10-22 18:21:42 +00:00
parent 7bd5d86c34
commit c7d26cea8e
9 changed files with 138 additions and 65 deletions
@@ -1,8 +1,9 @@
// //
// $Id: CharacterManager.java,v 1.1 2001/10/15 23:53:43 shaper Exp $ // $Id: CharacterManager.java,v 1.2 2001/10/22 18:21:41 shaper Exp $
package com.threerings.miso.scene; package com.threerings.miso.scene;
import java.awt.Point;
import java.io.IOException; import java.io.IOException;
import com.samskivert.util.Config; import com.samskivert.util.Config;
@@ -59,6 +60,7 @@ public class CharacterManager
AmbulatorySprite sprite = new AmbulatorySprite(0, 0, anims); AmbulatorySprite sprite = new AmbulatorySprite(0, 0, anims);
sprite.setFrameRate(info.frameRate); sprite.setFrameRate(info.frameRate);
sprite.setOrigin(info.origin.x, info.origin.y);
return sprite; return sprite;
} }
@@ -87,11 +89,12 @@ public class CharacterManager
public int tsid; public int tsid;
public int frameCount; public int frameCount;
public int frameRate; public int frameRate;
public Point origin = new Point();
public String toString () public String toString ()
{ {
return "[tsid=" + tsid + ", frameCount=" + frameCount + return "[tsid=" + tsid + ", frameCount=" + frameCount +
", frameRate=" + frameRate + "]"; ", frameRate=" + frameRate + ", origin=" + origin + "]";
} }
} }
} }
@@ -1,5 +1,5 @@
// //
// $Id: CharacterSprite.java,v 1.12 2001/10/15 23:53:43 shaper Exp $ // $Id: CharacterSprite.java,v 1.13 2001/10/22 18:21:41 shaper Exp $
package com.threerings.miso.scene; package com.threerings.miso.scene;
@@ -45,6 +45,32 @@ public class AmbulatorySprite extends Sprite implements Traverser
setFrames(_anims[_orient]); setFrames(_anims[_orient]);
} }
/**
* Sets the origin coordinates representing the "base" of the
* sprite, which in most cases corresponds to the center of the
* bottom of the sprite image.
*/
public void setOrigin (int x, int y)
{
_xorigin = x;
_yorigin = y;
updateRenderOffset();
updateRenderOrigin();
}
// documentation inherited
protected void updateRenderOffset ()
{
super.updateRenderOffset();
if (_frame != null) {
// our location is based on the character origin coordinates
_rxoff = -_xorigin;
_ryoff = -_yorigin;
}
}
// documentation inherited // documentation inherited
public void cancelMove () public void cancelMove ()
{ {
@@ -91,4 +117,7 @@ public class AmbulatorySprite extends Sprite implements Traverser
/** The animation frames for the sprite facing each direction. */ /** The animation frames for the sprite facing each direction. */
protected MultiFrameImage[] _anims; protected MultiFrameImage[] _anims;
/** The origin of the sprite. */
protected int _xorigin, _yorigin;
} }
@@ -1,5 +1,5 @@
// //
// $Id: IsoSceneView.java,v 1.64 2001/10/19 23:26:31 shaper Exp $ // $Id: IsoSceneView.java,v 1.65 2001/10/22 18:21:41 shaper Exp $
package com.threerings.miso.scene; package com.threerings.miso.scene;
@@ -78,10 +78,9 @@ public class IsoSceneView implements SceneView
Graphics2D gfx = (Graphics2D)g; Graphics2D gfx = (Graphics2D)g;
// clip the drawing region to our desired bounds since we // clip everything to the overall scene view bounds
// currently draw tiles willy-nilly in undesirable areas.
Shape oldclip = gfx.getClip(); Shape oldclip = gfx.getClip();
gfx.setClip(0, 0, _model.bounds.width, _model.bounds.height); gfx.setClip(_model.bounds);
if (_numDirty == 0) { if (_numDirty == 0) {
// invalidate the entire screen // invalidate the entire screen
@@ -134,8 +133,7 @@ public class IsoSceneView implements SceneView
protected void invalidate () protected void invalidate ()
{ {
DirtyRectList rects = new DirtyRectList(); DirtyRectList rects = new DirtyRectList();
rects.add(new Rectangle( rects.add(_model.bounds);
0, 0, _model.bounds.width,_model.bounds.height));
invalidateRects(rects); invalidateRects(rects);
} }
@@ -172,12 +170,23 @@ public class IsoSceneView implements SceneView
} }
// draw the dirty rectangles // draw the dirty rectangles
Stroke ostroke = gfx.getStroke();
gfx.setStroke(DIRTY_RECT_STROKE);
gfx.setColor(Color.red); gfx.setColor(Color.red);
int size = _dirtyRects.size(); int size = _dirtyRects.size();
for (int ii = 0; ii < size; ii++) { for (int ii = 0; ii < size; ii++) {
Rectangle rect = (Rectangle)_dirtyRects.get(ii); Rectangle rect = (Rectangle)_dirtyRects.get(ii);
gfx.draw(rect); gfx.draw(rect);
} }
gfx.setStroke(ostroke);
// draw the dirty item rectangles
gfx.setColor(Color.yellow);
size = _dirtyItems.size();
for (int ii = 0; ii < size; ii++) {
Rectangle rect = ((DirtyItem)_dirtyItems.get(ii)).dirtyRect;
gfx.draw(rect);
}
} }
/** /**
@@ -236,9 +245,36 @@ public class IsoSceneView implements SceneView
// save original clipping region // save original clipping region
Shape clip = gfx.getClip(); Shape clip = gfx.getClip();
// render dirty sprites and objects // merge all dirty rectangles for each item into a single
// rectangle before painting
Rectangle dirtyRect = new Rectangle();
DirtyItem cur = null, last = null;
for (int ii = 0; ii < size; ii++) { for (int ii = 0; ii < size; ii++) {
items[ii].paint(gfx); cur = items[ii];
if (last == null ||
(cur.x != last.x || cur.y != last.y)) {
if (last != null) {
// paint the item with its full dirty rectangle
// Log.info("Painting dirty item [item=" + last + "].");
last.paint(gfx, dirtyRect);
}
// update the current dirty item
last = cur;
dirtyRect.setBounds(cur.dirtyRect);
} else {
// expand the item's dirty rectangle
dirtyRect.add(cur.dirtyRect);
}
}
if (cur != null) {
// paint the final dirty item
cur.paint(gfx, dirtyRect);
// Log.info("Painting dirty item [item=" + cur + "].");
} }
// restore original clipping region // restore original clipping region
@@ -317,10 +353,11 @@ public class IsoSceneView implements SceneView
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++) {
if (_dirty[xx][yy]) { // get the top-left screen coordinates of the tile
Rectangle bounds = _polys[xx][yy].getBounds();
// get the top-left screen coordinates of the tile // only draw coordinates if the tile is on-screen
Rectangle bounds = _polys[xx][yy].getBounds(); if (bounds.intersects(_model.bounds)) {
int sx = bounds.x, sy = bounds.y; int sx = bounds.x, sy = bounds.y;
// draw x-coordinate // draw x-coordinate
@@ -332,7 +369,6 @@ public class IsoSceneView implements SceneView
str = "" + yy; str = "" + yy;
xpos = sx + cx - (fm.stringWidth(str) / 2); xpos = sx + cx - (fm.stringWidth(str) / 2);
gfx.drawString(str, xpos, sy + cy + fhei); gfx.drawString(str, xpos, sy + cy + fhei);
} }
} }
} }
@@ -405,10 +441,8 @@ public class IsoSceneView implements SceneView
// documentation inherited // documentation inherited
public void invalidateRects (DirtyRectList rects) public void invalidateRects (DirtyRectList rects)
{ {
// we specifically need to allow the dirty rects list to grow int size = rects.size();
// while we're iterating over it, so we're sure to call for (int ii = 0; ii < size; ii++) {
// rects.size() each time through the loop
for (int ii = 0; ii < rects.size(); ii++) {
Rectangle r = (Rectangle)rects.get(ii); Rectangle r = (Rectangle)rects.get(ii);
// dirty the tiles impacted by this rectangle // dirty the tiles impacted by this rectangle
@@ -423,14 +457,17 @@ public class IsoSceneView implements SceneView
} }
/** /**
* Invalidate the specified rectangle in screen pixel coordinates * Invalidates the given rectangle in screen pixel coordinates in
* in the view. * the view. Returns a rectangle that bounds all tiles that were
* dirtied.
* *
* @param rect the dirty rectangle. * @param rect the dirty rectangle.
*/ */
public Rectangle invalidateScreenRect (Rectangle r) public Rectangle invalidateScreenRect (Rectangle r)
{ {
Rectangle tileBounds = new Rectangle(); // initialize the rectangle bounding all tiles dirtied by the
// invalidated rectangle
Rectangle tileBounds = new Rectangle(-1, -1, 0, 0);
// note that corner tiles may be included unnecessarily, but // note that corner tiles may be included unnecessarily, but
// checking to determine whether they're actually needed // checking to determine whether they're actually needed
@@ -524,22 +561,17 @@ public class IsoSceneView implements SceneView
*/ */
protected void addDirtyTile (Rectangle tileBounds, int x, int y) protected void addDirtyTile (Rectangle tileBounds, int x, int y)
{ {
// constrain x-coordinate to a valid range if (!_model.isCoordinateValid(x, y)) {
if (x < 0) { return;
x = 0; }
} else if (x >= _model.scenewid) {
x = _model.scenewid - 1;
}
// constrain y-coordinate to a valid range
if (y < 0) {
y = 0;
} else if (y >= _model.scenehei) {
y = _model.scenehei - 1;
}
// expand the tile bounds rectangle to include this tile // expand the tile bounds rectangle to include this tile
tileBounds.add(_polys[x][y].getBounds()); Rectangle bounds = _polys[x][y].getBounds();
if (tileBounds.x == -1) {
tileBounds.setBounds(bounds);
} else {
tileBounds.add(bounds);
}
// do nothing if the tile's already dirty // do nothing if the tile's already dirty
if (_dirty[x][y]) { if (_dirty[x][y]) {
@@ -603,10 +635,7 @@ public class IsoSceneView implements SceneView
public Path getPath (AmbulatorySprite sprite, int x, int y) public Path getPath (AmbulatorySprite sprite, int x, int y)
{ {
// make sure the destination point is within our bounds // make sure the destination point is within our bounds
if (x < 0 || if (!_model.bounds.contains(x, y)) {
x >= _model.bounds.width ||
y < 0 ||
y >= _model.bounds.height) {
return null; return null;
} }
@@ -620,7 +649,11 @@ public class IsoSceneView implements SceneView
int tbx = IsoUtil.fullToTile(fpos.x); int tbx = IsoUtil.fullToTile(fpos.x);
int tby = IsoUtil.fullToTile(fpos.y); int tby = IsoUtil.fullToTile(fpos.y);
// get a reasonable path from start to end // Log.info("Seeking path [sx=" + stpos.x + ", sy=" + stpos.y +
// ", dx=" + tbx + ", dy=" + tby + " ,fdx=" + fpos.x +
// ", fdy=" + fpos.y + "].");
// get a reasonable path from start to end
List tilepath = AStarPathUtil.getPath( List tilepath = AStarPathUtil.getPath(
_scene.getBaseLayer(), _model.scenewid, _model.scenehei, _scene.getBaseLayer(), _model.scenewid, _model.scenehei,
sprite, stpos.x, stpos.y, tbx, tby); sprite, stpos.x, stpos.y, tbx, tby);
@@ -680,6 +713,9 @@ public class IsoSceneView implements SceneView
return path; return path;
} }
/** The stroke used to draw dirty rectangles. */
protected static final Stroke DIRTY_RECT_STROKE = new BasicStroke(2);
/** The font to draw tile coordinates. */ /** The font to draw tile coordinates. */
protected Font _font = new Font("Arial", Font.PLAIN, 7); protected Font _font = new Font("Arial", Font.PLAIN, 7);
@@ -1,9 +1,9 @@
// //
// $Id: IsoSceneViewModel.java,v 1.15 2001/10/18 21:01:44 shaper Exp $ // $Id: IsoSceneViewModel.java,v 1.16 2001/10/22 18:21:41 shaper Exp $
package com.threerings.miso.scene; package com.threerings.miso.scene;
import java.awt.Dimension; import java.awt.Rectangle;
import java.awt.Point; import java.awt.Point;
import java.util.ArrayList; import java.util.ArrayList;
@@ -37,8 +37,8 @@ public class IsoSceneViewModel
/** Scene dimensions in tile count. */ /** Scene dimensions in tile count. */
public int scenewid, scenehei; public int scenewid, scenehei;
/** The bounds dimensions for the view. */ /** The bounds of the view in screen pixel coordinates. */
public Dimension bounds; public Rectangle bounds;
/** The position in pixels at which tile (0, 0) is drawn. */ /** The position in pixels at which tile (0, 0) is drawn. */
public Point origin; public Point origin;
@@ -98,7 +98,7 @@ public class IsoSceneViewModel
// set the desired scene view bounds // set the desired scene view bounds
int svwid = config.getValue(SCENE_VWIDTH_KEY, DEF_SCENE_VWIDTH); int svwid = config.getValue(SCENE_VWIDTH_KEY, DEF_SCENE_VWIDTH);
int svhei = config.getValue(SCENE_VHEIGHT_KEY, DEF_SCENE_VHEIGHT); int svhei = config.getValue(SCENE_VHEIGHT_KEY, DEF_SCENE_VHEIGHT);
bounds = new Dimension(svwid * tilewid, svhei * tilehei); bounds = new Rectangle(0, 0, svwid * tilewid, svhei * tilehei);
// set the scene display origin // set the scene display origin
int offy = config.getValue(SCENE_OFFSET_Y_KEY, DEF_OFFSET_Y); int offy = config.getValue(SCENE_OFFSET_Y_KEY, DEF_OFFSET_Y);
@@ -139,7 +139,7 @@ public class IsoSceneViewModel
* Returns whether the given tile coordinate is a valid coordinate * Returns whether the given tile coordinate is a valid coordinate
* within the scene. * within the scene.
*/ */
public boolean isValidCoordinate (int x, int y) public boolean isCoordinateValid (int x, int y)
{ {
return (x >= 0 && x < scenewid && return (x >= 0 && x < scenewid &&
y >= 0 && y < scenehei); y >= 0 && y < scenehei);
@@ -149,11 +149,11 @@ public class IsoSceneViewModel
* Returns whether the given full coordinate is a valid coordinate * Returns whether the given full coordinate is a valid coordinate
* within the scene. * within the scene.
*/ */
public boolean isValidFullCoordinate (int x, int y) public boolean isFullCoordinateValid (int x, int y)
{ {
int tx = IsoUtil.fullToTile(x), ty = IsoUtil.fullToTile(y); int tx = IsoUtil.fullToTile(x), ty = IsoUtil.fullToTile(y);
int fx = IsoUtil.fullToFine(x), fy = IsoUtil.fullToFine(y); int fx = IsoUtil.fullToFine(x), fy = IsoUtil.fullToFine(y);
return (isValidCoordinate(tx, ty) && return (isCoordinateValid(tx, ty) &&
fx >= 0 && fx < finegran && fx >= 0 && fx < finegran &&
fy >= 0 && fy < finegran); fy >= 0 && fy < finegran);
} }
@@ -1,5 +1,5 @@
// //
// $Id: SceneView.java,v 1.16 2001/10/11 00:41:27 shaper Exp $ // $Id: SceneView.java,v 1.17 2001/10/22 18:21:41 shaper Exp $
package com.threerings.miso.scene; package com.threerings.miso.scene;
@@ -17,37 +17,37 @@ import com.threerings.media.sprite.Path;
public interface SceneView public interface SceneView
{ {
/** /**
* Invalidate a list of rectangles in screen pixel coordinates in the * Invalidates a list of rectangles in screen pixel coordinates in
* scene view for later repainting. * the scene view for later repainting.
* *
* @param rects the list of {@link java.awt.Rectangle} objects. * @param rects the list of {@link java.awt.Rectangle} objects.
*/ */
public void invalidateRects (DirtyRectList rects); public void invalidateRects (DirtyRectList rects);
/** /**
* Render the scene to the given graphics context. * Renders the scene to the given graphics context.
* *
* @param g the graphics context. * @param g the graphics context.
*/ */
public void paint (Graphics g); public void paint (Graphics g);
/** /**
* Set the scene that we're rendering. * Sets the scene that we're rendering.
* *
* @param scene the scene to render in the view. * @param scene the scene to render in the view.
*/ */
public void setScene (MisoScene scene); public void setScene (MisoScene scene);
/** /**
* Return a Path object detailing a valid path for the given * Returns a {@link Path} object detailing a valid path for the
* sprite to take in the scene to get from its current position to * given sprite to take in the scene to get from its current
* the destination position. * position to the destination position.
* *
* @param sprite the sprite to move. * @param sprite the sprite to move.
* @param x the destination x-position in pixel coordinates. * @param x the destination x-position in pixel coordinates.
* @param y the destination y-position in pixel coordinates. * @param y the destination y-position in pixel coordinates.
* *
* @return the sprite's path or null if no valid path exists. * @return the sprite's path, or null if no valid path exists.
*/ */
public Path getPath (AmbulatorySprite sprite, int x, int y); public Path getPath (AmbulatorySprite sprite, int x, int y);
} }
@@ -1,5 +1,5 @@
// //
// $Id: SceneViewPanel.java,v 1.18 2001/10/19 23:26:31 shaper Exp $ // $Id: SceneViewPanel.java,v 1.19 2001/10/22 18:21:41 shaper Exp $
package com.threerings.miso.scene; package com.threerings.miso.scene;
@@ -89,7 +89,7 @@ public class SceneViewPanel extends AnimatedPanel
public Dimension getPreferredSize () public Dimension getPreferredSize ()
{ {
Dimension psize = (_scenemodel == null) ? Dimension psize = (_scenemodel == null) ?
super.getPreferredSize() : _scenemodel.bounds; super.getPreferredSize() : _scenemodel.bounds.getSize();
return psize; return psize;
} }
@@ -1,5 +1,5 @@
// //
// $Id: IsoUtil.java,v 1.11 2001/10/19 23:26:31 shaper Exp $ // $Id: IsoUtil.java,v 1.12 2001/10/22 18:21:41 shaper Exp $
package com.threerings.miso.scene.util; package com.threerings.miso.scene.util;
@@ -419,6 +419,13 @@ public class IsoUtil
return comp; return comp;
} }
// check whether right edge of b overlaps with left edge of a
comp = getRightOverlap(db, da);
if (comp != 0) {
// reverse ordering per reversed overlap check
return (comp == -1) ? 1 : -1;
}
// determine ordering based purely on coordinates // determine ordering based purely on coordinates
if (da.x <= db.x && da.y <= db.y) { if (da.x <= db.x && da.y <= db.y) {
return -1; return -1;
@@ -1,9 +1,8 @@
// //
// $Id: XMLSceneParser.java,v 1.21 2001/10/17 22:21:22 shaper Exp $ // $Id: XMLSceneParser.java,v 1.22 2001/10/22 18:21:41 shaper Exp $
package com.threerings.miso.scene.xml; package com.threerings.miso.scene.xml;
import java.awt.Point;
import java.io.*; import java.io.*;
import java.util.ArrayList; import java.util.ArrayList;
@@ -1,9 +1,8 @@
// //
// $Id: XMLSceneWriter.java,v 1.16 2001/10/17 22:18:22 shaper Exp $ // $Id: XMLSceneWriter.java,v 1.17 2001/10/22 18:21:42 shaper Exp $
package com.threerings.miso.scene.xml; package com.threerings.miso.scene.xml;
import java.awt.Point;
import java.io.*; import java.io.*;
import java.util.List; import java.util.List;