diff --git a/src/java/com/threerings/cast/CharacterManager.java b/src/java/com/threerings/cast/CharacterManager.java index fb37edcb7..1eb65545d 100644 --- a/src/java/com/threerings/cast/CharacterManager.java +++ b/src/java/com/threerings/cast/CharacterManager.java @@ -1,21 +1,21 @@ // -// $Id: CharacterManager.java,v 1.26 2002/12/07 02:04:31 shaper Exp $ +// $Id: CharacterManager.java,v 1.27 2002/12/16 03:08:39 mdb Exp $ package com.threerings.cast; -import java.util.Arrays; import java.util.Iterator; import java.util.HashMap; import com.samskivert.util.LRUHashMap; import com.samskivert.util.StringUtil; +import com.samskivert.util.Throttle; import com.samskivert.util.Tuple; import com.threerings.media.util.Colorization; import com.threerings.util.DirectionCodes; +import com.threerings.cast.CompositedActionFrames.ComponentFrames; import com.threerings.cast.Log; -import com.samskivert.util.Throttle; /** * The character manager provides facilities for constructing sprites that @@ -229,52 +229,19 @@ public class CharacterManager Log.debug("Compositing action [action=" + action + ", descrip=" + descrip + "]."); - // obtain the necessary components - ColorizedComponent[] components = new ColorizedComponent[ccount]; - for (int i = 0; i < ccount; i++) { - ColorizedComponent cc = new ColorizedComponent(); - cc.component = _crepo.getComponent(cids[i]); - if (zations != null) { - cc.zations = zations[i]; - } - components[i] = cc; - } - - // sort them into the proper rendering order - Arrays.sort(components); - // create colorized versions of all of the source action frames - ActionFrames[] sources = new ActionFrames[ccount]; + ComponentFrames[] sources = new ComponentFrames[ccount]; for (int ii = 0; ii < ccount; ii++) { - ActionFrames source = components[ii].component.getFrames(action); - if (zations != null) { - sources[ii] = source.cloneColorized(components[ii].zations); - } else { - sources[ii] = source; - } + sources[ii] = new ComponentFrames(); + sources[ii].ccomp = _crepo.getComponent(cids[ii]); + ActionFrames source = sources[ii].ccomp.getFrames(action); + sources[ii].frames = (zations == null || zations[ii] == null) ? + source : source.cloneColorized(zations[ii]); } // use those to create an entity that will lazily composite things // together as they are needed - return new CompositedActionFrames(sources); - } - - /** Used when compositing component frame images. */ - protected static final class ColorizedComponent implements Comparable - { - /** The component to be colorized. */ - public CharacterComponent component; - - /** The colorizations to apply. */ - public Colorization[] zations; - - /** Sorts by render order. */ - public int compareTo (Object o) - { - ColorizedComponent co = (ColorizedComponent)o; - return (component.componentClass.renderPriority - - co.component.componentClass.renderPriority); - } + return new CompositedActionFrames(action, sources); } /** The component repository. */ diff --git a/src/java/com/threerings/cast/ComponentClass.java b/src/java/com/threerings/cast/ComponentClass.java index 65735337a..c026ef238 100644 --- a/src/java/com/threerings/cast/ComponentClass.java +++ b/src/java/com/threerings/cast/ComponentClass.java @@ -1,24 +1,35 @@ // -// $Id: ComponentClass.java,v 1.6 2002/11/20 02:21:10 mdb Exp $ +// $Id: ComponentClass.java,v 1.7 2002/12/16 03:08:39 mdb Exp $ package com.threerings.cast; import java.io.Serializable; -import java.util.Comparator; import com.samskivert.util.ArrayIntSet; +import com.samskivert.util.SortableArrayList; + +import com.threerings.util.DirectionUtil; /** * Denotes a class of components to which {@link CharacterComponent} * objects belong. Examples include "Hat", "Head", and "Feet". A component * class dictates a component's rendering priority so that components can * be rendered in an order that causes them to overlap properly. + * + *
Components support render priority overrides for particular
+ * actions, orientations or combinations of actions and orientations. The
+ * system is currently structured with the expectation that the overrides
+ * will be relatively few (less than fifteen, say) for any given component
+ * class. A system that relied on many overrides for its components would
+ * want to implement a more scalable algorithm for determining which, if
+ * any, override matches a particular action and orientation combination.
*/
public class ComponentClass implements Serializable
{
/** Used to effect custom render orders for particular actions,
* orientations, etc. */
public static class PriorityOverride
+ implements Comparable, Serializable
{
/** The overridden render priority value. */
public int renderPriority;
@@ -29,25 +40,106 @@ public class ComponentClass implements Serializable
/** The orientations, if any, for which this override is
* appropriate. */
public ArrayIntSet orients;
- }
- /** The comparator used to sort component class objects in render
- * priority order. */
- public static final Comparator RENDER_COMP = new RenderComparator();
+ /**
+ * Determines whether this priority override matches the specified
+ * action and orientation combination.
+ */
+ public boolean matches (String action, int orient)
+ {
+ return (((orients == null) || orients.contains(orient)) &&
+ ((this.action == null) || this.action.equals(action)));
+ }
+
+ // documentation inherited from interface
+ public int compareTo (Object other)
+ {
+ // overrides with both an action and an orientation should
+ // come first in the list
+ PriorityOverride po = (PriorityOverride)other;
+ int pri = priority(), opri = po.priority();
+ if (pri == opri) {
+ return hashCode() - po.hashCode();
+ } else {
+ return pri - opri;
+ }
+ }
+
+ protected int priority ()
+ {
+ int priority = 0;
+ if (action != null) {
+ priority++;
+ }
+ if (orients != null) {
+ priority++;
+ }
+ return priority;
+ }
+
+ /** Generates a string representation of this instance. */
+ public String toString ()
+ {
+ return "[pri=" + renderPriority + ", action=" + action +
+ ", orients=" + orients + "]";
+ }
+ }
/** The component class name. */
public String name;
- /** The render priority. */
+ /** The default render priority. */
public int renderPriority;
+ /**
+ * Creates a component class with the specified name and default
+ * render priority.
+ */
+ public ComponentClass (String name, int defaultRenderPriority)
+ {
+ this.name = name;
+ this.renderPriority = defaultRenderPriority;
+ }
+
+ /**
+ * Creates an uninitialized instance suitable for unserialization.
+ */
+ public ComponentClass ()
+ {
+ }
+
/**
* Returns the render priority appropriate for the specified action
* and orientation.
*/
public int getRenderPriority (String action, int orientation)
{
- return -1;
+ // because we expect there to be relatively few priority
+ // overrides, we simply search linearly through the list for the
+ // closest match
+ int ocount = (_overrides != null) ? _overrides.size() : 0;
+ for (int ii = 0; ii < ocount; ii++) {
+ PriorityOverride over = (PriorityOverride)_overrides.get(ii);
+ // based on the way the overrides are sorted, the first match
+ // is the most specific and the one we want
+ if (over.matches(action, orientation)) {
+ return over.renderPriority;
+ }
+ }
+
+ return renderPriority;
+ }
+
+ /**
+ * Adds the supplied render priority override record to this component
+ * class.
+ */
+ public void addPriorityOverride (PriorityOverride override)
+ {
+ if (_overrides == null) {
+ _overrides = new SortableArrayList();
+ }
+ _overrides.insertSorted(override);
}
/**
@@ -78,29 +170,10 @@ public class ComponentClass implements Serializable
return "[name=" + name + ", renderPriority=" + renderPriority + "]";
}
- /**
- * The comparator used to sort {@link CharacterComponent} instances in
- * render priority order so that compositing components into a single
- * character image can be done in the proper order.
- */
- protected static class RenderComparator implements Comparator
- {
- // documentation inherited
- public int compare (Object a, Object b)
- {
- if (!(a instanceof CharacterComponent) ||
- !(b instanceof CharacterComponent)) {
- return -1;
- }
-
- CharacterComponent ca = (CharacterComponent)a;
- CharacterComponent cb = (CharacterComponent)b;
- return (ca.componentClass.renderPriority -
- cb.componentClass.renderPriority);
- }
- }
+ /** A list of render priority overrides. */
+ protected SortableArrayList _overrides;
/** Increase this value when object's serialized state is impacted by
* a class change (modification of fields, inheritance). */
- private static final long serialVersionUID = 1;
+ private static final long serialVersionUID = 2;
}
diff --git a/src/java/com/threerings/cast/CompositedActionFrames.java b/src/java/com/threerings/cast/CompositedActionFrames.java
index 24cff36cd..3124c12e0 100644
--- a/src/java/com/threerings/cast/CompositedActionFrames.java
+++ b/src/java/com/threerings/cast/CompositedActionFrames.java
@@ -1,11 +1,13 @@
//
-// $Id: CompositedActionFrames.java,v 1.9 2002/12/07 02:04:31 shaper Exp $
+// $Id: CompositedActionFrames.java,v 1.10 2002/12/16 03:08:39 mdb Exp $
package com.threerings.cast;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
+import java.util.Arrays;
+import java.util.Comparator;
import com.samskivert.util.StringUtil;
@@ -13,6 +15,7 @@ import com.threerings.media.util.Colorization;
import com.threerings.media.util.ImageUtil;
import com.threerings.media.util.MultiFrameImage;
+import com.threerings.cast.CharacterComponent;
import com.threerings.util.DirectionCodes;
/**
@@ -20,14 +23,27 @@ import com.threerings.util.DirectionCodes;
* to lazily create composited character frames when they are requested.
*/
public class CompositedActionFrames
- implements ActionFrames, DirectionCodes
+ implements ActionFrames, DirectionCodes, Comparator
{
+ /** Used to associate a {@link CharacterComponent} with its {@link
+ * ActionFrames} for a particular action. */
+ public static class ComponentFrames
+ {
+ public CharacterComponent ccomp;
+
+ public ActionFrames frames;
+
+ public String toString () {
+ return ccomp + ":" + frames;
+ }
+ }
+
/**
* Constructs a set of composited action frames with the supplied
* source frames and colorization configuration. The actual component
* frame images will not be composited until they are requested.
*/
- public CompositedActionFrames (ActionFrames[] sources)
+ public CompositedActionFrames (String action, ComponentFrames[] sources)
{
// sanity check
if (sources == null || sources.length == 0) {
@@ -36,12 +52,13 @@ public class CompositedActionFrames
throw new RuntimeException(errmsg);
}
_sources = sources;
+ _action = action;
// the sources must all have the same orientation count, and each
// orientation must also have the same frame count, so we just use
// the counts from the first source and orientation
- _orientCount = _sources[0].getOrientationCount();
- _frameCount = _sources[0].getFrames(NORTH).getFrameCount();
+ _orientCount = _sources[0].frames.getOrientationCount();
+ _frameCount = _sources[0].frames.getFrames(NORTH).getFrameCount();
_images = new Image[_orientCount][_frameCount];
_bounds = new Rectangle[_orientCount][_frameCount];
}
@@ -129,6 +146,14 @@ public class CompositedActionFrames
return size;
}
+ // documentation inherited from interface
+ public int compare (Object o1, Object o2)
+ {
+ ComponentFrames cf1 = (ComponentFrames)o1, cf2 = (ComponentFrames)o2;
+ return (cf1.ccomp.componentClass.getRenderPriority(_action, _sorient) -
+ cf2.ccomp.componentClass.getRenderPriority(_action, _sorient));
+ }
+
// documentation inherited
protected Image getFrame (int orient, int index)
{
@@ -163,12 +188,17 @@ public class CompositedActionFrames
// // DEBUG
// int width = 0, height = 0;
+ // sort the sources appropriately for this orientation
+ _sorient = orient;
+ Arrays.sort(_sources, this);
+
// first we need to determine the bounds of the rectangle that
// will enclose all of our various components
Rectangle tbounds = new Rectangle();
Rectangle bounds = _bounds[orient][index] = new Rectangle(0, 0, 0, 0);
for (int ii = 0; ii < scount; ii++) {
- TrimmedMultiFrameImage source = _sources[ii].getFrames(orient);
+ TrimmedMultiFrameImage source =
+ _sources[ii].frames.getFrames(orient);
source.getTrimmedBounds(index, tbounds);
// the first one defines our initial bounds
if (bounds.width == 0 && bounds.height == 0) {
@@ -190,7 +220,8 @@ public class CompositedActionFrames
// now render each of the components into a composited frame
for (int ii = 0; ii < scount; ii++) {
- TrimmedMultiFrameImage source = _sources[ii].getFrames(orient);
+ TrimmedMultiFrameImage source =
+ _sources[ii].frames.getFrames(orient);
source.getTrimmedBounds(index, tbounds);
// render this frame for this particular action for this
@@ -208,8 +239,8 @@ public class CompositedActionFrames
// ", width=" + width + ", height=" + height + "].");
// keep track of our new origin
- bounds.x = (_sources[0].getXOrigin(orient, index) - bounds.x);
- bounds.y = (_sources[0].getYOrigin(orient, index) - bounds.y);
+ bounds.x = (_sources[0].frames.getXOrigin(orient, index) - bounds.x);
+ bounds.y = (_sources[0].frames.getYOrigin(orient, index) - bounds.y);
// Log.info("New origin [x=" + bounds.x + ", y=" + bounds.y + "].");
@@ -220,14 +251,20 @@ public class CompositedActionFrames
return dest;
}
+ /** The action for which we're compositing frames. */
+ protected String _action;
+
+ /** The orientation for which we're currently sorting. */
+ protected int _sorient;
+
/** The number of orientations. */
protected int _orientCount;
/** The number of frames in each orientation. */
protected int _frameCount;
- /** Our source action frames. */
- protected ActionFrames[] _sources;
+ /** Our source components and action frames. */
+ protected ComponentFrames[] _sources;
/** The frame images. */
protected Image[][] _images;
diff --git a/src/java/com/threerings/cast/tools/xml/ClassRuleSet.java b/src/java/com/threerings/cast/tools/xml/ClassRuleSet.java
index bf6ff0a2c..c5b59b254 100644
--- a/src/java/com/threerings/cast/tools/xml/ClassRuleSet.java
+++ b/src/java/com/threerings/cast/tools/xml/ClassRuleSet.java
@@ -1,14 +1,19 @@
//
-// $Id: ClassRuleSet.java,v 1.1 2001/11/27 08:09:35 mdb Exp $
+// $Id: ClassRuleSet.java,v 1.2 2002/12/16 03:08:39 mdb Exp $
package com.threerings.cast.tools.xml;
import org.apache.commons.digester.Digester;
import org.apache.commons.digester.RuleSetBase;
+import com.samskivert.util.ArrayIntSet;
+import com.samskivert.util.StringUtil;
import com.samskivert.xml.SetPropertyFieldsRule;
+import com.samskivert.xml.SetPropertyFieldsRule.FieldParser;
+import com.threerings.cast.ComponentClass.PriorityOverride;
import com.threerings.cast.ComponentClass;
+import com.threerings.util.DirectionUtil;
/**
* The class rule set is used to parse the attributes of a component class
@@ -53,6 +58,25 @@ public class ClassRuleSet extends RuleSetBase
// grab the attributes from the