Finished support for shadows. This is all some seriously complicated shit, but
the shadows sure look cool. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@3740 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -59,13 +59,15 @@ public class CharacterComponent implements Serializable
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the image frames for the specified action animation or null
|
||||
* if no animation for the specified action is available for this
|
||||
* component.
|
||||
* Returns the image frames for the specified action animation or null if
|
||||
* no animation for the specified action is available for this component.
|
||||
*
|
||||
* @param type null for the normal action frames or one of the custom
|
||||
* action sub-types: {@link StandardActions#SHADOW_TYPE}, etc.
|
||||
*/
|
||||
public ActionFrames getFrames (String action)
|
||||
public ActionFrames getFrames (String action, String type)
|
||||
{
|
||||
return _frameProvider.getFrames(this, action);
|
||||
return _frameProvider.getFrames(this, action, type);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,11 +21,14 @@
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import com.samskivert.util.LRUHashMap;
|
||||
import com.samskivert.util.RuntimeAdjust;
|
||||
import com.samskivert.util.StringUtil;
|
||||
import com.samskivert.util.Throttle;
|
||||
import com.samskivert.util.Tuple;
|
||||
|
||||
@@ -255,37 +258,99 @@ public class CharacterManager
|
||||
HashMap shadows = null;
|
||||
|
||||
// create colorized versions of all of the source action frames
|
||||
ComponentFrames[] sources = new ComponentFrames[ccount];
|
||||
ArrayList sources = new ArrayList(ccount);
|
||||
for (int ii = 0; ii < ccount; ii++) {
|
||||
sources[ii] = new ComponentFrames();
|
||||
sources[ii].ccomp = _crepo.getComponent(cids[ii]);
|
||||
ComponentFrames cframes = new ComponentFrames();
|
||||
sources.add(cframes);
|
||||
CharacterComponent ccomp =
|
||||
(cframes.ccomp = _crepo.getComponent(cids[ii]));
|
||||
|
||||
// load up the main component images
|
||||
ActionFrames source = sources[ii].ccomp.getFrames(action);
|
||||
ActionFrames source = ccomp.getFrames(action, null);
|
||||
if (source == null) {
|
||||
String errmsg = "Cannot composite action frames; no such " +
|
||||
"action for component [action=" + action +
|
||||
", desc=" + descrip + ", comp=" + sources[ii].ccomp + "]";
|
||||
", desc=" + descrip + ", comp=" + ccomp + "]";
|
||||
throw new RuntimeException(errmsg);
|
||||
}
|
||||
sources[ii].frames = (zations == null || zations[ii] == null) ?
|
||||
cframes.frames = (zations == null || zations[ii] == null) ?
|
||||
source : source.cloneColorized(zations[ii]);
|
||||
|
||||
// // load up the shadow images if they are needed
|
||||
// if (sources[ii].ccomp.componentClass.isShadowed()) {
|
||||
// sources[ii].shadowFrames = sources[ii].ccomp.getFrames(
|
||||
// action + StandardActions.SHADOW_SUFFIX);
|
||||
// if (sources[ii].shadowFrames == null) {
|
||||
// Log.warning("Missing shadow frames for action " +
|
||||
// "[action=" + action +
|
||||
// ", comp=" + sources[ii].ccomp + "].");
|
||||
// }
|
||||
// }
|
||||
// if this component has a shadow, make a note of it
|
||||
if (ccomp.componentClass.isShadowed()) {
|
||||
if (shadows == null) {
|
||||
shadows = new HashMap();
|
||||
}
|
||||
ArrayList shadlist = (ArrayList)
|
||||
shadows.get(ccomp.componentClass.shadow);
|
||||
if (shadlist == null) {
|
||||
shadows.put(ccomp.componentClass.shadow,
|
||||
shadlist = new ArrayList());
|
||||
}
|
||||
shadlist.add(ccomp);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (scf != null) {
|
||||
sources.add(scf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// use those to create an entity that will lazily composite things
|
||||
// together as they are needed
|
||||
return new CompositedActionFrames(_imgr, _frameCache, action, sources);
|
||||
ComponentFrames[] cfvec = (ComponentFrames[])sources.toArray(
|
||||
new ComponentFrames[sources.size()]);
|
||||
return new CompositedActionFrames(_imgr, _frameCache, action, cfvec);
|
||||
}
|
||||
|
||||
protected ComponentFrames compositeShadow (
|
||||
String action, String sclass, ArrayList scomps)
|
||||
{
|
||||
final ComponentClass cclass = _crepo.getComponentClass(sclass);
|
||||
if (cclass == null) {
|
||||
Log.warning("Components reference non-existent shadow layer class " +
|
||||
"[sclass=" + sclass +
|
||||
", scomps=" + StringUtil.toString(scomps) + "].");
|
||||
return null;
|
||||
}
|
||||
|
||||
ComponentFrames cframes = new ComponentFrames();
|
||||
// create a fake component for the shadow layer
|
||||
cframes.ccomp = new CharacterComponent(-1, "shadow", cclass, null);
|
||||
|
||||
ComponentFrames[] sources = new ComponentFrames[scomps.size()];
|
||||
for (int ii = 0, ll = scomps.size(); ii < ll; ii++) {
|
||||
sources[ii] = new ComponentFrames();
|
||||
sources[ii].ccomp = (CharacterComponent)scomps.get(ii);
|
||||
sources[ii].frames = sources[ii].ccomp.getFrames(
|
||||
action, StandardActions.SHADOW_TYPE);
|
||||
if (sources[ii].frames == null) {
|
||||
Log.warning("Missing shadow frames for action " +
|
||||
"[action=" + action +
|
||||
", comp=" + sources[ii].ccomp + "].");
|
||||
}
|
||||
}
|
||||
|
||||
// create custom action frames that use a special compositing
|
||||
// multi-frame image that does the necessary shadow magic
|
||||
cframes.frames = new CompositedActionFrames(
|
||||
_imgr, _frameCache, action, sources) {
|
||||
protected CompositedMultiFrameImage createFrames (int orient) {
|
||||
return new CompositedShadowImage(
|
||||
_imgr, _sources, _action, orient, cclass.shadowAlpha);
|
||||
}
|
||||
};
|
||||
|
||||
return cframes;
|
||||
}
|
||||
|
||||
/** The image manager with whom we interact. */
|
||||
|
||||
@@ -121,9 +121,9 @@ public class ComponentClass implements Serializable
|
||||
* class contributes a shadow. */
|
||||
public String shadow;
|
||||
|
||||
/** Null for a normal component, the color of the pre-composited shadow for
|
||||
* the special "shadow" component class. */
|
||||
public Color shadowColor;
|
||||
/** 1.0 for a normal component, the alpha value of the pre-composited
|
||||
* shadow for the special "shadow" component class. */
|
||||
public float shadowAlpha = 1.0f;
|
||||
|
||||
/**
|
||||
* Creates an uninitialized instance suitable for unserialization or
|
||||
@@ -183,7 +183,7 @@ public class ComponentClass implements Serializable
|
||||
*/
|
||||
public boolean isShadow ()
|
||||
{
|
||||
return (shadowColor != null);
|
||||
return (shadowAlpha != 1.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -217,9 +217,8 @@ public class ComponentClass implements Serializable
|
||||
if (colors != null) {
|
||||
buf.append(", colors=").append(StringUtil.toString(colors));
|
||||
}
|
||||
if (shadowColor != null) {
|
||||
buf.append(", shadow=");
|
||||
buf.append(StringUtil.toString(shadowColor.getComponents(null)));
|
||||
if (shadowAlpha != 1.0f) {
|
||||
buf.append(", shadow=").append(shadowAlpha);
|
||||
} else if (shadow != null) {
|
||||
buf.append(", shadow=").append(shadow);
|
||||
}
|
||||
|
||||
@@ -89,8 +89,7 @@ public class CompositedActionFrames
|
||||
CompositedMultiFrameImage cmfi =
|
||||
(CompositedMultiFrameImage)_frameCache.get(_key);
|
||||
if (cmfi == null) {
|
||||
cmfi = new CompositedMultiFrameImage(
|
||||
_imgr, _sources, _action, orient);
|
||||
cmfi = createFrames(orient);
|
||||
_frameCache.put(new CompositedFramesKey(orient), cmfi);
|
||||
}
|
||||
return cmfi;
|
||||
@@ -118,6 +117,14 @@ public class CompositedActionFrames
|
||||
throw new RuntimeException("What you talkin' about Willis?");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates our underlying multi-frame image for a particular orientation.
|
||||
*/
|
||||
protected CompositedMultiFrameImage createFrames (int orient)
|
||||
{
|
||||
return new CompositedMultiFrameImage(_imgr, _sources, _action, orient);
|
||||
}
|
||||
|
||||
/** Used to cache composited frames for a particular action and
|
||||
* orientation. */
|
||||
protected class CompositedFramesKey
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
//
|
||||
// $Id: CompositedMultiFrameImage.java 3310 2005-01-24 23:08:21Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.cast;
|
||||
|
||||
import java.awt.AlphaComposite;
|
||||
import java.awt.Composite;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
|
||||
import com.threerings.media.image.ImageManager;
|
||||
import com.threerings.media.image.Mirage;
|
||||
import com.threerings.media.image.VolatileMirage;
|
||||
|
||||
import com.threerings.cast.CompositedActionFrames.ComponentFrames;
|
||||
|
||||
/**
|
||||
* Used to composite the special shadow action frames for a particular
|
||||
* orientation of a {@link CompositedActionFrames}.
|
||||
*/
|
||||
public class CompositedShadowImage extends CompositedMultiFrameImage
|
||||
{
|
||||
public CompositedShadowImage (ImageManager imgr, ComponentFrames[] sources,
|
||||
String action, int orient, float shadowAlpha)
|
||||
{
|
||||
super(imgr, sources, action, orient);
|
||||
|
||||
// create the appropriate alpha composite for rendering the shadow
|
||||
_shadowAlpha = AlphaComposite.getInstance(
|
||||
AlphaComposite.SRC_OVER, shadowAlpha);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getWidth (int index) {
|
||||
return _sources[0].frames.getFrames(_orient).getWidth(index);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getHeight (int index) {
|
||||
return _sources[0].frames.getFrames(_orient).getHeight(index);
|
||||
}
|
||||
|
||||
public int getXOrigin (int index) {
|
||||
return _sources[0].frames.getXOrigin(_orient, index);
|
||||
}
|
||||
|
||||
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) {
|
||||
Composite ocomp = g.getComposite();
|
||||
g.setComposite(_shadowAlpha);
|
||||
_images[index].paint(g, x + _images[index].getX(),
|
||||
y + _images[index].getY());
|
||||
g.setComposite(ocomp);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public boolean hitTest (int index, int x, int y) {
|
||||
return _images[index].hitTest(x + _images[index].getX(),
|
||||
y + _images[index].getY());
|
||||
}
|
||||
|
||||
// documentation inherited from interface TrimmedMultiFrameImage
|
||||
public void getTrimmedBounds (int index, Rectangle bounds) {
|
||||
bounds.setBounds(_images[index].getX(), _images[index].getY(),
|
||||
_images[index].getWidth(), _images[index].getHeight());
|
||||
}
|
||||
|
||||
/** The alpha value at which we render our shadow. */
|
||||
protected AlphaComposite _shadowAlpha;
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: FrameProvider.java,v 1.3 2004/08/27 02:12:25 mdb Exp $
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
@@ -33,5 +33,5 @@ public interface FrameProvider
|
||||
* the specified action does not exist for the specified component.
|
||||
*/
|
||||
public ActionFrames getFrames (
|
||||
CharacterComponent component, String action);
|
||||
CharacterComponent component, String action, String type);
|
||||
}
|
||||
|
||||
@@ -34,9 +34,9 @@ public interface StandardActions
|
||||
/** The name of the standard walking action. */
|
||||
public static final String WALKING = "walking";
|
||||
|
||||
/** The suffix appended to an action to obtain its shadow image. */
|
||||
public static final String SHADOW_SUFFIX = "_shadow";
|
||||
/** A special action sub-type for shadow imagery. */
|
||||
public static final String SHADOW_TYPE = "shadow";
|
||||
|
||||
/** The suffix appended to an action to obtain its crop image. */
|
||||
public static final String CROP_SUFFIX = "_crop";
|
||||
/** A special action sub-type for crop imagery. */
|
||||
public static final String CROP_TYPE = "crop";
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: BundledComponentRepository.java,v 1.36 2004/10/28 17:49:01 mdb Exp $
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
@@ -297,7 +297,7 @@ public class BundledComponentRepository
|
||||
|
||||
// documentation inherited
|
||||
public ActionFrames getFrames (
|
||||
CharacterComponent component, String action)
|
||||
CharacterComponent component, String action, String type)
|
||||
{
|
||||
// obtain the action sequence definition for this action
|
||||
ActionSequence actseq = (ActionSequence)_actions.get(action);
|
||||
@@ -308,11 +308,17 @@ public class BundledComponentRepository
|
||||
return null;
|
||||
}
|
||||
|
||||
// determine our image path name
|
||||
String imgpath = action, dimgpath = ActionSequence.DEFAULT_SEQUENCE;
|
||||
if (type != null) {
|
||||
imgpath += "_" + type;
|
||||
dimgpath += "_" + type;
|
||||
}
|
||||
|
||||
String root = component.componentClass.name + "/" +
|
||||
component.name + "/";
|
||||
String cpath = root + action + BundleUtil.TILESET_EXTENSION;
|
||||
String dpath = root + ActionSequence.DEFAULT_SEQUENCE +
|
||||
BundleUtil.TILESET_EXTENSION;
|
||||
String cpath = root + imgpath + BundleUtil.TILESET_EXTENSION;
|
||||
String dpath = root + dimgpath + BundleUtil.TILESET_EXTENSION;
|
||||
|
||||
// look to see if this tileset is already cached (as the
|
||||
// custom action or the default action)
|
||||
@@ -342,7 +348,7 @@ public class BundledComponentRepository
|
||||
// if that failed too, we're hosed
|
||||
if (aset == null) {
|
||||
Log.warning("Unable to locate tileset for action '" +
|
||||
action + "' " + component + ".");
|
||||
imgpath + "' " + component + ".");
|
||||
if (_wipeOnFailure) {
|
||||
_bundle.wipeBundle(false);
|
||||
}
|
||||
@@ -354,7 +360,7 @@ public class BundledComponentRepository
|
||||
return new TileSetFrameImage(aset, actseq);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.warning("Error loading tileset for action '" + action +
|
||||
Log.warning("Error loading tileset for action '" + imgpath +
|
||||
"' " + component + ".");
|
||||
Log.logStackTrace(e);
|
||||
return null;
|
||||
|
||||
@@ -619,5 +619,6 @@ public class ComponentBundlerTask extends Task
|
||||
|
||||
/** Used to process auxilliary tilesets. */
|
||||
protected static final String[] AUX_EXTS = {
|
||||
StandardActions.SHADOW_SUFFIX, StandardActions.CROP_SUFFIX };
|
||||
"_" + StandardActions.SHADOW_TYPE,
|
||||
"_" + StandardActions.CROP_TYPE };
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: VolatileMirage.java,v 1.6 2004/08/27 02:12:38 mdb Exp $
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
@@ -83,6 +83,26 @@ public abstract class VolatileMirage implements Mirage
|
||||
// TODO: note number of attempted renders for performance
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the x offset into our source image, which is generally zero but
|
||||
* may be non-zero for a mirage that obtains its data from a region of its
|
||||
* source image.
|
||||
*/
|
||||
public int getX ()
|
||||
{
|
||||
return _bounds.x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the y offset into our source image, which is generally zero but
|
||||
* may be non-zero for a mirage that obtains its data from a region of its
|
||||
* source image.
|
||||
*/
|
||||
public int getY ()
|
||||
{
|
||||
return _bounds.y;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public int getWidth ()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user