Behold, Nenya, Ring of Water and repository for our media and animation related

goodies, both Java 2D and LWJGL/JME 3D.


git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@1 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
Michael Bayne
2006-06-23 18:07:28 +00:00
commit c2117ee86d
570 changed files with 61913 additions and 0 deletions
@@ -0,0 +1,160 @@
//
// $Id: FringeConfiguration.java 3403 2005-03-14 23:58:02Z 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.jme.tile;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import com.samskivert.util.StringUtil;
import com.threerings.jme.Log;
/**
* Used to manage data about which tiles fringe on which others and how
* they fringe.
*/
public class FringeConfiguration implements Serializable
{
/** Contains information on a type of tile and all of the fringe
* records associated with it. */
public static class TileRecord implements Serializable
{
/** The type of tile to which this applies. */
public String type;
/** The fringe priority of this type. */
public int priority;
/** A list of the fringe records that can be used for fringing. */
public ArrayList fringes = new ArrayList();
/** Used when parsing from an XML definition. */
public void addFringe (FringeRecord record)
{
if (record.isValid()) {
fringes.add(record);
} else {
Log.warning("Not adding invalid fringe record [tile=" + this +
", fringe=" + record + "].");
}
}
/** Did everything parse well? */
public boolean isValid ()
{
return ((type != null) && (priority > 0));
}
/** Generates a string representation of this instance. */
public String toString ()
{
return "[type=" + type + ", priority=" + priority +
", fringes=" + StringUtil.toString(fringes) + "]";
}
/** Increase this value when object's serialized state is impacted
* by a class change (modification of fields, inheritance). */
private static final long serialVersionUID = 1;
}
/** Used to parse the type fringe definitions. */
public static class FringeRecord implements Serializable
{
/** The name of the fringe tileset image. */
public String name;
/** Is this a mask? */
public boolean mask;
/** Did everything parse well? */
public boolean isValid ()
{
return (name != null);
}
/** Generates a string representation of this instance. */
public String toString ()
{
return "[name=" + name + ", mask=" + mask + "]";
}
/** Increase this value when object's serialized state is impacted
* by a class change (modification of fields, inheritance). */
private static final long serialVersionUID = 1;
}
/**
* Adds a parsed fringe record to this instance. This is used when
* parsing the fringe records from xml.
*/
public void addTileRecord (TileRecord record)
{
if (record.isValid()) {
_trecs.put(record.type, record);
} else {
Log.warning("Refusing to add invalid tile record " +
"[tile=" + record + "].");
}
}
/**
* If the first type fringes upon the second, return the fringe
* priority of the first type, otherwise return -1.
*/
public int fringesOn (String fringer, String fringed)
{
TileRecord f1 = (TileRecord)_trecs.get(fringer);
// we better have a fringe record for the fringer
if (null != f1) {
// it had better have some types defined
if (f1.fringes.size() > 0) {
TileRecord f2 = (TileRecord)_trecs.get(fringed);
// and we only fringe if fringed doesn't have a fringe
// record or has a lower priority
if ((null == f2) || (f1.priority > f2.priority)) {
return f1.priority;
}
}
}
return -1;
}
/**
* Get a random FringeRecord from amongst the ones listed for the
* specified type.
*/
public FringeRecord getFringe (String type, int hashValue)
{
TileRecord t = (TileRecord)_trecs.get(type);
return (FringeRecord)t.fringes.get(hashValue % t.fringes.size());
}
/** The mapping from tile type to tile record. */
protected HashMap _trecs = new HashMap();
/** Increase this value when object's serialized state is impacted by
* a class change (modification of fields, inheritance). */
private static final long serialVersionUID = 1;
}
@@ -0,0 +1,404 @@
//
// $Id: TileFringer.java 3177 2004-10-28 17:49:02Z 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.jme.tile;
import java.awt.Graphics2D;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashMap;
import com.samskivert.util.QuickSort;
import com.threerings.media.image.ImageUtil;
import com.threerings.media.tile.TileUtil;
import com.threerings.jme.Log;
/**
* Computes fringe tile images according to the rules in an associated
* fringe configuration.
*/
public class TileFringer
{
public static interface TileSource
{
/** Returns the type of tile at the specified coordinates or -1 if
* there is no tile at this coordinate. */
public String getTileType (int x, int y);
/** Returns the tile type to use when a coordinate has no tile. */
public String getDefaultType ();
}
public static interface ImageSource extends ImageUtil.ImageCreator
{
/** Creates a blank image into which various fringe images will be
* composited. */
public BufferedImage createImage (
int width, int height, int transparency);
/** Returns the source image for a tile of the specified type.
* This can be randomly selected and change from call to call. */
public BufferedImage getTileSource (String type);
/** Returns the named fringe source image (one long strip). */
public BufferedImage getFringeSource (String name);
}
/**
* Creates a fringer that will fringe according to the rules in the
* supplied configuration.
*/
public TileFringer (FringeConfiguration config, ImageSource isrc)
{
_config = config;
_isrc = isrc;
}
/**
* Computes, creates and returns the base tile with the appropriate
* fringe imagery applied to it for the specified location.
*
* @param masks used to cache intermediate images of tiles cut out
* using a fringe mask.
*/
public BufferedImage getFringeTile (
TileSource tiles, int col, int row, HashMap masks)
{
// get the type of the tile we are considering
String baseType = tiles.getTileType(col, row);
if (baseType == null) {
baseType = tiles.getDefaultType();
}
// start with an empty fringer list
FringerRec fringers = null;
// walk through our influence tiles
for (int y = row - 1, maxy = row + 2; y < maxy; y++) {
for (int x = col - 1, maxx = col + 2; x < maxx; x++) {
// we sensibly do not consider ourselves
if ((x == col) && (y == row)) {
continue;
}
// determine the type of our fringing neighbor
String fringerType = tiles.getTileType(x, y);
if (fringerType == null) {
fringerType = tiles.getDefaultType();
}
// determine if it fringes on our tile
int pri = _config.fringesOn(fringerType, baseType);
if (pri == -1) {
continue;
}
FringerRec fringer = (fringers == null) ?
null : fringers.find(fringerType);
if (fringer == null) {
fringer = fringers =
new FringerRec(fringerType, pri, fringers);
}
// now turn on the appropriate fringebits
int dy = y - row, dx = x - col;
fringer.bits |= FLAGMATRIX[dy*3+dx+4];
}
}
// if nothing fringed, we're done
if (fringers == null) {
return null;
}
// otherwise compose a fringe tile from the specified fringes
return composeFringeTile(
baseType, fringers.toArray(), masks, TileUtil.getTileHash(col, row));
}
/**
* Compose a fringe tile out of the various fringe images needed.
*/
protected BufferedImage composeFringeTile (
String baseType, FringerRec[] fringers, HashMap masks, int hashValue)
{
// sort the array so that higher priority fringers get drawn first
QuickSort.sort(fringers);
BufferedImage source = _isrc.getTileSource(baseType);
if (source == null) {
Log.warning("Missing source tile [type=" + baseType + "].");
return null;
}
BufferedImage ftimg = _isrc.createImage(
source.getWidth(), source.getHeight(), Transparency.OPAQUE);
Graphics2D gfx = (Graphics2D)ftimg.getGraphics();
try {
// start with the base tile image
gfx.drawImage(source, 0, 0, null);
// and stamp the fringers on top of it
for (int ii = 0; ii < fringers.length; ii++) {
int[] indexes = getFringeIndexes(fringers[ii].bits);
for (int jj = 0; jj < indexes.length; jj++) {
stampTileImage(gfx, fringers[ii].fringerType,
indexes[jj], masks, hashValue);
}
}
} finally {
gfx.dispose();
}
return ftimg;
}
/**
* Looks up or creates the appropriate fringe mask and draws it into
* the supplied graphics context.
*/
protected void stampTileImage (
Graphics2D gfx, String fringerType, int index,
HashMap masks, int hashValue)
{
FringeConfiguration.FringeRecord frec =
_config.getFringe(fringerType, hashValue);
BufferedImage fsimg = (frec == null) ? null :
_isrc.getFringeSource(frec.name);
if (fsimg == null) {
Log.warning("Missing fringe source image [type=" + fringerType +
", hash=" + hashValue + ", frec=" + frec + "].");
return;
}
if (frec.mask) {
// it's a mask; look for it in the cache
String maskkey = fringerType + ":" + frec.name + ":" + index;
BufferedImage mimg = (BufferedImage)masks.get(maskkey);
if (mimg == null) {
BufferedImage fsrc = getSubimage(fsimg, index);
BufferedImage bsrc = _isrc.getTileSource(fringerType);
mimg = ImageUtil.composeMaskedImage(_isrc, fsrc, bsrc);
masks.put(maskkey, mimg);
}
gfx.drawImage(mimg, 0, 0, null);
} else {
// this is a non-mask image so just use the data from the
// fringe source image directly
gfx.drawImage(getSubimage(fsimg, index), 0, 0, null);
}
}
/**
* Returns the <code>index</code>th tile image from the supplied
* source image. The source image is assumed to be a single strip of
* tile images, each with equal width and height.
*/
protected BufferedImage getSubimage (BufferedImage source, int index)
{
int size = source.getHeight(), x = size * index;
return source.getSubimage(x, 0, size, size);
}
/**
* Get the fringe index specified by the fringebits. If no index
* is available, try breaking down the bits into contiguous regions of
* bits and look for indexes for those.
*/
protected int[] getFringeIndexes (int bits)
{
int index = BITS_TO_INDEX[bits];
if (index != -1) {
int[] ret = new int[1];
ret[0] = index;
return ret;
}
// otherwise, split the bits into contiguous components
// look for a zero and start our first split
int start = 0;
while ((((1 << start) & bits) != 0) && (start < NUM_FRINGEBITS)) {
start++;
}
if (start == NUM_FRINGEBITS) {
// we never found an empty fringebit, and since index (above)
// was already -1, we have no fringe tile for these bits.. sad.
return new int[0];
}
ArrayList indexes = new ArrayList();
int weebits = 0;
for (int ii=(start + 1) % NUM_FRINGEBITS; ii != start;
ii = (ii + 1) % NUM_FRINGEBITS) {
if (((1 << ii) & bits) != 0) {
weebits |= (1 << ii);
} else if (weebits != 0) {
index = BITS_TO_INDEX[weebits];
if (index != -1) {
indexes.add(Integer.valueOf(index));
}
weebits = 0;
}
}
if (weebits != 0) {
index = BITS_TO_INDEX[weebits];
if (index != -1) {
indexes.add(Integer.valueOf(index));
}
}
int[] ret = new int[indexes.size()];
for (int ii=0; ii < ret.length; ii++) {
ret[ii] = ((Integer) indexes.get(ii)).intValue();
}
return ret;
}
/** A record for holding information about a particular fringe as
* we're computing what it will look like. */
protected static class FringerRec implements Comparable
{
public String fringerType;
public int priority;
public int bits;
public FringerRec next;
public FringerRec (String type, int pri, FringerRec next) {
fringerType = type;
priority = pri;
this.next = next;
}
public FringerRec find (String type)
{
if (fringerType.equals(type)) {
return this;
} else if (next != null) {
return next.find(type);
} else {
return null;
}
}
public FringerRec[] toArray ()
{
return toArray(0);
}
public int compareTo (Object o) {
return priority - ((FringerRec) o).priority;
}
public String toString () {
return "[type=" + fringerType + ", pri=" + priority +
", bits=" + Integer.toString(bits, 16) + "]";
}
protected FringerRec[] toArray (int index)
{
FringerRec[] array;
if (next == null) {
array = new FringerRec[index+1];
} else {
array = next.toArray(index+1);
}
array[index] = this;
return array;
}
}
protected static final int NORTH = 1 << 0;
protected static final int NORTHEAST = 1 << 1;
protected static final int EAST = 1 << 2;
protected static final int SOUTHEAST = 1 << 3;
protected static final int SOUTH = 1 << 4;
protected static final int SOUTHWEST = 1 << 5;
protected static final int WEST = 1 << 6;
protected static final int NORTHWEST = 1 << 7;
protected static final int NUM_FRINGEBITS = 8;
/** A matrix mapping adjacent tiles to which fringe bits they affect.
* (x and y are offset by +1, since we can't have -1 as an array index).
* These are "upside down" thanks to OpenGL. */
protected static final int[] FLAGMATRIX = {
SOUTHWEST, (SOUTHEAST | SOUTH | SOUTHWEST), SOUTHEAST,
(NORTHWEST | WEST | SOUTHWEST), 0, (NORTHEAST | EAST | SOUTHEAST),
NORTHWEST, (NORTHWEST | NORTH | NORTHEAST), NORTHEAST,
};
/** The fringe tiles we use. These are the 17 possible tiles made up
* of continuous fringebits sections. */
protected static final int[] FRINGETILES = {
SOUTHEAST,
SOUTHWEST | SOUTH | SOUTHEAST,
SOUTHWEST,
NORTHEAST | EAST | SOUTHEAST,
NORTHWEST | WEST | SOUTHWEST,
NORTHEAST,
NORTHWEST | NORTH | NORTHEAST,
NORTHWEST,
SOUTHWEST | WEST | NORTHWEST | NORTH | NORTHEAST,
NORTHWEST | NORTH | NORTHEAST | EAST | SOUTHEAST,
NORTHWEST | WEST | SOUTHWEST | SOUTH | SOUTHEAST,
SOUTHWEST | SOUTH | SOUTHEAST | EAST | NORTHEAST,
NORTHEAST | NORTH | NORTHWEST | WEST | SOUTHWEST | SOUTH | SOUTHEAST,
SOUTHEAST | EAST | NORTHEAST | NORTH | NORTHWEST | WEST | SOUTHWEST,
SOUTHWEST | SOUTH | SOUTHEAST | EAST | NORTHEAST | NORTH | NORTHWEST,
NORTHWEST | WEST | SOUTHWEST | SOUTH | SOUTHEAST | EAST | NORTHEAST,
// all the directions!
NORTH | NORTHEAST | EAST | SOUTHEAST | SOUTH | SOUTHWEST |
WEST | NORTHWEST
};
/** A reverse map of the {@link #FRINGETILES} array, for quickly
* looking up which tile we want. */
protected static final int[] BITS_TO_INDEX;
// Construct the BITS_TO_INDEX array.
static {
int num = (1 << NUM_FRINGEBITS);
BITS_TO_INDEX = new int[num];
// first clear everything to -1 (meaning there is no tile defined)
for (int ii=0; ii < num; ii++) {
BITS_TO_INDEX[ii] = -1;
}
// then fill in with the defined tiles.
for (int ii=0; ii < FRINGETILES.length; ii++) {
BITS_TO_INDEX[FRINGETILES[ii]] = ii;
}
}
protected ImageSource _isrc;
protected FringeConfiguration _config;
}
@@ -0,0 +1,76 @@
//
// $Id: CompileFringeConfigurationTask.java 3099 2004-08-27 02:21:06Z 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.jme.tile.tools;
import java.io.File;
import java.io.Serializable;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import com.samskivert.io.PersistenceException;
import com.threerings.util.CompiledConfig;
import com.threerings.jme.tile.tools.xml.FringeConfigurationParser;
/**
* Compiles a fringe configuration from XML format into binary format.
*/
public class CompileFringeConfigurationTask extends Task
{
public void setConfig (File config)
{
_config = config;
}
public void setTarget (File target)
{
_target = target;
}
public void execute () throws BuildException
{
// make sure the config file exists
if (!_config.exists()) {
throw new BuildException(
"Fringe configuration file not found [path=" + _config + "].");
}
FringeConfigurationParser parser = new FringeConfigurationParser();
Serializable config;
try {
config = parser.parseConfig(_config);
} catch (Exception e) {
throw new BuildException("Failure parsing fringe config", e);
}
try {
// and write it on out
CompiledConfig.saveConfig(_target, config);
} catch (Exception e) {
throw new BuildException("Failure writing serialized config", e);
}
}
protected File _config;
protected File _target;
}
@@ -0,0 +1,84 @@
//
// $Id: FringeConfigurationParser.java 3254 2004-11-30 20:03:47Z 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.jme.tile.tools.xml;
import java.io.Serializable;
import org.apache.commons.digester.Digester;
import com.samskivert.util.StringUtil;
import com.samskivert.xml.SetPropertyFieldsRule;
import com.threerings.tools.xml.CompiledConfigParser;
import com.threerings.jme.Log;
import com.threerings.jme.tile.FringeConfiguration.TileRecord;
import com.threerings.jme.tile.FringeConfiguration.FringeRecord;
import com.threerings.jme.tile.FringeConfiguration;
/**
* Parses fringe config definitions, which look like so (with angle
* brackets instead of square):
* <pre>
* [fringe]
* [tile type="water" priority="100"]
* [fringe name="water_fringe_1"/]
* [fringe name="water_fringe_2"/]
* [fringe name="water_fringe_3"/]
* [/tile]
* [tile type="dirt" priority="10"]
* [fringe name="dirt_fringe_1" mask="true"/]
* [/tile]
* [tile type="cobble" priority="100"]
* [fringe name="cobble_fringe_1" mask="false"/]
* [/tile]
* [/fringe]
* </pre>
*/
public class FringeConfigurationParser extends CompiledConfigParser
{
// documentation inherited
protected Serializable createConfigObject ()
{
return new FringeConfiguration();
}
// documentation inherited
protected void addRules (Digester digest)
{
// configure top-level constraints
String prefix = "fringe";
digest.addRule(prefix, new SetPropertyFieldsRule());
// create and configure fringe config instances
prefix += "/tile";
digest.addObjectCreate(prefix, TileRecord.class.getName());
digest.addRule(prefix, new SetPropertyFieldsRule());
digest.addSetNext(prefix, "addTileRecord");
// create the fringe type records in each tile record
prefix += "/fringe";
digest.addObjectCreate(prefix, FringeRecord.class.getName());
digest.addRule(prefix, new SetPropertyFieldsRule());
digest.addSetNext(prefix, "addFringe");
}
}