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:
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user