Finished up custom render order support.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@2070 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -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. */
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
* <p> 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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 <class> tag
|
||||
digester.addRule(_prefix + CLASS_PATH,
|
||||
new SetPropertyFieldsRule(digester));
|
||||
|
||||
// parse render priority overrides
|
||||
String opath = _prefix + CLASS_PATH + "/override";
|
||||
digester.addObjectCreate(opath, PriorityOverride.class.getName());
|
||||
SetPropertyFieldsRule rule = new SetPropertyFieldsRule(digester);
|
||||
rule.addFieldParser("orients", new FieldParser() {
|
||||
public Object parse (String text) {
|
||||
String[] orients = StringUtil.parseStringArray(text);
|
||||
ArrayIntSet oset = new ArrayIntSet();
|
||||
for (int ii = 0; ii < orients.length; ii++) {
|
||||
oset.add(DirectionUtil.fromShortString(orients[ii]));
|
||||
}
|
||||
return oset;
|
||||
}
|
||||
});
|
||||
digester.addRule(opath, rule);
|
||||
|
||||
digester.addSetNext(opath, "addPriorityOverride",
|
||||
PriorityOverride.class.getName());
|
||||
}
|
||||
|
||||
/** The prefix at which me match our component classes. */
|
||||
|
||||
Reference in New Issue
Block a user