Added the ability to add translations on a per-component-instance basis.

git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@160 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
Andrzej Kapolka
2007-02-28 21:51:47 +00:00
parent dc4b721198
commit 6e7b1741ff
7 changed files with 170 additions and 83 deletions
@@ -64,4 +64,10 @@ public interface ActionFrames
* colorizations applied to the frame images.
*/
public ActionFrames cloneColorized (Colorization[] zations);
/**
* Creates a clone of these action frames which will have the supplied
* translation applied to the frame images.
*/
public ActionFrames cloneTranslated (int dx, int dy);
}
@@ -21,6 +21,7 @@
package com.threerings.cast;
import java.awt.Point;
import java.util.Arrays;
import com.samskivert.util.StringUtil;
import com.threerings.media.image.Colorization;
@@ -75,6 +76,23 @@ public class CharacterDescriptor
_zations = zations;
}
/**
* Returns the array of translations to be applied to the components
* when compositing action images.
*/
public Point[] getTranslations ()
{
return _xlations;
}
/**
* Updates the translations to be used by this character descriptor.
*/
public void setTranslations (Point[] xlations)
{
_xlations = xlations;
}
/**
* Compute a sensible hashcode for this object.
*/
@@ -124,7 +142,7 @@ public class CharacterDescriptor
}
}
return true;
return Arrays.equals(_xlations, odesc._xlations);
}
/**
@@ -141,4 +159,7 @@ public class CharacterDescriptor
/** The colorizations to apply when compositing this character. */
protected Colorization[][] _zations;
/** The translations to apply when compositing this character. */
protected Point[] _xlations;
}
@@ -21,6 +21,8 @@
package com.threerings.cast;
import java.awt.Point;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@@ -94,7 +96,7 @@ public class CharacterManager
{
// sanity check
if (!CharacterSprite.class.isAssignableFrom(charClass)) {
String errmsg = "Requested to use character sprite class that " +
String errmsg = "Requested to use character sprite class that " +
"does not derive from CharacterSprite " +
"[class=" + charClass.getName() + "].";
throw new IllegalArgumentException(errmsg);
@@ -250,25 +252,25 @@ public class CharacterManager
int[] cids = descrip.getComponentIds();
int ccount = cids.length;
Colorization[][] zations = descrip.getColorizations();
Point[] xlations = descrip.getTranslations();
Log.debug("Compositing action [action=" + action +
", descrip=" + descrip + "].");
// this will be used to construct any shadow layers
HashMap shadows = null;
HashMap<String, ArrayList<TranslatedComponent>> shadows = null;
// maps components by class name for masks
HashMap ccomps = new HashMap();
HashMap<String, ArrayList<TranslatedComponent>> ccomps =
new HashMap<String, ArrayList<TranslatedComponent>>();
// create colorized versions of all of the source action frames
ArrayList sources = new ArrayList(ccount);
ArrayList<ComponentFrames> sources = new ArrayList<ComponentFrames>(ccount);
for (int ii = 0; ii < ccount; ii++) {
ComponentFrames cframes = new ComponentFrames();
sources.add(cframes);
CharacterComponent ccomp =
(cframes.ccomp = _crepo.getComponent(cids[ii]));
ccomps.put(ccomp.componentClass.name, ccomp);
CharacterComponent ccomp = (cframes.ccomp = _crepo.getComponent(cids[ii]));
// load up the main component images
ActionFrames source = ccomp.getFrames(action, null);
if (source == null) {
@@ -277,32 +279,39 @@ public class CharacterManager
", desc=" + descrip + ", comp=" + ccomp + "]";
throw new RuntimeException(errmsg);
}
cframes.frames = (zations == null || zations[ii] == null) ?
source = (zations == null || zations[ii] == null) ?
source : source.cloneColorized(zations[ii]);
Point xlation = (xlations == null) ? null : xlations[ii];
cframes.frames = (xlation == null) ?
source : source.cloneTranslated(xlation.x, xlation.y);
// store the component with its translation under its class for masking
TranslatedComponent tcomp = new TranslatedComponent(ccomp, xlation);
ArrayList<TranslatedComponent> tcomps = ccomps.get(ccomp.componentClass.name);
if (tcomps == null) {
ccomps.put(ccomp.componentClass.name,
tcomps = new ArrayList<TranslatedComponent>());
}
tcomps.add(tcomp);
// if this component has a shadow, make a note of it
if (ccomp.componentClass.isShadowed()) {
if (shadows == null) {
shadows = new HashMap();
shadows = new HashMap<String, ArrayList<TranslatedComponent>>();
}
ArrayList shadlist = (ArrayList)
shadows.get(ccomp.componentClass.shadow);
ArrayList<TranslatedComponent> shadlist = shadows.get(ccomp.componentClass.shadow);
if (shadlist == null) {
shadows.put(ccomp.componentClass.shadow,
shadlist = new ArrayList());
shadlist = new ArrayList<TranslatedComponent>());
}
shadlist.add(ccomp);
shadlist.add(tcomp);
}
}
// now create any necessary shadow layers
if (shadows != null) {
Iterator iter = shadows.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry)iter.next();
String sclass = (String)entry.getKey();
ArrayList scomps = (ArrayList)entry.getValue();
ComponentFrames scf = compositeShadow(action, sclass, scomps);
for (Map.Entry<String, ArrayList<TranslatedComponent>> entry : shadows.entrySet()) {
ComponentFrames scf = compositeShadow(action, entry.getKey(), entry.getValue());
if (scf != null) {
sources.add(scf);
}
@@ -310,46 +319,21 @@ public class CharacterManager
}
// add any necessary masks
for (int ii = 0, nn = sources.size(); ii < nn; ii++) {
ComponentFrames cframes = (ComponentFrames)sources.get(ii);
CharacterComponent mcomp = (CharacterComponent)ccomps.get(
cframes.ccomp.componentClass.mask);
if (mcomp != null) {
cframes.frames = compositeMask(action, cframes.ccomp,
cframes.frames, mcomp);
for (ComponentFrames cframes : sources) {
ArrayList<TranslatedComponent> mcomps = ccomps.get(cframes.ccomp.componentClass.mask);
if (mcomps != null) {
cframes.frames = compositeMask(action, cframes.ccomp, cframes.frames, mcomps);
}
}
// use those to create an entity that will lazily composite things
// together as they are needed
ComponentFrames[] cfvec = (ComponentFrames[])sources.toArray(
new ComponentFrames[sources.size()]);
ComponentFrames[] cfvec = sources.toArray(new ComponentFrames[sources.size()]);
return new CompositedActionFrames(_imgr, _frameCache, action, cfvec);
}
protected ActionFrames compositeMask (
String action, CharacterComponent ccomp, ActionFrames cframes,
CharacterComponent mcomp)
{
ActionFrames mframes = mcomp.getFrames(action,
StandardActions.CROP_TYPE);
if (mframes == null) {
return cframes;
}
return new CompositedActionFrames(
_imgr, _frameCache, action, new ComponentFrames[] {
new ComponentFrames(ccomp, cframes),
new ComponentFrames(mcomp, mframes) }) {
protected CompositedMultiFrameImage createFrames (int orient) {
return new CompositedMaskedImage(
_imgr, _sources, _action, orient);
}
};
}
protected ComponentFrames compositeShadow (
String action, String sclass, ArrayList scomps)
String action, String sclass, ArrayList<TranslatedComponent> scomps)
{
final ComponentClass cclass = _crepo.getComponentClass(sclass);
if (cclass == null) {
@@ -363,12 +347,11 @@ public class CharacterManager
// create a fake component for the shadow layer
cframes.ccomp = new CharacterComponent(-1, "shadow", cclass, null);
ArrayList sources = new ArrayList();
for (int ii = 0, ll = scomps.size(); ii < ll; ii++) {
ArrayList<ComponentFrames> sources = new ArrayList<ComponentFrames>();
for (TranslatedComponent scomp : scomps) {
ComponentFrames source = new ComponentFrames();
source.ccomp = (CharacterComponent)scomps.get(ii);
source.frames = source.ccomp.getFrames(
action, StandardActions.SHADOW_TYPE);
source.ccomp = scomp.ccomp;
source.frames = scomp.getFrames(action, StandardActions.SHADOW_TYPE);
if (source.frames == null) {
// skip this shadow component
continue;
@@ -383,10 +366,8 @@ public class CharacterManager
// create custom action frames that use a special compositing
// multi-frame image that does the necessary shadow magic
ComponentFrames[] svec = (ComponentFrames[])
sources.toArray(new ComponentFrames[sources.size()]);
cframes.frames = new CompositedActionFrames(
_imgr, _frameCache, action, svec) {
ComponentFrames[] svec = sources.toArray(new ComponentFrames[sources.size()]);
cframes.frames = new CompositedActionFrames(_imgr, _frameCache, action, svec) {
protected CompositedMultiFrameImage createFrames (int orient) {
return new CompositedShadowImage(
_imgr, _sources, _action, orient, cclass.shadowAlpha);
@@ -396,6 +377,49 @@ public class CharacterManager
return cframes;
}
protected ActionFrames compositeMask (
String action, CharacterComponent ccomp, ActionFrames cframes,
ArrayList<TranslatedComponent> mcomps)
{
ArrayList<ComponentFrames> sources = new ArrayList<ComponentFrames>();
sources.add(new ComponentFrames(ccomp, cframes));
for (TranslatedComponent mcomp : mcomps) {
ActionFrames mframes = mcomp.getFrames(action, StandardActions.CROP_TYPE);
if (mframes != null) {
sources.add(new ComponentFrames(mcomp.ccomp, mframes));
}
}
if (sources.size() == 1) {
return cframes;
}
ComponentFrames[] mvec = sources.toArray(new ComponentFrames[sources.size()]);
return new CompositedActionFrames(_imgr, _frameCache, action, mvec) {
protected CompositedMultiFrameImage createFrames (int orient) {
return new CompositedMaskedImage(_imgr, _sources, _action, orient);
}
};
}
/** Combines a component with an optional translation for shadowing or masking. */
protected static class TranslatedComponent
{
public CharacterComponent ccomp;
public Point xlation;
public TranslatedComponent (CharacterComponent ccomp, Point xlation)
{
this.ccomp = ccomp;
this.xlation = xlation;
}
public ActionFrames getFrames (String action, String type)
{
ActionFrames frames = ccomp.getFrames(action, type);
return (frames == null || xlation == null) ?
frames : frames.cloneTranslated(xlation.x, xlation.y);
}
}
/** The image manager with whom we interact. */
protected ImageManager _imgr;
@@ -129,6 +129,9 @@ public class ComponentClass implements Serializable
* shadow for the special "shadow" component class. */
public float shadowAlpha = 1.0f;
/** Whether or not components of this class will have translations applied. */
public boolean translate;
/**
* Creates an uninitialized instance suitable for unserialization or
* population during XML parsing.
@@ -237,5 +240,5 @@ public class ComponentClass implements Serializable
/** Increase this value when object's serialized state is impacted by
* a class change (modification of fields, inheritance). */
private static final long serialVersionUID = 3;
private static final long serialVersionUID = 4;
}
@@ -48,13 +48,13 @@ public class CompositedActionFrames
public ComponentFrames () {
}
public ComponentFrames (
CharacterComponent ccomp, ActionFrames frames) {
this.ccomp = ccomp;
this.frames = frames;
}
public String toString () {
return ccomp + ":" + frames;
}
@@ -127,6 +127,17 @@ public class CompositedActionFrames
throw new RuntimeException("What you talkin' about Willis?");
}
// documentation inherited from interface
public ActionFrames cloneTranslated (int dx, int dy)
{
ComponentFrames[] tsources = new ComponentFrames[_sources.length];
for (int ii = 0; ii < _sources.length; ii++) {
tsources[ii] = new ComponentFrames(
_sources[ii].ccomp, _sources[ii].frames.cloneTranslated(dx, dy));
}
return new CompositedActionFrames(_imgr, _frameCache, _action, tsources);
}
/**
* Creates our underlying multi-frame image for a particular orientation.
*/
@@ -45,7 +45,7 @@ public class CompositedMaskedImage extends CompositedMultiFrameImage
{
super(imgr, sources, action, orient);
}
// documentation inherited from interface
public int getWidth (int index) {
return _sources[0].frames.getFrames(_orient).getWidth(index);
@@ -63,7 +63,7 @@ public class CompositedMaskedImage extends CompositedMultiFrameImage
public int getYOrigin (int index) {
return _sources[0].frames.getYOrigin(_orient, index);
}
// documentation inherited from interface
public void paintFrame (Graphics2D g, int index, int x, int y) {
_images[index].paint(g, x + _images[index].getX(),
@@ -85,18 +85,18 @@ public class CompositedMaskedImage extends CompositedMultiFrameImage
// documentation inherited
protected CompositedMirage createCompositedMirage (int index)
{
return new MaskedMirage(index);
return new MaskedMirage(index);
}
/**
* Combines the image in the first source with the mask in the second. */
* Combines the image in the first source with the masks in the rest. */
protected class MaskedMirage extends CompositedMirage
{
public MaskedMirage (int index)
{
super(index);
}
// documentation inherited
protected Rectangle combineBounds (Rectangle bounds, Rectangle tbounds)
{
@@ -107,19 +107,20 @@ public class CompositedMaskedImage extends CompositedMultiFrameImage
}
return bounds;
}
// documentation inherited
protected void refreshVolatileImage ()
{
Graphics2D g = (Graphics2D)_image.getGraphics();
try {
TrimmedMultiFrameImage source =
_sources[0].frames.getFrames(_orient),
mask = _sources[1].frames.getFrames(_orient);
TrimmedMultiFrameImage source = _sources[0].frames.getFrames(_orient);
source.paintFrame(g, _index, -_bounds.x, -_bounds.y);
g.setComposite(AlphaComposite.DstIn);
mask.paintFrame(g, _index, -_bounds.x, -_bounds.y);
for (int ii = 1; ii < _sources.length; ii++) {
TrimmedMultiFrameImage mask = _sources[ii].frames.getFrames(_orient);
mask.paintFrame(g, _index, -_bounds.x, -_bounds.y);
}
} finally {
// clean up after ourselves
if (g != null) {
@@ -345,9 +345,9 @@ public class BundledComponentRepository
// if that failed too, we're hosed
if (aset == null) {
// if this is a shadow or crop image, no need to freak out
// if this is a shadow or crop image, no need to freak out
// as they are optional
if (!StandardActions.CROP_TYPE.equals(type) &&
if (!StandardActions.CROP_TYPE.equals(type) &&
!StandardActions.SHADOW_TYPE.equals(type)) {
Log.warning("Unable to locate tileset for action '" +
imgpath + "' " + component + ".");
@@ -388,9 +388,20 @@ public class BundledComponentRepository
* for the specified orientation.
*/
public TileSetFrameImage (TileSet set, ActionSequence actseq)
{
this(set, actseq, 0, 0);
}
/**
* Constructs a tileset frame image with the specified tileset and
* for the specified orientation, with an optional translation.
*/
public TileSetFrameImage (TileSet set, ActionSequence actseq, int dx, int dy)
{
_set = set;
_actseq = actseq;
_dx = dx;
_dy = dy;
// compute these now to avoid pointless recomputation later
_ocount = actseq.orients.length;
@@ -438,7 +449,7 @@ public class BundledComponentRepository
{
Tile tile = getTile(orient, index);
if (tile != null) {
tile.paint(g, x, y);
tile.paint(g, x + _dx, y + _dy);
}
}
@@ -446,7 +457,7 @@ public class BundledComponentRepository
public boolean hitTest (int index, int x, int y)
{
Tile tile = getTile(orient, index);
return (tile != null) ? tile.hitTest(x, y) : false;
return (tile != null) ? tile.hitTest(x + _dx, y + _dy) : false;
}
// documentation inherited from interface
@@ -459,6 +470,7 @@ public class BundledComponentRepository
bounds.setBounds(
0, 0, tile.getWidth(), tile.getHeight());
}
bounds.translate(_dx, _dy);
}
};
}
@@ -481,6 +493,12 @@ public class BundledComponentRepository
return new TileSetFrameImage(_set.clone(zations), _actseq);
}
// documentation inherited from interface
public ActionFrames cloneTranslated (int dx, int dy)
{
return new TileSetFrameImage(_set, _actseq, dx, dy);
}
/**
* Fetches the requested tile.
*/
@@ -496,6 +514,9 @@ public class BundledComponentRepository
/** The action sequence for which we're providing frame images. */
protected ActionSequence _actseq;
/** A translation to apply to the images. */
protected int _dx, _dy;
/** Frame and orientation counts. */
protected int _fcount, _ocount;