Hold onto your hats kids, it's the biggest little refactor this side of the
Pecos. I'll sort out the per-project niggling bits in just a moment. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@4209 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -61,11 +61,7 @@
|
|||||||
<providerless service="LocationService"/>
|
<providerless service="LocationService"/>
|
||||||
<providerless service="BodyService"/>
|
<providerless service="BodyService"/>
|
||||||
<providerless service="SimulatorService"/>
|
<providerless service="SimulatorService"/>
|
||||||
<providerless service="ParlorService"/>
|
|
||||||
<providerless service="TimeBaseService"/>
|
<providerless service="TimeBaseService"/>
|
||||||
<providerless service="SpotService"/>
|
|
||||||
<providerless service="ZoneService"/>
|
|
||||||
<providerless service="SceneService"/>
|
|
||||||
</service>
|
</service>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
@@ -89,30 +85,6 @@
|
|||||||
</receiver>
|
</receiver>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<!-- checks the availability of certain libraries -->
|
|
||||||
<target name="check-available">
|
|
||||||
<available property="lwjgl.present"
|
|
||||||
classname="org.lwjgl.LWJGLException" classpathref="classpath"/>
|
|
||||||
<available property="jme.present"
|
|
||||||
classname="com.jme.scene.Node" classpathref="classpath"/>
|
|
||||||
<available property="jme-bui.present"
|
|
||||||
classname="com.jmex.bui.BWindow" classpathref="classpath"/>
|
|
||||||
<available property="jme-sound.present"
|
|
||||||
classname="com.jmex.sound.openAL.SoundSystem" classpathref="classpath"/>
|
|
||||||
<condition property="build.jme">
|
|
||||||
<and>
|
|
||||||
<isset property="jme.present"/>
|
|
||||||
<isset property="jme-bui.present"/>
|
|
||||||
</and>
|
|
||||||
</condition>
|
|
||||||
<condition property="build.openal">
|
|
||||||
<and>
|
|
||||||
<isset property="lwjgl.present"/>
|
|
||||||
<isset property="jme-sound.present"/>
|
|
||||||
</and>
|
|
||||||
</condition>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<!-- prepares the application directories -->
|
<!-- prepares the application directories -->
|
||||||
<target name="prepare">
|
<target name="prepare">
|
||||||
<mkdir dir="${deploy.dir}"/>
|
<mkdir dir="${deploy.dir}"/>
|
||||||
@@ -135,17 +107,11 @@
|
|||||||
</target>
|
</target>
|
||||||
|
|
||||||
<!-- build the java class files -->
|
<!-- build the java class files -->
|
||||||
<target name="compile" depends="check-available,prepare">
|
<target name="compile" depends="prepare">
|
||||||
<javac srcdir="src/java" destdir="${classes.dir}"
|
<javac srcdir="src/java" destdir="${classes.dir}"
|
||||||
debug="on" optimize="{$build.optimize}" deprecation="on"
|
debug="on" optimize="{$build.optimize}" deprecation="on"
|
||||||
source="1.5" target="1.5">
|
source="1.5" target="1.5">
|
||||||
<classpath refid="classpath"/>
|
<classpath refid="classpath"/>
|
||||||
<exclude name="com/threerings/jme/**" unless="build.jme"/>
|
|
||||||
<exclude name="com/threerings/openal/**" unless="build.openal"/>
|
|
||||||
<exclude name="**/OggPlayer.java"/>
|
|
||||||
<exclude name="**/ModPlayer.java"/>
|
|
||||||
<exclude name="**/MidiPlayer.java"/>
|
|
||||||
<exclude name="**/Mp3Player.java"/>
|
|
||||||
<!--<compilerarg value="-Xlint:unchecked"/>-->
|
<!--<compilerarg value="-Xlint:unchecked"/>-->
|
||||||
</javac>
|
</javac>
|
||||||
</target>
|
</target>
|
||||||
@@ -155,10 +121,6 @@
|
|||||||
<echo message="Doing native compilation on ${os.name}..."/>
|
<echo message="Doing native compilation on ${os.name}..."/>
|
||||||
<exec dir="src/java/com/threerings/util/signal/${os.name}"
|
<exec dir="src/java/com/threerings/util/signal/${os.name}"
|
||||||
executable="make"><arg line="install"/></exec>
|
executable="make"><arg line="install"/></exec>
|
||||||
<exec os="Linux" dir="src/java/com/threerings/util/keybd/Linux"
|
|
||||||
executable="make"><arg line="install"/></exec>
|
|
||||||
<exec dir="src/java/com/threerings/util/unsafe/${os.name}"
|
|
||||||
executable="make"><arg line="install"/></exec>
|
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<!-- build the javadoc documentation -->
|
<!-- build the javadoc documentation -->
|
||||||
@@ -172,15 +134,14 @@
|
|||||||
<!-- ant documentation is not available online, sorry kids -->
|
<!-- ant documentation is not available online, sorry kids -->
|
||||||
<link href="file:///usr/share/doc/ant-doc/javadocs"/>
|
<link href="file:///usr/share/doc/ant-doc/javadocs"/>
|
||||||
<link href="http://samskivert.com/code/samskivert/samskivert/docs/api"/>
|
<link href="http://samskivert.com/code/samskivert/samskivert/docs/api"/>
|
||||||
<link href="http://www.jmonkeyengine.com/doc"/>
|
|
||||||
</javadoc>
|
</javadoc>
|
||||||
<copy todir="${javadoc.home}">
|
<copy todir="${javadoc.home}">
|
||||||
<fileset dir="src/java" includes="**/*.png"/>
|
<fileset dir="src/java" includes="**/*.png"/>
|
||||||
</copy>
|
</copy>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<!-- builds the javadocs and stuffs them in a directory where they won't
|
<!-- builds the javadocs and stuffs them in a directory where they -->
|
||||||
be blown away when we do "clean" next time -->
|
<!-- won't be blown away when we do "clean" next time -->
|
||||||
<target name="savedoc" depends="javadoc">
|
<target name="savedoc" depends="javadoc">
|
||||||
<delete dir="${savedoc.dir}/api"/>
|
<delete dir="${savedoc.dir}/api"/>
|
||||||
<copy todir="${savedoc.dir}/api">
|
<copy todir="${savedoc.dir}/api">
|
||||||
@@ -203,13 +164,8 @@
|
|||||||
<jar destfile="${deploy.dir}/${app.name}-base.jar">
|
<jar destfile="${deploy.dir}/${app.name}-base.jar">
|
||||||
<fileset dir="${classes.dir}" includes="com/threerings/io/**"/>
|
<fileset dir="${classes.dir}" includes="com/threerings/io/**"/>
|
||||||
<fileset dir="${classes.dir}" includes="com/threerings/util/**"/>
|
<fileset dir="${classes.dir}" includes="com/threerings/util/**"/>
|
||||||
<fileset dir="${classes.dir}" includes="com/threerings/resource/**"/>
|
|
||||||
<fileset dir="${classes.dir}" includes="com/threerings/tools/**"/>
|
<fileset dir="${classes.dir}" includes="com/threerings/tools/**"/>
|
||||||
</jar>
|
</jar>
|
||||||
<jar destfile="${deploy.dir}/${app.name}-media.jar">
|
|
||||||
<fileset dir="${classes.dir}" includes="com/threerings/geom/**"/>
|
|
||||||
<fileset dir="${classes.dir}" includes="com/threerings/media/**"/>
|
|
||||||
</jar>
|
|
||||||
<jar destfile="${deploy.dir}/${app.name}-distrib.jar">
|
<jar destfile="${deploy.dir}/${app.name}-distrib.jar">
|
||||||
<fileset dir="${classes.dir}" includes="com/threerings/presents/**"/>
|
<fileset dir="${classes.dir}" includes="com/threerings/presents/**"/>
|
||||||
<fileset dir="${classes.dir}" includes="com/threerings/crowd/**"/>
|
<fileset dir="${classes.dir}" includes="com/threerings/crowd/**"/>
|
||||||
@@ -217,39 +173,8 @@
|
|||||||
<fileset dir="${classes.dir}" includes="rsrc/config/presents/**"/>
|
<fileset dir="${classes.dir}" includes="rsrc/config/presents/**"/>
|
||||||
<fileset dir="${classes.dir}" includes="rsrc/config/crowd/**"/>
|
<fileset dir="${classes.dir}" includes="rsrc/config/crowd/**"/>
|
||||||
</jar>
|
</jar>
|
||||||
<jar destfile="${deploy.dir}/${app.name}-parlor.jar">
|
<jar destfile="${deploy.dir}/${app.name}-tests.jar">
|
||||||
<fileset dir="${classes.dir}" includes="com/threerings/parlor/**"/>
|
<fileset dir="tests/${classes.dir}" includes="com/threerings/**"/>
|
||||||
</jar>
|
|
||||||
<jar destfile="${deploy.dir}/${app.name}-puzzle.jar">
|
|
||||||
<fileset dir="${classes.dir}" includes="com/threerings/puzzle/**"/>
|
|
||||||
</jar>
|
|
||||||
<jar destfile="${deploy.dir}/${app.name}-micasa.jar">
|
|
||||||
<fileset dir="${classes.dir}" includes="com/threerings/micasa/**"/>
|
|
||||||
<fileset dir="${classes.dir}" includes="rsrc/config/micasa/**"/>
|
|
||||||
<fileset dir="${classes.dir}" includes="rsrc/i18n/micasa*"/>
|
|
||||||
</jar>
|
|
||||||
<jar destfile="${deploy.dir}/${app.name}-whirled.jar">
|
|
||||||
<fileset dir="${classes.dir}" includes="com/threerings/whirled/**"/>
|
|
||||||
<fileset dir="${classes.dir}" includes="rsrc/config/whirled/**"/>
|
|
||||||
</jar>
|
|
||||||
<jar destfile="${deploy.dir}/${app.name}-miso.jar">
|
|
||||||
<fileset dir="${classes.dir}" includes="com/threerings/miso/**"/>
|
|
||||||
<fileset dir="${classes.dir}" includes="rsrc/config/miso/**"/>
|
|
||||||
</jar>
|
|
||||||
<jar destfile="${deploy.dir}/${app.name}-cast.jar">
|
|
||||||
<fileset dir="${classes.dir}" includes="com/threerings/cast/**"/>
|
|
||||||
</jar>
|
|
||||||
<jar destfile="${deploy.dir}/${app.name}-stage.jar">
|
|
||||||
<fileset dir="${classes.dir}" includes="com/threerings/stage/**"/>
|
|
||||||
<fileset dir="${classes.dir}" includes="rsrc/media/stage/**"/>
|
|
||||||
</jar>
|
|
||||||
<jar destfile="${deploy.dir}/${app.name}-jme.jar">
|
|
||||||
<fileset dir="${classes.dir}" includes="com/threerings/jme/**"/>
|
|
||||||
<fileset dir="${classes.dir}" includes="rsrc/media/jme/**"/>
|
|
||||||
<fileset dir="${classes.dir}" includes="rsrc/i18n/jme/**"/>
|
|
||||||
</jar>
|
|
||||||
<jar destfile="${deploy.dir}/${app.name}-openal.jar">
|
|
||||||
<fileset dir="${classes.dir}" includes="com/threerings/openal/**"/>
|
|
||||||
</jar>
|
</jar>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
@@ -279,27 +204,11 @@
|
|||||||
<!-- we weave everything a first time without verification so that -->
|
<!-- we weave everything a first time without verification so that -->
|
||||||
<!-- interdependencies will resolve the second time -->
|
<!-- interdependencies will resolve the second time -->
|
||||||
<weave inputjar="${inpre}-base.jar" outputjar="${outpre}-base.jar"/>
|
<weave inputjar="${inpre}-base.jar" outputjar="${outpre}-base.jar"/>
|
||||||
<weave inputjar="${inpre}-cast.jar" outputjar="${outpre}-cast.jar"/>
|
|
||||||
<weave inputjar="${inpre}-distrib.jar" outputjar="${outpre}-distrib.jar"/>
|
<weave inputjar="${inpre}-distrib.jar" outputjar="${outpre}-distrib.jar"/>
|
||||||
<weave inputjar="${inpre}-micasa.jar" outputjar="${outpre}-micasa.jar"/>
|
|
||||||
<weave inputjar="${inpre}-miso.jar" outputjar="${outpre}-miso.jar"/>
|
|
||||||
<weave inputjar="${inpre}-media.jar" outputjar="${outpre}-media.jar"/>
|
|
||||||
<weave inputjar="${inpre}-parlor.jar" outputjar="${outpre}-parlor.jar"/>
|
|
||||||
<weave inputjar="${inpre}-puzzle.jar" outputjar="${outpre}-puzzle.jar"/>
|
|
||||||
<weave inputjar="${inpre}-stage.jar" outputjar="${outpre}-stage.jar"/>
|
|
||||||
<weave inputjar="${inpre}-whirled.jar" outputjar="${outpre}-whirled.jar"/>
|
|
||||||
|
|
||||||
<!-- now weave again with the verifier to check for unweavable 1.5isms -->
|
<!-- now weave again with the verifier to check for unweavable 1.5isms -->
|
||||||
<antcall target="vweave"><param name="which" value="base"/></antcall>
|
<antcall target="vweave"><param name="which" value="base"/></antcall>
|
||||||
<antcall target="vweave"><param name="which" value="cast"/></antcall>
|
|
||||||
<antcall target="vweave"><param name="which" value="distrib"/></antcall>
|
<antcall target="vweave"><param name="which" value="distrib"/></antcall>
|
||||||
<antcall target="vweave"><param name="which" value="micasa"/></antcall>
|
|
||||||
<antcall target="vweave"><param name="which" value="miso"/></antcall>
|
|
||||||
<antcall target="vweave"><param name="which" value="media"/></antcall>
|
|
||||||
<antcall target="vweave"><param name="which" value="parlor"/></antcall>
|
|
||||||
<antcall target="vweave"><param name="which" value="puzzle"/></antcall>
|
|
||||||
<antcall target="vweave"><param name="which" value="stage"/></antcall>
|
|
||||||
<antcall target="vweave"><param name="which" value="whirled"/></antcall>
|
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<!-- generate a class hierarchy diagram -->
|
<!-- generate a class hierarchy diagram -->
|
||||||
|
|||||||
@@ -23,11 +23,11 @@ package com.threerings.admin.client;
|
|||||||
|
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
import javax.swing.JTabbedPane;
|
import javax.swing.JTabbedPane;
|
||||||
|
|
||||||
import com.samskivert.swing.VGroupLayout;
|
import com.samskivert.swing.VGroupLayout;
|
||||||
|
|
||||||
import com.threerings.media.SafeScrollPane;
|
|
||||||
import com.threerings.presents.util.PresentsContext;
|
import com.threerings.presents.util.PresentsContext;
|
||||||
|
|
||||||
import com.threerings.admin.Log;
|
import com.threerings.admin.Log;
|
||||||
@@ -84,7 +84,7 @@ public class ConfigEditorPanel extends JPanel
|
|||||||
// when we're hidden, we want to clear out our subscriptions
|
// when we're hidden, we want to clear out our subscriptions
|
||||||
int ccount = _oeditors.getComponentCount();
|
int ccount = _oeditors.getComponentCount();
|
||||||
for (int ii = 0; ii < ccount; ii++) {
|
for (int ii = 0; ii < ccount; ii++) {
|
||||||
SafeScrollPane scrolly = (SafeScrollPane)_oeditors.getComponent(ii);
|
JScrollPane scrolly = (JScrollPane)_oeditors.getComponent(ii);
|
||||||
ObjectEditorPanel opanel = (ObjectEditorPanel)
|
ObjectEditorPanel opanel = (ObjectEditorPanel)
|
||||||
scrolly.getViewport().getView();
|
scrolly.getViewport().getView();
|
||||||
opanel.cleanup();
|
opanel.cleanup();
|
||||||
@@ -101,7 +101,7 @@ public class ConfigEditorPanel extends JPanel
|
|||||||
for (int ii = 0; ii < keys.length; ii++) {
|
for (int ii = 0; ii < keys.length; ii++) {
|
||||||
ObjectEditorPanel panel =
|
ObjectEditorPanel panel =
|
||||||
new ObjectEditorPanel(_ctx, keys[ii], oids[ii]);
|
new ObjectEditorPanel(_ctx, keys[ii], oids[ii]);
|
||||||
SafeScrollPane scrolly = new SafeScrollPane(panel);
|
JScrollPane scrolly = new JScrollPane(panel);
|
||||||
_oeditors.addTab(keys[ii], scrolly);
|
_oeditors.addTab(keys[ii], scrolly);
|
||||||
if (keys[ii].equals(_defaultPane)) {
|
if (keys[ii].equals(_defaultPane)) {
|
||||||
_oeditors.setSelectedComponent(scrolly);
|
_oeditors.setSelectedComponent(scrolly);
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import java.lang.reflect.Field;
|
|||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
|
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
import javax.swing.JTable;
|
import javax.swing.JTable;
|
||||||
import javax.swing.table.AbstractTableModel;
|
import javax.swing.table.AbstractTableModel;
|
||||||
|
|
||||||
@@ -39,8 +40,6 @@ import com.samskivert.util.ClassUtil;
|
|||||||
import com.samskivert.util.ComparableArrayList;
|
import com.samskivert.util.ComparableArrayList;
|
||||||
import com.samskivert.util.ListUtil;
|
import com.samskivert.util.ListUtil;
|
||||||
|
|
||||||
import com.threerings.media.SafeScrollPane;
|
|
||||||
|
|
||||||
import com.threerings.presents.Log;
|
import com.threerings.presents.Log;
|
||||||
import com.threerings.presents.dobj.AttributeChangeListener;
|
import com.threerings.presents.dobj.AttributeChangeListener;
|
||||||
import com.threerings.presents.dobj.AttributeChangedEvent;
|
import com.threerings.presents.dobj.AttributeChangedEvent;
|
||||||
@@ -109,7 +108,7 @@ public class DSetEditor extends JPanel
|
|||||||
|
|
||||||
_table = new ObjectEditorTable(entryClass, editableFields, interp);
|
_table = new ObjectEditorTable(entryClass, editableFields, interp);
|
||||||
|
|
||||||
add(new SafeScrollPane(_table), BorderLayout.CENTER);
|
add(new JScrollPane(_table), BorderLayout.CENTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A mechanism for caching composited character action animations on disk.
|
|
||||||
*/
|
|
||||||
public interface ActionCache
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Fetches from the cache a composited set of images for a particular
|
|
||||||
* character for a particular action.
|
|
||||||
*/
|
|
||||||
public ActionFrames getActionFrames (
|
|
||||||
CharacterDescriptor descrip, String action);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Requests that the specified set of action frames for the specified
|
|
||||||
* character be cached.
|
|
||||||
*/
|
|
||||||
public void cacheActionFrames (
|
|
||||||
CharacterDescriptor descrip, String action, ActionFrames frames);
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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 com.threerings.media.image.Colorization;
|
|
||||||
import com.threerings.util.DirectionCodes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encapsulates a set of frames in each of {@link
|
|
||||||
* DirectionCodes#DIRECTION_COUNT} orientations that are used to render a
|
|
||||||
* character sprite.
|
|
||||||
*/
|
|
||||||
public interface ActionFrames
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Returns the number of orientations available in this set of action
|
|
||||||
* frames.
|
|
||||||
*/
|
|
||||||
public int getOrientationCount ();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the multi-frame image that comprises the frames for the
|
|
||||||
* specified orientation.
|
|
||||||
*/
|
|
||||||
public TrimmedMultiFrameImage getFrames (int orient);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the x offset from the upper left of the image to the
|
|
||||||
* "origin" for this character frame. A sprite with location (x, y)
|
|
||||||
* will be rendered such that its origin is coincident with that
|
|
||||||
* location.
|
|
||||||
*/
|
|
||||||
public int getXOrigin (int orient, int frameIdx);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the y offset from the upper left of the image to the
|
|
||||||
* "origin" for this character frame. A sprite with location (x, y)
|
|
||||||
* will be rendered such that its origin is coincident with that
|
|
||||||
* location.
|
|
||||||
*/
|
|
||||||
public int getYOrigin (int orient, int frameIdx);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a clone of these action frames which will have the supplied
|
|
||||||
* colorizations applied to the frame images.
|
|
||||||
*/
|
|
||||||
public ActionFrames cloneColorized (Colorization[] zations);
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.Point;
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The action sequence class describes a particular character animation
|
|
||||||
* sequence. An animation sequence consists of one or more frames of
|
|
||||||
* animation, renders at a particular frame rate, and has an origin point
|
|
||||||
* that specifies the location of the base of the character in relation to
|
|
||||||
* the bounds of the animation images.
|
|
||||||
*/
|
|
||||||
public class ActionSequence implements Serializable
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Defines the name of the default action sequence. When component
|
|
||||||
* tilesets are loaded to build a set of composited images for a
|
|
||||||
* particular action sequence, a check is first made for a component
|
|
||||||
* tileset specific to the action sequence and then for the
|
|
||||||
* component's default tileset if the action specific tileset did not
|
|
||||||
* exist.
|
|
||||||
*/
|
|
||||||
public static final String DEFAULT_SEQUENCE = "default";
|
|
||||||
|
|
||||||
/** The action sequence name. */
|
|
||||||
public String name;
|
|
||||||
|
|
||||||
/** The number of frames per second to show when animating. */
|
|
||||||
public float framesPerSecond;
|
|
||||||
|
|
||||||
/** The position of the character's base for this sequence. */
|
|
||||||
public Point origin = new Point();
|
|
||||||
|
|
||||||
/** Orientation codes for the orientations available for this
|
|
||||||
* action. */
|
|
||||||
public int[] orients;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a string representation of this action sequence.
|
|
||||||
*/
|
|
||||||
public String toString ()
|
|
||||||
{
|
|
||||||
return "[name=" + name + ", framesPerSecond=" + framesPerSecond +
|
|
||||||
", origin=" + origin +
|
|
||||||
", orients=" + (orients == null ? 0 : orients.length) + "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Increase this value when object's serialized state is impacted by
|
|
||||||
* a class change (modification of fields, inheritance). */
|
|
||||||
private static final long serialVersionUID = 1;
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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 com.samskivert.util.Config;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides access to runtime configuration parameters for this package
|
|
||||||
* and its subpackages.
|
|
||||||
*/
|
|
||||||
public class CastPrefs
|
|
||||||
{
|
|
||||||
/** Used to load our preferences from a properties file and map them
|
|
||||||
* to the persistent Java preferences repository. */
|
|
||||||
public static Config config = new Config("rsrc/config/cast");
|
|
||||||
}
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.io.Serializable;
|
|
||||||
|
|
||||||
import com.samskivert.util.StringUtil;
|
|
||||||
|
|
||||||
import com.threerings.media.sprite.Sprite;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The character component represents a single component that can be
|
|
||||||
* composited with other character components to generate an image
|
|
||||||
* representing a complete character displayable in any of the eight
|
|
||||||
* compass directions as detailed in the {@link Sprite} class direction
|
|
||||||
* constants.
|
|
||||||
*/
|
|
||||||
public class CharacterComponent implements Serializable
|
|
||||||
{
|
|
||||||
/** The unique component identifier. */
|
|
||||||
public int componentId;
|
|
||||||
|
|
||||||
/** The component's name. */
|
|
||||||
public String name;
|
|
||||||
|
|
||||||
/** The class of components to which this one belongs. */
|
|
||||||
public ComponentClass componentClass;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a character component with the specified id of the
|
|
||||||
* specified class.
|
|
||||||
*/
|
|
||||||
public CharacterComponent (int componentId, String name,
|
|
||||||
ComponentClass compClass, FrameProvider fprov)
|
|
||||||
{
|
|
||||||
this.componentId = componentId;
|
|
||||||
this.name = name;
|
|
||||||
this.componentClass = compClass;
|
|
||||||
_frameProvider = fprov;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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, String type)
|
|
||||||
{
|
|
||||||
return _frameProvider.getFrames(this, action, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if this component is equal to the other component. The
|
|
||||||
* comparison is made on <code>componentId</code>.
|
|
||||||
*/
|
|
||||||
public boolean equals (Object other)
|
|
||||||
{
|
|
||||||
if (other instanceof CharacterComponent) {
|
|
||||||
return componentId == ((CharacterComponent)other).componentId;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a string representation of this character component.
|
|
||||||
*/
|
|
||||||
public String toString ()
|
|
||||||
{
|
|
||||||
return StringUtil.fieldsToString(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The entity from which we obtain our animation frames. */
|
|
||||||
protected FrameProvider _frameProvider;
|
|
||||||
|
|
||||||
/** Increase this value when object's serialized state is impacted by
|
|
||||||
* a class change (modification of fields, inheritance). */
|
|
||||||
private static final long serialVersionUID = 1;
|
|
||||||
}
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.util.Arrays;
|
|
||||||
import com.samskivert.util.StringUtil;
|
|
||||||
import com.threerings.media.image.Colorization;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The character descriptor object details the components that are
|
|
||||||
* pieced together to create a single character image.
|
|
||||||
*/
|
|
||||||
public class CharacterDescriptor
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Constructs a character descriptor.
|
|
||||||
*
|
|
||||||
* @param components the component ids of the individual components
|
|
||||||
* that make up this character.
|
|
||||||
* @param zations the colorizations to apply to each of the character
|
|
||||||
* component images when compositing actions (an array of
|
|
||||||
* colorizations for each component, elements of which can be null to
|
|
||||||
* make it easier to support per-component specialized colorizations).
|
|
||||||
* This can be null if image recolorization is not desired.
|
|
||||||
*/
|
|
||||||
public CharacterDescriptor (int[] components, Colorization[][] zations)
|
|
||||||
{
|
|
||||||
_components = components;
|
|
||||||
_zations = zations;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array of the component identifiers comprising the
|
|
||||||
* character described by this descriptor.
|
|
||||||
*/
|
|
||||||
public int[] getComponentIds ()
|
|
||||||
{
|
|
||||||
return _components;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array of colorization arrays to be applied to the
|
|
||||||
* components when compositing action images (one array per
|
|
||||||
* component).
|
|
||||||
*/
|
|
||||||
public Colorization[][] getColorizations ()
|
|
||||||
{
|
|
||||||
return _zations;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the colorizations to be used by this character descriptor.
|
|
||||||
*/
|
|
||||||
public void setColorizations (Colorization[][] zations)
|
|
||||||
{
|
|
||||||
_zations = zations;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compute a sensible hashcode for this object.
|
|
||||||
*/
|
|
||||||
public int hashCode ()
|
|
||||||
{
|
|
||||||
int code = 0, clength = _components.length;
|
|
||||||
for (int i = 0; i < clength; i++) {
|
|
||||||
code ^= _components[i];
|
|
||||||
}
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compares this character descriptor to another.
|
|
||||||
*/
|
|
||||||
public boolean equals (Object other)
|
|
||||||
{
|
|
||||||
if (!(other instanceof CharacterDescriptor)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// both the component ids and the colorizations must be equal
|
|
||||||
CharacterDescriptor odesc = (CharacterDescriptor)other;
|
|
||||||
if (!Arrays.equals(_components, odesc._components)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Colorization[][] zations = odesc._zations;
|
|
||||||
if (zations == null && _zations == null) {
|
|
||||||
// if neither has colorizations, we're clear
|
|
||||||
return true;
|
|
||||||
|
|
||||||
} else if (zations == null || _zations == null) {
|
|
||||||
// if one has colorizations whilst the other doesn't, they
|
|
||||||
// can't be equal
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise, all of the colorizations must be equal as well
|
|
||||||
int zlength = zations.length;
|
|
||||||
if (zlength != _zations.length) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (int ii = 0; ii < zlength; ii++) {
|
|
||||||
if (!Arrays.equals(_zations[ii], zations[ii])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a string representation of this character descriptor.
|
|
||||||
*/
|
|
||||||
public String toString ()
|
|
||||||
{
|
|
||||||
return "[cids=" + StringUtil.toString(_components) +
|
|
||||||
", colors=" + StringUtil.toString(_zations) + "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The component identifiers comprising the character. */
|
|
||||||
protected int[] _components;
|
|
||||||
|
|
||||||
/** The colorizations to apply when compositing this character. */
|
|
||||||
protected Colorization[][] _zations;
|
|
||||||
}
|
|
||||||
@@ -1,430 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.samskivert.swing.RuntimeAdjust;
|
|
||||||
import com.samskivert.util.LRUHashMap;
|
|
||||||
import com.samskivert.util.StringUtil;
|
|
||||||
import com.samskivert.util.Throttle;
|
|
||||||
import com.samskivert.util.Tuple;
|
|
||||||
|
|
||||||
import com.threerings.media.image.Colorization;
|
|
||||||
import com.threerings.media.image.ImageManager;
|
|
||||||
import com.threerings.util.DirectionCodes;
|
|
||||||
|
|
||||||
import com.threerings.cast.CompositedActionFrames.ComponentFrames;
|
|
||||||
import com.threerings.cast.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The character manager provides facilities for constructing sprites that
|
|
||||||
* are used to represent characters in a scene. It also handles the
|
|
||||||
* compositing and caching of composited character animations.
|
|
||||||
*/
|
|
||||||
public class CharacterManager
|
|
||||||
implements DirectionCodes
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Constructs the character manager.
|
|
||||||
*/
|
|
||||||
public CharacterManager (ImageManager imgr, ComponentRepository crepo)
|
|
||||||
{
|
|
||||||
// keep these around
|
|
||||||
_imgr = imgr;
|
|
||||||
_crepo = crepo;
|
|
||||||
|
|
||||||
// populate our actions table
|
|
||||||
Iterator iter = crepo.enumerateActionSequences();
|
|
||||||
while (iter.hasNext()) {
|
|
||||||
ActionSequence action = (ActionSequence)iter.next();
|
|
||||||
_actions.put(action.name, action);
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a cache for our composited action frames
|
|
||||||
int acsize = _cacheSize.getValue();
|
|
||||||
Log.debug("Creating action cache [size=" + acsize + "k].");
|
|
||||||
_frameCache = new LRUHashMap(acsize*1024, new LRUHashMap.ItemSizer() {
|
|
||||||
public int computeSize (Object value) {
|
|
||||||
return (int)((CompositedMultiFrameImage)
|
|
||||||
value).getEstimatedMemoryUsage();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
_frameCache.setTracking(true); // TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the component repository being used by this manager.
|
|
||||||
*/
|
|
||||||
public ComponentRepository getComponentRepository ()
|
|
||||||
{
|
|
||||||
return _crepo;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instructs the character manager to construct instances of this
|
|
||||||
* derived class of {@link CharacterSprite} when creating new sprites.
|
|
||||||
*
|
|
||||||
* @exception IllegalArgumentException thrown if the supplied class
|
|
||||||
* does not derive from {@link CharacterSprite}.
|
|
||||||
*/
|
|
||||||
public void setCharacterClass (Class charClass)
|
|
||||||
{
|
|
||||||
// sanity check
|
|
||||||
if (!CharacterSprite.class.isAssignableFrom(charClass)) {
|
|
||||||
String errmsg = "Requested to use character sprite class that " +
|
|
||||||
"does not derive from CharacterSprite " +
|
|
||||||
"[class=" + charClass.getName() + "].";
|
|
||||||
throw new IllegalArgumentException(errmsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// make a note of it
|
|
||||||
_charClass = charClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instructs the character manager to use the provided cache for
|
|
||||||
* composited action animations.
|
|
||||||
*/
|
|
||||||
public void setActionCache (ActionCache cache)
|
|
||||||
{
|
|
||||||
_acache = cache;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a {@link CharacterSprite} representing the character
|
|
||||||
* described by the given {@link CharacterDescriptor}, or
|
|
||||||
* <code>null</code> if an error occurs.
|
|
||||||
*
|
|
||||||
* @param desc the character descriptor.
|
|
||||||
*/
|
|
||||||
public CharacterSprite getCharacter (CharacterDescriptor desc)
|
|
||||||
{
|
|
||||||
return getCharacter(desc, _charClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a {@link CharacterSprite} representing the character
|
|
||||||
* described by the given {@link CharacterDescriptor}, or
|
|
||||||
* <code>null</code> if an error occurs.
|
|
||||||
*
|
|
||||||
* @param desc the character descriptor.
|
|
||||||
* @param charClass the {@link CharacterSprite} derived class that
|
|
||||||
* should be instantiated instead of the configured default (which is
|
|
||||||
* set via {@link #setCharacterClass}).
|
|
||||||
*/
|
|
||||||
public CharacterSprite getCharacter (CharacterDescriptor desc,
|
|
||||||
Class charClass)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
CharacterSprite sprite = (CharacterSprite)
|
|
||||||
charClass.newInstance();
|
|
||||||
sprite.init(desc, this);
|
|
||||||
return sprite;
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.warning("Failed to instantiate character sprite " +
|
|
||||||
"[e=" + e + "].");
|
|
||||||
Log.logStackTrace(e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Obtains the composited animation frames for the specified action for a
|
|
||||||
* character with the specified descriptor. The resulting composited
|
|
||||||
* animation will be cached.
|
|
||||||
*
|
|
||||||
* @exception NoSuchComponentException thrown if any of the components in
|
|
||||||
* the supplied descriptor do not exist.
|
|
||||||
* @exception IllegalArgumentException thrown if any of the components
|
|
||||||
* referenced in the descriptor do not support the specified action.
|
|
||||||
*/
|
|
||||||
public ActionFrames getActionFrames (
|
|
||||||
CharacterDescriptor descrip, String action)
|
|
||||||
throws NoSuchComponentException
|
|
||||||
{
|
|
||||||
Tuple key = new Tuple(descrip, action);
|
|
||||||
ActionFrames frames = (ActionFrames)_actionFrames.get(key);
|
|
||||||
if (frames == null) {
|
|
||||||
// this doesn't actually composite the images, but prepares an
|
|
||||||
// object to be able to do so
|
|
||||||
frames = createCompositeFrames(descrip, action);
|
|
||||||
_actionFrames.put(key, frames);
|
|
||||||
}
|
|
||||||
|
|
||||||
// periodically report our frame image cache performance
|
|
||||||
if (!_cacheStatThrottle.throttleOp()) {
|
|
||||||
long size = getEstimatedCacheMemoryUsage();
|
|
||||||
int[] eff = _frameCache.getTrackedEffectiveness();
|
|
||||||
Log.debug("CharacterManager LRU [mem=" + (size / 1024) + "k" +
|
|
||||||
", size=" + _frameCache.size() + ", hits=" + eff[0] +
|
|
||||||
", misses=" + eff[1] + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
return frames;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Informs the character manager that the action sequence for the
|
|
||||||
* given character descriptor is likely to be needed in the near
|
|
||||||
* future and so any efforts that can be made to load it into the
|
|
||||||
* action sequence cache in advance should be undertaken.
|
|
||||||
*
|
|
||||||
* <p> This will eventually be revamped to spiffily load action
|
|
||||||
* sequences in the background.
|
|
||||||
*/
|
|
||||||
public void resolveActionSequence (CharacterDescriptor desc, String action)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
if (getActionFrames(desc, action) == null) {
|
|
||||||
Log.warning("Failed to resolve action sequence " +
|
|
||||||
"[desc=" + desc + ", action=" + action + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (NoSuchComponentException nsce) {
|
|
||||||
Log.warning("Failed to resolve action sequence " +
|
|
||||||
"[nsce=" + nsce + "].");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the action sequence instance with the specified name or
|
|
||||||
* null if no such sequence exists.
|
|
||||||
*/
|
|
||||||
public ActionSequence getActionSequence (String action)
|
|
||||||
{
|
|
||||||
return (ActionSequence)_actions.get(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the estimated memory usage in bytes for all images
|
|
||||||
* currently cached by the cached action frames.
|
|
||||||
*/
|
|
||||||
protected long getEstimatedCacheMemoryUsage ()
|
|
||||||
{
|
|
||||||
long size = 0;
|
|
||||||
Iterator iter = _frameCache.values().iterator();
|
|
||||||
while (iter.hasNext()) {
|
|
||||||
size += ((CompositedMultiFrameImage)
|
|
||||||
iter.next()).getEstimatedMemoryUsage();
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the composited animation frames for the specified action
|
|
||||||
* for a character with the specified descriptor.
|
|
||||||
*
|
|
||||||
* @exception NoSuchComponentException thrown if any of the components
|
|
||||||
* in the supplied descriptor do not exist.
|
|
||||||
* @exception IllegalArgumentException thrown if any of the components
|
|
||||||
* referenced in the descriptor do not support the specified action.
|
|
||||||
*/
|
|
||||||
protected ActionFrames createCompositeFrames (
|
|
||||||
CharacterDescriptor descrip, String action)
|
|
||||||
throws NoSuchComponentException
|
|
||||||
{
|
|
||||||
int[] cids = descrip.getComponentIds();
|
|
||||||
int ccount = cids.length;
|
|
||||||
Colorization[][] zations = descrip.getColorizations();
|
|
||||||
|
|
||||||
Log.debug("Compositing action [action=" + action +
|
|
||||||
", descrip=" + descrip + "].");
|
|
||||||
|
|
||||||
// this will be used to construct any shadow layers
|
|
||||||
HashMap shadows = null;
|
|
||||||
|
|
||||||
// maps components by class name for masks
|
|
||||||
HashMap ccomps = new HashMap();
|
|
||||||
|
|
||||||
// create colorized versions of all of the source action frames
|
|
||||||
ArrayList sources = new ArrayList(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);
|
|
||||||
|
|
||||||
// load up the main component images
|
|
||||||
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=" + ccomp + "]";
|
|
||||||
throw new RuntimeException(errmsg);
|
|
||||||
}
|
|
||||||
cframes.frames = (zations == null || zations[ii] == null) ?
|
|
||||||
source : source.cloneColorized(zations[ii]);
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add any necessary masks
|
|
||||||
for (int ii = 0; ii < ccount; 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
ComponentFrames[] cfvec = (ComponentFrames[])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)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
|
|
||||||
ArrayList sources = new ArrayList();
|
|
||||||
for (int ii = 0, ll = scomps.size(); ii < ll; ii++) {
|
|
||||||
ComponentFrames source = new ComponentFrames();
|
|
||||||
source.ccomp = (CharacterComponent)scomps.get(ii);
|
|
||||||
source.frames = source.ccomp.getFrames(
|
|
||||||
action, StandardActions.SHADOW_TYPE);
|
|
||||||
if (source.frames == null) {
|
|
||||||
// skip this shadow component
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
sources.add(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we ended up with no shadow, no problem!
|
|
||||||
if (sources.size() == 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
protected CompositedMultiFrameImage createFrames (int orient) {
|
|
||||||
return new CompositedShadowImage(
|
|
||||||
_imgr, _sources, _action, orient, cclass.shadowAlpha);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return cframes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The image manager with whom we interact. */
|
|
||||||
protected ImageManager _imgr;
|
|
||||||
|
|
||||||
/** The component repository. */
|
|
||||||
protected ComponentRepository _crepo;
|
|
||||||
|
|
||||||
/** A table of our action sequences. */
|
|
||||||
protected HashMap _actions = new HashMap();
|
|
||||||
|
|
||||||
/** A table of composited action sequences (these don't reference the
|
|
||||||
* actual image data directly and thus take up little memory). */
|
|
||||||
protected HashMap _actionFrames = new HashMap();
|
|
||||||
|
|
||||||
/** A cache of composited animation frames. */
|
|
||||||
protected LRUHashMap _frameCache;
|
|
||||||
|
|
||||||
/** The character class to be created. */
|
|
||||||
protected Class _charClass = CharacterSprite.class;
|
|
||||||
|
|
||||||
/** The action animation cache, if we have one. */
|
|
||||||
protected ActionCache _acache;
|
|
||||||
|
|
||||||
/** Throttle our cache status logging to once every 30 seconds. */
|
|
||||||
protected Throttle _cacheStatThrottle = new Throttle(1, 30000L);
|
|
||||||
|
|
||||||
/** Register our image cache size with the runtime adjustments
|
|
||||||
* framework. */
|
|
||||||
protected static RuntimeAdjust.IntAdjust _cacheSize =
|
|
||||||
new RuntimeAdjust.IntAdjust(
|
|
||||||
"Size (in kb of memory used) of the character manager LRU " +
|
|
||||||
"action cache [requires restart]", "narya.cast.action_cache_size",
|
|
||||||
CastPrefs.config, 1024);
|
|
||||||
}
|
|
||||||
@@ -1,402 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.Graphics2D;
|
|
||||||
import java.awt.Point;
|
|
||||||
import java.awt.Rectangle;
|
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
|
|
||||||
import com.threerings.media.sprite.ImageSprite;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A character sprite is a sprite that animates itself while walking
|
|
||||||
* about in a scene.
|
|
||||||
*/
|
|
||||||
public class CharacterSprite extends ImageSprite
|
|
||||||
implements StandardActions
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Initializes this character sprite with the specified character
|
|
||||||
* descriptor and character manager. It will obtain animation data
|
|
||||||
* from the supplied character manager.
|
|
||||||
*/
|
|
||||||
public void init (CharacterDescriptor descrip, CharacterManager charmgr)
|
|
||||||
{
|
|
||||||
// keep track of this stuff
|
|
||||||
_descrip = descrip;
|
|
||||||
_charmgr = charmgr;
|
|
||||||
|
|
||||||
// sanity check our values
|
|
||||||
sanityCheckDescrip();
|
|
||||||
|
|
||||||
// assign an arbitrary starting orientation
|
|
||||||
_orient = SOUTHWEST;
|
|
||||||
|
|
||||||
// pass the buck to derived classes
|
|
||||||
didInit();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called after this sprite has been initialized with its character
|
|
||||||
* descriptor and character manager. Derived classes can do post-init
|
|
||||||
* business here.
|
|
||||||
*/
|
|
||||||
protected void didInit ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reconfigures this sprite to use the specified character descriptor.
|
|
||||||
*/
|
|
||||||
public void setCharacterDescriptor (CharacterDescriptor descrip)
|
|
||||||
{
|
|
||||||
// keep the new descriptor
|
|
||||||
_descrip = descrip;
|
|
||||||
|
|
||||||
// sanity check our values
|
|
||||||
sanityCheckDescrip();
|
|
||||||
|
|
||||||
// update our action frames
|
|
||||||
updateActionFrames();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies the action to use when the sprite is at rest. The default
|
|
||||||
* is <code>STANDING</code>.
|
|
||||||
*/
|
|
||||||
public void setRestingAction (String action)
|
|
||||||
{
|
|
||||||
_restingAction = action;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the action to be used when the sprite is at rest. Derived
|
|
||||||
* classes may wish to override this method and vary the action based
|
|
||||||
* on external parameters (or randomly).
|
|
||||||
*/
|
|
||||||
public String getRestingAction ()
|
|
||||||
{
|
|
||||||
return _restingAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies the action to use when the sprite is following a path.
|
|
||||||
* The default is <code>WALKING</code>.
|
|
||||||
*/
|
|
||||||
public void setFollowingPathAction (String action)
|
|
||||||
{
|
|
||||||
_followingPathAction = action;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the action to be used when the sprite is following a path.
|
|
||||||
* Derived classes may wish to override this method and vary the
|
|
||||||
* action based on external parameters (or randomly).
|
|
||||||
*/
|
|
||||||
public String getFollowingPathAction ()
|
|
||||||
{
|
|
||||||
return _followingPathAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the action sequence used when rendering the character, from
|
|
||||||
* the set of available sequences.
|
|
||||||
*/
|
|
||||||
public void setActionSequence (String action)
|
|
||||||
{
|
|
||||||
// sanity check
|
|
||||||
if (action == null) {
|
|
||||||
Log.warning("Refusing to set null action sequence " + this + ".");
|
|
||||||
Thread.dumpStack();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// no need to noop
|
|
||||||
if (action.equals(_action)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_action = action;
|
|
||||||
updateActionFrames();
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public void setOrientation (int orient)
|
|
||||||
{
|
|
||||||
if (orient < 0 || orient >= FINE_DIRECTION_COUNT) {
|
|
||||||
Log.info("Refusing to set invalid orientation [sprite=" + this +
|
|
||||||
", orient=" + orient + "].");
|
|
||||||
Thread.dumpStack();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int oorient = _orient;
|
|
||||||
super.setOrientation(orient);
|
|
||||||
if (_orient != oorient) {
|
|
||||||
_frames = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public boolean hitTest (int x, int y)
|
|
||||||
{
|
|
||||||
// the irect adjustments are to account for our decorations
|
|
||||||
return (_frames != null && _ibounds.contains(x, y) &&
|
|
||||||
_frames.hitTest(_frameIdx, x - _ibounds.x, y - _ibounds.y));
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public void tick (long tickStamp)
|
|
||||||
{
|
|
||||||
// composite our action frames if something since the last call to
|
|
||||||
// tick caused them to become invalid
|
|
||||||
compositeActionFrames();
|
|
||||||
super.tick(tickStamp);
|
|
||||||
// composite our action frames if something during tick() caused
|
|
||||||
// them to become invalid
|
|
||||||
compositeActionFrames();
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public void cancelMove ()
|
|
||||||
{
|
|
||||||
super.cancelMove();
|
|
||||||
halt();
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public void pathBeginning ()
|
|
||||||
{
|
|
||||||
super.pathBeginning();
|
|
||||||
|
|
||||||
// enable walking animation
|
|
||||||
setAnimationMode(TIME_BASED);
|
|
||||||
setActionSequence(getFollowingPathAction());
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public void pathCompleted (long timestamp)
|
|
||||||
{
|
|
||||||
super.pathCompleted(timestamp);
|
|
||||||
halt();
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public void paint (Graphics2D gfx)
|
|
||||||
{
|
|
||||||
if (_frames != null) {
|
|
||||||
decorateBehind(gfx);
|
|
||||||
// paint the image using _ibounds rather than _bounds which
|
|
||||||
// has been modified to include the bounds of our decorations
|
|
||||||
_frames.paintFrame(gfx, _frameIdx, _ibounds.x, _ibounds.y);
|
|
||||||
decorateInFront(gfx);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
super.paint(gfx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called to paint any decorations that should appear behind the
|
|
||||||
* character sprite image.
|
|
||||||
*/
|
|
||||||
protected void decorateBehind (Graphics2D gfx)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called to paint any decorations that should appear in front of the
|
|
||||||
* character sprite image.
|
|
||||||
*/
|
|
||||||
protected void decorateInFront (Graphics2D gfx)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rebuilds our action frames given our current character descriptor
|
|
||||||
* and action sequence. This is called when either of those two things
|
|
||||||
* changes.
|
|
||||||
*/
|
|
||||||
protected void updateActionFrames ()
|
|
||||||
{
|
|
||||||
// get a reference to the action sequence so that we can obtain
|
|
||||||
// our animation frames and configure our frames per second
|
|
||||||
ActionSequence actseq = _charmgr.getActionSequence(_action);
|
|
||||||
if (actseq == null) {
|
|
||||||
String errmsg = "No such action '" + _action + "'.";
|
|
||||||
throw new IllegalArgumentException(errmsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// obtain our animation frames for this action sequence
|
|
||||||
_aframes = _charmgr.getActionFrames(_descrip, _action);
|
|
||||||
|
|
||||||
// clear out our frames so that we recomposite on next tick
|
|
||||||
_frames = null;
|
|
||||||
|
|
||||||
// update the sprite render attributes
|
|
||||||
setFrameRate(actseq.framesPerSecond);
|
|
||||||
|
|
||||||
} catch (NoSuchComponentException nsce) {
|
|
||||||
Log.warning("Character sprite references non-existent " +
|
|
||||||
"component [sprite=" + this + ", err=" + nsce + "].");
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.warning("Failed to obtain action frames [sprite=" + this +
|
|
||||||
", descrip=" + _descrip + ", action=" + _action + "].");
|
|
||||||
Log.logStackTrace(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Called to recomposite our action frames if needed. */
|
|
||||||
protected final void compositeActionFrames ()
|
|
||||||
{
|
|
||||||
if (_frames == null && _aframes != null) {
|
|
||||||
setFrames(_aframes.getFrames(_orient));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Makes it easier to track down problems with bogus character descriptors.
|
|
||||||
*/
|
|
||||||
protected void sanityCheckDescrip ()
|
|
||||||
{
|
|
||||||
if (_descrip.getComponentIds() == null ||
|
|
||||||
_descrip.getComponentIds().length == 0) {
|
|
||||||
Log.warning("Invalid character descriptor [sprite=" + this +
|
|
||||||
", descrip=" + _descrip + "].");
|
|
||||||
Thread.dumpStack();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
protected boolean tickPath (long tickStamp)
|
|
||||||
{
|
|
||||||
boolean moved = super.tickPath(tickStamp);
|
|
||||||
// composite our action frames if our path caused them to become
|
|
||||||
// invalid
|
|
||||||
compositeActionFrames();
|
|
||||||
return moved;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
protected void updateRenderOrigin ()
|
|
||||||
{
|
|
||||||
super.updateRenderOrigin();
|
|
||||||
|
|
||||||
// adjust our image bounds to reflect the new location
|
|
||||||
_ibounds.x = _bounds.x + _ioff.x;
|
|
||||||
_ibounds.y = _bounds.y + _ioff.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
protected void accomodateFrame (int frameIdx, int width, int height)
|
|
||||||
{
|
|
||||||
// this will update our width and height
|
|
||||||
super.accomodateFrame(frameIdx, width, height);
|
|
||||||
|
|
||||||
// we now need to update the render offset for this frame
|
|
||||||
if (_aframes == null) {
|
|
||||||
Log.warning("Have no action frames! " + _aframes + ".");
|
|
||||||
} else {
|
|
||||||
_oxoff = _aframes.getXOrigin(_orient, frameIdx);
|
|
||||||
_oyoff = _aframes.getYOrigin(_orient, frameIdx);
|
|
||||||
}
|
|
||||||
|
|
||||||
// and cause those changes to be reflected in our bounds
|
|
||||||
updateRenderOrigin();
|
|
||||||
|
|
||||||
// start out with our bounds the same as our image bounds
|
|
||||||
_ibounds.setBounds(_bounds);
|
|
||||||
|
|
||||||
// now we can call down and incorporate the dimensions of any
|
|
||||||
// decorations that will be rendered along with our image
|
|
||||||
unionDecorationBounds(_bounds);
|
|
||||||
|
|
||||||
// compute our new render origin
|
|
||||||
_oxoff = _ox - _bounds.x;
|
|
||||||
_oyoff = _oy - _bounds.y;
|
|
||||||
|
|
||||||
// track the offset from our expanded bounds to our image bounds
|
|
||||||
_ioff.x = _ibounds.x - _bounds.x;
|
|
||||||
_ioff.y = _ibounds.y - _bounds.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by {@link #accomodateFrame} to give derived classes an
|
|
||||||
* opportunity to incorporate the bounds of any decorations that will
|
|
||||||
* be drawn along with this sprite. The {@link #_ibounds} rectangle
|
|
||||||
* will contain the bounds of the image that comprises the undecorated
|
|
||||||
* sprite. From that the position and size of decorations can be
|
|
||||||
* computed and unioned with the supplied bounds rectangle (most
|
|
||||||
* likely by using {@link SwingUtilities#computeUnion} which applies
|
|
||||||
* the union in place rather than creating a new rectangle).
|
|
||||||
*/
|
|
||||||
protected void unionDecorationBounds (Rectangle bounds)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the sprite animation frame to reflect the cessation of
|
|
||||||
* movement and disables any further animation.
|
|
||||||
*/
|
|
||||||
protected void halt ()
|
|
||||||
{
|
|
||||||
// only do something if we're actually animating
|
|
||||||
if (_animMode != NO_ANIMATION) {
|
|
||||||
// disable animation
|
|
||||||
setAnimationMode(NO_ANIMATION);
|
|
||||||
// come to a halt looking settled and at peace
|
|
||||||
String rest = getRestingAction();
|
|
||||||
if (rest != null) {
|
|
||||||
setActionSequence(rest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The action to use when at rest. */
|
|
||||||
protected String _restingAction = STANDING;
|
|
||||||
|
|
||||||
/** The action to use when following a path. */
|
|
||||||
protected String _followingPathAction = WALKING;
|
|
||||||
|
|
||||||
/** A reference to the descriptor for the character that we're
|
|
||||||
* visualizing. */
|
|
||||||
protected CharacterDescriptor _descrip;
|
|
||||||
|
|
||||||
/** A reference to the character manager that created us. */
|
|
||||||
protected CharacterManager _charmgr;
|
|
||||||
|
|
||||||
/** The action we are currently displaying. */
|
|
||||||
protected String _action;
|
|
||||||
|
|
||||||
/** The animation frames for the active action sequence in each
|
|
||||||
* orientation. */
|
|
||||||
protected ActionFrames _aframes;
|
|
||||||
|
|
||||||
/** The offset from the upper-left of the total sprite bounds to the
|
|
||||||
* upper-left of the image within those bounds. */
|
|
||||||
protected Point _ioff = new Point();
|
|
||||||
|
|
||||||
/** The bounds of the current sprite image. */
|
|
||||||
protected Rectangle _ibounds = new Rectangle();
|
|
||||||
}
|
|
||||||
@@ -1,241 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.Color;
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import com.samskivert.util.ArrayIntSet;
|
|
||||||
import com.samskivert.util.ComparableArrayList;
|
|
||||||
import com.samskivert.util.StringUtil;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
/** The action, if any, for which this override is appropriate. */
|
|
||||||
public String action;
|
|
||||||
|
|
||||||
/** The orientations, if any, for which this override is
|
|
||||||
* appropriate. */
|
|
||||||
public ArrayIntSet orients;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 + "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Increase this value when object's serialized state is impacted
|
|
||||||
* by a class change (modification of fields, inheritance). */
|
|
||||||
private static final long serialVersionUID = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The component class name. */
|
|
||||||
public String name;
|
|
||||||
|
|
||||||
/** The default render priority. */
|
|
||||||
public int renderPriority;
|
|
||||||
|
|
||||||
/** The color classes to use when recoloring components of this class. May
|
|
||||||
* be null if a system does not use recolorable components. */
|
|
||||||
public String[] colors;
|
|
||||||
|
|
||||||
/** The class name of the layer from which this component class obtains a
|
|
||||||
* mask to limit rendering to certain areas. */
|
|
||||||
public String mask;
|
|
||||||
|
|
||||||
/** Indicates the class name of the shadow layer to which this component
|
|
||||||
* class contributes a shadow. */
|
|
||||||
public String shadow;
|
|
||||||
|
|
||||||
/** 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
|
|
||||||
* population during XML parsing.
|
|
||||||
*/
|
|
||||||
public ComponentClass ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the render priority appropriate for the specified action
|
|
||||||
* and orientation.
|
|
||||||
*/
|
|
||||||
public int getRenderPriority (String action, int orientation)
|
|
||||||
{
|
|
||||||
// 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 ComparableArrayList();
|
|
||||||
}
|
|
||||||
_overrides.insertSorted(override);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if this component class contributes a shadow to a
|
|
||||||
* particular shadow layer. Note: this is different from <em>being</em> a
|
|
||||||
* shadow layer which is determined by calling {@link #isShadow}.
|
|
||||||
*/
|
|
||||||
public boolean isShadowed ()
|
|
||||||
{
|
|
||||||
return (shadow != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if this component class is a shadow layer rather than a
|
|
||||||
* normal component class.
|
|
||||||
*/
|
|
||||||
public boolean isShadow ()
|
|
||||||
{
|
|
||||||
return (shadowAlpha != 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Classes with the same name are the same.
|
|
||||||
*/
|
|
||||||
public boolean equals (Object other)
|
|
||||||
{
|
|
||||||
if (other instanceof ComponentClass) {
|
|
||||||
return name.equals(((ComponentClass)other).name);
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hashcode is based on component class name.
|
|
||||||
*/
|
|
||||||
public int hashCode ()
|
|
||||||
{
|
|
||||||
return name.hashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a string representation of this component class.
|
|
||||||
*/
|
|
||||||
public String toString ()
|
|
||||||
{
|
|
||||||
StringBuilder buf = new StringBuilder("[");
|
|
||||||
buf.append("name=").append(name);
|
|
||||||
buf.append(", pri=").append(renderPriority);
|
|
||||||
if (colors != null) {
|
|
||||||
buf.append(", colors=").append(StringUtil.toString(colors));
|
|
||||||
}
|
|
||||||
if (mask != null) {
|
|
||||||
buf.append(", mask=").append(mask);
|
|
||||||
}
|
|
||||||
if (shadowAlpha != 1.0f) {
|
|
||||||
buf.append(", shadow=").append(shadowAlpha);
|
|
||||||
} else if (shadow != null) {
|
|
||||||
buf.append(", shadow=").append(shadow);
|
|
||||||
}
|
|
||||||
return buf.append("]").toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A list of render priority overrides. */
|
|
||||||
protected ComparableArrayList _overrides;
|
|
||||||
|
|
||||||
/** Increase this value when object's serialized state is impacted by
|
|
||||||
* a class change (modification of fields, inheritance). */
|
|
||||||
private static final long serialVersionUID = 3;
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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 com.samskivert.io.PersistenceException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Brokers component ids. The component repository interface makes
|
|
||||||
* available a collection of components based on a unique identifier. The
|
|
||||||
* expectation is that a collection of components will be used to populate
|
|
||||||
* a repository and in that population process, component ids will be
|
|
||||||
* assigned to the components. The component id broker system provides a
|
|
||||||
* means by which named components can be mapped consistently to a set of
|
|
||||||
* component ids. Humans can then be responsible for assigning unique
|
|
||||||
* names to the components and the broker will ensure that those names map
|
|
||||||
* to unique ids that won't change if the repository is rebuilt from the
|
|
||||||
* source components.
|
|
||||||
*/
|
|
||||||
public interface ComponentIDBroker
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Returns the unique identifier for the named component. If no
|
|
||||||
* identifier has yet been assigned to the specified named component,
|
|
||||||
* one should be assigned and returned.
|
|
||||||
*
|
|
||||||
* @param cclass the name of the class to which the component belongs.
|
|
||||||
* @param cname the name of the component.
|
|
||||||
*
|
|
||||||
* @exception PersistenceException thrown if an error occurs
|
|
||||||
* communicating with the underlying persistence mechanism used to
|
|
||||||
* store the name to id mappings.
|
|
||||||
*/
|
|
||||||
public int getComponentID (String cclass, String cname)
|
|
||||||
throws PersistenceException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When the user of a component id broker is done obtaining component
|
|
||||||
* ids, it must call this method to give the component id broker an
|
|
||||||
* opportunity to flush any newly created component ids back to its
|
|
||||||
* persistent store.
|
|
||||||
*/
|
|
||||||
public void commit ()
|
|
||||||
throws PersistenceException;
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.util.Iterator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Makes available a collection of character components and associated
|
|
||||||
* metadata. Character components are animated sequences that can be
|
|
||||||
* composited together to create a complete character visualization
|
|
||||||
* (imagine interchanging pairs of boots, torsos, hats, etc.).
|
|
||||||
*/
|
|
||||||
public interface ComponentRepository
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Returns the {@link CharacterComponent} object for the given
|
|
||||||
* component identifier.
|
|
||||||
*/
|
|
||||||
public CharacterComponent getComponent (int componentId)
|
|
||||||
throws NoSuchComponentException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the {@link CharacterComponent} object with the given
|
|
||||||
* component class and name.
|
|
||||||
*/
|
|
||||||
public CharacterComponent getComponent (String className, String compName)
|
|
||||||
throws NoSuchComponentException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the {@link ComponentClass} with the specified name or null
|
|
||||||
* if none exists with that name.
|
|
||||||
*/
|
|
||||||
public ComponentClass getComponentClass (String className);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Iterates over the {@link ComponentClass} instances representing all
|
|
||||||
* available character component classes.
|
|
||||||
*/
|
|
||||||
public Iterator enumerateComponentClasses ();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Iterates over the {@link ActionSequence} instances representing
|
|
||||||
* every available action sequence.
|
|
||||||
*/
|
|
||||||
public Iterator enumerateActionSequences ();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Iterates over the component ids of all components in the specified
|
|
||||||
* class.
|
|
||||||
*/
|
|
||||||
public Iterator enumerateComponentIds (ComponentClass compClass);
|
|
||||||
}
|
|
||||||
@@ -1,186 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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 com.samskivert.util.LRUHashMap;
|
|
||||||
import com.samskivert.util.StringUtil;
|
|
||||||
|
|
||||||
import com.threerings.media.image.Colorization;
|
|
||||||
import com.threerings.media.image.ImageManager;
|
|
||||||
import com.threerings.media.util.MultiFrameImage;
|
|
||||||
|
|
||||||
import com.threerings.cast.CharacterComponent;
|
|
||||||
import com.threerings.util.DirectionCodes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An implementation of the {@link MultiFrameImage} interface that is used
|
|
||||||
* to lazily create composited character frames when they are requested.
|
|
||||||
*/
|
|
||||||
public class CompositedActionFrames
|
|
||||||
implements ActionFrames, DirectionCodes
|
|
||||||
{
|
|
||||||
/** 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 ComponentFrames () {
|
|
||||||
}
|
|
||||||
|
|
||||||
public ComponentFrames (
|
|
||||||
CharacterComponent ccomp, ActionFrames frames) {
|
|
||||||
this.ccomp = ccomp;
|
|
||||||
this.frames = 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 (ImageManager imgr, LRUHashMap frameCache,
|
|
||||||
String action, ComponentFrames[] sources)
|
|
||||||
{
|
|
||||||
// sanity check
|
|
||||||
if (sources == null || sources.length == 0) {
|
|
||||||
String errmsg = "Requested to composite invalid set of source " +
|
|
||||||
"frames! [action=" + action +
|
|
||||||
", sources=" + StringUtil.toString(sources) + "].";
|
|
||||||
throw new RuntimeException(errmsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
_imgr = imgr;
|
|
||||||
_frameCache = frameCache;
|
|
||||||
_sources = sources;
|
|
||||||
_action = action;
|
|
||||||
|
|
||||||
// the sources must all have the same orientation count, so we
|
|
||||||
// just use the first
|
|
||||||
_orientCount = _sources[0].frames.getOrientationCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface
|
|
||||||
public int getOrientationCount ()
|
|
||||||
{
|
|
||||||
return _orientCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface
|
|
||||||
public TrimmedMultiFrameImage getFrames (int orient)
|
|
||||||
{
|
|
||||||
_key.setOrient(orient);
|
|
||||||
CompositedMultiFrameImage cmfi =
|
|
||||||
(CompositedMultiFrameImage)_frameCache.get(_key);
|
|
||||||
if (cmfi == null) {
|
|
||||||
cmfi = createFrames(orient);
|
|
||||||
_frameCache.put(new CompositedFramesKey(orient), cmfi);
|
|
||||||
}
|
|
||||||
return cmfi;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface
|
|
||||||
public int getXOrigin (int orient, int frameIdx)
|
|
||||||
{
|
|
||||||
CompositedMultiFrameImage cmfi = (CompositedMultiFrameImage)
|
|
||||||
getFrames(orient);
|
|
||||||
return cmfi.getXOrigin(frameIdx);
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface
|
|
||||||
public int getYOrigin (int orient, int frameIdx)
|
|
||||||
{
|
|
||||||
CompositedMultiFrameImage cmfi = (CompositedMultiFrameImage)
|
|
||||||
getFrames(orient);
|
|
||||||
return cmfi.getYOrigin(frameIdx);
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface
|
|
||||||
public ActionFrames cloneColorized (Colorization[] zations)
|
|
||||||
{
|
|
||||||
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
|
|
||||||
{
|
|
||||||
public CompositedFramesKey (int orient) {
|
|
||||||
_orient = orient;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOrient (int orient) {
|
|
||||||
_orient = orient;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompositedActionFrames getOwner () {
|
|
||||||
return CompositedActionFrames.this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean equals (Object other) {
|
|
||||||
CompositedFramesKey okey = (CompositedFramesKey)other;
|
|
||||||
return ((getOwner() == okey.getOwner()) &&
|
|
||||||
(_orient == okey._orient));
|
|
||||||
}
|
|
||||||
|
|
||||||
public int hashCode () {
|
|
||||||
return CompositedActionFrames.this.hashCode() ^ _orient;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int _orient;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The image manager from whom we can obtain prepared volatile images
|
|
||||||
* onto which to render our composited actions. */
|
|
||||||
protected ImageManager _imgr;
|
|
||||||
|
|
||||||
/** Used to cache our composited action frame images. */
|
|
||||||
protected LRUHashMap _frameCache;
|
|
||||||
|
|
||||||
/** The action for which we're compositing frames. */
|
|
||||||
protected String _action;
|
|
||||||
|
|
||||||
/** The number of orientations. */
|
|
||||||
protected int _orientCount;
|
|
||||||
|
|
||||||
/** Our source components and action frames. */
|
|
||||||
protected ComponentFrames[] _sources;
|
|
||||||
|
|
||||||
/** Used to avoid creating a new key object every time we do a cache
|
|
||||||
* lookup. */
|
|
||||||
protected CompositedFramesKey _key = new CompositedFramesKey(0);
|
|
||||||
}
|
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
//
|
|
||||||
// $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.Color;
|
|
||||||
import java.awt.Composite;
|
|
||||||
import java.awt.Graphics2D;
|
|
||||||
import java.awt.Rectangle;
|
|
||||||
import java.awt.Transparency;
|
|
||||||
|
|
||||||
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 action frames with mask frames.
|
|
||||||
*/
|
|
||||||
public class CompositedMaskedImage extends CompositedMultiFrameImage
|
|
||||||
{
|
|
||||||
public CompositedMaskedImage (
|
|
||||||
ImageManager imgr, ComponentFrames[] sources, String action,
|
|
||||||
int orient)
|
|
||||||
{
|
|
||||||
super(imgr, sources, action, orient);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
_images[index].paint(g, x + _images[index].getX(),
|
|
||||||
y + _images[index].getY());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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());
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
protected CompositedMirage createCompositedMirage (int index)
|
|
||||||
{
|
|
||||||
return new MaskedMirage(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Combines the image in the first source with the mask in the second. */
|
|
||||||
protected class MaskedMirage extends CompositedMirage
|
|
||||||
{
|
|
||||||
public MaskedMirage (int index)
|
|
||||||
{
|
|
||||||
super(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
protected Rectangle combineBounds (Rectangle bounds, Rectangle tbounds)
|
|
||||||
{
|
|
||||||
if (bounds.width == 0 && bounds.height == 0) {
|
|
||||||
bounds.setBounds(tbounds);
|
|
||||||
} else {
|
|
||||||
bounds = bounds.intersection(tbounds);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
source.paintFrame(g, _index, -_bounds.x, -_bounds.y);
|
|
||||||
g.setComposite(AlphaComposite.DstIn);
|
|
||||||
mask.paintFrame(g, _index, -_bounds.x, -_bounds.y);
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
// clean up after ourselves
|
|
||||||
if (g != null) {
|
|
||||||
g.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,254 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.Graphics2D;
|
|
||||||
import java.awt.Point;
|
|
||||||
import java.awt.Rectangle;
|
|
||||||
import java.awt.Transparency;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Comparator;
|
|
||||||
|
|
||||||
import com.threerings.media.image.ImageManager;
|
|
||||||
import com.threerings.media.image.Mirage;
|
|
||||||
import com.threerings.media.image.VolatileMirage;
|
|
||||||
|
|
||||||
import com.threerings.cast.CompositedActionFrames.ComponentFrames;
|
|
||||||
import com.threerings.cast.TrimmedMultiFrameImage;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to composite the action frames for a particular orientation of a
|
|
||||||
* {@link CompositedActionFrames}.
|
|
||||||
*/
|
|
||||||
public class CompositedMultiFrameImage
|
|
||||||
implements TrimmedMultiFrameImage
|
|
||||||
{
|
|
||||||
public CompositedMultiFrameImage (
|
|
||||||
ImageManager imgr, ComponentFrames[] sources,
|
|
||||||
String action, int orient)
|
|
||||||
{
|
|
||||||
_imgr = imgr;
|
|
||||||
_sources = sources;
|
|
||||||
_action = action;
|
|
||||||
_orient = orient;
|
|
||||||
|
|
||||||
// create our frame images (which will do the compositing)
|
|
||||||
int fcount = sources[0].frames.getFrames(orient).getFrameCount();
|
|
||||||
_images = new CompositedMirage[fcount];
|
|
||||||
for (int ii = 0; ii < fcount; ii++) {
|
|
||||||
_images[ii] = createCompositedMirage(ii);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public int getFrameCount () {
|
|
||||||
return _images.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface
|
|
||||||
public int getWidth (int index) {
|
|
||||||
return _images[index].getWidth();
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface
|
|
||||||
public int getHeight (int index) {
|
|
||||||
return _images[index].getHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getXOrigin (int index) {
|
|
||||||
return _images[index].getXOrigin();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getYOrigin (int index) {
|
|
||||||
return _images[index].getYOrigin();
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface
|
|
||||||
public void paintFrame (Graphics2D g, int index, int x, int y) {
|
|
||||||
_images[index].paint(g, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface
|
|
||||||
public boolean hitTest (int index, int x, int y) {
|
|
||||||
return _images[index].hitTest(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface
|
|
||||||
public void getTrimmedBounds (int index, Rectangle bounds) {
|
|
||||||
bounds.setBounds(0, 0, getWidth(index), getHeight(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the estimated memory usage of our composited frame images.
|
|
||||||
*/
|
|
||||||
public long getEstimatedMemoryUsage ()
|
|
||||||
{
|
|
||||||
long size = 0;
|
|
||||||
for (int ii = 0; ii < _images.length; ii++) {
|
|
||||||
size += _images[ii].getEstimatedMemoryUsage();
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a composited image for the specified frame.
|
|
||||||
*/
|
|
||||||
protected CompositedMirage createCompositedMirage (int index)
|
|
||||||
{
|
|
||||||
return new CompositedMirage(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
protected Mirage getFrame (int orient, int index)
|
|
||||||
{
|
|
||||||
return _images[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The image manager from whom we load our images. */
|
|
||||||
protected ImageManager _imgr;
|
|
||||||
|
|
||||||
/** The action frames from which we obtain our source imagery. */
|
|
||||||
protected ComponentFrames[] _sources;
|
|
||||||
|
|
||||||
/** The action we're compositing. */
|
|
||||||
protected String _action;
|
|
||||||
|
|
||||||
/** The orientation we're compositing. */
|
|
||||||
protected int _orient;
|
|
||||||
|
|
||||||
/** Our composited action frame images. */
|
|
||||||
protected CompositedMirage[] _images;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to create our mirage using the source action frame images.
|
|
||||||
*/
|
|
||||||
protected class CompositedMirage extends VolatileMirage
|
|
||||||
implements Comparator
|
|
||||||
{
|
|
||||||
public CompositedMirage (int index)
|
|
||||||
{
|
|
||||||
super(CompositedMultiFrameImage.this._imgr,
|
|
||||||
new Rectangle(0, 0, 0, 0));
|
|
||||||
|
|
||||||
// keep this for later
|
|
||||||
_index = index;
|
|
||||||
|
|
||||||
// first we need to determine the bounds of the rectangle that
|
|
||||||
// will enclose all of our various components
|
|
||||||
Rectangle tbounds = new Rectangle();
|
|
||||||
int scount = _sources.length;
|
|
||||||
for (int ii = 0; ii < scount; ii++) {
|
|
||||||
TrimmedMultiFrameImage source =
|
|
||||||
_sources[ii].frames.getFrames(_orient);
|
|
||||||
source.getTrimmedBounds(index, tbounds);
|
|
||||||
_bounds = combineBounds(_bounds, tbounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
// compute our new origin
|
|
||||||
_origin.x = (_sources[0].frames.getXOrigin(_orient, index) -
|
|
||||||
_bounds.x);
|
|
||||||
_origin.y = (_sources[0].frames.getYOrigin(_orient, index) -
|
|
||||||
_bounds.y);
|
|
||||||
// Log.info("New origin [x=" + _origin.x + ", y=" + _origin.y + "].");
|
|
||||||
|
|
||||||
// render our volatile image for the first time
|
|
||||||
createVolatileImage();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getXOrigin ()
|
|
||||||
{
|
|
||||||
return _origin.x;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getYOrigin ()
|
|
||||||
{
|
|
||||||
return _origin.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface
|
|
||||||
public int compare (Object o1, Object o2)
|
|
||||||
{
|
|
||||||
ComponentFrames cf1 = (ComponentFrames)o1,
|
|
||||||
cf2 = (ComponentFrames)o2;
|
|
||||||
return (cf1.ccomp.componentClass.getRenderPriority(
|
|
||||||
_action, _orient) -
|
|
||||||
cf2.ccomp.componentClass.getRenderPriority(
|
|
||||||
_action, _orient));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Combines the working bounds with a new set of bounds.
|
|
||||||
*/
|
|
||||||
protected Rectangle combineBounds (Rectangle bounds, Rectangle tbounds)
|
|
||||||
{
|
|
||||||
// the first one defines our initial bounds
|
|
||||||
if (bounds.width == 0 && bounds.height == 0) {
|
|
||||||
bounds.setBounds(tbounds);
|
|
||||||
} else {
|
|
||||||
bounds.add(tbounds);
|
|
||||||
}
|
|
||||||
return bounds;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
protected int getTransparency ()
|
|
||||||
{
|
|
||||||
return Transparency.BITMASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
protected void refreshVolatileImage ()
|
|
||||||
{
|
|
||||||
// long start = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// sort the sources appropriately for this orientation
|
|
||||||
Arrays.sort(_sources, this);
|
|
||||||
|
|
||||||
// now render each of the components into a composited frame
|
|
||||||
int scount = _sources.length;
|
|
||||||
Graphics2D g = (Graphics2D)_image.getGraphics();
|
|
||||||
try {
|
|
||||||
for (int ii = 0; ii < scount; ii++) {
|
|
||||||
TrimmedMultiFrameImage source =
|
|
||||||
_sources[ii].frames.getFrames(_orient);
|
|
||||||
source.paintFrame(g, _index, -_bounds.x, -_bounds.y);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
// clean up after ourselves
|
|
||||||
if (g != null) {
|
|
||||||
g.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log.info("Composited [orient=" + _orient + ", index=" + _index +
|
|
||||||
// ", tbounds=" + StringUtil.toString(_bounds) + "].");
|
|
||||||
|
|
||||||
// long now = System.currentTimeMillis();
|
|
||||||
// Log.info("Composited " + scount + " frames in " +
|
|
||||||
// (now-start) + " millis.");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int _index;
|
|
||||||
protected Point _origin = new Point();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
//
|
|
||||||
// $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,37 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides a mechanism where by a character component can obtain access
|
|
||||||
* to its image frames for a particular action in an on demand manner.
|
|
||||||
*/
|
|
||||||
public interface FrameProvider
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Returns the animation frames (in the eight sprite directions) for
|
|
||||||
* the specified action of the specified component. May return null if
|
|
||||||
* the specified action does not exist for the specified component.
|
|
||||||
*/
|
|
||||||
public ActionFrames getFrames (
|
|
||||||
CharacterComponent component, String action, String type);
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A placeholder class that contains a reference to the log object used by
|
|
||||||
* the cast package.
|
|
||||||
*/
|
|
||||||
public class Log
|
|
||||||
{
|
|
||||||
public static com.samskivert.util.Log log =
|
|
||||||
new com.samskivert.util.Log("cast");
|
|
||||||
|
|
||||||
/** Convenience function. */
|
|
||||||
public static void debug (String message)
|
|
||||||
{
|
|
||||||
log.debug(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Convenience function. */
|
|
||||||
public static void info (String message)
|
|
||||||
{
|
|
||||||
log.info(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Convenience function. */
|
|
||||||
public static void warning (String message)
|
|
||||||
{
|
|
||||||
log.warning(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Convenience function. */
|
|
||||||
public static void logStackTrace (Throwable t)
|
|
||||||
{
|
|
||||||
log.logStackTrace(com.samskivert.util.Log.WARNING, t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Thrown when an attempt is made to retrieve a non-existent character
|
|
||||||
* component from the component repository.
|
|
||||||
*/
|
|
||||||
public class NoSuchComponentException extends Exception
|
|
||||||
{
|
|
||||||
public NoSuchComponentException (int componentId)
|
|
||||||
{
|
|
||||||
super("No such component [componentId=" + componentId + "]");
|
|
||||||
_componentId = componentId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NoSuchComponentException (
|
|
||||||
String componentClass, String componentName)
|
|
||||||
{
|
|
||||||
super("No such component [class=" + componentClass +
|
|
||||||
", name=" + componentName + "]");
|
|
||||||
_componentId = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getComponentId ()
|
|
||||||
{
|
|
||||||
return _componentId;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int _componentId;
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Actions are referenced by name and this interface defines constants for
|
|
||||||
* standard actions and action suffixes used by the shadow and cropping
|
|
||||||
* support.
|
|
||||||
*/
|
|
||||||
public interface StandardActions
|
|
||||||
{
|
|
||||||
/** The name of the standard standing action. */
|
|
||||||
public static final String STANDING = "standing";
|
|
||||||
|
|
||||||
/** The name of the standard walking action. */
|
|
||||||
public static final String WALKING = "walking";
|
|
||||||
|
|
||||||
/** A special action sub-type for shadow imagery. */
|
|
||||||
public static final String SHADOW_TYPE = "shadow";
|
|
||||||
|
|
||||||
/** A special action sub-type for crop imagery. */
|
|
||||||
public static final String CROP_TYPE = "crop";
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.Rectangle;
|
|
||||||
|
|
||||||
import com.threerings.media.util.MultiFrameImage;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to generate more memory efficient composited images in
|
|
||||||
* circumstances where we have trimmed underlying component images.
|
|
||||||
*/
|
|
||||||
public interface TrimmedMultiFrameImage extends MultiFrameImage
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Fills in the minimum bounding rectangle for this image that
|
|
||||||
* contains all non-transparent pixels. If this information is
|
|
||||||
* unavailable, the bounds of the entire image may be returned in
|
|
||||||
* exchange for improved performance.
|
|
||||||
*/
|
|
||||||
public void getTrimmedBounds (int index, Rectangle bounds);
|
|
||||||
}
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.builder;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.samskivert.util.CollectionUtil;
|
|
||||||
|
|
||||||
import com.threerings.cast.ComponentRepository;
|
|
||||||
import com.threerings.cast.ComponentClass;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The builder model represents the current state of the character the
|
|
||||||
* user is building.
|
|
||||||
*/
|
|
||||||
public class BuilderModel
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Constructs a builder model.
|
|
||||||
*/
|
|
||||||
public BuilderModel (ComponentRepository crepo)
|
|
||||||
{
|
|
||||||
gatherComponentInfo(crepo);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a builder model listener.
|
|
||||||
*
|
|
||||||
* @param l the listener.
|
|
||||||
*/
|
|
||||||
public void addListener (BuilderModelListener l)
|
|
||||||
{
|
|
||||||
if (!_listeners.contains(l)) {
|
|
||||||
_listeners.add(l);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notifies all model listeners that the builder model has changed.
|
|
||||||
*/
|
|
||||||
protected void notifyListeners (int event)
|
|
||||||
{
|
|
||||||
int size = _listeners.size();
|
|
||||||
for (int ii = 0; ii < size; ii++) {
|
|
||||||
((BuilderModelListener)_listeners.get(ii)).modelChanged(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list of the available component classes.
|
|
||||||
*/
|
|
||||||
public List getComponentClasses ()
|
|
||||||
{
|
|
||||||
return Collections.unmodifiableList(_classes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the list of components available in the specified class.
|
|
||||||
*/
|
|
||||||
public List getComponents (ComponentClass cclass)
|
|
||||||
{
|
|
||||||
List list = (List)_components.get(cclass);
|
|
||||||
if (list == null) {
|
|
||||||
list = new ArrayList();
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the selected components in an array.
|
|
||||||
*/
|
|
||||||
public int[] getSelectedComponents ()
|
|
||||||
{
|
|
||||||
int[] values = new int[_selected.size()];
|
|
||||||
Iterator iter = _selected.values().iterator();
|
|
||||||
for (int i = 0; iter.hasNext(); i++) {
|
|
||||||
values[i] = ((Integer)iter.next()).intValue();
|
|
||||||
}
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the selected component for the given component class.
|
|
||||||
*/
|
|
||||||
public void setSelectedComponent (ComponentClass cclass, int cid)
|
|
||||||
{
|
|
||||||
_selected.put(cclass, Integer.valueOf(cid));
|
|
||||||
notifyListeners(BuilderModelListener.COMPONENT_CHANGED);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gathers component class and component information from the
|
|
||||||
* character manager for later reference by others.
|
|
||||||
*/
|
|
||||||
protected void gatherComponentInfo (ComponentRepository crepo)
|
|
||||||
{
|
|
||||||
// get the list of all component classes
|
|
||||||
CollectionUtil.addAll(_classes, crepo.enumerateComponentClasses());
|
|
||||||
|
|
||||||
for (int ii = 0; ii < _classes.size(); ii++) {
|
|
||||||
// get the list of components available for this class
|
|
||||||
ComponentClass cclass = (ComponentClass)_classes.get(ii);
|
|
||||||
Iterator iter = crepo.enumerateComponentIds(cclass);
|
|
||||||
|
|
||||||
while (iter.hasNext()) {
|
|
||||||
Integer cid = (Integer)iter.next();
|
|
||||||
ArrayList clist = (ArrayList)_components.get(cclass);
|
|
||||||
if (clist == null) {
|
|
||||||
_components.put(cclass, clist = new ArrayList());
|
|
||||||
}
|
|
||||||
|
|
||||||
clist.add(cid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The currently selected character components. */
|
|
||||||
protected HashMap _selected = new HashMap();
|
|
||||||
|
|
||||||
/** The hashtable of available component ids for each class. */
|
|
||||||
protected HashMap _components = new HashMap();
|
|
||||||
|
|
||||||
/** The list of all available component classes. */
|
|
||||||
protected ArrayList _classes = new ArrayList();
|
|
||||||
|
|
||||||
/** The model listeners. */
|
|
||||||
protected ArrayList _listeners = new ArrayList();
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.builder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The builder model listener interface should be implemented by
|
|
||||||
* classes that would like to be notified when the builder model is
|
|
||||||
* changed.
|
|
||||||
*
|
|
||||||
* @see BuilderModel
|
|
||||||
*/
|
|
||||||
public interface BuilderModelListener
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Called by the {@link BuilderModel} when the model is changed.
|
|
||||||
*/
|
|
||||||
public void modelChanged (int event);
|
|
||||||
|
|
||||||
/** Notification event constants. */
|
|
||||||
public static final int COMPONENT_CHANGED = 0;
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.builder;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import com.samskivert.swing.*;
|
|
||||||
|
|
||||||
import com.threerings.cast.CharacterManager;
|
|
||||||
import com.threerings.cast.ComponentRepository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The builder panel presents the user with an overview of a composited
|
|
||||||
* character and facilities for altering the individual components that
|
|
||||||
* comprise the character's display image.
|
|
||||||
*/
|
|
||||||
public class BuilderPanel extends JPanel
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Constructs the builder panel.
|
|
||||||
*/
|
|
||||||
public BuilderPanel (CharacterManager charmgr,
|
|
||||||
ComponentRepository crepo, String cprefix)
|
|
||||||
{
|
|
||||||
setLayout(new VGroupLayout());
|
|
||||||
|
|
||||||
// give ourselves a wee bit of a border
|
|
||||||
setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
|
||||||
|
|
||||||
GroupLayout gl = new HGroupLayout(GroupLayout.STRETCH);
|
|
||||||
gl.setOffAxisPolicy(GroupLayout.STRETCH);
|
|
||||||
|
|
||||||
// create the builder model
|
|
||||||
BuilderModel model = new BuilderModel(crepo);
|
|
||||||
|
|
||||||
// create the component selection and sprite display panels
|
|
||||||
JPanel sub = new JPanel(gl);
|
|
||||||
sub.add(new ComponentPanel(model, cprefix));
|
|
||||||
sub.add(new SpritePanel(charmgr, model));
|
|
||||||
add(sub);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.builder;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
import javax.swing.event.ChangeEvent;
|
|
||||||
import javax.swing.event.ChangeListener;
|
|
||||||
|
|
||||||
import com.samskivert.swing.*;
|
|
||||||
|
|
||||||
import com.threerings.cast.ComponentClass;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The class editor displays a label and a slider that allow the user to
|
|
||||||
* select the desired component for a given component class.
|
|
||||||
*/
|
|
||||||
public class ClassEditor extends JPanel implements ChangeListener
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Constructs a class editor.
|
|
||||||
*/
|
|
||||||
public ClassEditor (
|
|
||||||
BuilderModel model, ComponentClass cclass, List components)
|
|
||||||
{
|
|
||||||
_model = model;
|
|
||||||
_components = components;
|
|
||||||
_cclass = cclass;
|
|
||||||
|
|
||||||
GroupLayout gl = new VGroupLayout(GroupLayout.STRETCH);
|
|
||||||
gl.setOffAxisPolicy(GroupLayout.STRETCH);
|
|
||||||
setLayout(gl);
|
|
||||||
|
|
||||||
gl = new HGroupLayout();
|
|
||||||
gl.setJustification(GroupLayout.LEFT);
|
|
||||||
JPanel sub = new JPanel(gl);
|
|
||||||
|
|
||||||
sub.add(new JLabel(cclass.name + ": "));
|
|
||||||
sub.add(_clabel = new JLabel("0"));
|
|
||||||
add(sub);
|
|
||||||
|
|
||||||
// create the slider allowing selection of available components
|
|
||||||
int max = components.size() - 1;
|
|
||||||
JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, max, 0);
|
|
||||||
slider.setSnapToTicks(true);
|
|
||||||
slider.addChangeListener(this);
|
|
||||||
add(slider);
|
|
||||||
|
|
||||||
// set the starting component for this class
|
|
||||||
setSelectedComponent(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public void stateChanged (ChangeEvent e)
|
|
||||||
{
|
|
||||||
JSlider source = (JSlider)e.getSource();
|
|
||||||
if (!source.getValueIsAdjusting()) {
|
|
||||||
int val = source.getValue();
|
|
||||||
// update the model with the newly selected component
|
|
||||||
setSelectedComponent(val);
|
|
||||||
// update the label with the new value
|
|
||||||
_clabel.setText(Integer.toString(val));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the selected component in the builder model for the
|
|
||||||
* component class associated with this editor to the component at
|
|
||||||
* the given index in this editor's list of available components.
|
|
||||||
*/
|
|
||||||
protected void setSelectedComponent (int idx)
|
|
||||||
{
|
|
||||||
int cid = ((Integer)_components.get(idx)).intValue();
|
|
||||||
_model.setSelectedComponent(_cclass, cid);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The component class associated with this editor. */
|
|
||||||
protected ComponentClass _cclass;
|
|
||||||
|
|
||||||
/** The components selectable via this editor. */
|
|
||||||
protected List _components;
|
|
||||||
|
|
||||||
/** The label denoting the currently selected component index. */
|
|
||||||
protected JLabel _clabel;
|
|
||||||
|
|
||||||
/** The builder model. */
|
|
||||||
protected BuilderModel _model;
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.builder;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
import com.samskivert.swing.*;
|
|
||||||
|
|
||||||
import com.threerings.cast.Log;
|
|
||||||
import com.threerings.cast.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The component panel displays the available components for all
|
|
||||||
* component classes and allows the user to choose a set of components
|
|
||||||
* for compositing into a character image.
|
|
||||||
*/
|
|
||||||
public class ComponentPanel extends JPanel
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Constructs the component panel.
|
|
||||||
*/
|
|
||||||
public ComponentPanel (BuilderModel model, String cprefix)
|
|
||||||
{
|
|
||||||
setLayout(new VGroupLayout(GroupLayout.STRETCH));
|
|
||||||
// set up a border
|
|
||||||
setBorder(BorderFactory.createEtchedBorder());
|
|
||||||
// add the component editors to the panel
|
|
||||||
addClassEditors(model, cprefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds editor user interface elements for each component class to
|
|
||||||
* allow the user to select the desired component.
|
|
||||||
*/
|
|
||||||
protected void addClassEditors (BuilderModel model, String cprefix)
|
|
||||||
{
|
|
||||||
List classes = model.getComponentClasses();
|
|
||||||
int size = classes.size();
|
|
||||||
for (int ii = 0; ii < size; ii++) {
|
|
||||||
ComponentClass cclass = (ComponentClass)classes.get(ii);
|
|
||||||
if (!cclass.name.startsWith(cprefix)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
List ccomps = model.getComponents(cclass);
|
|
||||||
if (ccomps.size() > 0) {
|
|
||||||
add(new ClassEditor(model, cclass, ccomps));
|
|
||||||
} else {
|
|
||||||
Log.info("Not creating editor for empty class " +
|
|
||||||
"[class=" + cclass + "].");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.builder;
|
|
||||||
|
|
||||||
import java.awt.Dimension;
|
|
||||||
import java.awt.Graphics;
|
|
||||||
import java.awt.Graphics2D;
|
|
||||||
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
|
|
||||||
import com.threerings.util.DirectionCodes;
|
|
||||||
|
|
||||||
import com.threerings.cast.CharacterDescriptor;
|
|
||||||
import com.threerings.cast.CharacterManager;
|
|
||||||
import com.threerings.cast.CharacterSprite;
|
|
||||||
import com.threerings.cast.StandardActions;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The sprite panel displays a character sprite centered in the panel
|
|
||||||
* suitable for user perusal.
|
|
||||||
*/
|
|
||||||
public class SpritePanel extends JPanel
|
|
||||||
implements DirectionCodes, BuilderModelListener
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Constructs the sprite panel.
|
|
||||||
*/
|
|
||||||
public SpritePanel (CharacterManager charmgr, BuilderModel model)
|
|
||||||
{
|
|
||||||
// save off references
|
|
||||||
_charmgr = charmgr;
|
|
||||||
_model = model;
|
|
||||||
|
|
||||||
// listen to the builder model so that we can update the
|
|
||||||
// sprite when a new component is selected
|
|
||||||
_model.addListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public void paintComponent (Graphics g)
|
|
||||||
{
|
|
||||||
super.paintComponent(g);
|
|
||||||
Graphics2D gfx = (Graphics2D)g;
|
|
||||||
|
|
||||||
if (_sprite != null) {
|
|
||||||
// render the sprite
|
|
||||||
_sprite.paint(gfx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public void doLayout ()
|
|
||||||
{
|
|
||||||
super.doLayout();
|
|
||||||
generateSprite();
|
|
||||||
centerSprite();
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public void modelChanged (int event)
|
|
||||||
{
|
|
||||||
if (event == COMPONENT_CHANGED) {
|
|
||||||
generateSprite();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a new character sprite for display to reflect the
|
|
||||||
* currently selected character components.
|
|
||||||
*/
|
|
||||||
protected void generateSprite ()
|
|
||||||
{
|
|
||||||
int components[] = _model.getSelectedComponents();
|
|
||||||
CharacterDescriptor desc = new CharacterDescriptor(components, null);
|
|
||||||
CharacterSprite sprite = _charmgr.getCharacter(desc);
|
|
||||||
setSprite(sprite);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the sprite to be displayed.
|
|
||||||
*/
|
|
||||||
protected void setSprite (CharacterSprite sprite)
|
|
||||||
{
|
|
||||||
sprite.setActionSequence(StandardActions.STANDING);
|
|
||||||
sprite.setOrientation(WEST);
|
|
||||||
_sprite = sprite;
|
|
||||||
centerSprite();
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the sprite's location to render it centered within the panel.
|
|
||||||
*/
|
|
||||||
protected void centerSprite ()
|
|
||||||
{
|
|
||||||
if (_sprite != null) {
|
|
||||||
Dimension d = getSize();
|
|
||||||
int swid = _sprite.getWidth(), shei = _sprite.getHeight();
|
|
||||||
int x = d.width / 2, y = (d.height + shei) / 2;
|
|
||||||
_sprite.setLocation(x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The sprite displayed by the panel. */
|
|
||||||
protected CharacterSprite _sprite;
|
|
||||||
|
|
||||||
/** The character manager. */
|
|
||||||
protected CharacterManager _charmgr;
|
|
||||||
|
|
||||||
/** The builder model. */
|
|
||||||
protected BuilderModel _model;
|
|
||||||
}
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.bundle;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InvalidClassException;
|
|
||||||
import java.io.ObjectInputStream;
|
|
||||||
|
|
||||||
import com.samskivert.io.StreamUtil;
|
|
||||||
|
|
||||||
import com.threerings.cast.Log;
|
|
||||||
import com.threerings.resource.ResourceBundle;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility functions related to creating and manipulating component
|
|
||||||
* bundles.
|
|
||||||
*/
|
|
||||||
public class BundleUtil
|
|
||||||
{
|
|
||||||
/** The path in the metadata bundle to the serialized action table. */
|
|
||||||
public static final String ACTIONS_PATH = "actions.dat";
|
|
||||||
|
|
||||||
/** The path in the metadata bundle to the serialized action tile sets
|
|
||||||
* table. */
|
|
||||||
public static final String ACTION_SETS_PATH = "action_sets.dat";
|
|
||||||
|
|
||||||
/** The path in the metadata bundle to the serialized component class
|
|
||||||
* table. */
|
|
||||||
public static final String CLASSES_PATH = "classes.dat";
|
|
||||||
|
|
||||||
/** The path in the component bundle to the serialized component id to
|
|
||||||
* class/type mapping. */
|
|
||||||
public static final String COMPONENTS_PATH = "components.dat";
|
|
||||||
|
|
||||||
/** The file extension of our action tile images. */
|
|
||||||
public static final String IMAGE_EXTENSION = ".png";
|
|
||||||
|
|
||||||
/** The serialized tileset extension for our action tilesets. */
|
|
||||||
public static final String TILESET_EXTENSION = ".dat";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to load an object from the supplied resource bundle with
|
|
||||||
* the specified path.
|
|
||||||
*
|
|
||||||
* @param wipeBundleOnFailure if there is an error reading the object
|
|
||||||
* from the bundle and this parameter is true, we will instruct the
|
|
||||||
* bundle to delete its underlying jar file before propagating the
|
|
||||||
* exception with the expectation that it will be redownloaded and
|
|
||||||
* repaired the next time the application is run.
|
|
||||||
*
|
|
||||||
* @return the unserialized object in question.
|
|
||||||
*
|
|
||||||
* @exception IOException thrown if an I/O error occurs while reading
|
|
||||||
* the object from the bundle.
|
|
||||||
*/
|
|
||||||
public static Object loadObject (ResourceBundle bundle, String path,
|
|
||||||
boolean wipeBundleOnFailure)
|
|
||||||
throws IOException, ClassNotFoundException
|
|
||||||
{
|
|
||||||
InputStream bin = null;
|
|
||||||
try {
|
|
||||||
bin = bundle.getResource(path);
|
|
||||||
if (bin == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return new ObjectInputStream(bin).readObject();
|
|
||||||
|
|
||||||
} catch (InvalidClassException ice) {
|
|
||||||
Log.warning("Aiya! Serialized object is hosed " +
|
|
||||||
"[bundle=" + bundle.getSource().getPath() +
|
|
||||||
", element=" + path +
|
|
||||||
", error=" + ice.getMessage() + "].");
|
|
||||||
return null;
|
|
||||||
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
Log.warning("Error reading resource from bundle " +
|
|
||||||
"[bundle=" + bundle + ", path=" + path +
|
|
||||||
", wiping?=" + wipeBundleOnFailure + "].");
|
|
||||||
if (wipeBundleOnFailure) {
|
|
||||||
StreamUtil.close(bin);
|
|
||||||
bin = null;
|
|
||||||
bundle.wipeBundle(false);
|
|
||||||
}
|
|
||||||
throw ioe;
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
StreamUtil.close(bin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,528 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.bundle;
|
|
||||||
|
|
||||||
import java.awt.Graphics2D;
|
|
||||||
import java.awt.Rectangle;
|
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
import com.samskivert.util.HashIntMap;
|
|
||||||
import com.samskivert.util.IntIntMap;
|
|
||||||
import com.samskivert.util.Tuple;
|
|
||||||
|
|
||||||
import org.apache.commons.collections.iterators.FilterIterator;
|
|
||||||
import org.apache.commons.collections.Predicate;
|
|
||||||
|
|
||||||
import com.threerings.resource.ResourceBundle;
|
|
||||||
import com.threerings.resource.ResourceManager;
|
|
||||||
|
|
||||||
import com.threerings.media.image.Colorization;
|
|
||||||
import com.threerings.media.image.FastImageIO;
|
|
||||||
import com.threerings.media.image.ImageDataProvider;
|
|
||||||
import com.threerings.media.image.ImageManager;
|
|
||||||
|
|
||||||
import com.threerings.media.tile.IMImageProvider;
|
|
||||||
import com.threerings.media.tile.Tile;
|
|
||||||
import com.threerings.media.tile.TileSet;
|
|
||||||
import com.threerings.media.tile.TrimmedTile;
|
|
||||||
|
|
||||||
import com.threerings.util.DirectionCodes;
|
|
||||||
|
|
||||||
import com.threerings.cast.ActionFrames;
|
|
||||||
import com.threerings.cast.ActionSequence;
|
|
||||||
import com.threerings.cast.CharacterComponent;
|
|
||||||
import com.threerings.cast.ComponentClass;
|
|
||||||
import com.threerings.cast.ComponentRepository;
|
|
||||||
import com.threerings.cast.FrameProvider;
|
|
||||||
import com.threerings.cast.Log;
|
|
||||||
import com.threerings.cast.NoSuchComponentException;
|
|
||||||
import com.threerings.cast.StandardActions;
|
|
||||||
import com.threerings.cast.TrimmedMultiFrameImage;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A component repository implementation that obtains information from
|
|
||||||
* resource bundles.
|
|
||||||
*
|
|
||||||
* @see ResourceManager
|
|
||||||
*/
|
|
||||||
public class BundledComponentRepository
|
|
||||||
implements DirectionCodes, ComponentRepository
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Constructs a repository which will obtain its resource set from the
|
|
||||||
* supplied resource manager.
|
|
||||||
*
|
|
||||||
* @param rmgr the resource manager from which to obtain our resource
|
|
||||||
* set.
|
|
||||||
* @param imgr the image manager that we'll use to decode and cache
|
|
||||||
* images.
|
|
||||||
* @param name the name of the resource set from which we will be
|
|
||||||
* loading our component data.
|
|
||||||
*
|
|
||||||
* @exception IOException thrown if an I/O error occurs while reading
|
|
||||||
* our metadata from the resource bundles.
|
|
||||||
*/
|
|
||||||
public BundledComponentRepository (
|
|
||||||
ResourceManager rmgr, ImageManager imgr, String name)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
// keep this guy around
|
|
||||||
_imgr = imgr;
|
|
||||||
|
|
||||||
// first we obtain the resource set from whence will come our
|
|
||||||
// bundles
|
|
||||||
ResourceBundle[] rbundles = rmgr.getResourceSet(name);
|
|
||||||
|
|
||||||
// look for our metadata info in each of the bundles
|
|
||||||
try {
|
|
||||||
int rcount = (rbundles == null) ? 0 : rbundles.length;
|
|
||||||
for (int i = 0; i < rcount; i++) {
|
|
||||||
if (_actions == null) {
|
|
||||||
_actions = (HashMap)BundleUtil.loadObject(
|
|
||||||
rbundles[i], BundleUtil.ACTIONS_PATH, true);
|
|
||||||
}
|
|
||||||
if (_actionSets == null) {
|
|
||||||
_actionSets = (HashMap)BundleUtil.loadObject(
|
|
||||||
rbundles[i], BundleUtil.ACTION_SETS_PATH, true);
|
|
||||||
}
|
|
||||||
if (_classes == null) {
|
|
||||||
_classes = (HashMap)BundleUtil.loadObject(
|
|
||||||
rbundles[i], BundleUtil.CLASSES_PATH, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// now go back and load up all of the component information
|
|
||||||
for (int i = 0; i < rcount; i++) {
|
|
||||||
HashIntMap comps = null;
|
|
||||||
comps = (HashIntMap)BundleUtil.loadObject(
|
|
||||||
rbundles[i], BundleUtil.COMPONENTS_PATH, true);
|
|
||||||
if (comps == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a frame provider for this bundle
|
|
||||||
FrameProvider fprov =
|
|
||||||
new ResourceBundleProvider(_imgr, rbundles[i]);
|
|
||||||
|
|
||||||
// now create character component instances for each component
|
|
||||||
// in the serialized table
|
|
||||||
Iterator iter = comps.keySet().iterator();
|
|
||||||
while (iter.hasNext()) {
|
|
||||||
int componentId = ((Integer)iter.next()).intValue();
|
|
||||||
Tuple info = (Tuple)comps.get(componentId);
|
|
||||||
createComponent(componentId, (String)info.left,
|
|
||||||
(String)info.right, fprov);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (ClassNotFoundException cnfe) {
|
|
||||||
throw (IOException) new IOException(
|
|
||||||
"Internal error unserializing metadata").initCause(cnfe);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we failed to load our classes or actions, create empty
|
|
||||||
// hashtables so that we can safely enumerate our emptiness
|
|
||||||
if (_actions == null) {
|
|
||||||
_actions = new HashMap();
|
|
||||||
}
|
|
||||||
if (_classes == null) {
|
|
||||||
_classes = new HashMap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures the bundled component repository to wipe any bundles
|
|
||||||
* that report certain kinds of failure. In the event that an unpacked
|
|
||||||
* bundle becomes corrupt, this is useful in that it will force the
|
|
||||||
* bundle to be unpacked on the next application invocation,
|
|
||||||
* potentially remedying the problem of a corrupt unpacking.
|
|
||||||
*/
|
|
||||||
public void setWipeOnFailure (boolean wipeOnFailure)
|
|
||||||
{
|
|
||||||
_wipeOnFailure = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public CharacterComponent getComponent (int componentId)
|
|
||||||
throws NoSuchComponentException
|
|
||||||
{
|
|
||||||
CharacterComponent component = (CharacterComponent)
|
|
||||||
_components.get(componentId);
|
|
||||||
if (component == null) {
|
|
||||||
throw new NoSuchComponentException(componentId);
|
|
||||||
}
|
|
||||||
return component;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public CharacterComponent getComponent (String className, String compName)
|
|
||||||
throws NoSuchComponentException
|
|
||||||
{
|
|
||||||
// look up the list for that class
|
|
||||||
ArrayList comps = (ArrayList)_classComps.get(className);
|
|
||||||
if (comps != null) {
|
|
||||||
// scan the list for the named component
|
|
||||||
int ccount = comps.size();
|
|
||||||
for (int i = 0; i < ccount; i++) {
|
|
||||||
CharacterComponent comp = (CharacterComponent)comps.get(i);
|
|
||||||
if (comp.name.equals(compName)) {
|
|
||||||
return comp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new NoSuchComponentException(className, compName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public ComponentClass getComponentClass (String className)
|
|
||||||
{
|
|
||||||
return (ComponentClass)_classes.get(className);
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public Iterator enumerateComponentClasses ()
|
|
||||||
{
|
|
||||||
return _classes.values().iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public Iterator enumerateActionSequences ()
|
|
||||||
{
|
|
||||||
return _actions.values().iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public Iterator enumerateComponentIds (final ComponentClass compClass)
|
|
||||||
{
|
|
||||||
Predicate classP = new Predicate() {
|
|
||||||
public boolean evaluate (Object input) {
|
|
||||||
CharacterComponent comp = (CharacterComponent)
|
|
||||||
_components.get(input);
|
|
||||||
return comp.componentClass.equals(compClass);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return new FilterIterator(_components.keySet().iterator(), classP);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a component and inserts it into the component table.
|
|
||||||
*/
|
|
||||||
protected void createComponent (
|
|
||||||
int componentId, String cclass, String cname, FrameProvider fprov)
|
|
||||||
{
|
|
||||||
// look up the component class information
|
|
||||||
ComponentClass clazz = (ComponentClass)_classes.get(cclass);
|
|
||||||
if (clazz == null) {
|
|
||||||
Log.warning("Non-existent component class " +
|
|
||||||
"[class=" + cclass + ", name=" + cname +
|
|
||||||
", id=" + componentId + "].");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// create the component
|
|
||||||
CharacterComponent component = new CharacterComponent(
|
|
||||||
componentId, cname, clazz, fprov);
|
|
||||||
|
|
||||||
// stick it into the appropriate tables
|
|
||||||
_components.put(componentId, component);
|
|
||||||
|
|
||||||
// we have a hash of lists for mapping components by class/name
|
|
||||||
ArrayList comps = (ArrayList)_classComps.get(cclass);
|
|
||||||
if (comps == null) {
|
|
||||||
comps = new ArrayList();
|
|
||||||
_classComps.put(cclass, comps);
|
|
||||||
}
|
|
||||||
if (!comps.contains(component)) {
|
|
||||||
comps.add(component);
|
|
||||||
} else {
|
|
||||||
Log.info("Requested to register the same component twice? " +
|
|
||||||
"[component=" + component + "].");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instances of these provide images to our component action tilesets
|
|
||||||
* and frames to our components.
|
|
||||||
*/
|
|
||||||
protected class ResourceBundleProvider extends IMImageProvider
|
|
||||||
implements ImageDataProvider, FrameProvider
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Constructs an instance that will obtain image data from the
|
|
||||||
* specified resource bundle.
|
|
||||||
*/
|
|
||||||
public ResourceBundleProvider (ImageManager imgr, ResourceBundle bundle)
|
|
||||||
{
|
|
||||||
super(imgr, (String)null);
|
|
||||||
_dprov = this;
|
|
||||||
_bundle = bundle;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface
|
|
||||||
public String getIdent ()
|
|
||||||
{
|
|
||||||
return "bcr:" + _bundle.getSource();
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface
|
|
||||||
public BufferedImage loadImage (String path) throws IOException
|
|
||||||
{
|
|
||||||
return FastImageIO.read(_bundle.getResourceFile(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public ActionFrames getFrames (
|
|
||||||
CharacterComponent component, String action, String type)
|
|
||||||
{
|
|
||||||
// obtain the action sequence definition for this action
|
|
||||||
ActionSequence actseq = (ActionSequence)_actions.get(action);
|
|
||||||
if (actseq == null) {
|
|
||||||
Log.warning("Missing action sequence definition " +
|
|
||||||
"[action=" + action +
|
|
||||||
", component=" + component + "].");
|
|
||||||
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 + 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)
|
|
||||||
TileSet aset = (TileSet)_setcache.get(cpath);
|
|
||||||
if (aset == null) {
|
|
||||||
aset = (TileSet)_setcache.get(dpath);
|
|
||||||
if (aset != null) {
|
|
||||||
// save ourselves a lookup next time
|
|
||||||
_setcache.put(cpath, aset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// then try loading up a tileset customized for this action
|
|
||||||
if (aset == null) {
|
|
||||||
aset = (TileSet)BundleUtil.loadObject(
|
|
||||||
_bundle, cpath, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if that failed, try loading the default tileset
|
|
||||||
if (aset == null) {
|
|
||||||
aset = (TileSet)BundleUtil.loadObject(
|
|
||||||
_bundle, dpath, false);
|
|
||||||
_setcache.put(dpath, aset);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if that failed too, we're hosed
|
|
||||||
if (aset == null) {
|
|
||||||
// if this is a shadow image, no need to freak out as they
|
|
||||||
// are optional
|
|
||||||
if (!StandardActions.SHADOW_TYPE.equals(type)) {
|
|
||||||
Log.warning("Unable to locate tileset for action '" +
|
|
||||||
imgpath + "' " + component + ".");
|
|
||||||
if (_wipeOnFailure) {
|
|
||||||
_bundle.wipeBundle(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
aset.setImageProvider(this);
|
|
||||||
_setcache.put(cpath, aset);
|
|
||||||
return new TileSetFrameImage(aset, actseq);
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.warning("Error loading tileset for action '" + imgpath +
|
|
||||||
"' " + component + ".");
|
|
||||||
Log.logStackTrace(e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The resource bundle from which we obtain image data. */
|
|
||||||
protected ResourceBundle _bundle;
|
|
||||||
|
|
||||||
/** Cache of tilesets loaded from our bundle. */
|
|
||||||
protected HashMap _setcache = new HashMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to provide multiframe images using data obtained from a
|
|
||||||
* tileset.
|
|
||||||
*/
|
|
||||||
protected static class TileSetFrameImage implements ActionFrames
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Constructs a tileset frame image with the specified tileset and
|
|
||||||
* for the specified orientation.
|
|
||||||
*/
|
|
||||||
public TileSetFrameImage (TileSet set, ActionSequence actseq)
|
|
||||||
{
|
|
||||||
_set = set;
|
|
||||||
_actseq = actseq;
|
|
||||||
|
|
||||||
// compute these now to avoid pointless recomputation later
|
|
||||||
_ocount = actseq.orients.length;
|
|
||||||
_fcount = set.getTileCount() / _ocount;
|
|
||||||
|
|
||||||
// create our mapping from orientation to animation sequence
|
|
||||||
// index
|
|
||||||
for (int ii = 0; ii < _ocount; ii++) {
|
|
||||||
_orients.put(actseq.orients[ii], ii);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface
|
|
||||||
public int getOrientationCount ()
|
|
||||||
{
|
|
||||||
return _ocount;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface
|
|
||||||
public TrimmedMultiFrameImage getFrames (final int orient)
|
|
||||||
{
|
|
||||||
return new TrimmedMultiFrameImage() {
|
|
||||||
// documentation inherited
|
|
||||||
public int getFrameCount ()
|
|
||||||
{
|
|
||||||
return _fcount;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface
|
|
||||||
public int getWidth (int index)
|
|
||||||
{
|
|
||||||
Tile tile = getTile(orient, index);
|
|
||||||
return (tile == null) ? 0 : tile.getWidth();
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface
|
|
||||||
public int getHeight (int index)
|
|
||||||
{
|
|
||||||
Tile tile = getTile(orient, index);
|
|
||||||
return (tile == null) ? 0 : tile.getHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface
|
|
||||||
public void paintFrame (Graphics2D g, int index, int x, int y)
|
|
||||||
{
|
|
||||||
Tile tile = getTile(orient, index);
|
|
||||||
if (tile != null) {
|
|
||||||
tile.paint(g, x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface
|
|
||||||
public boolean hitTest (int index, int x, int y)
|
|
||||||
{
|
|
||||||
Tile tile = getTile(orient, index);
|
|
||||||
return (tile != null) ? tile.hitTest(x, y) : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface
|
|
||||||
public void getTrimmedBounds (int index, Rectangle bounds)
|
|
||||||
{
|
|
||||||
Tile tile = getTile(orient, index);
|
|
||||||
if (tile instanceof TrimmedTile) {
|
|
||||||
((TrimmedTile)tile).getTrimmedBounds(bounds);
|
|
||||||
} else {
|
|
||||||
bounds.setBounds(
|
|
||||||
0, 0, tile.getWidth(), tile.getHeight());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface
|
|
||||||
public int getXOrigin (int orient, int index)
|
|
||||||
{
|
|
||||||
return _actseq.origin.x;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface
|
|
||||||
public int getYOrigin (int orient, int index)
|
|
||||||
{
|
|
||||||
return _actseq.origin.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface
|
|
||||||
public ActionFrames cloneColorized (Colorization[] zations)
|
|
||||||
{
|
|
||||||
return new TileSetFrameImage(_set.clone(zations), _actseq);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetches the requested tile.
|
|
||||||
*/
|
|
||||||
protected Tile getTile (int orient, int index)
|
|
||||||
{
|
|
||||||
int tileIndex = _orients.get(orient) * _fcount + index;
|
|
||||||
return _set.getTile(tileIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The tileset from which we obtain our frame images. */
|
|
||||||
protected TileSet _set;
|
|
||||||
|
|
||||||
/** The action sequence for which we're providing frame images. */
|
|
||||||
protected ActionSequence _actseq;
|
|
||||||
|
|
||||||
/** Frame and orientation counts. */
|
|
||||||
protected int _fcount, _ocount;
|
|
||||||
|
|
||||||
/** A mapping from orientation code to animation sequence
|
|
||||||
* index. */
|
|
||||||
protected IntIntMap _orients = new IntIntMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** We use the image manager to decode and cache images. */
|
|
||||||
protected ImageManager _imgr;
|
|
||||||
|
|
||||||
/** A table of action sequences. */
|
|
||||||
protected HashMap _actions;
|
|
||||||
|
|
||||||
/** A table of action sequence tilesets. */
|
|
||||||
protected HashMap _actionSets;
|
|
||||||
|
|
||||||
/** A table of component classes. */
|
|
||||||
protected HashMap _classes;
|
|
||||||
|
|
||||||
/** A table of component lists indexed on classname. */
|
|
||||||
protected HashMap _classComps = new HashMap();
|
|
||||||
|
|
||||||
/** The component table. */
|
|
||||||
protected HashIntMap _components = new HashIntMap();
|
|
||||||
|
|
||||||
/** Whether or not we wipe our bundles on any failure. */
|
|
||||||
protected boolean _wipeOnFailure;
|
|
||||||
}
|
|
||||||
@@ -1,624 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.bundle.tools;
|
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
import javax.imageio.ImageIO;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.BufferedWriter;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.FileReader;
|
|
||||||
import java.io.FileWriter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
import java.util.jar.JarOutputStream;
|
|
||||||
import java.util.jar.JarEntry;
|
|
||||||
import java.util.zip.Deflater;
|
|
||||||
|
|
||||||
import org.apache.commons.digester.Digester;
|
|
||||||
|
|
||||||
import com.samskivert.io.PersistenceException;
|
|
||||||
import com.samskivert.util.ComparableArrayList;
|
|
||||||
import com.samskivert.util.FileUtil;
|
|
||||||
import com.samskivert.util.HashIntMap;
|
|
||||||
import com.samskivert.util.Tuple;
|
|
||||||
|
|
||||||
import org.apache.tools.ant.BuildException;
|
|
||||||
import org.apache.tools.ant.DirectoryScanner;
|
|
||||||
import org.apache.tools.ant.Task;
|
|
||||||
import org.apache.tools.ant.types.FileSet;
|
|
||||||
|
|
||||||
import com.threerings.media.tile.ImageProvider;
|
|
||||||
import com.threerings.media.tile.SimpleCachingImageProvider;
|
|
||||||
import com.threerings.media.tile.TileSet;
|
|
||||||
import com.threerings.media.tile.TrimmedTileSet;
|
|
||||||
import com.threerings.media.tile.tools.xml.SwissArmyTileSetRuleSet;
|
|
||||||
|
|
||||||
import com.threerings.cast.ComponentIDBroker;
|
|
||||||
import com.threerings.cast.StandardActions;
|
|
||||||
import com.threerings.cast.bundle.BundleUtil;
|
|
||||||
import com.threerings.cast.tools.xml.ActionRuleSet;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ant task for creating component bundles. This task must be configured
|
|
||||||
* with a number of parameters:
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* target=[path to bundle file, which will be created]
|
|
||||||
* mapfile=[path to the component map file which maintains a mapping from
|
|
||||||
* component id to component class/name, it will be created the
|
|
||||||
* first time and updated as new components are mapped]
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* It should also contain one or more nested <fileset> elements that
|
|
||||||
* enumerate the action tileset images that should be included in the
|
|
||||||
* component bundle.
|
|
||||||
*/
|
|
||||||
public class ComponentBundlerTask extends Task
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Sets the path to the bundle file that we'll be creating.
|
|
||||||
*/
|
|
||||||
public void setTarget (File target)
|
|
||||||
{
|
|
||||||
_target = target;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the path to the component map file that we'll use to obtain
|
|
||||||
* component ids for the bundled components.
|
|
||||||
*/
|
|
||||||
public void setMapfile (File mapfile)
|
|
||||||
{
|
|
||||||
_mapfile = mapfile;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the path to the action tilesets definition file.
|
|
||||||
*/
|
|
||||||
public void setActiondef (File actiondef)
|
|
||||||
{
|
|
||||||
_actionDef = actiondef;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the root path which will be stripped from the image paths
|
|
||||||
* prior to parsing them to obtain the component class, type and
|
|
||||||
* action names.
|
|
||||||
*/
|
|
||||||
public void setRoot (File root)
|
|
||||||
{
|
|
||||||
_root = root.getPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a nested <fileset> element.
|
|
||||||
*/
|
|
||||||
public void addFileset (FileSet set)
|
|
||||||
{
|
|
||||||
_filesets.add(set);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs the actual work of the task.
|
|
||||||
*/
|
|
||||||
public void execute () throws BuildException
|
|
||||||
{
|
|
||||||
// make sure everything was set up properly
|
|
||||||
ensureSet(_target, "Must specify the path to the target bundle " +
|
|
||||||
"file via the 'target' attribute.");
|
|
||||||
ensureSet(_mapfile, "Must specify the path to the component map " +
|
|
||||||
"file via the 'mapfile' attribute.");
|
|
||||||
ensureSet(_actionDef, "Must specify the action sequence " +
|
|
||||||
"definitions via the 'actiondef' attribute.");
|
|
||||||
|
|
||||||
// parse in the action tilesets
|
|
||||||
HashMap actsets = parseActionTileSets();
|
|
||||||
|
|
||||||
// load up our component ID broker
|
|
||||||
ComponentIDBroker broker = loadBroker(_mapfile);
|
|
||||||
|
|
||||||
// check to see if any of the source files are newer than the
|
|
||||||
// target file
|
|
||||||
ArrayList sources = (ArrayList)_filesets.clone();
|
|
||||||
sources.add(_mapfile);
|
|
||||||
sources.add(_actionDef);
|
|
||||||
if (!outOfDate(sources, _target)) {
|
|
||||||
System.out.println(_target.getPath() + " is up to date.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// create an image provider for loading our component images
|
|
||||||
ImageProvider improv = new SimpleCachingImageProvider() {
|
|
||||||
protected BufferedImage loadImage (String path)
|
|
||||||
throws IOException {
|
|
||||||
return ImageIO.read(new File(path));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
System.out.println("Generating " + _target.getPath() + "...");
|
|
||||||
|
|
||||||
try {
|
|
||||||
// make sure we can create our bundle file
|
|
||||||
FileOutputStream fout = new FileOutputStream(_target);
|
|
||||||
JarOutputStream jout = new JarOutputStream(fout);
|
|
||||||
jout.setLevel(Deflater.BEST_COMPRESSION);
|
|
||||||
|
|
||||||
// we'll fill this with component id to tuple mappings
|
|
||||||
HashIntMap mapping = new HashIntMap();
|
|
||||||
|
|
||||||
// herein we'll insert trimmed tileset objects that go along
|
|
||||||
// with each of the trimmed action images
|
|
||||||
HashIntMap actionSets = new HashIntMap();
|
|
||||||
|
|
||||||
// deal with the filesets
|
|
||||||
for (int i = 0; i < _filesets.size(); i++) {
|
|
||||||
FileSet fs = (FileSet)_filesets.get(i);
|
|
||||||
DirectoryScanner ds = fs.getDirectoryScanner(getProject());
|
|
||||||
File fromDir = fs.getDir(getProject());
|
|
||||||
String[] srcFiles = ds.getIncludedFiles();
|
|
||||||
|
|
||||||
for (int f = 0; f < srcFiles.length; f++) {
|
|
||||||
File cfile = new File(fromDir, srcFiles[f]);
|
|
||||||
// determine the [class, name, action] triplet
|
|
||||||
String[] info = decomposePath(cfile.getPath());
|
|
||||||
|
|
||||||
// make sure we have an action tileset definition
|
|
||||||
TileSet aset = (TileSet)actsets.get(info[2]);
|
|
||||||
if (aset == null) {
|
|
||||||
System.err.println(
|
|
||||||
"No tileset definition for component action " +
|
|
||||||
"[class=" + info[0] + ", name=" + info[1] +
|
|
||||||
", action=" + info[2] + "].");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
aset.setImageProvider(improv);
|
|
||||||
|
|
||||||
// obtain the component id from our id broker
|
|
||||||
int cid = broker.getComponentID(info[0], info[1]);
|
|
||||||
// add a mapping for this component
|
|
||||||
mapping.put(cid, new Tuple(info[0], info[1]));
|
|
||||||
|
|
||||||
// process and store the main component image
|
|
||||||
processComponent(info, aset, cfile, jout);
|
|
||||||
|
|
||||||
// pick up any auxiliary images as well like the shadow or
|
|
||||||
// crop files
|
|
||||||
String action = info[2];
|
|
||||||
String ext = BundleUtil.IMAGE_EXTENSION;
|
|
||||||
for (int aa = 0; aa < AUX_EXTS.length; aa++) {
|
|
||||||
File afile = new File(
|
|
||||||
FileUtil.resuffix(cfile, ext, AUX_EXTS[aa] + ext));
|
|
||||||
if (afile.exists()) {
|
|
||||||
info[2] = action + AUX_EXTS[aa];
|
|
||||||
processComponent(info, aset, afile, jout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// write our mapping table to the jar file as well
|
|
||||||
jout.putNextEntry(new JarEntry(BundleUtil.COMPONENTS_PATH));
|
|
||||||
ObjectOutputStream oout = new ObjectOutputStream(jout);
|
|
||||||
oout.writeObject(mapping);
|
|
||||||
oout.flush();
|
|
||||||
|
|
||||||
// seal up our jar file
|
|
||||||
jout.close();
|
|
||||||
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
String errmsg = "Unable to create component bundle.";
|
|
||||||
throw new BuildException(errmsg, ioe);
|
|
||||||
|
|
||||||
} catch (PersistenceException pe) {
|
|
||||||
String errmsg = "Unable to obtain component ID mapping.";
|
|
||||||
throw new BuildException(errmsg, pe);
|
|
||||||
}
|
|
||||||
|
|
||||||
// save our updated component ID broker
|
|
||||||
saveBroker(_mapfile, broker);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void processComponent (
|
|
||||||
String[] info, TileSet aset, File cfile, JarOutputStream jout)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
// construct the path that'll go in the jar file
|
|
||||||
String ipath = composePath(
|
|
||||||
info, BundleUtil.IMAGE_EXTENSION);
|
|
||||||
jout.putNextEntry(new JarEntry(ipath));
|
|
||||||
|
|
||||||
// create a trimmed tileset based on the source action tileset and
|
|
||||||
// stuff the new trimmed image into the jar file at the same time
|
|
||||||
aset.setImagePath(cfile.getPath());
|
|
||||||
|
|
||||||
TrimmedTileSet tset = null;
|
|
||||||
try {
|
|
||||||
tset = TrimmedTileSet.trimTileSet(aset, jout);
|
|
||||||
tset.setImagePath(ipath);
|
|
||||||
} catch (Throwable t) {
|
|
||||||
System.err.println(
|
|
||||||
"Failure trimming tileset " +
|
|
||||||
"[class=" + info[0] + ", name=" + info[1] +
|
|
||||||
", action=" + info[2] +
|
|
||||||
", srcimg=" + aset.getImagePath() + "].");
|
|
||||||
t.printStackTrace(System.err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// then write our trimmed tileset to the jar file
|
|
||||||
if (tset != null) {
|
|
||||||
String tpath = composePath(
|
|
||||||
info, BundleUtil.TILESET_EXTENSION);
|
|
||||||
jout.putNextEntry(new JarEntry(tpath));
|
|
||||||
ObjectOutputStream oout = new ObjectOutputStream(jout);
|
|
||||||
oout.writeObject(tset);
|
|
||||||
oout.flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean outOfDate (ArrayList sources, File target)
|
|
||||||
{
|
|
||||||
for (int ii = 0; ii < sources.size(); ii++) {
|
|
||||||
if (outOfDate(sources.get(ii), target)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean outOfDate (Object source, File target)
|
|
||||||
{
|
|
||||||
if (source instanceof FileSet) {
|
|
||||||
FileSet fs = (FileSet)source;
|
|
||||||
DirectoryScanner ds = fs.getDirectoryScanner(getProject());
|
|
||||||
File fromDir = fs.getDir(getProject());
|
|
||||||
String[] srcFiles = ds.getIncludedFiles();
|
|
||||||
for (int f = 0; f < srcFiles.length; f++) {
|
|
||||||
File cfile = new File(fromDir, srcFiles[f]);
|
|
||||||
if (newer(cfile, target)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
|
|
||||||
} else if (source instanceof File) {
|
|
||||||
return newer((File)source, target);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
System.err.println("Can't compare " + source +
|
|
||||||
" to " + target + ".");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if <code>source</code> is newer than
|
|
||||||
* <code>target</code>.
|
|
||||||
*/
|
|
||||||
protected boolean newer (File source, File target)
|
|
||||||
{
|
|
||||||
return source.lastModified() > target.lastModified();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decomposes the full path to a component image into a [class, name,
|
|
||||||
* action] triplet.
|
|
||||||
*/
|
|
||||||
protected String[] decomposePath (String path)
|
|
||||||
throws BuildException
|
|
||||||
{
|
|
||||||
// first strip off the root
|
|
||||||
if (!path.startsWith(_root)) {
|
|
||||||
throw new BuildException("Can't bundle images outside the " +
|
|
||||||
"root directory [root=" + _root +
|
|
||||||
", path=" + path + "].");
|
|
||||||
}
|
|
||||||
path = path.substring(_root.length());
|
|
||||||
|
|
||||||
// strip off any preceding slash
|
|
||||||
if (path.startsWith("/")) {
|
|
||||||
path = path.substring(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// now strip off the file extension
|
|
||||||
if (!path.endsWith(BundleUtil.IMAGE_EXTENSION)) {
|
|
||||||
throw new BuildException("Can't bundle malformed image file " +
|
|
||||||
"[path=" + path + "].");
|
|
||||||
}
|
|
||||||
path = path.substring(0, path.length() -
|
|
||||||
BundleUtil.IMAGE_EXTENSION.length());
|
|
||||||
|
|
||||||
// now decompose the path; the component type and action must
|
|
||||||
// always be a single string but the class can span multiple
|
|
||||||
// directories for easier component organization; thus
|
|
||||||
// "male/head/goatee/standing" will be parsed as [class=male/head,
|
|
||||||
// type=goatee, action=standing]
|
|
||||||
String malmsg = "Can't decode malformed image path: '" + path + "'";
|
|
||||||
String[] info = new String[3];
|
|
||||||
int lsidx = path.lastIndexOf(File.separator);
|
|
||||||
if (lsidx == -1) {
|
|
||||||
throw new BuildException(malmsg);
|
|
||||||
}
|
|
||||||
info[2] = path.substring(lsidx+1);
|
|
||||||
int slsidx = path.lastIndexOf(File.separator, lsidx-1);
|
|
||||||
if (slsidx == -1) {
|
|
||||||
throw new BuildException(malmsg);
|
|
||||||
}
|
|
||||||
info[1] = path.substring(slsidx+1, lsidx);
|
|
||||||
info[0] = path.substring(0, slsidx);
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Composes a triplet of [class, name, action] into the path that
|
|
||||||
* should be supplied to the JarEntry that contains the associated
|
|
||||||
* image data.
|
|
||||||
*/
|
|
||||||
protected String composePath (String[] info, String extension)
|
|
||||||
{
|
|
||||||
return (info[0] + File.separator + info[1] +
|
|
||||||
File.separator + info[2] + extension);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void ensureSet (Object value, String errmsg)
|
|
||||||
throws BuildException
|
|
||||||
{
|
|
||||||
if (value == null) {
|
|
||||||
throw new BuildException(errmsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses the action tileset definitions and puts them into a hash
|
|
||||||
* map, keyed on action name.
|
|
||||||
*/
|
|
||||||
protected HashMap parseActionTileSets ()
|
|
||||||
{
|
|
||||||
Digester digester = new Digester();
|
|
||||||
SwissArmyTileSetRuleSet srules = new SwissArmyTileSetRuleSet();
|
|
||||||
String aprefix = "actions" + ActionRuleSet.ACTION_PATH;
|
|
||||||
srules.setPrefix(aprefix);
|
|
||||||
|
|
||||||
digester.addRuleSet(srules);
|
|
||||||
digester.addSetProperties(aprefix);
|
|
||||||
digester.addSetNext(aprefix + SwissArmyTileSetRuleSet.TILESET_PATH,
|
|
||||||
"addTileSet", TileSet.class.getName());
|
|
||||||
|
|
||||||
HashMap actsets = new ActionMap();
|
|
||||||
digester.push(actsets);
|
|
||||||
|
|
||||||
try {
|
|
||||||
FileInputStream fin = new FileInputStream(_actionDef);
|
|
||||||
BufferedInputStream bin = new BufferedInputStream(fin);
|
|
||||||
digester.parse(bin);
|
|
||||||
|
|
||||||
} catch (FileNotFoundException fnfe) {
|
|
||||||
String errmsg = "Unable to load action definition file " +
|
|
||||||
"[path=" + _actionDef.getPath() + "].";
|
|
||||||
throw new BuildException(errmsg, fnfe);
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new BuildException("Parsing error.", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return actsets;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Used when parsing action tilesets. */
|
|
||||||
public static class ActionMap extends HashMap
|
|
||||||
{
|
|
||||||
public void setName (String name) {
|
|
||||||
_name = name;
|
|
||||||
}
|
|
||||||
public void addTileSet (TileSet set) {
|
|
||||||
set.setName(_name);
|
|
||||||
put(_name, set);
|
|
||||||
}
|
|
||||||
protected String _name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads the hashmap ID broker from its persistent representation in
|
|
||||||
* the specified file.
|
|
||||||
*/
|
|
||||||
protected HashMapIDBroker loadBroker (File mapfile)
|
|
||||||
throws BuildException
|
|
||||||
{
|
|
||||||
HashMapIDBroker broker = new HashMapIDBroker();
|
|
||||||
|
|
||||||
try {
|
|
||||||
BufferedReader bin = new BufferedReader(new FileReader(mapfile));
|
|
||||||
broker.readFrom(bin);
|
|
||||||
bin.close();
|
|
||||||
|
|
||||||
} catch (FileNotFoundException fnfe) {
|
|
||||||
// if the file doesn't yet exist, start with a blank broker
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new BuildException("Error loading component ID map " +
|
|
||||||
"[mapfile=" + mapfile + "]", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return broker;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores a persistent representation of the supplied hashmap ID
|
|
||||||
* broker in the specified file.
|
|
||||||
*/
|
|
||||||
protected void saveBroker (File mapfile, ComponentIDBroker broker)
|
|
||||||
throws BuildException
|
|
||||||
{
|
|
||||||
HashMapIDBroker hbroker = (HashMapIDBroker)broker;
|
|
||||||
|
|
||||||
// bail if the broker wasn't modified
|
|
||||||
if (!hbroker.isModified()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
BufferedWriter bout = new BufferedWriter(new FileWriter(mapfile));
|
|
||||||
hbroker.writeTo(bout);
|
|
||||||
bout.close();
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
throw new BuildException("Unable to store component ID map " +
|
|
||||||
"[mapfile=" + mapfile + "]", ioe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static class HashMapIDBroker
|
|
||||||
extends HashMap implements ComponentIDBroker
|
|
||||||
{
|
|
||||||
public int getComponentID (String cclass, String cname)
|
|
||||||
throws PersistenceException
|
|
||||||
{
|
|
||||||
Tuple key = new Tuple(cclass, cname);
|
|
||||||
Integer cid = (Integer)get(key);
|
|
||||||
if (cid == null) {
|
|
||||||
cid = Integer.valueOf(++_nextCID);
|
|
||||||
put(key, cid);
|
|
||||||
}
|
|
||||||
return cid.intValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void commit ()
|
|
||||||
throws PersistenceException
|
|
||||||
{
|
|
||||||
// nothing doing
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isModified ()
|
|
||||||
{
|
|
||||||
return _nextCID != _startCID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeTo (BufferedWriter bout)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
// write out our most recently assigned component id
|
|
||||||
String cidline = "" + _nextCID;
|
|
||||||
bout.write(cidline, 0, cidline.length());
|
|
||||||
bout.newLine();
|
|
||||||
|
|
||||||
// write out the keys and values
|
|
||||||
ComparableArrayList lines = new ComparableArrayList();
|
|
||||||
Iterator keys = keySet().iterator();
|
|
||||||
while (keys.hasNext()) {
|
|
||||||
Tuple key = (Tuple)keys.next();
|
|
||||||
Integer value = (Integer)get(key);
|
|
||||||
String line = key.left + SEP_STR + key.right + SEP_STR + value;
|
|
||||||
lines.add(line);
|
|
||||||
}
|
|
||||||
|
|
||||||
// sort the output
|
|
||||||
lines.sort();
|
|
||||||
|
|
||||||
// now write it to the file
|
|
||||||
int lcount = lines.size();
|
|
||||||
for (int ii = 0; ii < lcount; ii++) {
|
|
||||||
String line = (String)lines.get(ii);
|
|
||||||
bout.write(line, 0, line.length());
|
|
||||||
bout.newLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void readFrom (BufferedReader bin)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
// read in our most recently assigned component id
|
|
||||||
_nextCID = readInt(bin);
|
|
||||||
// keep track of this so that we can tell if we were modified
|
|
||||||
_startCID = _nextCID;
|
|
||||||
|
|
||||||
// now read in our keys and values
|
|
||||||
String line;
|
|
||||||
while ((line = bin.readLine()) != null) {
|
|
||||||
String orig = line;
|
|
||||||
int sidx = line.indexOf(SEP_STR);
|
|
||||||
if (sidx == -1) {
|
|
||||||
throw new IOException("Malformed line '" + orig + "'");
|
|
||||||
}
|
|
||||||
String cclass = line.substring(0, sidx);
|
|
||||||
line = line.substring(sidx + SEP_STR.length());
|
|
||||||
sidx = line.indexOf(SEP_STR);
|
|
||||||
if (sidx == -1) {
|
|
||||||
throw new IOException("Malformed line '" + orig + "'");
|
|
||||||
}
|
|
||||||
String cname = line.substring(0, sidx);
|
|
||||||
line = line.substring(sidx + SEP_STR.length());
|
|
||||||
try {
|
|
||||||
put(new Tuple(cclass, cname), Integer.valueOf(line));
|
|
||||||
} catch (NumberFormatException nfe) {
|
|
||||||
String err = "Malformed line, invalid code '" + orig + "'";
|
|
||||||
throw new IOException(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int readInt (BufferedReader bin)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
String line = bin.readLine();
|
|
||||||
try {
|
|
||||||
return Integer.parseInt(line);
|
|
||||||
} catch (NumberFormatException nfe) {
|
|
||||||
throw new IOException("Expected number, got '" + line + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int _nextCID = 0;
|
|
||||||
protected int _startCID = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The path to our component bundle file. */
|
|
||||||
protected File _target;
|
|
||||||
|
|
||||||
/** The path to our component map file. */
|
|
||||||
protected File _mapfile;
|
|
||||||
|
|
||||||
/** The path to our action tilesets definition file. */
|
|
||||||
protected File _actionDef;
|
|
||||||
|
|
||||||
/** The component directory root. */
|
|
||||||
protected String _root;
|
|
||||||
|
|
||||||
/** A list of filesets that contain tile images. */
|
|
||||||
protected ArrayList _filesets = new ArrayList();
|
|
||||||
|
|
||||||
/** Used to separate keys and values in the map file. */
|
|
||||||
protected static final String SEP_STR = " := ";
|
|
||||||
|
|
||||||
/** Used to process auxilliary tilesets. */
|
|
||||||
protected static final String[] AUX_EXTS = {
|
|
||||||
"_" + StandardActions.SHADOW_TYPE,
|
|
||||||
"_" + StandardActions.CROP_TYPE };
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.bundle.tools;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.samskivert.util.HashIntMap;
|
|
||||||
import com.samskivert.util.StringUtil;
|
|
||||||
|
|
||||||
import com.threerings.resource.ResourceBundle;
|
|
||||||
import com.threerings.cast.bundle.BundleUtil;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dumps the contents of a component bundle to stdout.
|
|
||||||
*/
|
|
||||||
public class DumpBundle
|
|
||||||
{
|
|
||||||
public static void main (String[] args)
|
|
||||||
{
|
|
||||||
if (args.length < 1) {
|
|
||||||
String usage = "Usage: DumpBundle bundle.jar [bundle.jar ...]";
|
|
||||||
System.err.println(usage);
|
|
||||||
System.exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < args.length; i++) {
|
|
||||||
File file = new File(args[i]);
|
|
||||||
try {
|
|
||||||
ResourceBundle bundle = new ResourceBundle(file);
|
|
||||||
|
|
||||||
HashMap actions = (HashMap)BundleUtil.loadObject(
|
|
||||||
bundle, BundleUtil.ACTIONS_PATH, false);
|
|
||||||
dumpTable("actions: ", actions);
|
|
||||||
|
|
||||||
HashMap actionSets = (HashMap)BundleUtil.loadObject(
|
|
||||||
bundle, BundleUtil.ACTION_SETS_PATH, false);
|
|
||||||
dumpTable("actionSets: ", actionSets);
|
|
||||||
|
|
||||||
HashMap classes = (HashMap)BundleUtil.loadObject(
|
|
||||||
bundle, BundleUtil.CLASSES_PATH, false);
|
|
||||||
dumpTable("classes: ", classes);
|
|
||||||
|
|
||||||
HashIntMap comps = (HashIntMap)BundleUtil.loadObject(
|
|
||||||
bundle, BundleUtil.COMPONENTS_PATH, false);
|
|
||||||
dumpTable("components: ", comps);
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println("Error dumping bundle [path=" + args[i] +
|
|
||||||
", error=" + e + "].");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static void dumpTable (String prefix, Map table)
|
|
||||||
{
|
|
||||||
if (table != null) {
|
|
||||||
Iterator iter = table.entrySet().iterator();
|
|
||||||
System.out.println(prefix + StringUtil.toString(iter));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,267 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.bundle.tools;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import java.util.jar.JarOutputStream;
|
|
||||||
import java.util.jar.JarEntry;
|
|
||||||
import java.util.zip.Deflater;
|
|
||||||
|
|
||||||
import org.apache.tools.ant.BuildException;
|
|
||||||
import org.apache.tools.ant.Task;
|
|
||||||
|
|
||||||
import org.apache.commons.digester.Digester;
|
|
||||||
|
|
||||||
import com.samskivert.util.Tuple;
|
|
||||||
|
|
||||||
import com.threerings.media.tile.TileSet;
|
|
||||||
import com.threerings.media.tile.tools.xml.SwissArmyTileSetRuleSet;
|
|
||||||
|
|
||||||
import com.threerings.cast.ActionSequence;
|
|
||||||
import com.threerings.cast.ComponentClass;
|
|
||||||
import com.threerings.cast.bundle.BundleUtil;
|
|
||||||
|
|
||||||
import com.threerings.cast.tools.xml.ActionRuleSet;
|
|
||||||
import com.threerings.cast.tools.xml.ClassRuleSet;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ant task for creating metadata bundles, which contain action sequence
|
|
||||||
* and component class definition information. This task must be
|
|
||||||
* configured with a number of parameters:
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* actiondef=[path to actions.xml]
|
|
||||||
* classdef=[path to classes.xml]
|
|
||||||
* file=[path to metadata bundle, which will be created]
|
|
||||||
* </pre>
|
|
||||||
*/
|
|
||||||
public class MetadataBundlerTask extends Task
|
|
||||||
{
|
|
||||||
public void setActiondef (String actiondef)
|
|
||||||
{
|
|
||||||
_actionDef = actiondef;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setClassdef (String classdef)
|
|
||||||
{
|
|
||||||
_classDef = classdef;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTarget (File target)
|
|
||||||
{
|
|
||||||
_target = target;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs the actual work of the task.
|
|
||||||
*/
|
|
||||||
public void execute ()
|
|
||||||
throws BuildException
|
|
||||||
{
|
|
||||||
// make sure everythign was set up properly
|
|
||||||
ensureSet(_actionDef, "Must specify the action sequence " +
|
|
||||||
"definitions via the 'actiondef' attribute.");
|
|
||||||
ensureSet(_classDef, "Must specify the component class definitions " +
|
|
||||||
"via the 'classdef' attribute.");
|
|
||||||
ensureSet(_target, "Must specify the path to the target bundle " +
|
|
||||||
"file via the 'target' attribute.");
|
|
||||||
|
|
||||||
// make sure we can write to the target bundle file
|
|
||||||
FileOutputStream fout = null;
|
|
||||||
try {
|
|
||||||
fout = new FileOutputStream(_target);
|
|
||||||
|
|
||||||
// parse our metadata
|
|
||||||
Tuple tuple = parseActions();
|
|
||||||
HashMap actions = (HashMap)tuple.left;
|
|
||||||
HashMap actionSets = (HashMap)tuple.right;
|
|
||||||
HashMap classes = parseClasses();
|
|
||||||
|
|
||||||
// and create the bundle file
|
|
||||||
JarOutputStream jout = new JarOutputStream(fout);
|
|
||||||
jout.setLevel(Deflater.BEST_COMPRESSION);
|
|
||||||
|
|
||||||
// throw the serialized actions table in there
|
|
||||||
JarEntry aentry = new JarEntry(BundleUtil.ACTIONS_PATH);
|
|
||||||
jout.putNextEntry(aentry);
|
|
||||||
ObjectOutputStream oout = new ObjectOutputStream(jout);
|
|
||||||
oout.writeObject(actions);
|
|
||||||
oout.flush();
|
|
||||||
|
|
||||||
// throw the serialized action tilesets table in there
|
|
||||||
JarEntry sentry = new JarEntry(BundleUtil.ACTION_SETS_PATH);
|
|
||||||
jout.putNextEntry(sentry);
|
|
||||||
oout = new ObjectOutputStream(jout);
|
|
||||||
oout.writeObject(actionSets);
|
|
||||||
oout.flush();
|
|
||||||
|
|
||||||
// throw the serialized classes table in there
|
|
||||||
JarEntry centry = new JarEntry(BundleUtil.CLASSES_PATH);
|
|
||||||
jout.putNextEntry(centry);
|
|
||||||
oout = new ObjectOutputStream(jout);
|
|
||||||
oout.writeObject(classes);
|
|
||||||
oout.flush();
|
|
||||||
|
|
||||||
// close it up and we're done
|
|
||||||
jout.close();
|
|
||||||
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
String errmsg = "Unable to output to target bundle " +
|
|
||||||
"[path=" + _target.getPath() + "].";
|
|
||||||
throw new BuildException(errmsg, ioe);
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
if (fout != null) {
|
|
||||||
try {
|
|
||||||
fout.close();
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
// nothing to complain about here
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Tuple parseActions ()
|
|
||||||
throws BuildException
|
|
||||||
{
|
|
||||||
// scan through the XML once to read the actions
|
|
||||||
Digester digester = new Digester();
|
|
||||||
ActionRuleSet arules = new ActionRuleSet();
|
|
||||||
arules.setPrefix("actions");
|
|
||||||
digester.addRuleSet(arules);
|
|
||||||
digester.addSetNext("actions" + ActionRuleSet.ACTION_PATH,
|
|
||||||
"add", Object.class.getName());
|
|
||||||
ArrayList actlist = parseList(digester, _actionDef);
|
|
||||||
|
|
||||||
// now go through a second time reading the tileset info
|
|
||||||
digester = new Digester();
|
|
||||||
SwissArmyTileSetRuleSet srules = new SwissArmyTileSetRuleSet();
|
|
||||||
srules.setPrefix("actions" + ActionRuleSet.ACTION_PATH);
|
|
||||||
digester.addRuleSet(srules);
|
|
||||||
digester.addSetNext("actions" + ActionRuleSet.ACTION_PATH +
|
|
||||||
SwissArmyTileSetRuleSet.TILESET_PATH, "add",
|
|
||||||
Object.class.getName());
|
|
||||||
ArrayList setlist = parseList(digester, _actionDef);
|
|
||||||
|
|
||||||
// sanity check
|
|
||||||
if (actlist.size() != setlist.size()) {
|
|
||||||
String errmsg = "An action is missing its tileset " +
|
|
||||||
"definition, or something even wackier is going on.";
|
|
||||||
throw new BuildException(errmsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// now create our mappings
|
|
||||||
HashMap actmap = new HashMap();
|
|
||||||
HashMap setmap = new HashMap();
|
|
||||||
|
|
||||||
// create the action map
|
|
||||||
for (int i = 0; i < setlist.size(); i++) {
|
|
||||||
TileSet set = (TileSet)setlist.get(i);
|
|
||||||
ActionSequence act = (ActionSequence)actlist.get(i);
|
|
||||||
// make sure nothing was missing in the action sequence
|
|
||||||
// definition parsed from XML
|
|
||||||
String errmsg = ActionRuleSet.validate(act);
|
|
||||||
if (errmsg != null) {
|
|
||||||
errmsg = "Action sequence invalid [seq=" + act +
|
|
||||||
", error=" + errmsg + "].";
|
|
||||||
throw new BuildException(errmsg);
|
|
||||||
}
|
|
||||||
actmap.put(act.name, act);
|
|
||||||
setmap.put(act.name, set);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Tuple(actmap, setmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected HashMap parseClasses ()
|
|
||||||
throws BuildException
|
|
||||||
{
|
|
||||||
// load up our action and class info
|
|
||||||
Digester digester = new Digester();
|
|
||||||
|
|
||||||
// add our action rule set and a a rule to grab parsed actions
|
|
||||||
ClassRuleSet crules = new ClassRuleSet();
|
|
||||||
crules.setPrefix("classes");
|
|
||||||
digester.addRuleSet(crules);
|
|
||||||
digester.addSetNext("classes" + ClassRuleSet.CLASS_PATH,
|
|
||||||
"add", Object.class.getName());
|
|
||||||
|
|
||||||
ArrayList setlist = parseList(digester, _classDef);
|
|
||||||
HashMap clmap = new HashMap();
|
|
||||||
|
|
||||||
// create the action map
|
|
||||||
for (int i = 0; i < setlist.size(); i++) {
|
|
||||||
ComponentClass cl = (ComponentClass)setlist.get(i);
|
|
||||||
clmap.put(cl.name, cl);
|
|
||||||
}
|
|
||||||
|
|
||||||
return clmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ArrayList parseList (Digester digester, String path)
|
|
||||||
throws BuildException
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
FileInputStream fin = new FileInputStream(path);
|
|
||||||
BufferedInputStream bin = new BufferedInputStream(fin);
|
|
||||||
|
|
||||||
ArrayList setlist = new ArrayList();
|
|
||||||
digester.push(setlist);
|
|
||||||
|
|
||||||
// now fire up the digester to parse the stream
|
|
||||||
try {
|
|
||||||
digester.parse(bin);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new BuildException("Parsing error.", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return setlist;
|
|
||||||
|
|
||||||
} catch (FileNotFoundException fnfe) {
|
|
||||||
String errmsg = "Unable to load metadata definition file " +
|
|
||||||
"[path=" + path + "].";
|
|
||||||
throw new BuildException(errmsg, fnfe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void ensureSet (Object value, String errmsg)
|
|
||||||
throws BuildException
|
|
||||||
{
|
|
||||||
if (value == null) {
|
|
||||||
throw new BuildException(errmsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String _actionDef;
|
|
||||||
protected String _classDef;
|
|
||||||
protected File _target;
|
|
||||||
}
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.tools.xml;
|
|
||||||
|
|
||||||
import org.apache.commons.digester.Digester;
|
|
||||||
import org.apache.commons.digester.RuleSetBase;
|
|
||||||
|
|
||||||
import com.samskivert.util.StringUtil;
|
|
||||||
|
|
||||||
import com.samskivert.xml.CallMethodSpecialRule;
|
|
||||||
import com.samskivert.xml.SetFieldRule;
|
|
||||||
import com.samskivert.xml.SetPropertyFieldsRule;
|
|
||||||
|
|
||||||
import com.threerings.util.DirectionCodes;
|
|
||||||
import com.threerings.util.DirectionUtil;
|
|
||||||
|
|
||||||
import com.threerings.cast.ActionSequence;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The action rule set is used to parse the attributes of an action
|
|
||||||
* sequence instance.
|
|
||||||
*/
|
|
||||||
public class ActionRuleSet extends RuleSetBase
|
|
||||||
{
|
|
||||||
/** The component of the digester path that is appended by the action
|
|
||||||
* rule set to match a action. This is appended to whatever prefix is
|
|
||||||
* provided to the action rule set to obtain the complete XML path to
|
|
||||||
* a matched action. */
|
|
||||||
public static final String ACTION_PATH = "/action";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instructs the action rule set to match actions with the supplied
|
|
||||||
* prefix. For example, passing a prefix of <code>actions</code> will
|
|
||||||
* match actions in the following XML file:
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* <actions>
|
|
||||||
* <action>
|
|
||||||
* // ...
|
|
||||||
* </action>
|
|
||||||
* </actions>
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* This must be called before adding the ruleset to a digester.
|
|
||||||
*/
|
|
||||||
public void setPrefix (String prefix)
|
|
||||||
{
|
|
||||||
_prefix = prefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds the necessary rules to the digester to parse our actions.
|
|
||||||
*/
|
|
||||||
public void addRuleInstances (Digester digester)
|
|
||||||
{
|
|
||||||
// this creates the appropriate instance when we encounter a
|
|
||||||
// <action> tag
|
|
||||||
digester.addObjectCreate(_prefix + ACTION_PATH,
|
|
||||||
ActionSequence.class.getName());
|
|
||||||
|
|
||||||
// grab the name attribute from the <action> tag
|
|
||||||
digester.addRule(_prefix + ACTION_PATH, new SetPropertyFieldsRule());
|
|
||||||
|
|
||||||
// grab the other attributes from their respective tags
|
|
||||||
digester.addRule(_prefix + ACTION_PATH + "/framesPerSecond",
|
|
||||||
new SetFieldRule("framesPerSecond"));
|
|
||||||
|
|
||||||
CallMethodSpecialRule origin = new CallMethodSpecialRule() {
|
|
||||||
public void parseAndSet (String bodyText, Object target)
|
|
||||||
throws Exception {
|
|
||||||
int[] coords = StringUtil.parseIntArray(bodyText);
|
|
||||||
if (coords.length != 2) {
|
|
||||||
String errmsg = "Invalid <origin> specification '" +
|
|
||||||
bodyText + "'.";
|
|
||||||
throw new Exception(errmsg);
|
|
||||||
}
|
|
||||||
((ActionSequence)target).origin.setLocation(
|
|
||||||
coords[0], coords[1]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
digester.addRule(_prefix + ACTION_PATH + "/origin", origin);
|
|
||||||
|
|
||||||
CallMethodSpecialRule orient = new CallMethodSpecialRule() {
|
|
||||||
public void parseAndSet (String bodyText, Object target)
|
|
||||||
throws Exception {
|
|
||||||
ActionSequence seq = ((ActionSequence)target);
|
|
||||||
String[] ostrs = StringUtil.parseStringArray(bodyText);
|
|
||||||
seq.orients = new int[ostrs.length];
|
|
||||||
for (int ii = 0; ii < ostrs.length; ii++) {
|
|
||||||
int orient = DirectionUtil.fromShortString(ostrs[ii]);
|
|
||||||
if (orient != DirectionCodes.NONE) {
|
|
||||||
seq.orients[ii] = orient;
|
|
||||||
} else {
|
|
||||||
String errmsg = "Invalid orientation specification " +
|
|
||||||
"[index=" + ii + ", orient=" + ostrs[ii] + "].";
|
|
||||||
throw new Exception(errmsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
digester.addRule(_prefix + ACTION_PATH + "/orients", orient);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates that all necessary fields have been parsed and set in
|
|
||||||
* this action sequence object and are valid.
|
|
||||||
*
|
|
||||||
* @return null if the sequence is valid, a string explaining the
|
|
||||||
* invalidity if it is not.
|
|
||||||
*/
|
|
||||||
public static String validate (ActionSequence seq)
|
|
||||||
{
|
|
||||||
if (StringUtil.isBlank(seq.name)) {
|
|
||||||
return "Missing 'name' definition.";
|
|
||||||
}
|
|
||||||
if (seq.framesPerSecond == 0) {
|
|
||||||
return "Missing 'framesPerSecond' definition.";
|
|
||||||
}
|
|
||||||
if (seq.orients == null) {
|
|
||||||
return "Missing 'orients' definition.";
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The prefix at which me match our actions. */
|
|
||||||
protected String _prefix;
|
|
||||||
}
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.tools.xml;
|
|
||||||
|
|
||||||
import java.awt.Color;
|
|
||||||
|
|
||||||
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
|
|
||||||
* instance.
|
|
||||||
*/
|
|
||||||
public class ClassRuleSet extends RuleSetBase
|
|
||||||
{
|
|
||||||
/** The component of the digester path that is appended by the class
|
|
||||||
* rule set to match a component class. This is appended to whatever
|
|
||||||
* prefix is provided to the class rule set to obtain the complete XML
|
|
||||||
* path to a matched class. */
|
|
||||||
public static final String CLASS_PATH = "/class";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instructs the class rule set to match component classes with the
|
|
||||||
* supplied prefix. For example, passing a prefix of
|
|
||||||
* <code>classes</code> will match classes in the following XML file:
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* <classes>
|
|
||||||
* <class .../>
|
|
||||||
* </classes>
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* This must be called before adding the ruleset to a digester.
|
|
||||||
*/
|
|
||||||
public void setPrefix (String prefix)
|
|
||||||
{
|
|
||||||
_prefix = prefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds the necessary rules to the digester to parse our classes.
|
|
||||||
*/
|
|
||||||
public void addRuleInstances (Digester digester)
|
|
||||||
{
|
|
||||||
// this creates the appropriate instance when we encounter a
|
|
||||||
// <class> tag
|
|
||||||
digester.addObjectCreate(_prefix + CLASS_PATH,
|
|
||||||
ComponentClass.class.getName());
|
|
||||||
|
|
||||||
// grab the attributes from the <class> tag
|
|
||||||
SetPropertyFieldsRule rule = new SetPropertyFieldsRule();
|
|
||||||
rule.addFieldParser("shadowColor", new FieldParser() {
|
|
||||||
public Object parse (String text) {
|
|
||||||
int[] values = StringUtil.parseIntArray(text);
|
|
||||||
return new Color(values[0], values[1], values[2], values[3]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
digester.addRule(_prefix + CLASS_PATH, rule);
|
|
||||||
|
|
||||||
// parse render priority overrides
|
|
||||||
String opath = _prefix + CLASS_PATH + "/override";
|
|
||||||
digester.addObjectCreate(opath, PriorityOverride.class.getName());
|
|
||||||
rule = new SetPropertyFieldsRule();
|
|
||||||
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. */
|
|
||||||
protected String _prefix;
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.util;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
import com.samskivert.util.CollectionUtil;
|
|
||||||
import com.samskivert.util.RandomUtil;
|
|
||||||
|
|
||||||
import com.threerings.cast.CharacterDescriptor;
|
|
||||||
import com.threerings.cast.ComponentClass;
|
|
||||||
import com.threerings.cast.ComponentRepository;
|
|
||||||
import com.threerings.cast.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Miscellaneous cast utility routines.
|
|
||||||
*/
|
|
||||||
public class CastUtil
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Returns a new character descriptor populated with a random set of
|
|
||||||
* components.
|
|
||||||
*/
|
|
||||||
public static CharacterDescriptor getRandomDescriptor (
|
|
||||||
String gender, ComponentRepository crepo)
|
|
||||||
{
|
|
||||||
// get all available classes
|
|
||||||
ArrayList classes = new ArrayList();
|
|
||||||
for (int i = 0; i < CLASSES.length; i++) {
|
|
||||||
String cname = gender + "/" + CLASSES[i];
|
|
||||||
ComponentClass cclass = crepo.getComponentClass(cname);
|
|
||||||
|
|
||||||
// make sure the component class exists
|
|
||||||
if (cclass == null) {
|
|
||||||
Log.warning("Missing definition for component class " +
|
|
||||||
"[class=" + cname + "].");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure there are some components in this class
|
|
||||||
Iterator iter = crepo.enumerateComponentIds(cclass);
|
|
||||||
if (!iter.hasNext()) {
|
|
||||||
Log.info("Skipping class for which we have no components " +
|
|
||||||
"[class=" + cclass + "].");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
classes.add(cclass);
|
|
||||||
}
|
|
||||||
|
|
||||||
// select the components
|
|
||||||
int size = classes.size();
|
|
||||||
int components[] = new int[size];
|
|
||||||
for (int ii = 0; ii < size; ii++) {
|
|
||||||
ComponentClass cclass = (ComponentClass)classes.get(ii);
|
|
||||||
|
|
||||||
// get the components available for this class
|
|
||||||
ArrayList choices = new ArrayList();
|
|
||||||
Iterator iter = crepo.enumerateComponentIds(cclass);
|
|
||||||
CollectionUtil.addAll(choices, iter);
|
|
||||||
|
|
||||||
// choose a random component
|
|
||||||
if (choices.size() > 0) {
|
|
||||||
int idx = RandomUtil.getInt(choices.size());
|
|
||||||
components[ii] = ((Integer)choices.get(idx)).intValue();
|
|
||||||
} else {
|
|
||||||
Log.info("Have no components in class [class=" + cclass + "].");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new CharacterDescriptor(components, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static final String[] CLASSES = {
|
|
||||||
"legs", "feet", "hand_left", "hand_right", "torso",
|
|
||||||
"head", "hair", "hat", "eyepatch" };
|
|
||||||
}
|
|
||||||
@@ -1,251 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.geom;
|
|
||||||
|
|
||||||
import java.awt.Point;
|
|
||||||
import java.awt.Rectangle;
|
|
||||||
import java.awt.geom.Point2D;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* General geometry utilites.
|
|
||||||
*/
|
|
||||||
public class GeomUtil
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Computes and returns the dot product of the two vectors.
|
|
||||||
*
|
|
||||||
* @param v1s the starting point of the first vector.
|
|
||||||
* @param v1e the ending point of the first vector.
|
|
||||||
* @param v2s the starting point of the second vector.
|
|
||||||
* @param v2e the ending point of the second vector.
|
|
||||||
*/
|
|
||||||
public static int dot (Point v1s, Point v1e, Point v2s, Point v2e)
|
|
||||||
{
|
|
||||||
return ((v1e.x - v1s.x) * (v2e.x - v2s.x) +
|
|
||||||
(v1e.y - v1s.y) * (v2e.y - v2s.y));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Computes and returns the dot product of the two vectors. See
|
|
||||||
* {@link #dot(Point,Point,Point,Point)} for an explanation of the
|
|
||||||
* arguments
|
|
||||||
*/
|
|
||||||
public static int dot (int v1sx, int v1sy, int v1ex, int v1ey,
|
|
||||||
int v2sx, int v2sy, int v2ex, int v2ey)
|
|
||||||
{
|
|
||||||
return ((v1ex - v1sx) * (v2ex - v2sx) + (v1ey - v1sy) * (v2ey - v2sy));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Computes and returns the dot product of the two vectors. The
|
|
||||||
* vectors are assumed to start with the same coordinate and end with
|
|
||||||
* different coordinates.
|
|
||||||
*
|
|
||||||
* @param vs the starting point of both vectors.
|
|
||||||
* @param v1e the ending point of the first vector.
|
|
||||||
* @param v2e the ending point of the second vector.
|
|
||||||
*/
|
|
||||||
public static int dot (Point vs, Point v1e, Point v2e)
|
|
||||||
{
|
|
||||||
return ((v1e.x - vs.x) * (v2e.x - vs.x) +
|
|
||||||
(v1e.y - vs.y) * (v2e.y - vs.y));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Computes and returns the dot product of the two vectors.
|
|
||||||
* See {@link #dot(Point,Point,Point)} for an explanation of the
|
|
||||||
* arguments
|
|
||||||
*/
|
|
||||||
public static int dot (int vsx, int vsy, int v1ex, int v1ey,
|
|
||||||
int v2ex, int v2ey)
|
|
||||||
{
|
|
||||||
return ((v1ex - vsx) * (v2ex - vsx) + (v1ey - vsy) * (v2ey - vsy));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Computes the point nearest to the specified point <code>p3</code>
|
|
||||||
* on the line defined by the two points <code>p1</code> and
|
|
||||||
* <code>p2</code>. The computed point is stored into <code>n</code>.
|
|
||||||
* <em>Note:</em> <code>p1</code> and <code>p2</code> must not be
|
|
||||||
* coincident.
|
|
||||||
*
|
|
||||||
* @param p1 one point on the line.
|
|
||||||
* @param p2 another point on the line (not equal to <code>p1</code>).
|
|
||||||
* @param p3 the point to which we wish to be most near.
|
|
||||||
* @param n the point on the line defined by <code>p1</code> and
|
|
||||||
* <code>p2</code> that is nearest to <code>p</code>.
|
|
||||||
*
|
|
||||||
* @return the point object supplied via <code>n</code>.
|
|
||||||
*/
|
|
||||||
public static Point nearestToLine (Point p1, Point p2, Point p3, Point n)
|
|
||||||
{
|
|
||||||
// see http://astronomy.swin.edu.au/~pbourke/geometry/pointline/
|
|
||||||
// for a (not very good) explanation of the math
|
|
||||||
int Ax = p2.x - p1.x, Ay = p2.y - p1.y;
|
|
||||||
float u = (p3.x - p1.x) * Ax + (p3.y - p1.y) * Ay;
|
|
||||||
u /= (Ax * Ax + Ay * Ay);
|
|
||||||
n.x = p1.x + Math.round(Ax * u);
|
|
||||||
n.y = p1.y + Math.round(Ay * u);
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the intersection of two lines. Either line may be
|
|
||||||
* considered as a line segment, and the intersecting point
|
|
||||||
* is only considered valid if it lies upon the segment.
|
|
||||||
* Note that Point extends Point2D.
|
|
||||||
*
|
|
||||||
* @param p1 and p2 the coordinates of the first line.
|
|
||||||
* @param seg1 if the first line should be considered a segment.
|
|
||||||
* @param p3 and p4 the coordinates of the second line.
|
|
||||||
* @param seg2 if the second line should be considered a segment.
|
|
||||||
* @param result the point that will be filled in with the intersecting
|
|
||||||
* point.
|
|
||||||
*
|
|
||||||
* @return true if result was filled in, or false if the lines
|
|
||||||
* are parallel or the point of intersection lies outside of a
|
|
||||||
* segment.
|
|
||||||
*/
|
|
||||||
public static boolean lineIntersection (
|
|
||||||
Point2D p1, Point2D p2, boolean seg1,
|
|
||||||
Point2D p3, Point2D p4, boolean seg2, Point2D result)
|
|
||||||
{
|
|
||||||
// see http://astronomy.swin.edu.au/~pbourke/geometry/lineline2d/
|
|
||||||
double y43 = p4.getY() - p3.getY();
|
|
||||||
double x21 = p2.getX() - p1.getX();
|
|
||||||
double x43 = p4.getX() - p3.getX();
|
|
||||||
double y21 = p2.getY() - p1.getY();
|
|
||||||
double denom = y43 * x21 - x43 * y21;
|
|
||||||
if (denom == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
double y13 = p1.getY() - p3.getY();
|
|
||||||
double x13 = p1.getX() - p3.getX();
|
|
||||||
double ua = (x43 * y13 - y43 * x13) / denom;
|
|
||||||
if (seg1 && ((ua < 0) || (ua > 1))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (seg2) {
|
|
||||||
double ub = (x21 * y13 - y21 * x13) / denom;
|
|
||||||
if ((ub < 0) || (ub > 1)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double x = p1.getX() + ua * x21;
|
|
||||||
double y = p1.getY() + ua * y21;
|
|
||||||
result.setLocation(x, y);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns less than zero if <code>p2</code> is on the left hand side
|
|
||||||
* of the line created by <code>p1</code> and <code>theta</code> and
|
|
||||||
* greater than zero if it is on the right hand side. In theory, it
|
|
||||||
* will return zero if the point is on the line, but due to rounding
|
|
||||||
* errors it almost always decides that it's not exactly on the line.
|
|
||||||
*
|
|
||||||
* @param p1 the point on the line whose side we're checking.
|
|
||||||
* @param theta the (logical) angle defining the line.
|
|
||||||
* @param p2 the point that lies on one side or the other of the line.
|
|
||||||
*/
|
|
||||||
public static int whichSide (Point p1, double theta, Point p2)
|
|
||||||
{
|
|
||||||
// obtain the point defining the right hand normal (N)
|
|
||||||
theta += Math.PI/2;
|
|
||||||
int x = p1.x + (int)Math.round(1000*Math.cos(theta)),
|
|
||||||
y = p1.y + (int)Math.round(1000*Math.sin(theta));
|
|
||||||
|
|
||||||
// now dot the vector from p1->p2 with the vector from p1->N, if
|
|
||||||
// it's positive, we're on the right hand side, if it's negative
|
|
||||||
// we're on the left hand side and if it's zero, we're on the line
|
|
||||||
return dot(p1.x, p1.y, p2.x, p2.y, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shifts the position of the <code>tainer</code> rectangle to ensure
|
|
||||||
* that it contains the <code>tained</code> rectangle. The
|
|
||||||
* <code>tainer</code> rectangle must be larger than or equal to the
|
|
||||||
* size of the <code>tained</code> rectangle.
|
|
||||||
*/
|
|
||||||
public static void shiftToContain (Rectangle tainer, Rectangle tained)
|
|
||||||
{
|
|
||||||
if (tained.x < tainer.x) {
|
|
||||||
tainer.x = tained.x;
|
|
||||||
}
|
|
||||||
if (tained.y < tainer.y) {
|
|
||||||
tainer.y = tained.y;
|
|
||||||
}
|
|
||||||
if (tained.x + tained.width > tainer.x + tainer.width) {
|
|
||||||
tainer.x = tained.x - (tainer.width - tained.width);
|
|
||||||
}
|
|
||||||
if (tained.y + tained.height > tainer.y + tainer.height) {
|
|
||||||
tainer.y = tained.y - (tainer.height - tained.height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds the target rectangle to the bounds of the source rectangle. If
|
|
||||||
* the source rectangle is null, a new rectangle is created that is the
|
|
||||||
* size of the target rectangle.
|
|
||||||
*
|
|
||||||
* @return the source rectangle.
|
|
||||||
*/
|
|
||||||
public static Rectangle grow (Rectangle source, Rectangle target)
|
|
||||||
{
|
|
||||||
if (target == null) {
|
|
||||||
Log.warning("Can't grow with null rectangle [src=" + source +
|
|
||||||
", tgt=" + target + "].");
|
|
||||||
Thread.dumpStack();
|
|
||||||
} else if (source == null) {
|
|
||||||
source = new Rectangle(target);
|
|
||||||
} else {
|
|
||||||
source.add(target);
|
|
||||||
}
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the rectangle containing the specified tile in the supplied
|
|
||||||
* larger rectangle. Tiles go from left to right, top to bottom.
|
|
||||||
*/
|
|
||||||
public static Rectangle getTile (
|
|
||||||
int width, int height, int tileWidth, int tileHeight, int tileIndex)
|
|
||||||
{
|
|
||||||
// figure out from whence to crop the tile
|
|
||||||
int tilesPerRow = width / tileWidth;
|
|
||||||
|
|
||||||
// if we got a bogus region, return bogus tile bounds
|
|
||||||
if (tilesPerRow == 0) {
|
|
||||||
return new Rectangle(0, 0, width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
int row = tileIndex / tilesPerRow;
|
|
||||||
int col = tileIndex % tilesPerRow;
|
|
||||||
|
|
||||||
// crop the tile-sized image chunk from the full image
|
|
||||||
return new Rectangle(
|
|
||||||
tileWidth*col, tileHeight*row, tileWidth, tileHeight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.geom;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A placeholder class that contains a reference to the log object used by
|
|
||||||
* this package.
|
|
||||||
*/
|
|
||||||
public class Log
|
|
||||||
{
|
|
||||||
public static final String PACKAGE = "geom";
|
|
||||||
|
|
||||||
public static com.samskivert.util.Log log =
|
|
||||||
new com.samskivert.util.Log(PACKAGE);
|
|
||||||
|
|
||||||
/** Convenience function. */
|
|
||||||
public static void debug (String message)
|
|
||||||
{
|
|
||||||
log.debug(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Convenience function. */
|
|
||||||
public static void info (String message)
|
|
||||||
{
|
|
||||||
log.info(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Convenience function. */
|
|
||||||
public static void warning (String message)
|
|
||||||
{
|
|
||||||
log.warning(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Convenience function. */
|
|
||||||
public static void logStackTrace (Throwable t)
|
|
||||||
{
|
|
||||||
log.logStackTrace(com.samskivert.util.Log.WARNING, t);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getLevel ()
|
|
||||||
{
|
|
||||||
return com.samskivert.util.Log.getLevel(PACKAGE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,582 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
import com.samskivert.util.Queue;
|
|
||||||
import com.samskivert.util.RunQueue;
|
|
||||||
import com.samskivert.util.StringUtil;
|
|
||||||
|
|
||||||
import com.jme.renderer.Camera;
|
|
||||||
import com.jme.renderer.ColorRGBA;
|
|
||||||
import com.jme.renderer.Renderer;
|
|
||||||
|
|
||||||
import com.jme.scene.Node;
|
|
||||||
import com.jme.scene.state.LightState;
|
|
||||||
import com.jme.scene.state.ZBufferState;
|
|
||||||
|
|
||||||
import com.jme.system.DisplaySystem;
|
|
||||||
import com.jme.system.JmeException;
|
|
||||||
import com.jme.system.PropertiesIO;
|
|
||||||
import com.jme.system.lwjgl.LWJGLPropertiesDialog;
|
|
||||||
|
|
||||||
import com.jme.input.InputHandler;
|
|
||||||
import com.jme.input.KeyInput;
|
|
||||||
import com.jme.input.Mouse;
|
|
||||||
import com.jme.input.MouseInput;
|
|
||||||
import com.jmex.bui.BRootNode;
|
|
||||||
import com.jmex.bui.PolledRootNode;
|
|
||||||
|
|
||||||
import com.jme.light.PointLight;
|
|
||||||
import com.jme.math.Vector3f;
|
|
||||||
import com.jme.util.Timer;
|
|
||||||
|
|
||||||
import com.threerings.jme.camera.CameraHandler;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines a basic application framework providing integration with the
|
|
||||||
* <a href="../presents/package.html">Presents</a> networking system and
|
|
||||||
* targeting the framerate of the display.
|
|
||||||
*/
|
|
||||||
public class JmeApp
|
|
||||||
implements RunQueue
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Returns a context implementation that provides access to all the
|
|
||||||
* necessary bits.
|
|
||||||
*/
|
|
||||||
public JmeContext getContext ()
|
|
||||||
{
|
|
||||||
return _ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does the main initialization of the application. This method should
|
|
||||||
* be called first, and then the {@link #run} method should be called
|
|
||||||
* to begin the rendering/event loop. Derived classes can override
|
|
||||||
* this, being sure to call super before doing their own
|
|
||||||
* initalization.
|
|
||||||
*
|
|
||||||
* @return true if the application initialized successfully, false if
|
|
||||||
* initialization failed. (See {@link #reportInitFailure}.)
|
|
||||||
*/
|
|
||||||
public boolean init ()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
// initialize the rendering system
|
|
||||||
initDisplay();
|
|
||||||
if (!_display.isCreated()) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Failed to initialize display?");
|
|
||||||
}
|
|
||||||
|
|
||||||
// create an appropriate timer
|
|
||||||
_timer = Timer.getTimer();
|
|
||||||
|
|
||||||
// start with the target FPS equal to the refresh rate (but
|
|
||||||
// sometimes the refresh rate is reported as zero so don't let that
|
|
||||||
// freak us out)
|
|
||||||
setTargetFPS(Math.max(_display.getFrequency(), 60));
|
|
||||||
|
|
||||||
// initialize our main camera controls and user input handling
|
|
||||||
initInput();
|
|
||||||
|
|
||||||
// initialize the root node
|
|
||||||
initRoot();
|
|
||||||
|
|
||||||
// initialize the lighting
|
|
||||||
initLighting();
|
|
||||||
|
|
||||||
// initialize the UI support stuff
|
|
||||||
initInterface();
|
|
||||||
|
|
||||||
// update everything for the zeroth tick
|
|
||||||
_iface.updateRenderState();
|
|
||||||
_geom.updateRenderState();
|
|
||||||
_root.updateGeometricState(0f, true);
|
|
||||||
_root.updateRenderState();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
} catch (Throwable t) {
|
|
||||||
reportInitFailure(t);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures whether or not we display FPS and other statistics atop the
|
|
||||||
* display.
|
|
||||||
*/
|
|
||||||
public void displayStatistics (boolean display)
|
|
||||||
{
|
|
||||||
if (display && (_stats == null)) {
|
|
||||||
_stats = new StatsDisplay(_display.getRenderer());
|
|
||||||
_stats.updateGeometricState(0f, true);
|
|
||||||
_stats.updateRenderState();
|
|
||||||
} else if (!display && (_stats != null)) {
|
|
||||||
_stats = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if we are displaying statistics, false if not.
|
|
||||||
*/
|
|
||||||
public boolean showingStatistics ()
|
|
||||||
{
|
|
||||||
return (_stats != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the target frames per second.
|
|
||||||
*
|
|
||||||
* @return the old target frames per second.
|
|
||||||
*/
|
|
||||||
public int setTargetFPS (int targetFPS)
|
|
||||||
{
|
|
||||||
int oldTargetFPS = _targetFPS;
|
|
||||||
_targetFPS = targetFPS;
|
|
||||||
_ticksPerFrame = _timer.getResolution() / _targetFPS;
|
|
||||||
return oldTargetFPS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the frames per second averaged over the last 32 frames.
|
|
||||||
*/
|
|
||||||
public float getRecentFrameRate ()
|
|
||||||
{
|
|
||||||
return _timer.getFrameRate();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables or disables the update and render part of the
|
|
||||||
* update/render/process events application loop.
|
|
||||||
*/
|
|
||||||
public void setEnabled (boolean update, boolean render)
|
|
||||||
{
|
|
||||||
_updateEnabled = update;
|
|
||||||
_renderEnabled = render;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts up the main rendering and event processing loop. This method
|
|
||||||
* will not return until the application is terminated with a call to
|
|
||||||
* {@link #stop}.
|
|
||||||
*/
|
|
||||||
public void run ()
|
|
||||||
{
|
|
||||||
synchronized (this) {
|
|
||||||
_dispatchThread = Thread.currentThread();
|
|
||||||
}
|
|
||||||
|
|
||||||
// enter the main rendering and event processing loop
|
|
||||||
while (!_finished && !_display.isClosing()) {
|
|
||||||
try {
|
|
||||||
// render the current frame
|
|
||||||
long frameStart = processFrame();
|
|
||||||
|
|
||||||
// and process events until it's time to render the next
|
|
||||||
PROCESS_EVENTS:
|
|
||||||
do {
|
|
||||||
Runnable r = (Runnable)_evqueue.getNonBlocking();
|
|
||||||
if (r != null) {
|
|
||||||
r.run();
|
|
||||||
}
|
|
||||||
if (_timer.getTime() - frameStart >= _ticksPerFrame) {
|
|
||||||
break PROCESS_EVENTS;
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
Thread.sleep(1);
|
|
||||||
} catch (InterruptedException ie) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (!_finished);
|
|
||||||
_failures = 0;
|
|
||||||
|
|
||||||
} catch (Throwable t) {
|
|
||||||
Log.logStackTrace(t);
|
|
||||||
// stick a fork in things if we fail too many times in a row
|
|
||||||
if (++_failures > MAX_SUCCESSIVE_FAILURES) {
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
cleanup();
|
|
||||||
} catch (Throwable t) {
|
|
||||||
Log.logStackTrace(t);
|
|
||||||
} finally {
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the duration (in seconds) between the previous frame and the
|
|
||||||
* current frame.
|
|
||||||
*/
|
|
||||||
public float getFrameTime ()
|
|
||||||
{
|
|
||||||
return _frameTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instructs the application to stop the main loop, cleanup and exit.
|
|
||||||
*/
|
|
||||||
public void stop ()
|
|
||||||
{
|
|
||||||
_finished = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface RunQueue
|
|
||||||
public void postRunnable (Runnable r)
|
|
||||||
{
|
|
||||||
_evqueue.append(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface RunQueue
|
|
||||||
public boolean isDispatchThread ()
|
|
||||||
{
|
|
||||||
return Thread.currentThread() == _dispatchThread;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines the display configuration and creates the display. This must
|
|
||||||
* fill in {@link #_api} as a side-effect.
|
|
||||||
*/
|
|
||||||
protected DisplaySystem createDisplay ()
|
|
||||||
{
|
|
||||||
PropertiesIO props = new PropertiesIO(getConfigPath("jme.cfg"));
|
|
||||||
if (!props.load()) {
|
|
||||||
LWJGLPropertiesDialog dialog =
|
|
||||||
new LWJGLPropertiesDialog(props, (String)null);
|
|
||||||
while (dialog.isVisible()) {
|
|
||||||
try {
|
|
||||||
Thread.sleep(5);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Log.warning("Error waiting for dialog system, " +
|
|
||||||
"using defaults.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_api = props.getRenderer();
|
|
||||||
DisplaySystem display = DisplaySystem.getDisplaySystem(_api);
|
|
||||||
display.createWindow(props.getWidth(), props.getHeight(),
|
|
||||||
props.getDepth(), props.getFreq(),
|
|
||||||
props.getFullscreen());
|
|
||||||
return display;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the underlying rendering system, creating a display of
|
|
||||||
* the proper resolution and depth.
|
|
||||||
*/
|
|
||||||
protected void initDisplay ()
|
|
||||||
throws JmeException
|
|
||||||
{
|
|
||||||
// create the main display system
|
|
||||||
_display = createDisplay();
|
|
||||||
|
|
||||||
// create a camera
|
|
||||||
int width = _display.getWidth(), height = _display.getHeight();
|
|
||||||
_camera = _display.getRenderer().createCamera(width, height);
|
|
||||||
|
|
||||||
// start with a black background
|
|
||||||
_display.getRenderer().setBackgroundColor(ColorRGBA.black);
|
|
||||||
|
|
||||||
// set up the camera
|
|
||||||
_camera.setFrustumPerspective(45.0f, width/(float)height, 1, 10000);
|
|
||||||
Vector3f loc = new Vector3f(0.0f, 0.0f, 25.0f);
|
|
||||||
Vector3f left = new Vector3f(-1.0f, 0.0f, 0.0f);
|
|
||||||
Vector3f up = new Vector3f(0.0f, 1.0f, 0.0f);
|
|
||||||
Vector3f dir = new Vector3f(0.0f, 0f, -1.0f);
|
|
||||||
_camera.setFrame(loc, left, up, dir);
|
|
||||||
_camera.update();
|
|
||||||
_display.getRenderer().setCamera(_camera);
|
|
||||||
|
|
||||||
// tell the renderer to keep track of rendering information (total
|
|
||||||
// triangles drawn, etc.)
|
|
||||||
_display.getRenderer().enableStatistics(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets up a main input controller to handle the camera and deal with
|
|
||||||
* global user input.
|
|
||||||
*/
|
|
||||||
protected void initInput ()
|
|
||||||
{
|
|
||||||
_camhand = createCameraHandler(_camera);
|
|
||||||
_input = createInputHandler(_camhand);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the camera handler which provides various camera manipulation
|
|
||||||
* functionality.
|
|
||||||
*/
|
|
||||||
protected CameraHandler createCameraHandler (Camera camera)
|
|
||||||
{
|
|
||||||
return new CameraHandler(camera);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the input handler used to control our camera and manage non-UI
|
|
||||||
* keyboard input.
|
|
||||||
*/
|
|
||||||
protected InputHandler createInputHandler (CameraHandler hand)
|
|
||||||
{
|
|
||||||
return new InputHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates our root node and sets up the basic rendering system.
|
|
||||||
*/
|
|
||||||
protected void initRoot ()
|
|
||||||
{
|
|
||||||
_root = new Node("Root");
|
|
||||||
|
|
||||||
// set up a node for our geometry
|
|
||||||
_geom = new Node("Geometry");
|
|
||||||
|
|
||||||
// make everything opaque by default
|
|
||||||
_geom.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
|
|
||||||
|
|
||||||
// set up a zbuffer
|
|
||||||
ZBufferState zbuf = _display.getRenderer().createZBufferState();
|
|
||||||
zbuf.setEnabled(true);
|
|
||||||
zbuf.setFunction(ZBufferState.CF_LEQUAL);
|
|
||||||
_geom.setRenderState(zbuf);
|
|
||||||
_root.attachChild(_geom);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets up some default lighting.
|
|
||||||
*/
|
|
||||||
protected void initLighting ()
|
|
||||||
{
|
|
||||||
PointLight light = new PointLight();
|
|
||||||
light.setDiffuse(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
|
|
||||||
light.setAmbient(new ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f));
|
|
||||||
light.setLocation(new Vector3f(100, 100, 100));
|
|
||||||
light.setEnabled(true);
|
|
||||||
|
|
||||||
_lights = _display.getRenderer().createLightState();
|
|
||||||
_lights.setEnabled(true);
|
|
||||||
_lights.attach(light);
|
|
||||||
_geom.setRenderState(_lights);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes our user interface bits.
|
|
||||||
*/
|
|
||||||
protected void initInterface ()
|
|
||||||
{
|
|
||||||
// set up a node for our interface
|
|
||||||
_iface = new Node("Interface");
|
|
||||||
_root.attachChild(_iface);
|
|
||||||
|
|
||||||
// create our root node
|
|
||||||
_rnode = createRootNode();
|
|
||||||
_iface.attachChild(_rnode);
|
|
||||||
|
|
||||||
// we don't hide the cursor
|
|
||||||
MouseInput.get().setCursorVisible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows a customized root node to be created.
|
|
||||||
*/
|
|
||||||
protected BRootNode createRootNode ()
|
|
||||||
{
|
|
||||||
return new PolledRootNode(_timer, _input);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when initialization fails to give the application a chance
|
|
||||||
* to report the failure to the user.
|
|
||||||
*/
|
|
||||||
protected void reportInitFailure (Throwable t)
|
|
||||||
{
|
|
||||||
Log.logStackTrace(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Processes a single frame.
|
|
||||||
*/
|
|
||||||
protected final long processFrame ()
|
|
||||||
{
|
|
||||||
// update our simulation and render a frame
|
|
||||||
long frameStart = _timer.getTime();
|
|
||||||
if (_updateEnabled) {
|
|
||||||
update(frameStart);
|
|
||||||
}
|
|
||||||
if (_renderEnabled) {
|
|
||||||
render(frameStart);
|
|
||||||
_display.getRenderer().displayBackBuffer();
|
|
||||||
}
|
|
||||||
return frameStart;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called every frame to update whatever sort of real time business we
|
|
||||||
* have that needs updating.
|
|
||||||
*/
|
|
||||||
protected void update (long frameTick)
|
|
||||||
{
|
|
||||||
// recalculate the frame rate
|
|
||||||
_timer.update();
|
|
||||||
|
|
||||||
// update the camera handler
|
|
||||||
_camhand.update(_frameTime);
|
|
||||||
|
|
||||||
// run all of the controllers attached to nodes
|
|
||||||
_frameTime = (_lastTick == 0L) ? 0f : (float)(frameTick - _lastTick) /
|
|
||||||
_timer.getResolution();
|
|
||||||
_lastTick = frameTick;
|
|
||||||
_root.updateGeometricState(_frameTime, true);
|
|
||||||
|
|
||||||
// update our stats display if we have one
|
|
||||||
if (_stats != null) {
|
|
||||||
_stats.update(_timer, _display.getRenderer());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called every frame to issue the rendering instructions for this frame.
|
|
||||||
*/
|
|
||||||
protected void render (float frameTime)
|
|
||||||
{
|
|
||||||
// clear out our previous information
|
|
||||||
_display.getRenderer().clearStatistics();
|
|
||||||
_display.getRenderer().clearBuffers();
|
|
||||||
|
|
||||||
// draw the root node and all of its children
|
|
||||||
_display.getRenderer().draw(_root);
|
|
||||||
|
|
||||||
// this would render bounding boxes
|
|
||||||
// _display.getRenderer().drawBounds(_root);
|
|
||||||
|
|
||||||
// draw our stats atop everything
|
|
||||||
if (_stats != null) {
|
|
||||||
_display.getRenderer().draw(_stats);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the application is terminating cleanly after having
|
|
||||||
* successfully completed initialization and begun the main loop.
|
|
||||||
*/
|
|
||||||
protected void cleanup ()
|
|
||||||
{
|
|
||||||
_display.reset();
|
|
||||||
KeyInput.destroyIfInitalized();
|
|
||||||
MouseInput.destroyIfInitalized();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the display and exits the JVM process.
|
|
||||||
*/
|
|
||||||
protected void exit ()
|
|
||||||
{
|
|
||||||
if (_display != null) {
|
|
||||||
_display.close();
|
|
||||||
}
|
|
||||||
System.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepends the necessary bits onto the supplied path to properly
|
|
||||||
* locate it in our configuration directory.
|
|
||||||
*/
|
|
||||||
protected String getConfigPath (String file)
|
|
||||||
{
|
|
||||||
String cfgdir = ".narya";
|
|
||||||
String home = System.getProperty("user.home");
|
|
||||||
if (!StringUtil.isBlank(home)) {
|
|
||||||
cfgdir = home + File.separator + cfgdir;
|
|
||||||
}
|
|
||||||
// create the configuration directory if it does not already exist
|
|
||||||
File dir = new File(cfgdir);
|
|
||||||
if (!dir.exists()) {
|
|
||||||
dir.mkdir();
|
|
||||||
}
|
|
||||||
return cfgdir + File.separator + file;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Provides access to various needed bits. */
|
|
||||||
protected JmeContext _ctx = new JmeContext() {
|
|
||||||
public DisplaySystem getDisplay () {
|
|
||||||
return _display;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Renderer getRenderer () {
|
|
||||||
return _display.getRenderer();
|
|
||||||
}
|
|
||||||
|
|
||||||
public CameraHandler getCameraHandler () {
|
|
||||||
return _camhand;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Node getGeometry () {
|
|
||||||
return _geom;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Node getInterface () {
|
|
||||||
return _iface;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InputHandler getInputHandler () {
|
|
||||||
return _input;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BRootNode getRootNode () {
|
|
||||||
return _rnode;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
protected Timer _timer;
|
|
||||||
protected Thread _dispatchThread;
|
|
||||||
protected Queue _evqueue = new Queue();
|
|
||||||
protected long _lastTick;
|
|
||||||
protected float _frameTime;
|
|
||||||
|
|
||||||
protected String _api;
|
|
||||||
protected DisplaySystem _display;
|
|
||||||
protected Camera _camera;
|
|
||||||
protected CameraHandler _camhand;
|
|
||||||
|
|
||||||
protected InputHandler _input;
|
|
||||||
protected BRootNode _rnode;
|
|
||||||
|
|
||||||
protected long _ticksPerFrame;
|
|
||||||
protected int _targetFPS;
|
|
||||||
protected boolean _updateEnabled = true, _renderEnabled = true;
|
|
||||||
protected boolean _finished;
|
|
||||||
protected int _failures;
|
|
||||||
|
|
||||||
protected Node _root, _geom, _iface;
|
|
||||||
protected LightState _lights;
|
|
||||||
protected StatsDisplay _stats;
|
|
||||||
|
|
||||||
/** If we fail 100 frames in a row, stick a fork in ourselves. */
|
|
||||||
protected static final int MAX_SUCCESSIVE_FAILURES = 100;
|
|
||||||
}
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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;
|
|
||||||
|
|
||||||
import java.awt.Canvas;
|
|
||||||
import java.awt.Graphics;
|
|
||||||
import java.awt.EventQueue;
|
|
||||||
import java.awt.event.ComponentAdapter;
|
|
||||||
import java.awt.event.ComponentEvent;
|
|
||||||
|
|
||||||
import org.lwjgl.LWJGLException;
|
|
||||||
|
|
||||||
import com.jme.renderer.Renderer;
|
|
||||||
import com.jme.renderer.lwjgl.LWJGLRenderer;
|
|
||||||
import com.jme.scene.Node;
|
|
||||||
import com.jme.system.DisplaySystem;
|
|
||||||
import com.jmex.awt.JMECanvas;
|
|
||||||
import com.jmex.awt.JMECanvasImplementor;
|
|
||||||
import com.jmex.awt.lwjgl.LWJGLCanvas;
|
|
||||||
|
|
||||||
import com.jmex.bui.CanvasRootNode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extends the basic {@link JmeApp} with the necessary wiring to use the
|
|
||||||
* GL/AWT bridge to display our GL interface in an AWT component.
|
|
||||||
*/
|
|
||||||
public class JmeCanvasApp extends JmeApp
|
|
||||||
{
|
|
||||||
public JmeCanvasApp (int width, int height)
|
|
||||||
{
|
|
||||||
_display = DisplaySystem.getDisplaySystem("LWJGL");
|
|
||||||
|
|
||||||
// create a throwaway canvas so that the display system knows it's
|
|
||||||
// created, then create our custom canvas
|
|
||||||
_display.createCanvas(width, height);
|
|
||||||
try {
|
|
||||||
_canvas = createCanvas();
|
|
||||||
} catch (LWJGLException e) {
|
|
||||||
Log.warning("Failed to create LWJGL canvas [error=" + e + "].");
|
|
||||||
}
|
|
||||||
((JMECanvas)_canvas).setImplementor(_winimp);
|
|
||||||
_canvas.setBounds(0, 0, width, height);
|
|
||||||
_canvas.addComponentListener(new ComponentAdapter() {
|
|
||||||
public void componentResized (ComponentEvent ce) {
|
|
||||||
_winimp.resizeCanvas(_canvas.getWidth(), _canvas.getHeight());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the AWT canvas that contains our GL display.
|
|
||||||
*/
|
|
||||||
public Canvas getCanvas ()
|
|
||||||
{
|
|
||||||
return _canvas;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run ()
|
|
||||||
{
|
|
||||||
Thread t = new Thread() {
|
|
||||||
public void run () {
|
|
||||||
while (!_finished) {
|
|
||||||
// queue up another repaint
|
|
||||||
_canvas.repaint();
|
|
||||||
try {
|
|
||||||
Thread.sleep(10);
|
|
||||||
} catch (Exception e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
t.setDaemon(true);
|
|
||||||
t.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface RunQueue
|
|
||||||
public void postRunnable (Runnable r)
|
|
||||||
{
|
|
||||||
EventQueue.invokeLater(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface RunQueue
|
|
||||||
public boolean isDispatchThread ()
|
|
||||||
{
|
|
||||||
return EventQueue.isDispatchThread();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates and returns the LWJGL canvas instance.
|
|
||||||
*/
|
|
||||||
protected Canvas createCanvas ()
|
|
||||||
throws LWJGLException
|
|
||||||
{
|
|
||||||
// LWJGL's canvas releases the context after painting. we make it
|
|
||||||
// current again, because we want it valid when we process events.
|
|
||||||
return new LWJGLCanvas() {
|
|
||||||
public void update (Graphics g) {
|
|
||||||
super.update(g);
|
|
||||||
try {
|
|
||||||
makeCurrent();
|
|
||||||
} catch (LWJGLException e) {
|
|
||||||
Log.warning("Failed to make context current [error=" +
|
|
||||||
e + "].");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
protected DisplaySystem createDisplay ()
|
|
||||||
{
|
|
||||||
// we've already created our display earlier so just return it
|
|
||||||
_api = "LWJGL";
|
|
||||||
return _display;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
protected void initInterface ()
|
|
||||||
{
|
|
||||||
_iface = new Node("Interface");
|
|
||||||
_root.attachChild(_iface);
|
|
||||||
|
|
||||||
_rnode = new CanvasRootNode(_canvas);
|
|
||||||
_iface.attachChild(_rnode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** This is used if we embed our GL display in an AWT component. */
|
|
||||||
protected JMECanvasImplementor _winimp = new JMECanvasImplementor() {
|
|
||||||
public void doSetup () {
|
|
||||||
super.doSetup();
|
|
||||||
|
|
||||||
LWJGLRenderer renderer =
|
|
||||||
new LWJGLRenderer(_canvas.getWidth(), _canvas.getHeight());
|
|
||||||
renderer.setHeadless(true);
|
|
||||||
setRenderer(renderer);
|
|
||||||
_display.setRenderer(renderer);
|
|
||||||
DisplaySystem.updateStates(renderer);
|
|
||||||
|
|
||||||
if (!init()) {
|
|
||||||
Log.warning("JmeCanvasApp init failed.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void doUpdate () {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void doRender () {
|
|
||||||
// here we do our normal frame processing
|
|
||||||
try {
|
|
||||||
processFrame();
|
|
||||||
// we don't process events as the AWT queue handles them
|
|
||||||
_failures = 0;
|
|
||||||
} catch (Throwable t) {
|
|
||||||
Log.logStackTrace(t);
|
|
||||||
// stick a fork in things if we fail too many
|
|
||||||
// times in a row
|
|
||||||
if (++_failures > MAX_SUCCESSIVE_FAILURES) {
|
|
||||||
JmeCanvasApp.this.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
protected Canvas _canvas;
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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;
|
|
||||||
|
|
||||||
import com.jme.input.InputHandler;
|
|
||||||
import com.jme.renderer.Renderer;
|
|
||||||
import com.jme.scene.Node;
|
|
||||||
import com.jme.system.DisplaySystem;
|
|
||||||
|
|
||||||
import com.jmex.bui.BRootNode;
|
|
||||||
|
|
||||||
import com.threerings.jme.camera.CameraHandler;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides access to the various bits needed by things that operate in
|
|
||||||
* JME land.
|
|
||||||
*/
|
|
||||||
public interface JmeContext
|
|
||||||
{
|
|
||||||
/** Returns the display to which we are rendering. */
|
|
||||||
public DisplaySystem getDisplay ();
|
|
||||||
|
|
||||||
/** Returns the renderer being used to draw everything. */
|
|
||||||
public Renderer getRenderer ();
|
|
||||||
|
|
||||||
/** Returns the handler for the camera being used to view the scene. */
|
|
||||||
public CameraHandler getCameraHandler ();
|
|
||||||
|
|
||||||
/** Returns the main geometry of our scene graph. */
|
|
||||||
public Node getGeometry ();
|
|
||||||
|
|
||||||
/** Returns the main interface node of our scene graph. */
|
|
||||||
public Node getInterface ();
|
|
||||||
|
|
||||||
/** Returns our main input handler. */
|
|
||||||
public InputHandler getInputHandler ();
|
|
||||||
|
|
||||||
/** Returns our UI root node. */
|
|
||||||
public BRootNode getRootNode ();
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id: Log.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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A placeholder class that contains a reference to the log object used by
|
|
||||||
* this package.
|
|
||||||
*/
|
|
||||||
public class Log
|
|
||||||
{
|
|
||||||
public static com.samskivert.util.Log log =
|
|
||||||
new com.samskivert.util.Log("narya.jme");
|
|
||||||
|
|
||||||
/** Convenience function. */
|
|
||||||
public static void debug (String message)
|
|
||||||
{
|
|
||||||
log.debug(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Convenience function. */
|
|
||||||
public static void info (String message)
|
|
||||||
{
|
|
||||||
log.info(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Convenience function. */
|
|
||||||
public static void warning (String message)
|
|
||||||
{
|
|
||||||
log.warning(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Convenience function. */
|
|
||||||
public static void logStackTrace (Throwable t)
|
|
||||||
{
|
|
||||||
log.logStackTrace(com.samskivert.util.Log.WARNING, t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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;
|
|
||||||
|
|
||||||
import com.jme.image.Texture;
|
|
||||||
import com.jme.renderer.Renderer;
|
|
||||||
import com.jme.scene.Node;
|
|
||||||
import com.jme.scene.Text;
|
|
||||||
import com.jme.scene.state.AlphaState;
|
|
||||||
import com.jme.scene.state.TextureState;
|
|
||||||
import com.jme.util.TextureManager;
|
|
||||||
import com.jme.util.Timer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tracks and displays render statistics.
|
|
||||||
*/
|
|
||||||
public class StatsDisplay extends Node
|
|
||||||
{
|
|
||||||
public StatsDisplay (Renderer renderer)
|
|
||||||
{
|
|
||||||
super("StatsNode");
|
|
||||||
|
|
||||||
// create an alpha state that will allow us to blend the text on
|
|
||||||
// top of whatever else is below
|
|
||||||
AlphaState astate = renderer.createAlphaState();
|
|
||||||
astate.setBlendEnabled(true);
|
|
||||||
astate.setSrcFunction(AlphaState.SB_SRC_ALPHA);
|
|
||||||
astate.setDstFunction(AlphaState.DB_ONE);
|
|
||||||
astate.setTestEnabled(true);
|
|
||||||
astate.setTestFunction(AlphaState.TF_GREATER);
|
|
||||||
astate.setEnabled(true);
|
|
||||||
|
|
||||||
// create a font texture
|
|
||||||
TextureState font = renderer.createTextureState();
|
|
||||||
font.setTexture(
|
|
||||||
TextureManager.loadTexture(
|
|
||||||
getClass().getClassLoader().getResource(DEFAULT_JME_FONT),
|
|
||||||
Texture.MM_LINEAR, Texture.FM_LINEAR));
|
|
||||||
font.setEnabled(true);
|
|
||||||
|
|
||||||
_text = new Text("StatsLabel", "");
|
|
||||||
_text.setCullMode(CULL_NEVER);
|
|
||||||
_text.setTextureCombineMode(TextureState.REPLACE);
|
|
||||||
|
|
||||||
attachChild(_text);
|
|
||||||
setRenderState(font);
|
|
||||||
setRenderState(astate);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void update (Timer timer, Renderer renderer)
|
|
||||||
{
|
|
||||||
_stats.setLength(0);
|
|
||||||
_stats.append("FPS: ").append((int)timer.getFrameRate());
|
|
||||||
_stats.append(" - ").append(renderer.getStatistics(_temp));
|
|
||||||
_text.print(_stats);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Text _text;
|
|
||||||
protected StringBuffer _stats = new StringBuffer();
|
|
||||||
protected StringBuffer _temp = new StringBuffer();
|
|
||||||
|
|
||||||
protected static final String DEFAULT_JME_FONT =
|
|
||||||
"rsrc/media/jme/defaultfont.tga";
|
|
||||||
}
|
|
||||||
@@ -1,412 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.camera;
|
|
||||||
|
|
||||||
import com.jme.math.FastMath;
|
|
||||||
import com.jme.math.Matrix3f;
|
|
||||||
import com.jme.math.Plane;
|
|
||||||
import com.jme.math.Vector3f;
|
|
||||||
import com.jme.renderer.Camera;
|
|
||||||
|
|
||||||
import com.samskivert.util.ObserverList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides various useful mechanisms for manipulating the camera.
|
|
||||||
*/
|
|
||||||
public class CameraHandler
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Creates a new camera handler. The camera begins life at the origin,
|
|
||||||
* facing in the negative z direction (pointing at the ground).
|
|
||||||
*/
|
|
||||||
public CameraHandler (Camera camera)
|
|
||||||
{
|
|
||||||
_camera = camera;
|
|
||||||
resetAxes();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets the camera orientation to its initial state.
|
|
||||||
*/
|
|
||||||
public void resetAxes ()
|
|
||||||
{
|
|
||||||
_camera.getDirection().set(0, 0, -1);
|
|
||||||
_camera.getLeft().set(-1, 0, 0);
|
|
||||||
_camera.getUp().set(0, 1, 0);
|
|
||||||
_camera.update();
|
|
||||||
|
|
||||||
_rxdir.set(1, 0, 0);
|
|
||||||
_rydir.set(0, 1, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures limits on the camera tilt.
|
|
||||||
*/
|
|
||||||
public void setTiltLimits (float minAngle, float maxAngle)
|
|
||||||
{
|
|
||||||
_minTilt = minAngle;
|
|
||||||
_maxTilt = maxAngle;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures limits on the distance the camera can be panned.
|
|
||||||
*
|
|
||||||
* @param boundViaFrustum if true instead of bounding the camera's
|
|
||||||
* position, we will compute the intersections of the view frustum with the
|
|
||||||
* ground plan and bound that rectangle into the specified bounds.
|
|
||||||
* <em>Note:</em> the camera must generally be pointing down at the ground
|
|
||||||
* (up to perhaps 45 degrees or so) for this to work. At higher angles the
|
|
||||||
* back of the view frustum will intersect the ground plane at or near
|
|
||||||
* infinity.
|
|
||||||
*/
|
|
||||||
public void setPanLimits (float minX, float minY, float maxX, float maxY,
|
|
||||||
boolean boundViaFrustum)
|
|
||||||
{
|
|
||||||
_minX = minX;
|
|
||||||
_minY = minY;
|
|
||||||
_maxX = maxX;
|
|
||||||
_maxY = maxY;
|
|
||||||
_boundViaFrustum = boundViaFrustum;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures the minimum and maximum zoom values allowed for the
|
|
||||||
* camera.
|
|
||||||
*/
|
|
||||||
public void setZoomLimits (float minZoom, float maxZoom)
|
|
||||||
{
|
|
||||||
_minZoom = minZoom;
|
|
||||||
_maxZoom = maxZoom;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables or disables the current set of limits.
|
|
||||||
*/
|
|
||||||
public void setLimitsEnabled (boolean enabled)
|
|
||||||
{
|
|
||||||
_limitsEnabled = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the camera zoom level to a value between zero (zoomed in maximally)
|
|
||||||
* and 1 (zoomed out maximally). Zoom limits must have already been set up
|
|
||||||
* via a call to {@link #setZoomLimits}.
|
|
||||||
*/
|
|
||||||
public void setZoomLevel (float level)
|
|
||||||
{
|
|
||||||
// Log.info("Zoom " + level + " " + _camera.getLocation());
|
|
||||||
level = Math.max(0f, Math.min(level, 1f));
|
|
||||||
_camera.getLocation().z = _minZ + (_maxZ - _minZ) * level;
|
|
||||||
_camera.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current camera zoom level.
|
|
||||||
*/
|
|
||||||
public float getZoomLevel ()
|
|
||||||
{
|
|
||||||
return (_camera.getLocation().z - _minZ) / (_maxZ - _minZ);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a camera path observer.
|
|
||||||
*/
|
|
||||||
public void addCameraObserver (CameraPath.Observer camobs)
|
|
||||||
{
|
|
||||||
_campathobs.add(camobs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes a camera path observer.
|
|
||||||
*/
|
|
||||||
public void removeCameraObserver (CameraPath.Observer camobs)
|
|
||||||
{
|
|
||||||
_campathobs.remove(camobs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the camera moving along a path which will be updated every tick
|
|
||||||
* until it is complete.
|
|
||||||
*/
|
|
||||||
public void moveCamera (CameraPath path)
|
|
||||||
{
|
|
||||||
if (_campath != null) {
|
|
||||||
_campath.abort();
|
|
||||||
_campathobs.apply(new CompletedOp(_campath));
|
|
||||||
}
|
|
||||||
_campath = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the camera is currently animating along a path, false if
|
|
||||||
* it is not.
|
|
||||||
*/
|
|
||||||
public boolean cameraIsMoving ()
|
|
||||||
{
|
|
||||||
return (_campath != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Skips immediately to the end of the current camera path.
|
|
||||||
*/
|
|
||||||
public void skipPath ()
|
|
||||||
{
|
|
||||||
// fake an update far enough into the future to trick the camera path
|
|
||||||
// into thinking it's done
|
|
||||||
update(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is called by the {@link JmeApp} on every frame to allow the handler
|
|
||||||
* to update the camera as necessary.
|
|
||||||
*/
|
|
||||||
public void update (float frameTime)
|
|
||||||
{
|
|
||||||
if (_campath != null) {
|
|
||||||
if (_campath.tick(frameTime)) {
|
|
||||||
CameraPath opath = _campath;
|
|
||||||
_campath = null;
|
|
||||||
_campathobs.apply(new CompletedOp(opath));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the camera being manipulated by this handler.
|
|
||||||
*/
|
|
||||||
public Camera getCamera ()
|
|
||||||
{
|
|
||||||
return _camera;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adjusts the camera's location. The specified location will be bounded
|
|
||||||
* within the current pan and zoom limits.
|
|
||||||
*/
|
|
||||||
public void setLocation (Vector3f location)
|
|
||||||
{
|
|
||||||
_camera.setLocation(bound(location));
|
|
||||||
_camera.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pans the camera the specified distance in the x and y directions. These
|
|
||||||
* distances will be multiplied by the current "view" x and y axes and
|
|
||||||
* added to the camera's position.
|
|
||||||
*/
|
|
||||||
public void panCamera (float x, float y)
|
|
||||||
{
|
|
||||||
Vector3f loc = _camera.getLocation();
|
|
||||||
loc.addLocal(_rxdir.mult(x, _temp));
|
|
||||||
loc.addLocal(_rydir.mult(y, _temp));
|
|
||||||
setLocation(loc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Zooms the camera in (distance < 0) and out (distance > 0) by the
|
|
||||||
* specified amount. The distance is multiplied by the camera's current
|
|
||||||
* direction and added to its current position, which is then bounded into
|
|
||||||
* the pan and zoom volume.
|
|
||||||
*/
|
|
||||||
public void zoomCamera (float distance)
|
|
||||||
{
|
|
||||||
Vector3f loc = _camera.getLocation();
|
|
||||||
float dist = getGroundPoint().distance(loc),
|
|
||||||
ndist = Math.min(Math.max(dist + distance, _minZoom), _maxZoom);
|
|
||||||
if ((distance = ndist - dist) == 0f) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
loc.subtractLocal(_camera.getDirection().mult(distance, _temp));
|
|
||||||
setLocation(loc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Locates the point on the ground at which the camera is "looking" and
|
|
||||||
* rotates the camera from that point around the specified vector by the
|
|
||||||
* specified angle. Additionally zooms the camera in (deltaZoom < 0) or out
|
|
||||||
* (deltaZoom > 0) along its direction of view by the specified amount.
|
|
||||||
*/
|
|
||||||
public void rotateCamera (
|
|
||||||
Vector3f spot, Vector3f axis, float deltaAngle, float deltaZoom)
|
|
||||||
{
|
|
||||||
// get a vector from the camera's current position to the point around
|
|
||||||
// which we're going to orbit
|
|
||||||
Vector3f direction = _camera.getLocation().subtract(spot);
|
|
||||||
|
|
||||||
// if we're rotating around the left vector, impose tilt limits
|
|
||||||
if (axis == _camera.getLeft()) {
|
|
||||||
float angle = FastMath.asin(_ground.normal.dot(direction) /
|
|
||||||
direction.length());
|
|
||||||
float nangle = Math.min(Math.max(angle + deltaAngle, _minTilt),
|
|
||||||
_maxTilt);
|
|
||||||
if ((deltaAngle = nangle - angle) == 0f) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a rotation matrix
|
|
||||||
_rotm.fromAxisAngle(axis, deltaAngle);
|
|
||||||
|
|
||||||
// rotate the direction vector and the camera itself
|
|
||||||
_rotm.mult(direction, direction);
|
|
||||||
_rotm.mult(_camera.getUp(), _camera.getUp());
|
|
||||||
_rotm.mult(_camera.getLeft(), _camera.getLeft());
|
|
||||||
_rotm.mult(_camera.getDirection(), _camera.getDirection());
|
|
||||||
|
|
||||||
// if we're rotating around the ground normal, we need to update our
|
|
||||||
// notion of side-to-side and forward for panning
|
|
||||||
if (axis == _ground.normal) {
|
|
||||||
_rotm.mult(_rxdir, _rxdir);
|
|
||||||
_rotm.mult(_rydir, _rydir);
|
|
||||||
}
|
|
||||||
|
|
||||||
// finally move the camera to its new location, zooming in or out in
|
|
||||||
// the process
|
|
||||||
float scale = 1 + (deltaZoom / direction.length());
|
|
||||||
direction.scaleAdd(scale, spot);
|
|
||||||
setLocation(direction);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Swings the camera perpendicular to the ground normal, around the point
|
|
||||||
* on the ground at which it is looking.
|
|
||||||
*/
|
|
||||||
public void orbitCamera (float deltaAngle)
|
|
||||||
{
|
|
||||||
rotateCamera(getGroundPoint(), _ground.normal, deltaAngle, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Swings the camera perpendicular to its left vector around the point on
|
|
||||||
* the ground at which it is looking.
|
|
||||||
*/
|
|
||||||
public void tiltCamera (float deltaAngle)
|
|
||||||
{
|
|
||||||
rotateCamera(getGroundPoint(), _camera.getLeft(), deltaAngle, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the point on the ground (z = 0) at which the camera is looking.
|
|
||||||
*/
|
|
||||||
public Vector3f getGroundPoint ()
|
|
||||||
{
|
|
||||||
float dist = -1f * _ground.normal.dot(_camera.getLocation()) /
|
|
||||||
_ground.normal.dot(_camera.getDirection());
|
|
||||||
return _camera.getLocation().add(_camera.getDirection().mult(dist));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the ground normal. Z is the default ground plane and the normal
|
|
||||||
* points in the positive z direction. <em>Do not modify this value.</em>
|
|
||||||
*/
|
|
||||||
public Vector3f getGroundNormal ()
|
|
||||||
{
|
|
||||||
return _ground.normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Vector3f bound (Vector3f loc)
|
|
||||||
{
|
|
||||||
if (!_limitsEnabled) {
|
|
||||||
return loc;
|
|
||||||
}
|
|
||||||
if (_boundViaFrustum) {
|
|
||||||
bound(_camera.getFrustumLeft(), _camera.getFrustumTop(), loc);
|
|
||||||
bound(_camera.getFrustumLeft(), _camera.getFrustumBottom(), loc);
|
|
||||||
bound(_camera.getFrustumRight(), _camera.getFrustumTop(), loc);
|
|
||||||
bound(_camera.getFrustumRight(), _camera.getFrustumBottom(), loc);
|
|
||||||
} else {
|
|
||||||
loc.x = Math.max(Math.min(loc.x, _maxX), _minX);
|
|
||||||
loc.y = Math.max(Math.min(loc.y, _maxY), _minY);
|
|
||||||
}
|
|
||||||
loc.z = Math.max(Math.min(loc.z, _maxZ), _minZ);
|
|
||||||
return loc;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void bound (float left, float up, Vector3f loc)
|
|
||||||
{
|
|
||||||
// start with the location of the camera moved out into the near
|
|
||||||
// frustum plane
|
|
||||||
_temp.set(loc);
|
|
||||||
_temp.scaleAdd(_camera.getFrustumNear(), _camera.getDirection(), loc);
|
|
||||||
|
|
||||||
// then slide it over to a corner of the near frustum rectangle
|
|
||||||
_temp.scaleAdd(left, _camera.getLeft(), _temp);
|
|
||||||
_temp.scaleAdd(up, _camera.getUp(), _temp);
|
|
||||||
|
|
||||||
// turn this into a vector with origin at the camera's location
|
|
||||||
_temp.subtractLocal(loc);
|
|
||||||
_temp.normalizeLocal();
|
|
||||||
|
|
||||||
// determine the intersection of said vector with the ground plane
|
|
||||||
float dist = -1f * _ground.normal.dot(loc) / _ground.normal.dot(_temp);
|
|
||||||
_temp.scaleAdd(dist, _temp, loc);
|
|
||||||
|
|
||||||
// we then assume that if the corner of the "viewable ground rectangle"
|
|
||||||
// is outside our bounds that we can simply adjust the camera location
|
|
||||||
// by the amount it is out of bounds
|
|
||||||
if (_temp.x > _maxX) {
|
|
||||||
loc.x -= (_temp.x - _maxX);
|
|
||||||
} else if (_temp.x < _minX) {
|
|
||||||
loc.x += (_minX - _temp.x);
|
|
||||||
}
|
|
||||||
if (_temp.y > _maxY) {
|
|
||||||
loc.y -= (_temp.y - _maxY);
|
|
||||||
} else if (_temp.y < _minY) {
|
|
||||||
loc.y += (_minY - _temp.y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Used to dispatch {@link CameraPath.Observer#pathCompleted}. */
|
|
||||||
protected static class CompletedOp implements ObserverList.ObserverOp
|
|
||||||
{
|
|
||||||
public CompletedOp (CameraPath path) {
|
|
||||||
_path = path;
|
|
||||||
}
|
|
||||||
public boolean apply (Object observer) {
|
|
||||||
return ((CameraPath.Observer)observer).pathCompleted(_path);
|
|
||||||
}
|
|
||||||
protected CameraPath _path;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Camera _camera;
|
|
||||||
protected CameraPath _campath;
|
|
||||||
protected ObserverList _campathobs =
|
|
||||||
new ObserverList(ObserverList.SAFE_IN_ORDER_NOTIFY);
|
|
||||||
|
|
||||||
protected Matrix3f _rotm = new Matrix3f();
|
|
||||||
protected Vector3f _temp = new Vector3f();
|
|
||||||
|
|
||||||
protected boolean _boundViaFrustum, _limitsEnabled = true;
|
|
||||||
protected float _minX = -Float.MAX_VALUE, _maxX = Float.MAX_VALUE;
|
|
||||||
protected float _minY = -Float.MAX_VALUE, _maxY = Float.MAX_VALUE;
|
|
||||||
protected float _minZ = -Float.MAX_VALUE, _maxZ = Float.MAX_VALUE;
|
|
||||||
protected float _minZoom = 0f, _maxZoom = Float.MAX_VALUE;
|
|
||||||
protected float _minTilt = -Float.MAX_VALUE, _maxTilt = Float.MAX_VALUE;
|
|
||||||
|
|
||||||
protected Vector3f _rxdir = new Vector3f(1, 0, 0);
|
|
||||||
protected Vector3f _rydir = new Vector3f(0, 1, 0);
|
|
||||||
|
|
||||||
protected static final Vector3f _xdir = new Vector3f(1, 0, 0);
|
|
||||||
protected static final Vector3f _ydir = new Vector3f(0, 1, 0);
|
|
||||||
|
|
||||||
protected static final Plane _ground = new Plane(new Vector3f(0, 0, 1), 0);
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.camera;
|
|
||||||
|
|
||||||
import com.jme.renderer.Camera;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to move the camera along a particular path.
|
|
||||||
*/
|
|
||||||
public abstract class CameraPath
|
|
||||||
{
|
|
||||||
/** Used to inform observers when a camera path is completed or aborted. */
|
|
||||||
public interface Observer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Called when this path is finished (potentially early because another
|
|
||||||
* path was set before this path completed).
|
|
||||||
*
|
|
||||||
* @param path the path that was completed.
|
|
||||||
*
|
|
||||||
* @return true if the observer should remain in the list, false if it
|
|
||||||
* should be removed following this notification.
|
|
||||||
*/
|
|
||||||
public boolean pathCompleted (CameraPath path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is called on every frame to allow the path to adjust the position
|
|
||||||
* of the camera.
|
|
||||||
*
|
|
||||||
* @return true if the path is completed and can be disposed, false if it
|
|
||||||
* is not yet completed.
|
|
||||||
*/
|
|
||||||
public abstract boolean tick (float secondsSince);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called if this path is aborted prior to completion due to being replaced
|
|
||||||
* by a new camera path.
|
|
||||||
*/
|
|
||||||
public void abort ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected CameraPath (CameraHandler camhand)
|
|
||||||
{
|
|
||||||
_camhand = camhand;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected CameraHandler _camhand;
|
|
||||||
}
|
|
||||||
@@ -1,191 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
package com.threerings.jme.camera;
|
|
||||||
|
|
||||||
import com.jme.math.FastMath;
|
|
||||||
import com.jme.math.Matrix3f;
|
|
||||||
import com.jme.math.Vector3f;
|
|
||||||
import com.jme.renderer.Camera;
|
|
||||||
|
|
||||||
import com.jme.input.InputHandler;
|
|
||||||
import com.jme.input.KeyBindingManager;
|
|
||||||
import com.jme.input.KeyInput;
|
|
||||||
import com.jme.input.RelativeMouse;
|
|
||||||
import com.jme.input.action.*;
|
|
||||||
import com.jme.input.action.InputActionEvent;
|
|
||||||
|
|
||||||
import com.threerings.jme.JmeApp;
|
|
||||||
import com.threerings.jme.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets up camera controls for moving around from a top-down perspective,
|
|
||||||
* suitable for strategy games and their ilk. The "ground" is assumed to be the
|
|
||||||
* XY plane.
|
|
||||||
*/
|
|
||||||
public class GodViewHandler extends InputHandler
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Creates the handler.
|
|
||||||
*
|
|
||||||
* @param cam The camera to move with this handler.
|
|
||||||
*/
|
|
||||||
public GodViewHandler (CameraHandler camhand)
|
|
||||||
{
|
|
||||||
_camhand = camhand;
|
|
||||||
setKeyBindings();
|
|
||||||
addActions();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void setKeyBindings ()
|
|
||||||
{
|
|
||||||
KeyBindingManager keyboard = KeyBindingManager.getKeyBindingManager();
|
|
||||||
|
|
||||||
// the key bindings for the pan actions
|
|
||||||
keyboard.set("forward", KeyInput.KEY_W);
|
|
||||||
keyboard.set("arrow_forward", KeyInput.KEY_UP);
|
|
||||||
keyboard.set("backward", KeyInput.KEY_S);
|
|
||||||
keyboard.set("arrow_backward", KeyInput.KEY_DOWN);
|
|
||||||
keyboard.set("left", KeyInput.KEY_A);
|
|
||||||
keyboard.set("arrow_left", KeyInput.KEY_LEFT);
|
|
||||||
keyboard.set("right", KeyInput.KEY_D);
|
|
||||||
keyboard.set("arrow_right", KeyInput.KEY_RIGHT);
|
|
||||||
|
|
||||||
// the key bindings for the zoom actions
|
|
||||||
keyboard.set("zoomIn", KeyInput.KEY_UP);
|
|
||||||
keyboard.set("zoomOut", KeyInput.KEY_DOWN);
|
|
||||||
|
|
||||||
// the key bindings for the orbit actions
|
|
||||||
keyboard.set("turnRight", KeyInput.KEY_RIGHT);
|
|
||||||
keyboard.set("turnLeft", KeyInput.KEY_LEFT);
|
|
||||||
|
|
||||||
// the key bindings for the tilt actions
|
|
||||||
keyboard.set("tiltForward", KeyInput.KEY_HOME);
|
|
||||||
keyboard.set("tiltBack", KeyInput.KEY_END);
|
|
||||||
|
|
||||||
keyboard.set("screenshot", KeyInput.KEY_F12);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void addActions ()
|
|
||||||
{
|
|
||||||
addAction(new KeyScreenShotAction(), "screenshot", false);
|
|
||||||
addPanActions();
|
|
||||||
addZoomActions();
|
|
||||||
addOrbitActions();
|
|
||||||
addTiltActions();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds actions for panning the camera around the scene.
|
|
||||||
*/
|
|
||||||
protected void addPanActions ()
|
|
||||||
{
|
|
||||||
InputAction forward = new InputAction() {
|
|
||||||
public void performAction (InputActionEvent evt) {
|
|
||||||
_camhand.panCamera(0, speed * evt.getTime());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
forward.setSpeed(0.5f);
|
|
||||||
addAction(forward, "forward", true);
|
|
||||||
addAction(forward, "arrow_forward", true);
|
|
||||||
|
|
||||||
InputAction backward = new InputAction() {
|
|
||||||
public void performAction (InputActionEvent evt) {
|
|
||||||
_camhand.panCamera(0, -speed * evt.getTime());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
backward.setSpeed(0.5f);
|
|
||||||
addAction(backward, "backward", true);
|
|
||||||
addAction(backward, "arrow_backward", true);
|
|
||||||
|
|
||||||
InputAction left = new InputAction() {
|
|
||||||
public void performAction (InputActionEvent evt) {
|
|
||||||
_camhand.panCamera(-speed * evt.getTime(), 0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
left.setSpeed(0.5f);
|
|
||||||
addAction(left, "left", true);
|
|
||||||
addAction(left, "arrow_left", true);
|
|
||||||
|
|
||||||
InputAction right = new InputAction() {
|
|
||||||
public void performAction (InputActionEvent evt) {
|
|
||||||
_camhand.panCamera(speed * evt.getTime(), 0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
right.setSpeed(0.5f);
|
|
||||||
addAction(right, "right", true);
|
|
||||||
addAction(right, "arrow_right", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds actions for zooming the camaera in and out.
|
|
||||||
*/
|
|
||||||
protected void addZoomActions ()
|
|
||||||
{
|
|
||||||
InputAction zoomIn = new InputAction() {
|
|
||||||
public void performAction (InputActionEvent evt) {
|
|
||||||
_camhand.zoomCamera(-speed * evt.getTime());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
zoomIn.setSpeed(0.5f);
|
|
||||||
addAction(zoomIn, "zoomIn", true);
|
|
||||||
|
|
||||||
InputAction zoomOut = new InputAction() {
|
|
||||||
public void performAction (InputActionEvent evt) {
|
|
||||||
_camhand.zoomCamera(speed * evt.getTime());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
zoomOut.setSpeed(0.5f);
|
|
||||||
addAction(zoomOut, "zoomOut", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds actions for orbiting the camera around the viewpoint.
|
|
||||||
*/
|
|
||||||
protected void addOrbitActions ()
|
|
||||||
{
|
|
||||||
addAction(new OrbitAction(-FastMath.PI / 2), "turnRight", true);
|
|
||||||
addAction(new OrbitAction(FastMath.PI / 2), "turnLeft", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds actions for tilting the camera (rotating around the yaw axis).
|
|
||||||
*/
|
|
||||||
protected void addTiltActions ()
|
|
||||||
{
|
|
||||||
addAction(new TiltAction(-FastMath.PI / 2), "tiltForward", true);
|
|
||||||
addAction(new TiltAction(FastMath.PI / 2), "tiltBack", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected class OrbitAction extends InputAction
|
|
||||||
{
|
|
||||||
public OrbitAction (float radPerSec)
|
|
||||||
{
|
|
||||||
_radPerSec = radPerSec;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void performAction (InputActionEvent evt)
|
|
||||||
{
|
|
||||||
_camhand.orbitCamera(_radPerSec * evt.getTime());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected float _radPerSec;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected class TiltAction extends InputAction
|
|
||||||
{
|
|
||||||
public TiltAction (float radPerSec)
|
|
||||||
{
|
|
||||||
_radPerSec = radPerSec;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void performAction (InputActionEvent evt)
|
|
||||||
{
|
|
||||||
_camhand.tiltCamera(_radPerSec * evt.getTime());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected float _radPerSec;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected CameraHandler _camhand;
|
|
||||||
}
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.camera;
|
|
||||||
|
|
||||||
import com.jme.math.Quaternion;
|
|
||||||
import com.jme.math.Vector3f;
|
|
||||||
import com.jme.renderer.Camera;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pans the camera to the specified location in the specified amount of time.
|
|
||||||
*/
|
|
||||||
public class PanPath extends CameraPath
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Creates a panning path for the specified camera that will retain the
|
|
||||||
* camera's current orientation.
|
|
||||||
*
|
|
||||||
* @param target the target position for the camera.
|
|
||||||
* @param duration the number of seconds in which to pan the camera.
|
|
||||||
*/
|
|
||||||
public PanPath (CameraHandler camhand, Vector3f target, float duration)
|
|
||||||
{
|
|
||||||
this(camhand, target, null, duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a panning path for the specified camera.
|
|
||||||
*
|
|
||||||
* @param target the target position for the camera.
|
|
||||||
* @param trot the target rotation for the camera (or <code>null</code> to
|
|
||||||
* keep its current orientation)
|
|
||||||
* @param duration the number of seconds in which to pan the camera.
|
|
||||||
*/
|
|
||||||
public PanPath (CameraHandler camhand, Vector3f target, Quaternion trot,
|
|
||||||
float duration)
|
|
||||||
{
|
|
||||||
super(camhand);
|
|
||||||
|
|
||||||
Camera cam = camhand.getCamera();
|
|
||||||
_start = new Vector3f(cam.getLocation());
|
|
||||||
_velocity = target.subtract(_start);
|
|
||||||
_velocity.divideLocal(duration);
|
|
||||||
if (trot != null) {
|
|
||||||
_irot = new Quaternion();
|
|
||||||
_irot.fromAxes(cam.getLeft(), cam.getUp(), cam.getDirection());
|
|
||||||
_trot = trot;
|
|
||||||
}
|
|
||||||
|
|
||||||
_duration = duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public boolean tick (float secondsSince)
|
|
||||||
{
|
|
||||||
_elapsed = Math.min(_elapsed + secondsSince, _duration);
|
|
||||||
_camloc.scaleAdd(_elapsed, _velocity, _start);
|
|
||||||
_camhand.setLocation(_camloc);
|
|
||||||
if (_irot != null) {
|
|
||||||
_camhand.getCamera().setAxes(_axes.slerp(_irot, _trot,
|
|
||||||
_elapsed / _duration));
|
|
||||||
}
|
|
||||||
return (_elapsed >= _duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Vector3f _start, _velocity, _camloc = new Vector3f();
|
|
||||||
protected Quaternion _irot, _trot, _axes = new Quaternion();
|
|
||||||
protected float _elapsed, _duration;
|
|
||||||
}
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.camera;
|
|
||||||
|
|
||||||
import com.jme.math.Vector3f;
|
|
||||||
import com.jme.renderer.Camera;
|
|
||||||
|
|
||||||
import com.threerings.jme.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Moves the camera along a cubic Hermite spline path defined by the start and
|
|
||||||
* end locations and directions. Spline formulas obtained from
|
|
||||||
* <a href="http://en.wikipedia.org/wiki/Cubic_Hermite_spline">Wikipedia</a>.
|
|
||||||
*/
|
|
||||||
public class SplinePath extends CameraPath
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Creates a cubic spline path for the camera to follow.
|
|
||||||
*
|
|
||||||
* @param tloc the target location
|
|
||||||
* @param tdir the target direction
|
|
||||||
* @param axis the heading axis (typically {@link Vector3f#UNIT_Z})
|
|
||||||
* @param duration the duration of the path
|
|
||||||
* @param tension the tension parameter, which can range from zero to one:
|
|
||||||
* higher tension values create a more direct path with a sharper turn,
|
|
||||||
* lower values create a rounder path with a smoother turn
|
|
||||||
*/
|
|
||||||
public SplinePath (CameraHandler camhand, Vector3f tloc,
|
|
||||||
Vector3f tdir, Vector3f axis, float duration, float tension)
|
|
||||||
{
|
|
||||||
super(camhand);
|
|
||||||
|
|
||||||
// get the spline function coefficients
|
|
||||||
Camera cam = camhand.getCamera();
|
|
||||||
_p0 = new Vector3f(cam.getLocation());
|
|
||||||
_p1 = new Vector3f(tloc);
|
|
||||||
float tscale = (1f - tension) * _p0.distance(_p1);
|
|
||||||
_m0 = cam.getDirection().mult(tscale);
|
|
||||||
_m1 = tdir.mult(tscale);
|
|
||||||
|
|
||||||
_axis = axis;
|
|
||||||
_duration = duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public boolean tick (float secondsSince)
|
|
||||||
{
|
|
||||||
_elapsed = Math.min(_elapsed + secondsSince, _duration);
|
|
||||||
float t = _elapsed / _duration, t2 = t*t, t3 = t2*t,
|
|
||||||
h00 = 2*t3 - 3*t2 + 1,
|
|
||||||
h00p = 6*t2 - 6*t,
|
|
||||||
h10 = t3 - 2*t2 + t,
|
|
||||||
h10p = 3*t2 - 4*t + 1,
|
|
||||||
h01 = -2*t3 + 3*t2,
|
|
||||||
h01p = -6*t2 + 6*t,
|
|
||||||
h11 = t3 - t2,
|
|
||||||
h11p = 3*t2 - 2*t;
|
|
||||||
|
|
||||||
// take the derivative to find the direction
|
|
||||||
_p0.mult(h00p, _dir);
|
|
||||||
_dir.scaleAdd(h10p, _m0, _dir);
|
|
||||||
_dir.scaleAdd(h01p, _p1, _dir);
|
|
||||||
_dir.scaleAdd(h11p, _m1, _dir);
|
|
||||||
_dir.normalizeLocal();
|
|
||||||
|
|
||||||
// evaluate the spline function to find the location
|
|
||||||
_p0.mult(h00, _loc);
|
|
||||||
_loc.scaleAdd(h10, _m0, _loc);
|
|
||||||
_loc.scaleAdd(h01, _p1, _loc);
|
|
||||||
_loc.scaleAdd(h11, _m1, _loc);
|
|
||||||
|
|
||||||
// compute the left and up vectors using the direction and
|
|
||||||
// axis vectors
|
|
||||||
Camera cam = _camhand.getCamera();
|
|
||||||
_axis.cross(_dir, _left);
|
|
||||||
_left.normalizeLocal();
|
|
||||||
_dir.cross(_left, _up);
|
|
||||||
|
|
||||||
// update the camera's location and orientation
|
|
||||||
cam.setFrame(_loc, _left, _up, _dir);
|
|
||||||
|
|
||||||
return _elapsed >= _duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The parameters of the spline. */
|
|
||||||
Vector3f _p0, _m0, _p1, _m1, _axis;
|
|
||||||
|
|
||||||
/** Working vectors. */
|
|
||||||
Vector3f _loc = new Vector3f(), _left = new Vector3f(),
|
|
||||||
_up = new Vector3f(), _dir = new Vector3f();
|
|
||||||
|
|
||||||
/** The elapsed time and total duration. */
|
|
||||||
float _elapsed, _duration;
|
|
||||||
}
|
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.camera;
|
|
||||||
|
|
||||||
import com.jme.math.FastMath;
|
|
||||||
import com.jme.math.Quaternion;
|
|
||||||
import com.jme.math.Vector3f;
|
|
||||||
import com.jme.renderer.Camera;
|
|
||||||
|
|
||||||
import com.threerings.jme.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Swings the camera around a point of interest (which should be somewhere
|
|
||||||
* along the camera's view vector). Also optionally pans the camera and/or
|
|
||||||
* zooms it in or out (moves it along its view vector) in the process.
|
|
||||||
*
|
|
||||||
* <p align="center"><img src="rotate_zoom.png">
|
|
||||||
*/
|
|
||||||
public class SwingPath extends CameraPath
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Creates a rotating, zooming path for the specified camera.
|
|
||||||
*
|
|
||||||
* @param spot the point of interest around which to swing the camera.
|
|
||||||
* @param axis the axis around which to rotate the camera.
|
|
||||||
* @param angle the angle through which to rotate the camera.
|
|
||||||
* @param angvel the (absolute value of the) velocity at which to rotate
|
|
||||||
* the camera (in radians per second).
|
|
||||||
* @param zoom the distance to zoom along the camera's view axis (negative
|
|
||||||
* = in, positive = out).
|
|
||||||
*/
|
|
||||||
public SwingPath (CameraHandler camhand, Vector3f spot, Vector3f axis,
|
|
||||||
float angle, float angvel, float zoom)
|
|
||||||
{
|
|
||||||
this(camhand, spot, axis, angle, angvel, null, 0f, null, zoom);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a rotating, panning, zooming path for the specified camera.
|
|
||||||
*
|
|
||||||
* @param spot the point of interest around which to swing the camera.
|
|
||||||
* @param paxis the primary axis around which to rotate the camera.
|
|
||||||
* @param pangle the angle through which to rotate the camera about the
|
|
||||||
* primary axis.
|
|
||||||
* @param angvel the (absolute value of the) velocity at which to rotate
|
|
||||||
* the camera (in radians per second) about the primary angle.
|
|
||||||
* @param saxis the secondary axis around which to rotate the camera, or
|
|
||||||
* <code>null</code> for none
|
|
||||||
* @param sangle the angle through which to rotate the camera about the
|
|
||||||
* secondary axis
|
|
||||||
* @param pan the amount to pan the camera, or <code>null</code> for none
|
|
||||||
* @param zoom the distance to zoom along the camera's view axis (negative
|
|
||||||
* = in, positive = out).
|
|
||||||
*/
|
|
||||||
public SwingPath (CameraHandler camhand, Vector3f spot, Vector3f paxis,
|
|
||||||
float pangle, float angvel, Vector3f saxis, float sangle,
|
|
||||||
Vector3f pan, float zoom)
|
|
||||||
{
|
|
||||||
super(camhand);
|
|
||||||
|
|
||||||
if (pangle == 0) {
|
|
||||||
Log.warning("Requested to swing camera through zero degrees " +
|
|
||||||
"[spot=" + spot + ", paxis=" + paxis +
|
|
||||||
", angvel=" + angvel + ", zoom=" + zoom + "].");
|
|
||||||
pangle = 0.0001f;
|
|
||||||
}
|
|
||||||
if (angvel <= 0) {
|
|
||||||
Log.warning("Requested to swing camera with invalid velocity " +
|
|
||||||
"[spot=" + spot + ", paxis=" + paxis + ", pangle=" +
|
|
||||||
pangle + ", angvel=" + angvel + ", zoom=" + zoom +
|
|
||||||
"].");
|
|
||||||
angvel = FastMath.PI;
|
|
||||||
}
|
|
||||||
|
|
||||||
_spot = new Vector3f(spot);
|
|
||||||
_paxis = paxis;
|
|
||||||
_pangle = pangle;
|
|
||||||
_pangvel = (pangle > 0) ? angvel : -1 * angvel;
|
|
||||||
_saxis = (saxis == null) ? null : new Vector3f(saxis);
|
|
||||||
_sangle = sangle;
|
|
||||||
_sangvel = _sangle * _pangvel / _pangle;
|
|
||||||
_pan = pan;
|
|
||||||
if (_pan != null) {
|
|
||||||
_panvel = _pan.mult(_pangvel / _pangle);
|
|
||||||
_panned = new Vector3f();
|
|
||||||
}
|
|
||||||
_zoom = zoom;
|
|
||||||
_zoomvel = _zoom * _pangvel / _pangle;
|
|
||||||
|
|
||||||
// Log.info("Swinging camera [angle=" + _angle + ", angvel=" + _angvel +
|
|
||||||
// ", zoom=" + _zoom + ", zoomvel=" + _zoomvel + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public boolean tick (float secondsSince)
|
|
||||||
{
|
|
||||||
float deltaPAngle = (secondsSince * _pangvel);
|
|
||||||
float deltaSAngle = (secondsSince * _sangvel);
|
|
||||||
float deltaZoom = (secondsSince * _zoomvel);
|
|
||||||
_protated += deltaPAngle;
|
|
||||||
_srotated += deltaSAngle;
|
|
||||||
_zoomed += deltaZoom;
|
|
||||||
if (_pan != null) {
|
|
||||||
_panvel.mult(secondsSince, _deltaPan);
|
|
||||||
_panned.addLocal(_deltaPan);
|
|
||||||
}
|
|
||||||
|
|
||||||
// clamp our rotation at the target angle and determine whether or not
|
|
||||||
// we're done
|
|
||||||
boolean done = false;
|
|
||||||
if (_pangle > 0 && _protated > _pangle ||
|
|
||||||
_pangle < 0 && _protated < _pangle) {
|
|
||||||
deltaPAngle -= (_protated - _pangle);
|
|
||||||
deltaSAngle -= (_srotated - _sangle);
|
|
||||||
deltaZoom -= (_zoomed - _zoom);
|
|
||||||
if (_pan != null) {
|
|
||||||
_deltaPan.subtractLocal(_panned).addLocal(_pan);
|
|
||||||
}
|
|
||||||
_protated = _pangle;
|
|
||||||
_srotated = _sangle;
|
|
||||||
_panned = _pan;
|
|
||||||
_zoomed = _zoom;
|
|
||||||
done = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// have the camera handler do the necessary rotating, panning, zooming
|
|
||||||
if (_pan != null) {
|
|
||||||
_camhand.setLocation(
|
|
||||||
_camhand.getCamera().getLocation().addLocal(_deltaPan));
|
|
||||||
_spot.addLocal(_deltaPan);
|
|
||||||
}
|
|
||||||
_camhand.rotateCamera(_spot, _paxis, deltaPAngle, deltaZoom);
|
|
||||||
if (_saxis != null) {
|
|
||||||
_rot.fromAngleAxis(deltaPAngle, _paxis).multLocal(_saxis);
|
|
||||||
_camhand.rotateCamera(_spot, _saxis, deltaSAngle, 0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
return done;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Vector3f _spot, _paxis, _saxis;
|
|
||||||
protected float _pangle, _pangvel, _protated;
|
|
||||||
protected float _sangle, _sangvel, _srotated;
|
|
||||||
protected Vector3f _pan, _panvel, _panned;
|
|
||||||
protected float _zoom, _zoomvel, _zoomed;
|
|
||||||
|
|
||||||
protected Quaternion _rot = new Quaternion();
|
|
||||||
protected Vector3f _deltaPan = new Vector3f();
|
|
||||||
}
|
|
||||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB |
@@ -1,181 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.chat;
|
|
||||||
|
|
||||||
import java.util.StringTokenizer;
|
|
||||||
|
|
||||||
import com.jmex.bui.BContainer;
|
|
||||||
import com.jmex.bui.BTextArea;
|
|
||||||
import com.jmex.bui.BTextField;
|
|
||||||
import com.jmex.bui.event.ActionEvent;
|
|
||||||
import com.jmex.bui.event.ActionListener;
|
|
||||||
import com.jmex.bui.layout.BorderLayout;
|
|
||||||
|
|
||||||
import com.jme.renderer.ColorRGBA;
|
|
||||||
|
|
||||||
import com.threerings.util.Name;
|
|
||||||
|
|
||||||
import com.threerings.crowd.chat.client.ChatDirector;
|
|
||||||
import com.threerings.crowd.chat.client.ChatDisplay;
|
|
||||||
import com.threerings.crowd.chat.client.SpeakService;
|
|
||||||
import com.threerings.crowd.chat.data.ChatCodes;
|
|
||||||
import com.threerings.crowd.chat.data.ChatMessage;
|
|
||||||
import com.threerings.crowd.chat.data.SystemMessage;
|
|
||||||
import com.threerings.crowd.chat.data.UserMessage;
|
|
||||||
import com.threerings.crowd.data.PlaceObject;
|
|
||||||
|
|
||||||
import com.threerings.jme.JmeContext;
|
|
||||||
import com.threerings.jme.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Displays chat messages and allows for their input.
|
|
||||||
*/
|
|
||||||
public class ChatView extends BContainer
|
|
||||||
implements ChatDisplay
|
|
||||||
{
|
|
||||||
public ChatView (JmeContext ctx, ChatDirector chatdtr)
|
|
||||||
{
|
|
||||||
_chatdtr = chatdtr;
|
|
||||||
setLayoutManager(new BorderLayout(2, 2));
|
|
||||||
add(_text = new BTextArea(), BorderLayout.CENTER);
|
|
||||||
add(_input = new BTextField(), BorderLayout.SOUTH);
|
|
||||||
|
|
||||||
_input.addListener(new ActionListener() {
|
|
||||||
public void actionPerformed (ActionEvent event) {
|
|
||||||
if (handleInput(_input.getText())) {
|
|
||||||
_input.setText("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void willEnterPlace (PlaceObject plobj)
|
|
||||||
{
|
|
||||||
setSpeakService(plobj.speakService);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void didLeavePlace (PlaceObject plobj)
|
|
||||||
{
|
|
||||||
clearSpeakService();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSpeakService (SpeakService spsvc)
|
|
||||||
{
|
|
||||||
if (spsvc != null) {
|
|
||||||
_spsvc = spsvc;
|
|
||||||
_chatdtr.addChatDisplay(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clearSpeakService ()
|
|
||||||
{
|
|
||||||
if (_spsvc != null) {
|
|
||||||
_chatdtr.removeChatDisplay(this);
|
|
||||||
_spsvc = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instructs our chat input field to request focus.
|
|
||||||
*/
|
|
||||||
public void requestFocus ()
|
|
||||||
{
|
|
||||||
_input.requestFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface ChatDisplay
|
|
||||||
public void clear ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface ChatDisplay
|
|
||||||
public void displayMessage (ChatMessage msg)
|
|
||||||
{
|
|
||||||
// we may be restricted in the chat types we handle
|
|
||||||
if (!handlesType(msg.localtype)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg instanceof UserMessage) {
|
|
||||||
UserMessage umsg = (UserMessage) msg;
|
|
||||||
if (umsg.localtype == ChatCodes.USER_CHAT_TYPE) {
|
|
||||||
append("[" + umsg.speaker + " whispers] ", ColorRGBA.green);
|
|
||||||
append(umsg.message + "\n");
|
|
||||||
} else {
|
|
||||||
append("<" + umsg.speaker + "> ", ColorRGBA.blue);
|
|
||||||
append(umsg.message + "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (msg instanceof SystemMessage) {
|
|
||||||
append(msg.message + "\n", ColorRGBA.red);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Log.warning("Received unknown message type: " + msg + ".");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public void setEnabled (boolean enabled)
|
|
||||||
{
|
|
||||||
_input.setEnabled(enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void displayError (String message)
|
|
||||||
{
|
|
||||||
append(message + "\n", ColorRGBA.red);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void append (String text, ColorRGBA color)
|
|
||||||
{
|
|
||||||
_text.appendText(text, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void append (String text)
|
|
||||||
{
|
|
||||||
_text.appendText(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean handlesType (String localType)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean handleInput (String text)
|
|
||||||
{
|
|
||||||
if (text.length() == 0) {
|
|
||||||
// no empty banter
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
String msg = _chatdtr.requestChat(_spsvc, text, true);
|
|
||||||
if (msg.equals(ChatCodes.SUCCESS)) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
_chatdtr.displayFeedback(null, msg);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ChatDirector _chatdtr;
|
|
||||||
protected SpeakService _spsvc;
|
|
||||||
protected BTextArea _text;
|
|
||||||
protected BTextField _input;
|
|
||||||
}
|
|
||||||
@@ -1,148 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.effect;
|
|
||||||
|
|
||||||
import com.jme.math.Vector3f;
|
|
||||||
import com.jme.renderer.ColorRGBA;
|
|
||||||
import com.jme.renderer.Renderer;
|
|
||||||
import com.jme.scene.Geometry;
|
|
||||||
import com.jme.scene.Node;
|
|
||||||
import com.jme.scene.shape.Quad;
|
|
||||||
import com.jme.scene.state.AlphaState;
|
|
||||||
import com.jme.system.DisplaySystem;
|
|
||||||
|
|
||||||
import com.threerings.jme.util.LinearTimeFunction;
|
|
||||||
import com.threerings.jme.util.TimeFunction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fades a supplied quad (or one that covers the screen) in from a solid color
|
|
||||||
* or out to a solid color.
|
|
||||||
*/
|
|
||||||
public class FadeInOutEffect extends Node
|
|
||||||
{
|
|
||||||
public FadeInOutEffect (ColorRGBA color, float startAlpha, float endAlpha,
|
|
||||||
float duration, boolean overUI)
|
|
||||||
{
|
|
||||||
this(color, new LinearTimeFunction(startAlpha, endAlpha, duration),
|
|
||||||
overUI);
|
|
||||||
}
|
|
||||||
|
|
||||||
public FadeInOutEffect (ColorRGBA color, TimeFunction alphaFunc,
|
|
||||||
boolean overUI)
|
|
||||||
{
|
|
||||||
this(null, color, alphaFunc, overUI);
|
|
||||||
setQuad(createCurtain());
|
|
||||||
}
|
|
||||||
|
|
||||||
public FadeInOutEffect (Quad quad, ColorRGBA color, TimeFunction alphaFunc,
|
|
||||||
boolean overUI)
|
|
||||||
{
|
|
||||||
super("FadeInOut");
|
|
||||||
|
|
||||||
_color = new ColorRGBA(
|
|
||||||
color.r, color.g, color.b, alphaFunc.getValue(0));
|
|
||||||
_alphaFunc = alphaFunc;
|
|
||||||
|
|
||||||
DisplaySystem ds = DisplaySystem.getDisplaySystem();
|
|
||||||
_astate = ds.getRenderer().createAlphaState();
|
|
||||||
_astate.setBlendEnabled(true);
|
|
||||||
_astate.setSrcFunction(AlphaState.SB_SRC_ALPHA);
|
|
||||||
_astate.setDstFunction(AlphaState.DB_ONE_MINUS_SRC_ALPHA);
|
|
||||||
_astate.setEnabled(true);
|
|
||||||
|
|
||||||
if (quad != null) {
|
|
||||||
setQuad(quad);
|
|
||||||
}
|
|
||||||
|
|
||||||
setRenderQueueMode(Renderer.QUEUE_ORTHO);
|
|
||||||
setZOrder(overUI ? -1 : 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures the quad that will be faded in or out.
|
|
||||||
*/
|
|
||||||
public void setQuad (Quad quad)
|
|
||||||
{
|
|
||||||
attachChild(quad);
|
|
||||||
quad.setDefaultColor(_color);
|
|
||||||
quad.setRenderState(_astate);
|
|
||||||
quad.updateRenderState();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows the fade to be paused.
|
|
||||||
*/
|
|
||||||
public void setPaused (boolean paused)
|
|
||||||
{
|
|
||||||
_paused = paused;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates whether or not the fade is paused.
|
|
||||||
*/
|
|
||||||
public boolean isPaused ()
|
|
||||||
{
|
|
||||||
return _paused;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public void updateGeometricState (float time, boolean initiator)
|
|
||||||
{
|
|
||||||
super.updateGeometricState(time, initiator);
|
|
||||||
if (_paused) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
float alpha = _alphaFunc.getValue(time);
|
|
||||||
_color.a = Math.min(1f, Math.max(0f, alpha));
|
|
||||||
if (_alphaFunc.isComplete()) {
|
|
||||||
fadeComplete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called (only once) when we have reached the end of our fade.
|
|
||||||
* Automatically detaches this effect from the hierarchy.
|
|
||||||
*/
|
|
||||||
protected void fadeComplete ()
|
|
||||||
{
|
|
||||||
getParent().detachChild(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a quad that covers the entire screen for full-screen fades.
|
|
||||||
*/
|
|
||||||
protected Quad createCurtain ()
|
|
||||||
{
|
|
||||||
// create a quad the size of the screen
|
|
||||||
DisplaySystem ds = DisplaySystem.getDisplaySystem();
|
|
||||||
float width = ds.getWidth(), height = ds.getHeight();
|
|
||||||
Quad curtain = new Quad("curtain" + hashCode(), width, height);
|
|
||||||
curtain.setLocalTranslation(new Vector3f(width/2, height/2, 0f));
|
|
||||||
return curtain;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ColorRGBA _color;
|
|
||||||
protected AlphaState _astate;
|
|
||||||
protected TimeFunction _alphaFunc;
|
|
||||||
protected boolean _paused;
|
|
||||||
}
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
|
|
||||||
package com.threerings.jme.effect;
|
|
||||||
|
|
||||||
import com.jme.scene.Node;
|
|
||||||
import com.jme.system.DisplaySystem;
|
|
||||||
|
|
||||||
import com.jmex.bui.BWindow;
|
|
||||||
|
|
||||||
import com.threerings.jme.util.LinearTimeFunction;
|
|
||||||
import com.threerings.jme.util.TimeFunction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Slides a window onto the center of the screen from offscreen or offscreen
|
|
||||||
* from the center of the screen.
|
|
||||||
*/
|
|
||||||
public class WindowSlider extends Node
|
|
||||||
{
|
|
||||||
public static final int FROM_TOP = 0;
|
|
||||||
public static final int FROM_RIGHT = 1;
|
|
||||||
public static final int TO_TOP = 2;
|
|
||||||
public static final int TO_RIGHT = 3;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a window slider with the specified mode and window that will
|
|
||||||
* slide the window either onto or off of the screen in the specified
|
|
||||||
* number of seconds.
|
|
||||||
*
|
|
||||||
* @param dx an offset applied to the starting or destination position
|
|
||||||
* along the x axis (starting for sliding off, destination for sliding on).
|
|
||||||
* @param dy an offset applied along the y axis.
|
|
||||||
*/
|
|
||||||
public WindowSlider (BWindow window, int mode, float duration,
|
|
||||||
int dx, int dy)
|
|
||||||
{
|
|
||||||
super("slider");
|
|
||||||
|
|
||||||
DisplaySystem ds = DisplaySystem.getDisplaySystem();
|
|
||||||
int swidth = ds.getWidth(), sheight = ds.getHeight();
|
|
||||||
int wwidth = window.getWidth(), wheight = window.getHeight();
|
|
||||||
|
|
||||||
int start = 0, end = 0;
|
|
||||||
switch (mode) {
|
|
||||||
case FROM_TOP:
|
|
||||||
start = sheight+wheight;
|
|
||||||
end = (sheight-wheight)/2 + dy;
|
|
||||||
window.setLocation((swidth-wwidth)/2 + dx, start);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FROM_RIGHT:
|
|
||||||
start = swidth+wwidth;
|
|
||||||
end = (swidth-wwidth)/2 + dx;
|
|
||||||
window.setLocation(start, (sheight-wheight)/2 + dy);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TO_TOP:
|
|
||||||
start = (sheight-wheight)/2 + dy;
|
|
||||||
end = sheight+wheight;
|
|
||||||
window.setLocation((swidth-wwidth)/2 + dx, start);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TO_RIGHT:
|
|
||||||
start = (swidth-wwidth)/2 + dx;
|
|
||||||
end = swidth+wwidth;
|
|
||||||
window.setLocation(start, (sheight-wheight)/2 + dy);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
_mode = mode;
|
|
||||||
_window = window;
|
|
||||||
_tfunc = new LinearTimeFunction(start, end, duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows some number of ticks to be skipped to give the window that is
|
|
||||||
* being slid a chance to be layed out before we start keeping track of
|
|
||||||
* time. The layout may be expensive and cause the frame rate to drop for a
|
|
||||||
* frame or two, thus booching our smooth sliding onto the screen.
|
|
||||||
*/
|
|
||||||
public void setSkipTicks (int skipTicks)
|
|
||||||
{
|
|
||||||
_skipTicks = skipTicks;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public void updateGeometricState (float time, boolean initiator)
|
|
||||||
{
|
|
||||||
super.updateGeometricState(time, initiator);
|
|
||||||
|
|
||||||
// skip ticks as long as we need to
|
|
||||||
if (_skipTicks-- > 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int winx, winy;
|
|
||||||
if (_mode % 2 == 1) {
|
|
||||||
winx = (int)_tfunc.getValue(time);
|
|
||||||
winy = _window.getY();
|
|
||||||
} else {
|
|
||||||
winx = _window.getX();
|
|
||||||
winy = (int)_tfunc.getValue(time);
|
|
||||||
}
|
|
||||||
_window.setLocation(winx, winy);
|
|
||||||
|
|
||||||
if (_tfunc.isComplete()) {
|
|
||||||
slideComplete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called (only once) when we have reached the end of our slide.
|
|
||||||
* Automatically detaches this effect from the hierarchy.
|
|
||||||
*/
|
|
||||||
protected void slideComplete ()
|
|
||||||
{
|
|
||||||
getParent().detachChild(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int _mode;
|
|
||||||
protected BWindow _window;
|
|
||||||
protected TimeFunction _tfunc;
|
|
||||||
|
|
||||||
// skip two frames by default as that generally handles the normal window
|
|
||||||
// layout process
|
|
||||||
protected int _skipTicks = 2;
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.model;
|
|
||||||
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
import com.jme.math.Vector3f;
|
|
||||||
import com.jme.scene.Geometry;
|
|
||||||
import com.jme.scene.Spatial;
|
|
||||||
import com.jme.util.geom.BufferUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A model controller whose target represents an emitter.
|
|
||||||
*/
|
|
||||||
public abstract class EmissionController extends ModelController
|
|
||||||
{
|
|
||||||
@Override // documentation inherited
|
|
||||||
public void configure (Properties props, Spatial target)
|
|
||||||
{
|
|
||||||
// substitute underlying mesh for geometry targets
|
|
||||||
if (target instanceof ModelNode) {
|
|
||||||
Spatial mesh = ((ModelNode)target).getChild("mesh");
|
|
||||||
if (mesh != null) {
|
|
||||||
target = mesh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.configure(props, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
public void init (Model model)
|
|
||||||
{
|
|
||||||
super.init(model);
|
|
||||||
_target.setCullMode(Spatial.CULL_ALWAYS);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines the current location of the emitter in world coordinates.
|
|
||||||
*/
|
|
||||||
protected void getEmitterLocation (Vector3f result)
|
|
||||||
{
|
|
||||||
result.set(_target.getWorldTranslation());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines the current direction of the emitter in world coordinates.
|
|
||||||
*/
|
|
||||||
protected void getEmitterDirection (Vector3f result)
|
|
||||||
{
|
|
||||||
if (_target instanceof Geometry) {
|
|
||||||
BufferUtils.populateFromBuffer(result,
|
|
||||||
((Geometry)_target).getNormalBuffer(0), 0);
|
|
||||||
} else {
|
|
||||||
result.set(0f, 0f, -1f);
|
|
||||||
}
|
|
||||||
_target.getWorldRotation().multLocal(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1;
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,167 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.model;
|
|
||||||
|
|
||||||
import java.io.Externalizable;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectInput;
|
|
||||||
import java.io.ObjectOutput;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
import com.jme.scene.Controller;
|
|
||||||
import com.jme.scene.Node;
|
|
||||||
import com.jme.scene.Spatial;
|
|
||||||
|
|
||||||
import com.samskivert.util.StringUtil;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The superclass of procedural animation controllers for models.
|
|
||||||
*/
|
|
||||||
public abstract class ModelController extends Controller
|
|
||||||
implements Externalizable
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Configures this controller based on the supplied (sub-)properties and
|
|
||||||
* controller target.
|
|
||||||
*/
|
|
||||||
public void configure (Properties props, Spatial target)
|
|
||||||
{
|
|
||||||
_target = target;
|
|
||||||
String[] anims = StringUtil.parseStringArray(
|
|
||||||
props.getProperty("animations", ""));
|
|
||||||
if (anims.length == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_animations = new HashSet<String>();
|
|
||||||
Collections.addAll(_animations, anims);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a reference to the controller's target.
|
|
||||||
*/
|
|
||||||
public Spatial getTarget ()
|
|
||||||
{
|
|
||||||
return _target;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolves any textures required by the controller.
|
|
||||||
*/
|
|
||||||
public void resolveTextures (TextureProvider tprov)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes this controller.
|
|
||||||
*/
|
|
||||||
public void init (Model model)
|
|
||||||
{
|
|
||||||
model.addAnimationObserver(_animobs);
|
|
||||||
if (_animations != null) {
|
|
||||||
setActive(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates or populates and returns a clone of this object using the given
|
|
||||||
* clone properties.
|
|
||||||
*
|
|
||||||
* @param store an instance of this class to populate, or <code>null</code>
|
|
||||||
* to create a new instance
|
|
||||||
*/
|
|
||||||
public Controller putClone (
|
|
||||||
Controller store, Model.CloneCreator properties)
|
|
||||||
{
|
|
||||||
if (store == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
ModelController mstore = (ModelController)store;
|
|
||||||
mstore._target = ((ModelSpatial)_target).putClone(null, properties);
|
|
||||||
mstore._animations = _animations;
|
|
||||||
return mstore;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface Externalizable
|
|
||||||
public void writeExternal (ObjectOutput out)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
out.writeObject(_target);
|
|
||||||
out.writeObject(_animations);
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface Externalizable
|
|
||||||
public void readExternal (ObjectInput in)
|
|
||||||
throws IOException, ClassNotFoundException
|
|
||||||
{
|
|
||||||
_target = (Spatial)in.readObject();
|
|
||||||
_animations = (HashSet<String>)in.readObject();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when an animation is started on the model.
|
|
||||||
*/
|
|
||||||
protected void animationStarted (String anim)
|
|
||||||
{
|
|
||||||
if (_animations != null && _animations.contains(anim)) {
|
|
||||||
setActive(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when an animation is stopped on the model.
|
|
||||||
*/
|
|
||||||
protected void animationStopped (String anim)
|
|
||||||
{
|
|
||||||
if (_animations != null) {
|
|
||||||
setActive(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The target to control. */
|
|
||||||
protected Spatial _target;
|
|
||||||
|
|
||||||
/** The animations for which this controller should be active, or
|
|
||||||
* <code>null</code> for all of them. */
|
|
||||||
protected HashSet<String> _animations;
|
|
||||||
|
|
||||||
/** Listens to the model's animation state. */
|
|
||||||
protected Model.AnimationObserver _animobs =
|
|
||||||
new Model.AnimationObserver() {
|
|
||||||
public boolean animationStarted (Model model, String anim) {
|
|
||||||
ModelController.this.animationStarted(anim);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
public boolean animationCompleted (Model model, String anim) {
|
|
||||||
animationStopped(anim);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
public boolean animationCancelled (Model model, String anim) {
|
|
||||||
animationStopped(anim);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1;
|
|
||||||
}
|
|
||||||
@@ -1,557 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.model;
|
|
||||||
|
|
||||||
import java.io.Externalizable;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectInput;
|
|
||||||
import java.io.ObjectOutput;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.ByteOrder;
|
|
||||||
import java.nio.FloatBuffer;
|
|
||||||
import java.nio.IntBuffer;
|
|
||||||
import java.nio.MappedByteBuffer;
|
|
||||||
import java.nio.channels.FileChannel;
|
|
||||||
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
import com.jme.bounding.BoundingVolume;
|
|
||||||
import com.jme.math.Quaternion;
|
|
||||||
import com.jme.math.Vector3f;
|
|
||||||
import com.jme.renderer.Renderer;
|
|
||||||
import com.jme.scene.Controller;
|
|
||||||
import com.jme.scene.SharedMesh;
|
|
||||||
import com.jme.scene.Spatial;
|
|
||||||
import com.jme.scene.TriMesh;
|
|
||||||
import com.jme.scene.VBOInfo;
|
|
||||||
import com.jme.scene.batch.TriangleBatch;
|
|
||||||
import com.jme.scene.state.AlphaState;
|
|
||||||
import com.jme.scene.state.CullState;
|
|
||||||
import com.jme.scene.state.RenderState;
|
|
||||||
import com.jme.scene.state.TextureState;
|
|
||||||
import com.jme.scene.state.ZBufferState;
|
|
||||||
import com.jme.system.DisplaySystem;
|
|
||||||
import com.jme.util.geom.BufferUtils;
|
|
||||||
|
|
||||||
import com.samskivert.util.StringUtil;
|
|
||||||
|
|
||||||
import com.threerings.jme.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link TriMesh} with a serialization mechanism tailored to stored models.
|
|
||||||
*/
|
|
||||||
public class ModelMesh extends TriMesh
|
|
||||||
implements Externalizable, ModelSpatial
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* No-arg constructor for deserialization.
|
|
||||||
*/
|
|
||||||
public ModelMesh ()
|
|
||||||
{
|
|
||||||
super("mesh");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a mesh with no vertex data.
|
|
||||||
*/
|
|
||||||
public ModelMesh (String name)
|
|
||||||
{
|
|
||||||
super(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures this mesh based on the given parameters and (sub-)properties.
|
|
||||||
*
|
|
||||||
* @param texture the texture specified in the model export, if any (can be
|
|
||||||
* overridden by textures specified in the properties)
|
|
||||||
* @param solid whether or not the mesh allows back face culling
|
|
||||||
* @param transparent whether or not the mesh is (partially) transparent
|
|
||||||
*/
|
|
||||||
public void configure (
|
|
||||||
boolean solid, String texture, boolean transparent, Properties props)
|
|
||||||
{
|
|
||||||
_textures = (texture == null) ? null : StringUtil.parseStringArray(
|
|
||||||
props.getProperty(texture, texture));
|
|
||||||
_solid = solid;
|
|
||||||
_transparent = transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the buffers as {@link ByteBuffer}s, because we can't create byte
|
|
||||||
* views of non-byte buffers. This method is where the model is
|
|
||||||
* initialized after loading.
|
|
||||||
*/
|
|
||||||
public void reconstruct (
|
|
||||||
ByteBuffer vertices, ByteBuffer normals, ByteBuffer colors,
|
|
||||||
ByteBuffer textures, ByteBuffer indices)
|
|
||||||
{
|
|
||||||
reconstruct(
|
|
||||||
vertices == null ? null : vertices.asFloatBuffer(),
|
|
||||||
normals == null ? null : normals.asFloatBuffer(),
|
|
||||||
colors == null ? null : colors.asFloatBuffer(),
|
|
||||||
textures == null ? null : textures.asFloatBuffer(),
|
|
||||||
indices == null ? null : indices.asIntBuffer());
|
|
||||||
_vertexByteBuffer = vertices;
|
|
||||||
_normalByteBuffer = normals;
|
|
||||||
_colorByteBuffer = colors;
|
|
||||||
_textureByteBuffer = textures;
|
|
||||||
_indexByteBuffer = indices;
|
|
||||||
|
|
||||||
// initialize the model if we're displaying
|
|
||||||
if (DisplaySystem.getDisplaySystem() == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (_backCull == null) {
|
|
||||||
initSharedStates();
|
|
||||||
}
|
|
||||||
if (_solid) {
|
|
||||||
setRenderState(_backCull);
|
|
||||||
}
|
|
||||||
if (_transparent) {
|
|
||||||
setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);
|
|
||||||
setRenderState(_blendAlpha);
|
|
||||||
setRenderState(_overlayZBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adjusts the vertices and the transform of the mesh so that the mesh's
|
|
||||||
* position lies at the center of its bounding volume.
|
|
||||||
*/
|
|
||||||
public void centerVertices ()
|
|
||||||
{
|
|
||||||
Vector3f offset = getBatch(0).getModelBound().getCenter().negate();
|
|
||||||
if (!offset.equals(Vector3f.ZERO)) {
|
|
||||||
getLocalTranslation().subtractLocal(offset);
|
|
||||||
getBatch(0).getModelBound().getCenter().set(Vector3f.ZERO);
|
|
||||||
getBatch(0).translatePoints(offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
public void reconstruct (
|
|
||||||
FloatBuffer vertices, FloatBuffer normals, FloatBuffer colors,
|
|
||||||
FloatBuffer textures, IntBuffer indices)
|
|
||||||
{
|
|
||||||
super.reconstruct(vertices, normals, colors, textures, indices);
|
|
||||||
|
|
||||||
_vertexBufferSize = (vertices == null) ? 0 : vertices.capacity();
|
|
||||||
_normalBufferSize = (normals == null) ? 0 : normals.capacity();
|
|
||||||
_colorBufferSize = (colors == null) ? 0 : colors.capacity();
|
|
||||||
_textureBufferSize = (textures == null) ? 0 : textures.capacity();
|
|
||||||
_indexBufferSize = (indices == null) ? 0 : indices.capacity();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
public void reconstruct (
|
|
||||||
FloatBuffer vertices, FloatBuffer normals, FloatBuffer colors,
|
|
||||||
FloatBuffer textures)
|
|
||||||
{
|
|
||||||
super.reconstruct(vertices, normals, colors, textures);
|
|
||||||
|
|
||||||
_vertexBufferSize = (vertices == null) ? 0 : vertices.capacity();
|
|
||||||
_normalBufferSize = (normals == null) ? 0 : normals.capacity();
|
|
||||||
_colorBufferSize = (colors == null) ? 0 : colors.capacity();
|
|
||||||
_textureBufferSize = (textures == null) ? 0 : textures.capacity();
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface ModelSpatial
|
|
||||||
public Spatial putClone (Spatial store, Model.CloneCreator properties)
|
|
||||||
{
|
|
||||||
ModelMesh mstore = (ModelMesh)properties.originalToCopy.get(this);
|
|
||||||
if (mstore != null) {
|
|
||||||
return mstore;
|
|
||||||
} else if (store == null) {
|
|
||||||
mstore = new ModelMesh(getName());
|
|
||||||
} else {
|
|
||||||
mstore = (ModelMesh)store;
|
|
||||||
}
|
|
||||||
properties.originalToCopy.put(this, mstore);
|
|
||||||
mstore.normalsMode = normalsMode;
|
|
||||||
mstore.cullMode = cullMode;
|
|
||||||
for (int ii = 0; ii < RenderState.RS_MAX_STATE; ii++) {
|
|
||||||
RenderState rstate = getRenderState(ii);
|
|
||||||
if (rstate != null) {
|
|
||||||
mstore.setRenderState(rstate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mstore.renderQueueMode = renderQueueMode;
|
|
||||||
mstore.lockedMode = lockedMode;
|
|
||||||
mstore.lightCombineMode = lightCombineMode;
|
|
||||||
mstore.textureCombineMode = textureCombineMode;
|
|
||||||
mstore.name = name;
|
|
||||||
mstore.isCollidable = isCollidable;
|
|
||||||
mstore.localRotation.set(localRotation);
|
|
||||||
mstore.localTranslation.set(localTranslation);
|
|
||||||
mstore.localScale.set(localScale);
|
|
||||||
for (Object controller : getControllers()) {
|
|
||||||
if (controller instanceof ModelController) {
|
|
||||||
mstore.addController(
|
|
||||||
((ModelController)controller).putClone(null, properties));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TriangleBatch batch = (TriangleBatch)getBatch(0),
|
|
||||||
mbatch = (TriangleBatch)mstore.getBatch(0);
|
|
||||||
mbatch.setVertexBuffer(properties.isSet("vertices") ?
|
|
||||||
batch.getVertexBuffer() :
|
|
||||||
BufferUtils.clone(batch.getVertexBuffer()));
|
|
||||||
mbatch.setColorBuffer(properties.isSet("colors") ?
|
|
||||||
batch.getColorBuffer() :
|
|
||||||
BufferUtils.clone(batch.getColorBuffer()));
|
|
||||||
mbatch.setNormalBuffer(properties.isSet("normals") ?
|
|
||||||
batch.getNormalBuffer() :
|
|
||||||
BufferUtils.clone(batch.getNormalBuffer()));
|
|
||||||
FloatBuffer texcoords;
|
|
||||||
for (int ii = 0; (texcoords = batch.getTextureBuffer(ii)) != null;
|
|
||||||
ii++) {
|
|
||||||
mbatch.setTextureBuffer(properties.isSet("texcoords") ?
|
|
||||||
texcoords : BufferUtils.clone(texcoords), ii);
|
|
||||||
}
|
|
||||||
mbatch.setIndexBuffer(properties.isSet("indices") ?
|
|
||||||
batch.getIndexBuffer() :
|
|
||||||
BufferUtils.clone(batch.getIndexBuffer()));
|
|
||||||
if (properties.isSet("vboinfo")) {
|
|
||||||
mbatch.setVBOInfo(batch.getVBOInfo());
|
|
||||||
}
|
|
||||||
if (properties.isSet("obbtree")) {
|
|
||||||
mbatch.setCollisionTree(batch.getCollisionTree());
|
|
||||||
}
|
|
||||||
if (properties.isSet("displaylistid")) {
|
|
||||||
mbatch.setDisplayListID(batch.getDisplayListID());
|
|
||||||
}
|
|
||||||
if (batch.getModelBound() != null) {
|
|
||||||
mbatch.setModelBound(properties.isSet("bound") ?
|
|
||||||
batch.getModelBound() : batch.getModelBound().clone(null));
|
|
||||||
}
|
|
||||||
if (_textures != null && _textures.length > 1) {
|
|
||||||
int tidx = properties.random % _textures.length;
|
|
||||||
mstore._textures = new String[] { _textures[tidx] };
|
|
||||||
mstore._tstates = new TextureState[] { _tstates[tidx] };
|
|
||||||
mstore.setRenderState(_tstates[tidx]);
|
|
||||||
} else {
|
|
||||||
mstore._textures = _textures;
|
|
||||||
mstore._tstates = _tstates;
|
|
||||||
}
|
|
||||||
return mstore;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
public void updateWorldVectors ()
|
|
||||||
{
|
|
||||||
if (!_transformLocked) {
|
|
||||||
super.updateWorldVectors();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface Externalizable
|
|
||||||
public void writeExternal (ObjectOutput out)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
out.writeUTF(getName());
|
|
||||||
out.writeObject(getLocalTranslation());
|
|
||||||
out.writeObject(getLocalRotation());
|
|
||||||
out.writeObject(getLocalScale());
|
|
||||||
out.writeObject(getBatch(0).getModelBound());
|
|
||||||
out.writeInt(_vertexBufferSize);
|
|
||||||
out.writeInt(_normalBufferSize);
|
|
||||||
out.writeInt(_colorBufferSize);
|
|
||||||
out.writeInt(_textureBufferSize);
|
|
||||||
out.writeInt(_indexBufferSize);
|
|
||||||
out.writeObject(_textures);
|
|
||||||
out.writeBoolean(_solid);
|
|
||||||
out.writeBoolean(_transparent);
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface Externalizable
|
|
||||||
public void readExternal (ObjectInput in)
|
|
||||||
throws IOException, ClassNotFoundException
|
|
||||||
{
|
|
||||||
setName(in.readUTF());
|
|
||||||
setLocalTranslation((Vector3f)in.readObject());
|
|
||||||
setLocalRotation((Quaternion)in.readObject());
|
|
||||||
setLocalScale((Vector3f)in.readObject());
|
|
||||||
setModelBound((BoundingVolume)in.readObject());
|
|
||||||
_vertexBufferSize = in.readInt();
|
|
||||||
_normalBufferSize = in.readInt();
|
|
||||||
_colorBufferSize = in.readInt();
|
|
||||||
_textureBufferSize = in.readInt();
|
|
||||||
_indexBufferSize = in.readInt();
|
|
||||||
_textures = (String[])in.readObject();
|
|
||||||
_solid = in.readBoolean();
|
|
||||||
_transparent = in.readBoolean();
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface ModelSpatial
|
|
||||||
public void expandModelBounds ()
|
|
||||||
{
|
|
||||||
// no-op
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface ModelSpatial
|
|
||||||
public void setReferenceTransforms ()
|
|
||||||
{
|
|
||||||
// no-op
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface ModelSpatial
|
|
||||||
public void lockStaticMeshes (
|
|
||||||
Renderer renderer, boolean useVBOs, boolean useDisplayLists)
|
|
||||||
{
|
|
||||||
if (useVBOs && renderer.supportsVBO()) {
|
|
||||||
VBOInfo vboinfo = new VBOInfo(true);
|
|
||||||
vboinfo.setVBOIndexEnabled(true);
|
|
||||||
setVBOInfo(vboinfo);
|
|
||||||
|
|
||||||
} else if (useDisplayLists) {
|
|
||||||
lockMeshes(renderer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface ModelSpatial
|
|
||||||
public void resolveTextures (TextureProvider tprov)
|
|
||||||
{
|
|
||||||
if (_textures == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_tstates = new TextureState[_textures.length];
|
|
||||||
for (int ii = 0; ii < _textures.length; ii++) {
|
|
||||||
_tstates[ii] = tprov.getTexture(_textures[ii]);
|
|
||||||
}
|
|
||||||
if (_tstates[0] != null) {
|
|
||||||
setRenderState(_tstates[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface ModelSpatial
|
|
||||||
public void storeMeshFrame (int frameId, boolean blend)
|
|
||||||
{
|
|
||||||
// no-op
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface ModelSpatial
|
|
||||||
public void setMeshFrame (int frameId)
|
|
||||||
{
|
|
||||||
// no-op
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface ModelSpatial
|
|
||||||
public void blendMeshFrames (int frameId1, int frameId2, float alpha)
|
|
||||||
{
|
|
||||||
// no-op
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface ModelSpatial
|
|
||||||
public void writeBuffers (FileChannel out)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
if (_vertexBufferSize > 0) {
|
|
||||||
_vertexByteBuffer.rewind();
|
|
||||||
convertOrder(_vertexByteBuffer, ByteOrder.LITTLE_ENDIAN);
|
|
||||||
out.write(_vertexByteBuffer);
|
|
||||||
convertOrder(_vertexByteBuffer, ByteOrder.nativeOrder());
|
|
||||||
}
|
|
||||||
if (_normalBufferSize > 0) {
|
|
||||||
_normalByteBuffer.rewind();
|
|
||||||
convertOrder(_normalByteBuffer, ByteOrder.LITTLE_ENDIAN);
|
|
||||||
out.write(_normalByteBuffer);
|
|
||||||
convertOrder(_normalByteBuffer, ByteOrder.nativeOrder());
|
|
||||||
}
|
|
||||||
if (_colorBufferSize > 0) {
|
|
||||||
_colorByteBuffer.rewind();
|
|
||||||
convertOrder(_colorByteBuffer, ByteOrder.LITTLE_ENDIAN);
|
|
||||||
out.write(_colorByteBuffer);
|
|
||||||
convertOrder(_colorByteBuffer, ByteOrder.nativeOrder());
|
|
||||||
}
|
|
||||||
if (_textureBufferSize > 0) {
|
|
||||||
_textureByteBuffer.rewind();
|
|
||||||
convertOrder(_textureByteBuffer, ByteOrder.LITTLE_ENDIAN);
|
|
||||||
out.write(_textureByteBuffer);
|
|
||||||
convertOrder(_textureByteBuffer, ByteOrder.nativeOrder());
|
|
||||||
}
|
|
||||||
if (_indexBufferSize > 0) {
|
|
||||||
_indexByteBuffer.rewind();
|
|
||||||
convertOrder(_indexByteBuffer, ByteOrder.LITTLE_ENDIAN);
|
|
||||||
out.write(_indexByteBuffer);
|
|
||||||
convertOrder(_indexByteBuffer, ByteOrder.nativeOrder());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface ModelSpatial
|
|
||||||
public void readBuffers (FileChannel in)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
ByteBuffer vbbuf = null, nbbuf = null, cbbuf = null, tbbuf = null,
|
|
||||||
ibbuf = null;
|
|
||||||
ByteOrder le = ByteOrder.LITTLE_ENDIAN;
|
|
||||||
if (_vertexBufferSize > 0) {
|
|
||||||
vbbuf = ByteBuffer.allocateDirect(_vertexBufferSize*4).order(le);
|
|
||||||
in.read(vbbuf);
|
|
||||||
vbbuf.rewind();
|
|
||||||
convertOrder(vbbuf, ByteOrder.nativeOrder());
|
|
||||||
}
|
|
||||||
if (_normalBufferSize > 0) {
|
|
||||||
nbbuf = ByteBuffer.allocateDirect(_normalBufferSize*4).order(le);
|
|
||||||
in.read(nbbuf);
|
|
||||||
nbbuf.rewind();
|
|
||||||
convertOrder(nbbuf, ByteOrder.nativeOrder());
|
|
||||||
}
|
|
||||||
if (_colorBufferSize > 0) {
|
|
||||||
cbbuf = ByteBuffer.allocateDirect(_colorBufferSize*4).order(le);
|
|
||||||
in.read(cbbuf);
|
|
||||||
cbbuf.rewind();
|
|
||||||
convertOrder(cbbuf, ByteOrder.nativeOrder());
|
|
||||||
}
|
|
||||||
if (_textureBufferSize > 0) {
|
|
||||||
tbbuf = ByteBuffer.allocateDirect(_textureBufferSize*4).order(le);
|
|
||||||
in.read(tbbuf);
|
|
||||||
tbbuf.rewind();
|
|
||||||
convertOrder(tbbuf, ByteOrder.nativeOrder());
|
|
||||||
}
|
|
||||||
if (_indexBufferSize > 0) {
|
|
||||||
ibbuf = ByteBuffer.allocateDirect(_indexBufferSize*4).order(le);
|
|
||||||
in.read(ibbuf);
|
|
||||||
ibbuf.rewind();
|
|
||||||
convertOrder(ibbuf, ByteOrder.nativeOrder());
|
|
||||||
}
|
|
||||||
reconstruct(vbbuf, nbbuf, cbbuf, tbbuf, ibbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface ModelSpatial
|
|
||||||
public void sliceBuffers (MappedByteBuffer map)
|
|
||||||
{
|
|
||||||
ByteBuffer vbbuf = null, nbbuf = null, cbbuf = null, tbbuf = null,
|
|
||||||
ibbuf = null;
|
|
||||||
int total = 0;
|
|
||||||
if (_vertexBufferSize > 0) {
|
|
||||||
int npos = map.position() + _vertexBufferSize*4;
|
|
||||||
map.limit(npos);
|
|
||||||
vbbuf = map.slice().order(ByteOrder.LITTLE_ENDIAN);
|
|
||||||
map.position(npos);
|
|
||||||
}
|
|
||||||
if (_normalBufferSize > 0) {
|
|
||||||
int npos = map.position() + _normalBufferSize*4;
|
|
||||||
map.limit(npos);
|
|
||||||
nbbuf = map.slice().order(ByteOrder.LITTLE_ENDIAN);
|
|
||||||
map.position(npos);
|
|
||||||
}
|
|
||||||
if (_colorBufferSize > 0) {
|
|
||||||
int npos = map.position() + _colorBufferSize*4;
|
|
||||||
map.limit(npos);
|
|
||||||
cbbuf = map.slice().order(ByteOrder.LITTLE_ENDIAN);
|
|
||||||
map.position(npos);
|
|
||||||
}
|
|
||||||
if (_textureBufferSize > 0) {
|
|
||||||
int npos = map.position() + _textureBufferSize*4;
|
|
||||||
map.limit(npos);
|
|
||||||
tbbuf = map.slice().order(ByteOrder.LITTLE_ENDIAN);
|
|
||||||
map.position(npos);
|
|
||||||
}
|
|
||||||
if (_indexBufferSize > 0) {
|
|
||||||
int npos = map.position() + _indexBufferSize*4;
|
|
||||||
map.limit(npos);
|
|
||||||
ibbuf = map.slice().order(ByteOrder.LITTLE_ENDIAN);
|
|
||||||
map.position(npos);
|
|
||||||
}
|
|
||||||
reconstruct(vbbuf, nbbuf, cbbuf, tbbuf, ibbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Locks the transform and bounds of this mesh on the assumption that its
|
|
||||||
* position will not change.
|
|
||||||
*/
|
|
||||||
protected void lockInstance ()
|
|
||||||
{
|
|
||||||
lockBounds();
|
|
||||||
_transformLocked = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Imposes the specified order on the given buffer of 32 bit values.
|
|
||||||
*/
|
|
||||||
protected static void convertOrder (ByteBuffer buf, ByteOrder order)
|
|
||||||
{
|
|
||||||
if (buf.order() == order) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
IntBuffer obuf = buf.asIntBuffer(),
|
|
||||||
nbuf = buf.order(order).asIntBuffer();
|
|
||||||
while (obuf.hasRemaining()) {
|
|
||||||
nbuf.put(obuf.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the states shared between all models. Requires an active
|
|
||||||
* display.
|
|
||||||
*/
|
|
||||||
protected static void initSharedStates ()
|
|
||||||
{
|
|
||||||
Renderer renderer = DisplaySystem.getDisplaySystem().getRenderer();
|
|
||||||
_backCull = renderer.createCullState();
|
|
||||||
_backCull.setCullMode(CullState.CS_BACK);
|
|
||||||
_blendAlpha = renderer.createAlphaState();
|
|
||||||
_blendAlpha.setBlendEnabled(true);
|
|
||||||
_overlayZBuffer = renderer.createZBufferState();
|
|
||||||
_overlayZBuffer.setFunction(ZBufferState.CF_LEQUAL);
|
|
||||||
_overlayZBuffer.setWritable(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The sizes of the various buffers (zero for <code>null</code>). */
|
|
||||||
protected int _vertexBufferSize, _normalBufferSize, _colorBufferSize,
|
|
||||||
_textureBufferSize, _indexBufferSize;
|
|
||||||
|
|
||||||
/** The backing byte buffers for the various buffers. */
|
|
||||||
protected ByteBuffer _vertexByteBuffer, _normalByteBuffer,
|
|
||||||
_colorByteBuffer, _textureByteBuffer, _indexByteBuffer;
|
|
||||||
|
|
||||||
/** The type of bounding volume that this mesh should use. */
|
|
||||||
protected int _boundingType;
|
|
||||||
|
|
||||||
/** The name of this model's textures, or <code>null</code> for none. */
|
|
||||||
protected String[] _textures;
|
|
||||||
|
|
||||||
/** Whether or not this mesh can enable back-face culling. */
|
|
||||||
protected boolean _solid;
|
|
||||||
|
|
||||||
/** Whether or not this mesh must be rendered as transparent. */
|
|
||||||
protected boolean _transparent;
|
|
||||||
|
|
||||||
/** For prototype meshes, the resolved texture states. */
|
|
||||||
protected TextureState[] _tstates;
|
|
||||||
|
|
||||||
/** Whether or not the transform has been locked. This operates in a
|
|
||||||
* slightly different way than JME's locking, in that it allows applying
|
|
||||||
* transformations to display lists. */
|
|
||||||
protected boolean _transformLocked;
|
|
||||||
|
|
||||||
/** The shared state for back face culling. */
|
|
||||||
protected static CullState _backCull;
|
|
||||||
|
|
||||||
/** The shared state for alpha blending. */
|
|
||||||
protected static AlphaState _blendAlpha;
|
|
||||||
|
|
||||||
/** The shared state for checking, but not writing to, the z buffer. */
|
|
||||||
protected static ZBufferState _overlayZBuffer;
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1;
|
|
||||||
}
|
|
||||||
@@ -1,407 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.model;
|
|
||||||
|
|
||||||
import java.io.DataOutput;
|
|
||||||
import java.io.Externalizable;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectInput;
|
|
||||||
import java.io.ObjectOutput;
|
|
||||||
|
|
||||||
import java.nio.MappedByteBuffer;
|
|
||||||
import java.nio.channels.FileChannel;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
|
|
||||||
import com.jme.math.Matrix4f;
|
|
||||||
import com.jme.math.Quaternion;
|
|
||||||
import com.jme.math.Vector3f;
|
|
||||||
import com.jme.renderer.Renderer;
|
|
||||||
import com.jme.scene.Controller;
|
|
||||||
import com.jme.scene.Node;
|
|
||||||
import com.jme.scene.Spatial;
|
|
||||||
import com.jme.scene.state.RenderState;
|
|
||||||
|
|
||||||
import com.threerings.jme.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link Node} with a serialization mechanism tailored to stored models.
|
|
||||||
*/
|
|
||||||
public class ModelNode extends Node
|
|
||||||
implements Externalizable, ModelSpatial
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* No-arg constructor for deserialization.
|
|
||||||
*/
|
|
||||||
public ModelNode ()
|
|
||||||
{
|
|
||||||
super("node");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Standard constructor.
|
|
||||||
*/
|
|
||||||
public ModelNode (String name)
|
|
||||||
{
|
|
||||||
super(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursively searches the scene graph rooted at this node for a
|
|
||||||
* node with the provided name.
|
|
||||||
*/
|
|
||||||
public Spatial getDescendant (String name)
|
|
||||||
{
|
|
||||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
|
||||||
Spatial child = getChild(ii);
|
|
||||||
if (child.getName().equals(name)) {
|
|
||||||
return child;
|
|
||||||
} else if (child instanceof ModelNode) {
|
|
||||||
child = ((ModelNode)child).getDescendant(name);
|
|
||||||
if (child != null) {
|
|
||||||
return child;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a reference to the model space transform of the node.
|
|
||||||
*/
|
|
||||||
public Matrix4f getModelTransform ()
|
|
||||||
{
|
|
||||||
return _modelTransform;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
public void updateWorldData (float time)
|
|
||||||
{
|
|
||||||
// we use locked bounds as an indication that we can skip the update
|
|
||||||
// altogether
|
|
||||||
if ((lockedMode & LOCKED_BOUNDS) == 0) {
|
|
||||||
super.updateWorldData(time);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
public void updateWorldBound ()
|
|
||||||
{
|
|
||||||
// if the node is culled, there are no mesh descendants and thus no
|
|
||||||
// bounds
|
|
||||||
if (cullMode != CULL_ALWAYS) {
|
|
||||||
super.updateWorldBound();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
public void updateWorldVectors ()
|
|
||||||
{
|
|
||||||
super.updateWorldVectors();
|
|
||||||
if (parent instanceof ModelNode) {
|
|
||||||
setTransform(getLocalTranslation(), getLocalRotation(),
|
|
||||||
getLocalScale(), _localTransform);
|
|
||||||
((ModelNode)parent).getModelTransform().mult(_localTransform,
|
|
||||||
_modelTransform);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
_modelTransform.loadIdentity();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface ModelSpatial
|
|
||||||
public Spatial putClone (Spatial store, Model.CloneCreator properties)
|
|
||||||
{
|
|
||||||
ModelNode mstore = (ModelNode)properties.originalToCopy.get(this);
|
|
||||||
if (mstore != null) {
|
|
||||||
return mstore;
|
|
||||||
} else if (store == null) {
|
|
||||||
mstore = new ModelNode(getName());
|
|
||||||
} else {
|
|
||||||
mstore = (ModelNode)store;
|
|
||||||
}
|
|
||||||
properties.originalToCopy.put(this, mstore);
|
|
||||||
mstore.normalsMode = normalsMode;
|
|
||||||
mstore.cullMode = cullMode;
|
|
||||||
for (int ii = 0; ii < RenderState.RS_MAX_STATE; ii++) {
|
|
||||||
RenderState rstate = getRenderState(ii);
|
|
||||||
if (rstate != null) {
|
|
||||||
mstore.setRenderState(rstate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mstore.renderQueueMode = renderQueueMode;
|
|
||||||
mstore.lockedMode = lockedMode;
|
|
||||||
mstore.lightCombineMode = lightCombineMode;
|
|
||||||
mstore.textureCombineMode = textureCombineMode;
|
|
||||||
mstore.name = name;
|
|
||||||
mstore.isCollidable = isCollidable;
|
|
||||||
mstore.localRotation.set(localRotation);
|
|
||||||
mstore.localTranslation.set(localTranslation);
|
|
||||||
mstore.localScale.set(localScale);
|
|
||||||
for (Object controller : getControllers()) {
|
|
||||||
if (controller instanceof ModelController) {
|
|
||||||
mstore.addController(
|
|
||||||
((ModelController)controller).putClone(null, properties));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
|
||||||
Spatial child = getChild(ii);
|
|
||||||
if (child instanceof ModelSpatial) {
|
|
||||||
mstore.attachChild(
|
|
||||||
((ModelSpatial)child).putClone(null, properties));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mstore;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface Externalizable
|
|
||||||
public void writeExternal (ObjectOutput out)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
out.writeUTF(getName());
|
|
||||||
out.writeObject(getLocalTranslation());
|
|
||||||
out.writeObject(getLocalRotation());
|
|
||||||
out.writeObject(getLocalScale());
|
|
||||||
out.writeObject(getChildren());
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface Externalizable
|
|
||||||
public void readExternal (ObjectInput in)
|
|
||||||
throws IOException, ClassNotFoundException
|
|
||||||
{
|
|
||||||
setName(in.readUTF());
|
|
||||||
setLocalTranslation((Vector3f)in.readObject());
|
|
||||||
setLocalRotation((Quaternion)in.readObject());
|
|
||||||
setLocalScale((Vector3f)in.readObject());
|
|
||||||
ArrayList<Spatial> children = (ArrayList<Spatial>)in.readObject();
|
|
||||||
if (children != null) {
|
|
||||||
for (Spatial child : children) {
|
|
||||||
attachChild(child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface ModelSpatial
|
|
||||||
public void expandModelBounds ()
|
|
||||||
{
|
|
||||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
|
||||||
Spatial child = getChild(ii);
|
|
||||||
if (child instanceof ModelSpatial) {
|
|
||||||
((ModelSpatial)child).expandModelBounds();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface ModelSpatial
|
|
||||||
public void setReferenceTransforms ()
|
|
||||||
{
|
|
||||||
updateWorldVectors();
|
|
||||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
|
||||||
Spatial child = getChild(ii);
|
|
||||||
if (child instanceof ModelSpatial) {
|
|
||||||
((ModelSpatial)child).setReferenceTransforms();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface ModelSpatial
|
|
||||||
public void lockStaticMeshes (
|
|
||||||
Renderer renderer, boolean useVBOs, boolean useDisplayLists)
|
|
||||||
{
|
|
||||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
|
||||||
Spatial child = getChild(ii);
|
|
||||||
if (child instanceof ModelSpatial) {
|
|
||||||
((ModelSpatial)child).lockStaticMeshes(renderer, useVBOs,
|
|
||||||
useDisplayLists);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface ModelSpatial
|
|
||||||
public void resolveTextures (TextureProvider tprov)
|
|
||||||
{
|
|
||||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
|
||||||
Spatial child = getChild(ii);
|
|
||||||
if (child instanceof ModelSpatial) {
|
|
||||||
((ModelSpatial)child).resolveTextures(tprov);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface ModelSpatial
|
|
||||||
public void storeMeshFrame (int frameId, boolean blend)
|
|
||||||
{
|
|
||||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
|
||||||
Spatial child = getChild(ii);
|
|
||||||
if (child instanceof ModelSpatial) {
|
|
||||||
((ModelSpatial)child).storeMeshFrame(frameId, blend);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface ModelSpatial
|
|
||||||
public void setMeshFrame (int frameId)
|
|
||||||
{
|
|
||||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
|
||||||
Spatial child = getChild(ii);
|
|
||||||
if (child instanceof ModelSpatial) {
|
|
||||||
((ModelSpatial)child).setMeshFrame(frameId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface ModelSpatial
|
|
||||||
public void blendMeshFrames (int frameId1, int frameId2, float alpha)
|
|
||||||
{
|
|
||||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
|
||||||
Spatial child = getChild(ii);
|
|
||||||
if (child instanceof ModelSpatial) {
|
|
||||||
((ModelSpatial)child).blendMeshFrames(
|
|
||||||
frameId1, frameId2, alpha);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface ModelSpatial
|
|
||||||
public void writeBuffers (FileChannel out)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
|
||||||
Spatial child = getChild(ii);
|
|
||||||
if (child instanceof ModelSpatial) {
|
|
||||||
((ModelSpatial)child).writeBuffers(out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface ModelSpatial
|
|
||||||
public void readBuffers (FileChannel in)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
|
||||||
Spatial child = getChild(ii);
|
|
||||||
if (child instanceof ModelSpatial) {
|
|
||||||
((ModelSpatial)child).readBuffers(in);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface ModelSpatial
|
|
||||||
public void sliceBuffers (MappedByteBuffer map)
|
|
||||||
{
|
|
||||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
|
||||||
Spatial child = getChild(ii);
|
|
||||||
if (child instanceof ModelSpatial) {
|
|
||||||
((ModelSpatial)child).sliceBuffers(map);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the cull state of any nodes that do not contain geometric
|
|
||||||
* descendants to {@link CULL_ALWAYS} so that they don't waste
|
|
||||||
* rendering time.
|
|
||||||
*
|
|
||||||
* @return true if this node should be drawn, false if it contains
|
|
||||||
* no mesh descendants
|
|
||||||
*/
|
|
||||||
protected boolean cullInvisibleNodes ()
|
|
||||||
{
|
|
||||||
boolean hasVisibleDescendants = false;
|
|
||||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
|
||||||
Spatial child = getChild(ii);
|
|
||||||
if (!(child instanceof ModelNode) ||
|
|
||||||
((ModelNode)child).cullInvisibleNodes()) {
|
|
||||||
hasVisibleDescendants = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setCullMode(hasVisibleDescendants ? CULL_INHERIT : CULL_ALWAYS);
|
|
||||||
return hasVisibleDescendants;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Locks the transforms and bounds of this instance with the assumption
|
|
||||||
* that the position will never change.
|
|
||||||
*
|
|
||||||
* @param targets the targets of the model's controllers, which determine
|
|
||||||
* the subset of nodes that can be locked
|
|
||||||
* @return true if this node is a target or contains any targets, otherwise
|
|
||||||
* false
|
|
||||||
*/
|
|
||||||
protected boolean lockInstance (HashSet<Spatial> targets)
|
|
||||||
{
|
|
||||||
updateWorldVectors();
|
|
||||||
lockedMode |= LOCKED_TRANSFORMS;
|
|
||||||
|
|
||||||
boolean containsTargets = false;
|
|
||||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
|
||||||
Spatial child = getChild(ii);
|
|
||||||
if (targets.contains(child) || (child instanceof ModelNode &&
|
|
||||||
((ModelNode)child).lockInstance(targets))) {
|
|
||||||
containsTargets = true;
|
|
||||||
|
|
||||||
} else if (child instanceof ModelMesh) {
|
|
||||||
((ModelMesh)child).lockInstance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (containsTargets) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
updateWorldBound();
|
|
||||||
lockedMode |= LOCKED_BOUNDS;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a matrix to the transform defined by the given translation,
|
|
||||||
* rotation, and scale values.
|
|
||||||
*/
|
|
||||||
protected static Matrix4f setTransform (
|
|
||||||
Vector3f translation, Quaternion rotation, Vector3f scale,
|
|
||||||
Matrix4f result)
|
|
||||||
{
|
|
||||||
result.set(rotation);
|
|
||||||
result.setTranslation(translation);
|
|
||||||
|
|
||||||
result.m00 *= scale.x;
|
|
||||||
result.m01 *= scale.y;
|
|
||||||
result.m02 *= scale.z;
|
|
||||||
|
|
||||||
result.m10 *= scale.x;
|
|
||||||
result.m11 *= scale.y;
|
|
||||||
result.m12 *= scale.z;
|
|
||||||
|
|
||||||
result.m20 *= scale.x;
|
|
||||||
result.m21 *= scale.y;
|
|
||||||
result.m22 *= scale.z;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The node's transform in local and model space. */
|
|
||||||
protected Matrix4f _localTransform = new Matrix4f(),
|
|
||||||
_modelTransform = new Matrix4f();
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1;
|
|
||||||
}
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.model;
|
|
||||||
|
|
||||||
import java.io.Externalizable;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import java.nio.MappedByteBuffer;
|
|
||||||
import java.nio.channels.FileChannel;
|
|
||||||
|
|
||||||
import com.jme.math.Matrix4f;
|
|
||||||
import com.jme.renderer.Renderer;
|
|
||||||
import com.jme.scene.Spatial;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Contains method common to both {@link ModelNode}s and {@link ModelMesh}es.
|
|
||||||
*/
|
|
||||||
public interface ModelSpatial
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Recursively expands the model bounds of any deformable meshes so that
|
|
||||||
* they include the current vertex positions.
|
|
||||||
*/
|
|
||||||
public void expandModelBounds ();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursively sets the reference transforms for any bones in the model.
|
|
||||||
*/
|
|
||||||
public void setReferenceTransforms ();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursively compiles any static meshes to vertex buffer objects (VBOs)
|
|
||||||
* or display lists.
|
|
||||||
*
|
|
||||||
* @param useVBOs if true, use VBOs if the graphics card supports them
|
|
||||||
* @param useDisplayLists if true and not using VBOs, compile static
|
|
||||||
* objects to display lists
|
|
||||||
*/
|
|
||||||
public void lockStaticMeshes (
|
|
||||||
Renderer renderer, boolean useVBOs, boolean useDisplayLists);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursively resolves texture references using the given provider.
|
|
||||||
*/
|
|
||||||
public void resolveTextures (TextureProvider tprov);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursively requests that the current state of all skinned meshes be
|
|
||||||
* stored as an animation frame on the next update.
|
|
||||||
*
|
|
||||||
* @param frameId the frame id, which uniquely identifies one frame of
|
|
||||||
* one animation
|
|
||||||
* @param blend whether or not the stored frames will be retrieved by
|
|
||||||
* calls to {@link #blendAnimationFrames} as opposed to
|
|
||||||
* {@link #setAnimationFrames}
|
|
||||||
*/
|
|
||||||
public void storeMeshFrame (int frameId, boolean blend);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursively switches all skinned meshes to a stored animation frame for
|
|
||||||
* the next update.
|
|
||||||
*/
|
|
||||||
public void setMeshFrame (int frameId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursively blends all skinned meshes between two stored animation
|
|
||||||
* frames for the next update.
|
|
||||||
*/
|
|
||||||
public void blendMeshFrames (int frameId1, int frameId2, float alpha);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates or populates and returns a clone of this object using the given
|
|
||||||
* clone properties.
|
|
||||||
*
|
|
||||||
* @param store an instance of this class to populate, or <code>null</code>
|
|
||||||
* to create a new instance
|
|
||||||
*/
|
|
||||||
public Spatial putClone (Spatial store, Model.CloneCreator properties);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursively writes any data buffers to the output channel.
|
|
||||||
*/
|
|
||||||
public void writeBuffers (FileChannel out)
|
|
||||||
throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursively reads any data buffers from the input channel.
|
|
||||||
*/
|
|
||||||
public void readBuffers (FileChannel in)
|
|
||||||
throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recursively slices any data buffers from the buffer map.
|
|
||||||
*/
|
|
||||||
public void sliceBuffers (MappedByteBuffer map);
|
|
||||||
}
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.model;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectInput;
|
|
||||||
import java.io.ObjectOutput;
|
|
||||||
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
import com.jme.math.Quaternion;
|
|
||||||
import com.jme.math.Vector3f;
|
|
||||||
import com.jme.scene.Controller;
|
|
||||||
import com.jme.scene.Spatial;
|
|
||||||
|
|
||||||
import com.samskivert.util.StringUtil;
|
|
||||||
|
|
||||||
import com.threerings.jme.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A procedural animation that rotates a node around at a constant angular
|
|
||||||
* velocity.
|
|
||||||
*/
|
|
||||||
public class Rotator extends ModelController
|
|
||||||
{
|
|
||||||
@Override // documentation inherited
|
|
||||||
public void configure (Properties props, Spatial target)
|
|
||||||
{
|
|
||||||
super.configure(props, target);
|
|
||||||
String axisstr = props.getProperty("axis", "x"),
|
|
||||||
rpsstr = props.getProperty("radpersec", "3.14");
|
|
||||||
if (axisstr.equalsIgnoreCase("x")) {
|
|
||||||
_axis = Vector3f.UNIT_X;
|
|
||||||
} else if (axisstr.equalsIgnoreCase("y")) {
|
|
||||||
_axis = Vector3f.UNIT_Y;
|
|
||||||
} else if (axisstr.equalsIgnoreCase("z")) {
|
|
||||||
_axis = Vector3f.UNIT_Z;
|
|
||||||
} else {
|
|
||||||
float[] axis = StringUtil.parseFloatArray(axisstr);
|
|
||||||
if (axis != null && axis.length == 3) {
|
|
||||||
_axis = new Vector3f(axis[0], axis[1],
|
|
||||||
axis[2]).normalizeLocal();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Log.warning("Invalid rotation axis [axis=" + axisstr + "].");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
_radpersec = Float.parseFloat(rpsstr);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
Log.warning("Invalid rotation rate [radpersec=" + rpsstr + "].");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public void update (float time)
|
|
||||||
{
|
|
||||||
if (!isActive()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_rot.fromAngleNormalAxis(time * _radpersec, _axis);
|
|
||||||
_target.getLocalRotation().multLocal(_rot);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
public Controller putClone (
|
|
||||||
Controller store, Model.CloneCreator properties)
|
|
||||||
{
|
|
||||||
Rotator rstore;
|
|
||||||
if (store == null) {
|
|
||||||
rstore = new Rotator();
|
|
||||||
} else {
|
|
||||||
rstore = (Rotator)store;
|
|
||||||
}
|
|
||||||
super.putClone(rstore, properties);
|
|
||||||
rstore._axis = _axis;
|
|
||||||
rstore._radpersec = _radpersec;
|
|
||||||
return rstore;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface Externalizable
|
|
||||||
public void writeExternal (ObjectOutput out)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
super.writeExternal(out);
|
|
||||||
out.writeObject(_axis);
|
|
||||||
out.writeFloat(_radpersec);
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface Externalizable
|
|
||||||
public void readExternal (ObjectInput in)
|
|
||||||
throws IOException, ClassNotFoundException
|
|
||||||
{
|
|
||||||
super.readExternal(in);
|
|
||||||
_axis = (Vector3f)in.readObject();
|
|
||||||
_radpersec = in.readFloat();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The axis about which to rotate. */
|
|
||||||
protected Vector3f _axis;
|
|
||||||
|
|
||||||
/** The velocity at which to rotate in radians per second. */
|
|
||||||
protected float _radpersec;
|
|
||||||
|
|
||||||
/** A temporary quaternion. */
|
|
||||||
protected Quaternion _rot = new Quaternion();
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1;
|
|
||||||
}
|
|
||||||
@@ -1,495 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.model;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectInput;
|
|
||||||
import java.io.ObjectInputStream;
|
|
||||||
import java.io.ObjectOutput;
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.FloatBuffer;
|
|
||||||
import java.nio.IntBuffer;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
|
|
||||||
import com.jme.bounding.BoundingVolume;
|
|
||||||
import com.jme.math.Matrix4f;
|
|
||||||
import com.jme.math.Vector3f;
|
|
||||||
import com.jme.renderer.Renderer;
|
|
||||||
import com.jme.scene.Spatial;
|
|
||||||
import com.jme.scene.TriMesh;
|
|
||||||
import com.jme.scene.VBOInfo;
|
|
||||||
import com.jme.scene.batch.SharedBatch;
|
|
||||||
import com.jme.scene.batch.TriangleBatch;
|
|
||||||
import com.jme.system.DisplaySystem;
|
|
||||||
import com.jme.util.geom.BufferUtils;
|
|
||||||
|
|
||||||
import com.samskivert.util.HashIntMap;
|
|
||||||
|
|
||||||
import com.threerings.jme.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A triangle mesh that deforms according to a bone hierarchy.
|
|
||||||
*/
|
|
||||||
public class SkinMesh extends ModelMesh
|
|
||||||
{
|
|
||||||
/** Represents the vertex weights of a group of vertices influenced by the
|
|
||||||
* same set of bones. */
|
|
||||||
public static class WeightGroup
|
|
||||||
implements Serializable
|
|
||||||
{
|
|
||||||
/** The number of vertices in this weight group. */
|
|
||||||
public int vertexCount;
|
|
||||||
|
|
||||||
/** The bones influencing this group. */
|
|
||||||
public Bone[] bones;
|
|
||||||
|
|
||||||
/** The array of interleaved weights (of length <code>vertexCount *
|
|
||||||
* boneIndices.length</code>): weights for first vertex, weights for
|
|
||||||
* second, etc. */
|
|
||||||
public float[] weights;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rebinds this weight group for a prototype instance.
|
|
||||||
*
|
|
||||||
* @param bmap the mapping from prototype to instance bones
|
|
||||||
*/
|
|
||||||
public WeightGroup rebind (HashMap<Bone, Bone> bmap)
|
|
||||||
{
|
|
||||||
WeightGroup wgroup = new WeightGroup();
|
|
||||||
wgroup.vertexCount = vertexCount;
|
|
||||||
wgroup.bones = new Bone[bones.length];
|
|
||||||
for (int ii = 0; ii < bones.length; ii++) {
|
|
||||||
wgroup.bones[ii] = bmap.get(bones[ii]);
|
|
||||||
}
|
|
||||||
wgroup.weights = weights;
|
|
||||||
return wgroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Represents a bone that influences the mesh. */
|
|
||||||
public static class Bone
|
|
||||||
implements Serializable
|
|
||||||
{
|
|
||||||
/** The node that defines the bone's position. */
|
|
||||||
public ModelNode node;
|
|
||||||
|
|
||||||
/** The inverse of the bone's model space reference transform. */
|
|
||||||
public transient Matrix4f invRefTransform;
|
|
||||||
|
|
||||||
/** The bone's current transform in model space. */
|
|
||||||
public transient Matrix4f transform;
|
|
||||||
|
|
||||||
public Bone (ModelNode node)
|
|
||||||
{
|
|
||||||
this.node = node;
|
|
||||||
transform = new Matrix4f();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rebinds this bone for a prototype instance.
|
|
||||||
*
|
|
||||||
* @param pnodes a mapping from prototype nodes to instance nodes
|
|
||||||
*/
|
|
||||||
public Bone rebind (HashMap pnodes)
|
|
||||||
{
|
|
||||||
Bone bone = new Bone((ModelNode)pnodes.get(node));
|
|
||||||
bone.invRefTransform = invRefTransform;
|
|
||||||
bone.transform = new Matrix4f();
|
|
||||||
return bone;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the bone's transient state.
|
|
||||||
*/
|
|
||||||
private void readObject (ObjectInputStream in)
|
|
||||||
throws IOException, ClassNotFoundException
|
|
||||||
{
|
|
||||||
in.defaultReadObject();
|
|
||||||
transform = new Matrix4f();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* No-arg constructor for deserialization.
|
|
||||||
*/
|
|
||||||
public SkinMesh ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an empty mesh.
|
|
||||||
*/
|
|
||||||
public SkinMesh (String name)
|
|
||||||
{
|
|
||||||
super(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the array of weight groups that determine how bones affect
|
|
||||||
* each vertex.
|
|
||||||
*/
|
|
||||||
public void setWeightGroups (WeightGroup[] weightGroups)
|
|
||||||
{
|
|
||||||
_weightGroups = weightGroups;
|
|
||||||
|
|
||||||
// compile a list of all referenced bones
|
|
||||||
HashSet<Bone> bones = new HashSet<Bone>();
|
|
||||||
for (WeightGroup group : weightGroups) {
|
|
||||||
Collections.addAll(bones, group.bones);
|
|
||||||
}
|
|
||||||
_bones = bones.toArray(new Bone[bones.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
public void reconstruct (
|
|
||||||
ByteBuffer vertices, ByteBuffer normals, ByteBuffer colors,
|
|
||||||
ByteBuffer textures, ByteBuffer indices)
|
|
||||||
{
|
|
||||||
super.reconstruct(vertices, normals, colors, textures, indices);
|
|
||||||
|
|
||||||
// store the current buffers as the originals
|
|
||||||
storeOriginalBuffers();
|
|
||||||
|
|
||||||
// initialize the quantized frame table
|
|
||||||
_frames = new HashIntMap<Object>();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
public void centerVertices ()
|
|
||||||
{
|
|
||||||
super.centerVertices();
|
|
||||||
storeOriginalBuffers();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
public Spatial putClone (Spatial store, Model.CloneCreator properties)
|
|
||||||
{
|
|
||||||
SkinMesh mstore = (SkinMesh)properties.originalToCopy.get(this);
|
|
||||||
if (mstore != null) {
|
|
||||||
return mstore;
|
|
||||||
} else if (store == null) {
|
|
||||||
mstore = new SkinMesh(getName());
|
|
||||||
} else {
|
|
||||||
mstore = (SkinMesh)store;
|
|
||||||
}
|
|
||||||
properties.removeProperty("vertices");
|
|
||||||
properties.removeProperty("normals");
|
|
||||||
properties.removeProperty("displaylistid");
|
|
||||||
super.putClone(mstore, properties);
|
|
||||||
properties.addProperty("vertices");
|
|
||||||
properties.addProperty("normals");
|
|
||||||
properties.addProperty("displaylistid");
|
|
||||||
mstore._frames = _frames;
|
|
||||||
mstore._useDisplayLists = _useDisplayLists;
|
|
||||||
mstore._invRefTransform = _invRefTransform;
|
|
||||||
mstore._bones = new Bone[_bones.length];
|
|
||||||
HashMap<Bone, Bone> bmap = new HashMap<Bone, Bone>();
|
|
||||||
for (int ii = 0; ii < _bones.length; ii++) {
|
|
||||||
bmap.put(_bones[ii], mstore._bones[ii] =
|
|
||||||
_bones[ii].rebind(properties.originalToCopy));
|
|
||||||
}
|
|
||||||
mstore._weightGroups = new WeightGroup[_weightGroups.length];
|
|
||||||
for (int ii = 0; ii < _weightGroups.length; ii++) {
|
|
||||||
mstore._weightGroups[ii] = _weightGroups[ii].rebind(bmap);
|
|
||||||
}
|
|
||||||
mstore._ovbuf = _ovbuf;
|
|
||||||
mstore._onbuf = _onbuf;
|
|
||||||
mstore._vbuf = new float[_vbuf.length];
|
|
||||||
mstore._nbuf = new float[_nbuf.length];
|
|
||||||
return mstore;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
public void writeExternal (ObjectOutput out)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
super.writeExternal(out);
|
|
||||||
out.writeObject(_weightGroups);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
public void readExternal (ObjectInput in)
|
|
||||||
throws IOException, ClassNotFoundException
|
|
||||||
{
|
|
||||||
super.readExternal(in);
|
|
||||||
setWeightGroups((WeightGroup[])in.readObject());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
public void expandModelBounds ()
|
|
||||||
{
|
|
||||||
BoundingVolume obound =
|
|
||||||
(BoundingVolume)getBatch(0).getModelBound().clone(null);
|
|
||||||
updateModelBound();
|
|
||||||
getBatch(0).getModelBound().mergeLocal(obound);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
public void setReferenceTransforms ()
|
|
||||||
{
|
|
||||||
_invRefTransform = new Matrix4f();
|
|
||||||
if (parent instanceof ModelNode) {
|
|
||||||
Matrix4f transform = new Matrix4f();
|
|
||||||
ModelNode.setTransform(getLocalTranslation(), getLocalRotation(),
|
|
||||||
getLocalScale(), transform);
|
|
||||||
((ModelNode)parent).getModelTransform().mult(transform,
|
|
||||||
_invRefTransform);
|
|
||||||
_invRefTransform.invertLocal();
|
|
||||||
}
|
|
||||||
for (Bone bone : _bones) {
|
|
||||||
bone.invRefTransform =
|
|
||||||
_invRefTransform.mult(bone.node.getModelTransform()).invert();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
public void lockStaticMeshes (
|
|
||||||
Renderer renderer, boolean useVBOs, boolean useDisplayLists)
|
|
||||||
{
|
|
||||||
// we can use VBOs for color, texture, and indices
|
|
||||||
if (useVBOs && renderer.supportsVBO()) {
|
|
||||||
VBOInfo vboinfo = new VBOInfo(false);
|
|
||||||
vboinfo.setVBOColorEnabled(true);
|
|
||||||
vboinfo.setVBOTextureEnabled(true);
|
|
||||||
vboinfo.setVBOIndexEnabled(true);
|
|
||||||
setVBOInfo(vboinfo);
|
|
||||||
}
|
|
||||||
_useDisplayLists = useDisplayLists;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
public void storeMeshFrame (int frameId, boolean blend)
|
|
||||||
{
|
|
||||||
_storeFrameId = frameId;
|
|
||||||
_storeBlend = blend;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
public void setMeshFrame (int frameId)
|
|
||||||
{
|
|
||||||
TriangleBatch batch = getBatch(0),
|
|
||||||
tbatch = (TriangleBatch)_frames.get(frameId);
|
|
||||||
if (batch instanceof SharedBatch) {
|
|
||||||
((SharedBatch)batch).setTarget(tbatch);
|
|
||||||
} else {
|
|
||||||
clearBatches();
|
|
||||||
addBatch(new SharedBatch(tbatch));
|
|
||||||
getBatch(0).updateRenderState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
public void blendMeshFrames (int frameId1, int frameId2, float alpha)
|
|
||||||
{
|
|
||||||
BlendFrame frame1 = (BlendFrame)_frames.get(frameId1),
|
|
||||||
frame2 = (BlendFrame)_frames.get(frameId2);
|
|
||||||
frame1.blend(frame2, alpha, _vbuf, _nbuf);
|
|
||||||
FloatBuffer vbuf = getVertexBuffer(0), nbuf = getNormalBuffer(0);
|
|
||||||
vbuf.rewind();
|
|
||||||
vbuf.put(_vbuf);
|
|
||||||
nbuf.rewind();
|
|
||||||
nbuf.put(_nbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
public void updateWorldData (float time)
|
|
||||||
{
|
|
||||||
super.updateWorldData(time);
|
|
||||||
if (_weightGroups == null || _storeFrameId == -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// update the bone transforms
|
|
||||||
for (Bone bone : _bones) {
|
|
||||||
_invRefTransform.mult(bone.node.getModelTransform(),
|
|
||||||
bone.transform);
|
|
||||||
bone.transform.multLocal(bone.invRefTransform);
|
|
||||||
}
|
|
||||||
|
|
||||||
// deform the mesh according to the positions of the bones (this code
|
|
||||||
// is ugly as sin because it's optimized at a low level)
|
|
||||||
Bone[] bones;
|
|
||||||
int vertexCount, jj, kk, ww;
|
|
||||||
float[] weights;
|
|
||||||
Matrix4f m;
|
|
||||||
float weight, ovx, ovy, ovz, onx, ony, onz, vx, vy, vz, nx, ny, nz;
|
|
||||||
for (int ii = 0, bidx = 0; ii < _weightGroups.length; ii++) {
|
|
||||||
vertexCount = _weightGroups[ii].vertexCount;
|
|
||||||
bones = _weightGroups[ii].bones;
|
|
||||||
weights = _weightGroups[ii].weights;
|
|
||||||
for (jj = 0, ww = 0; jj < vertexCount; jj++) {
|
|
||||||
ovx = _ovbuf[bidx];
|
|
||||||
ovy = _ovbuf[bidx + 1];
|
|
||||||
ovz = _ovbuf[bidx + 2];
|
|
||||||
onx = _onbuf[bidx];
|
|
||||||
ony = _onbuf[bidx + 1];
|
|
||||||
onz = _onbuf[bidx + 2];
|
|
||||||
vx = vy = vz = 0f;
|
|
||||||
nx = ny = nz = 0f;
|
|
||||||
for (kk = 0; kk < bones.length; kk++) {
|
|
||||||
m = bones[kk].transform;
|
|
||||||
weight = weights[ww++];
|
|
||||||
|
|
||||||
vx += (ovx*m.m00 + ovy*m.m01 + ovz*m.m02 + m.m03) * weight;
|
|
||||||
vy += (ovx*m.m10 + ovy*m.m11 + ovz*m.m12 + m.m13) * weight;
|
|
||||||
vz += (ovx*m.m20 + ovy*m.m21 + ovz*m.m22 + m.m23) * weight;
|
|
||||||
|
|
||||||
nx += (onx*m.m00 + ony*m.m01 + onz*m.m02) * weight;
|
|
||||||
ny += (onx*m.m10 + ony*m.m11 + onz*m.m12) * weight;
|
|
||||||
nz += (onx*m.m20 + ony*m.m21 + onz*m.m22) * weight;
|
|
||||||
}
|
|
||||||
_vbuf[bidx] = vx;
|
|
||||||
_vbuf[bidx + 1] = vy;
|
|
||||||
_vbuf[bidx + 2] = vz;
|
|
||||||
_nbuf[bidx++] = nx;
|
|
||||||
_nbuf[bidx++] = ny;
|
|
||||||
_nbuf[bidx++] = nz;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if skinning in real time, copy the data from arrays to buffers;
|
|
||||||
// otherwise, store the mesh as an animation frame
|
|
||||||
if (_storeFrameId == 0) {
|
|
||||||
FloatBuffer vbuf = getVertexBuffer(0), nbuf = getNormalBuffer(0);
|
|
||||||
vbuf.rewind();
|
|
||||||
vbuf.put(_vbuf);
|
|
||||||
nbuf.rewind();
|
|
||||||
nbuf.put(_nbuf);
|
|
||||||
} else {
|
|
||||||
storeFrame();
|
|
||||||
_storeFrameId = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores the current frame data for later use.
|
|
||||||
*/
|
|
||||||
protected void storeFrame ()
|
|
||||||
{
|
|
||||||
if (_storeBlend) {
|
|
||||||
_frames.put(_storeFrameId, new BlendFrame(
|
|
||||||
(float[])_vbuf.clone(), (float[])_nbuf.clone()));
|
|
||||||
} else {
|
|
||||||
TriangleBatch batch = getBatch(0), tbatch = new TriangleBatch();
|
|
||||||
tbatch.setParentGeom(DUMMY_MESH);
|
|
||||||
tbatch.setColorBuffer(batch.getColorBuffer());
|
|
||||||
tbatch.setTextureBuffer(batch.getTextureBuffer(0), 0);
|
|
||||||
tbatch.setIndexBuffer(batch.getIndexBuffer());
|
|
||||||
tbatch.setVertexBuffer(BufferUtils.createFloatBuffer(_vbuf));
|
|
||||||
tbatch.setNormalBuffer(BufferUtils.createFloatBuffer(_nbuf));
|
|
||||||
VBOInfo ovboinfo = batch.getVBOInfo();
|
|
||||||
if (ovboinfo != null) {
|
|
||||||
VBOInfo vboinfo = new VBOInfo(true);
|
|
||||||
vboinfo.setVBOIndexEnabled(true);
|
|
||||||
vboinfo.setVBOColorID(ovboinfo.getVBOColorID());
|
|
||||||
vboinfo.setVBOTextureID(0, ovboinfo.getVBOTextureID(0));
|
|
||||||
vboinfo.setVBOIndexID(ovboinfo.getVBOIndexID());
|
|
||||||
tbatch.setVBOInfo(vboinfo);
|
|
||||||
} else if (_useDisplayLists) {
|
|
||||||
tbatch.lockMeshes(
|
|
||||||
DisplaySystem.getDisplaySystem().getRenderer());
|
|
||||||
}
|
|
||||||
_frames.put(_storeFrameId, tbatch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores the current vertex and normal buffers for later deformation.
|
|
||||||
*/
|
|
||||||
protected void storeOriginalBuffers ()
|
|
||||||
{
|
|
||||||
FloatBuffer vbuf = getVertexBuffer(0), nbuf = getNormalBuffer(0);
|
|
||||||
vbuf.rewind();
|
|
||||||
nbuf.rewind();
|
|
||||||
FloatBuffer.wrap(_ovbuf = new float[vbuf.capacity()]).put(vbuf);
|
|
||||||
FloatBuffer.wrap(_onbuf = new float[nbuf.capacity()]).put(nbuf);
|
|
||||||
_vbuf = new float[_ovbuf.length];
|
|
||||||
_nbuf = new float[_onbuf.length];
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A stored frame used for linear blending. */
|
|
||||||
protected static class BlendFrame
|
|
||||||
{
|
|
||||||
/** The skinned vertex and normal values. */
|
|
||||||
public float[] vbuf, nbuf;
|
|
||||||
|
|
||||||
public BlendFrame (float[] vbuf, float[] nbuf)
|
|
||||||
{
|
|
||||||
this.vbuf = vbuf;
|
|
||||||
this.nbuf = nbuf;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void blend (
|
|
||||||
BlendFrame next, float alpha, float[] rvbuf, float[] rnbuf)
|
|
||||||
{
|
|
||||||
float[] nvbuf = next.vbuf, nnbuf = next.nbuf;
|
|
||||||
float ialpha = 1f - alpha;
|
|
||||||
for (int ii = 0, nn = vbuf.length; ii < nn; ii++) {
|
|
||||||
rvbuf[ii] = vbuf[ii] * ialpha + nvbuf[ii] * alpha;
|
|
||||||
rnbuf[ii] = nbuf[ii] * ialpha + nnbuf[ii] * alpha;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Pre-skinned {@link TriangleBatch}es or {@link BlendFrame}s shared
|
|
||||||
* between all instances corresponding to frame ids from
|
|
||||||
* {@link #storeAnimationFrame}. */
|
|
||||||
protected HashIntMap<Object> _frames;
|
|
||||||
|
|
||||||
/** Whether or to use display lists if VBOs are unavailable for quantized
|
|
||||||
* meshes. */
|
|
||||||
protected boolean _useDisplayLists;
|
|
||||||
|
|
||||||
/** The inverse of the model space reference transform. */
|
|
||||||
protected Matrix4f _invRefTransform;
|
|
||||||
|
|
||||||
/** The groups of vertices influenced by different sets of bones. */
|
|
||||||
protected WeightGroup[] _weightGroups;
|
|
||||||
|
|
||||||
/** The bones referenced by the weight groups. */
|
|
||||||
protected Bone[] _bones;
|
|
||||||
|
|
||||||
/** The original (undeformed) vertex and normal buffers and the deformed
|
|
||||||
* versions. */
|
|
||||||
protected float[] _ovbuf, _onbuf, _vbuf, _nbuf;
|
|
||||||
|
|
||||||
/** The frame id to store on the next update. If 0, don't store any frame
|
|
||||||
* and skin the mesh as normal. If -1, a frame has been stored and thus
|
|
||||||
* skinning should only take place when further frames are requested. */
|
|
||||||
protected int _storeFrameId;
|
|
||||||
|
|
||||||
/** Whether or not the stored frame id will be used for blending. */
|
|
||||||
protected boolean _storeBlend;
|
|
||||||
|
|
||||||
/** A dummy mesh that simply hold transformation values. */
|
|
||||||
protected static final TriMesh DUMMY_MESH = new TriMesh();
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1;
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.model;
|
|
||||||
|
|
||||||
import com.jme.scene.state.TextureState;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides a means for models to resolve their texture references.
|
|
||||||
*/
|
|
||||||
public interface TextureProvider
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Returns a texture state containing the named texture.
|
|
||||||
*
|
|
||||||
* @param name the name of the texture, which will be interpreted as an
|
|
||||||
* absolute resource path if it starts with a forward slash; otherwise,
|
|
||||||
* as a relative resource path
|
|
||||||
*/
|
|
||||||
public TextureState getTexture (String name);
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<!-- $Id: package.html 617 2001-11-13 00:12:20Z mdb $ -->
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body bgcolor="white">
|
|
||||||
|
|
||||||
Defines extensions to <a href="http://jmonkeyengine.com">jMonkeyEngine</a>
|
|
||||||
an OpenGL-based 3D rendering engine. An application framework is
|
|
||||||
provided that integrates jME with the Presents distributed object
|
|
||||||
system and other useful bits and bobs are also included.
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.sprite;
|
|
||||||
|
|
||||||
import com.jme.math.FastMath;
|
|
||||||
import com.jme.math.Vector3f;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Moves a sprite ballistically.
|
|
||||||
*/
|
|
||||||
public class BallisticPath extends Path
|
|
||||||
{
|
|
||||||
/** Gravity: it's the law. */
|
|
||||||
public static final float G = -9.8f;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Moves the supplied sprite from the starting coordinate (which will
|
|
||||||
* be modified) using the starting velocity, under the specified
|
|
||||||
* acceleration.
|
|
||||||
*/
|
|
||||||
public BallisticPath (Sprite sprite, Vector3f start, Vector3f velocity,
|
|
||||||
Vector3f accel, float duration)
|
|
||||||
{
|
|
||||||
super(sprite);
|
|
||||||
_curpos = start;
|
|
||||||
_velocity = velocity;
|
|
||||||
_accel = accel;
|
|
||||||
_duration = duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public void update (float time)
|
|
||||||
{
|
|
||||||
// adjust the position
|
|
||||||
_velocity.mult(time, _temp);
|
|
||||||
_curpos.addLocal(_temp);
|
|
||||||
_sprite.setLocalTranslation(_curpos);
|
|
||||||
|
|
||||||
// check to see if we're done
|
|
||||||
_accum += time;
|
|
||||||
if (_accum >= _duration) {
|
|
||||||
_sprite.pathCompleted();
|
|
||||||
} else {
|
|
||||||
// adjust our velocity due to acceleration
|
|
||||||
_accel.mult(time, _temp);
|
|
||||||
_velocity.addLocal(_temp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Computes and returns the angle of elevation needed to launch a
|
|
||||||
* ballistic projectile at the specified velocity and have it travel
|
|
||||||
* the specified range (when it will once again reach the launch
|
|
||||||
* elevation).
|
|
||||||
*/
|
|
||||||
public static float computeElevation (float range, float vel, float accel)
|
|
||||||
{
|
|
||||||
return FastMath.asin(accel * range / (vel * vel)) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Computes and returns the flight time of a projectile launched at an
|
|
||||||
* angle previously computed with {@link #computeElevation}..
|
|
||||||
*/
|
|
||||||
public static float computeFlightTime (float range, float vel, float angle)
|
|
||||||
{
|
|
||||||
return range / (vel * FastMath.cos(angle));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Vector3f _curpos, _velocity, _accel;
|
|
||||||
protected float _duration, _accum;
|
|
||||||
protected Vector3f _temp = new Vector3f(0, 0, 0);
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.sprite;
|
|
||||||
|
|
||||||
import com.jme.math.Vector3f;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Moves a sprite along a straight line.
|
|
||||||
*/
|
|
||||||
public class LinePath extends Path
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Moves the supplied sprite from the starting coordinate to the
|
|
||||||
* specified destination coordinate in the specified time.
|
|
||||||
*/
|
|
||||||
public LinePath (Sprite sprite, Vector3f start, Vector3f finish,
|
|
||||||
float duration)
|
|
||||||
{
|
|
||||||
super(sprite);
|
|
||||||
_start = start;
|
|
||||||
_finish = finish;
|
|
||||||
_duration = duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public void update (float time)
|
|
||||||
{
|
|
||||||
_accum += time;
|
|
||||||
if (_accum >= _duration) {
|
|
||||||
_sprite.setLocalTranslation(_finish);
|
|
||||||
_sprite.pathCompleted();
|
|
||||||
} else {
|
|
||||||
_temp.interpolate(_start, _finish, _accum / _duration);
|
|
||||||
_sprite.setLocalTranslation(_temp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Vector3f _start, _finish;
|
|
||||||
protected float _duration, _accum;
|
|
||||||
protected Vector3f _temp = new Vector3f(0, 0, 0);
|
|
||||||
}
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.sprite;
|
|
||||||
|
|
||||||
import com.jme.math.Quaternion;
|
|
||||||
import com.jme.math.Vector3f;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Moves a sprite along a series of straight lines.
|
|
||||||
*/
|
|
||||||
public class LineSegmentPath extends Path
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Creates a path for the supplied sprite traversing the supplied
|
|
||||||
* series of points with the specified duration between points.
|
|
||||||
*
|
|
||||||
* @param points a list of points between which the sprite will be
|
|
||||||
* moved linearly (the sprite will be moved immediately to the first
|
|
||||||
* point).
|
|
||||||
* @param durations defines the elapsed time between each successive
|
|
||||||
* traversal. This will as a result be shorter by one element than the
|
|
||||||
* points array.
|
|
||||||
*/
|
|
||||||
public LineSegmentPath (Sprite sprite, Vector3f up, Vector3f orient,
|
|
||||||
Vector3f[] points, float[] durations)
|
|
||||||
{
|
|
||||||
super(sprite);
|
|
||||||
_up = up;
|
|
||||||
_orient = orient;
|
|
||||||
_points = points;
|
|
||||||
_durations = durations;
|
|
||||||
updateRotation();
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public void update (float time)
|
|
||||||
{
|
|
||||||
// note the accumulated time
|
|
||||||
_accum += time;
|
|
||||||
|
|
||||||
// if we have surpassed the time for this segment, subtract the
|
|
||||||
// segment time and move on to the next segment
|
|
||||||
while (_current < _durations.length && _accum > _durations[_current]) {
|
|
||||||
_accum -= _durations[_current];
|
|
||||||
advance();
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we have completed our path, move the sprite to the final
|
|
||||||
// position and wrap everything up
|
|
||||||
if (_current >= _durations.length) {
|
|
||||||
_sprite.setLocalTranslation(_points[_points.length-1]);
|
|
||||||
_sprite.pathCompleted();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// move the sprite to the appropriate position between points
|
|
||||||
_temp.interpolate(_points[_current], _points[_current+1],
|
|
||||||
_accum / _durations[_current]);
|
|
||||||
_sprite.setLocalTranslation(_temp);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void advance ()
|
|
||||||
{
|
|
||||||
_current++;
|
|
||||||
if (_current < _points.length-1) {
|
|
||||||
updateRotation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void updateRotation ()
|
|
||||||
{
|
|
||||||
_points[_current+1].subtract(_points[_current], _temp);
|
|
||||||
PathUtil.computeRotation(_up, _orient, _temp, _rotate);
|
|
||||||
_sprite.getLocalRotation().set(_rotate);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Vector3f _up, _orient;
|
|
||||||
protected Vector3f[] _points;
|
|
||||||
protected float[] _durations;
|
|
||||||
protected float _accum;
|
|
||||||
protected int _current;
|
|
||||||
protected Vector3f _temp = new Vector3f(0, 0, 0);
|
|
||||||
protected Quaternion _rotate = new Quaternion();
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.sprite;
|
|
||||||
|
|
||||||
import com.jme.math.Quaternion;
|
|
||||||
import com.jme.math.Vector3f;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A ballistic path that orients the sprite toward the velocity vector as
|
|
||||||
* it traverses the path.
|
|
||||||
*/
|
|
||||||
public class OrientingBallisticPath extends BallisticPath
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Creates a {@link BallisticPath} that will rotate the sprite so that
|
|
||||||
* the supplied <code>orient</code> is aligned with the velocity
|
|
||||||
* vector at all points along the path. If the provided orientation is
|
|
||||||
* not initially in line with the starting velocity, the sprite will
|
|
||||||
* be rotated immediately and then adjusted as it follows the path.
|
|
||||||
*/
|
|
||||||
public OrientingBallisticPath (
|
|
||||||
Sprite sprite, Vector3f orient, Vector3f start, Vector3f velocity,
|
|
||||||
Vector3f accel, float duration)
|
|
||||||
{
|
|
||||||
super(sprite, start, velocity, accel, duration);
|
|
||||||
|
|
||||||
// TODO: handle orient that is not (1, 0, 0)
|
|
||||||
_orient = orient;
|
|
||||||
|
|
||||||
// compute the up vector (opposite of acceleration)
|
|
||||||
_up = accel.negate();
|
|
||||||
_up.normalizeLocal();
|
|
||||||
|
|
||||||
_sprite.setLocalRotation(
|
|
||||||
PathUtil.computeAxisRotation(_up, velocity, _rotate));
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public void update (float time)
|
|
||||||
{
|
|
||||||
// do the normal update
|
|
||||||
super.update(time);
|
|
||||||
|
|
||||||
_sprite.setLocalRotation(
|
|
||||||
PathUtil.computeAxisRotation(_up, _velocity, _rotate));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Vector3f _orient;
|
|
||||||
protected Vector3f _up;
|
|
||||||
protected Quaternion _rotate = new Quaternion();
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.sprite;
|
|
||||||
|
|
||||||
import com.jme.scene.Controller;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines a framework for moving sprites around and notifying interested
|
|
||||||
* parties when the sprite has completed its path or if the path has been
|
|
||||||
* cancelled.
|
|
||||||
*/
|
|
||||||
public abstract class Path extends Controller
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Creates and initializes this path with the sprite it will be
|
|
||||||
* manipulating.
|
|
||||||
*/
|
|
||||||
protected Path (Sprite sprite)
|
|
||||||
{
|
|
||||||
_sprite = sprite;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when this path is removed from its sprite (either due to
|
|
||||||
* completion or cancellation).
|
|
||||||
*/
|
|
||||||
public void wasRemoved ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Sprite _sprite;
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.sprite;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implement this interface to find out when a sprite completes or cancels
|
|
||||||
* its path.
|
|
||||||
*/
|
|
||||||
public interface PathObserver extends SpriteObserver
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Called when a sprite's path is cancelled either because a new path
|
|
||||||
* was started or the path was explicitly cancelled with {@link
|
|
||||||
* Sprite#cancelMove}.
|
|
||||||
*/
|
|
||||||
public void pathCancelled (Sprite sprite, Path path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a sprite completes its traversal of a path.
|
|
||||||
*
|
|
||||||
* @param sprite the sprite that completed its path.
|
|
||||||
* @param path the path that was completed.
|
|
||||||
*/
|
|
||||||
public void pathCompleted (Sprite sprite, Path path);
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.sprite;
|
|
||||||
|
|
||||||
import com.jme.math.FastMath;
|
|
||||||
import com.jme.math.Quaternion;
|
|
||||||
import com.jme.math.Vector3f;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Path related utility functions.
|
|
||||||
*/
|
|
||||||
public class PathUtil
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Computes a rotation to align the X axis with the specified
|
|
||||||
* orientation and stores it in the provided target. The provided up
|
|
||||||
* vector is crossed with the new orientation to find the left vector
|
|
||||||
* and the left vector is recrossed with the orientation to find the
|
|
||||||
* correct up vector.
|
|
||||||
*
|
|
||||||
* @return the supplied target quaternion.
|
|
||||||
*/
|
|
||||||
public static Quaternion computeAxisRotation (
|
|
||||||
Vector3f up, Vector3f orient, Quaternion target)
|
|
||||||
{
|
|
||||||
_axes[0].set(orient);
|
|
||||||
_axes[0].normalizeLocal();
|
|
||||||
up.cross(_axes[0], _axes[1]);
|
|
||||||
_axes[1].normalizeLocal();
|
|
||||||
_axes[0].cross(_axes[1], _axes[2]);
|
|
||||||
target.fromAxes(_axes);
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Computes a rotation from the one vector to another.
|
|
||||||
*
|
|
||||||
* @param axis will be used as the axis of rotation if the two vectors
|
|
||||||
* are parallel.
|
|
||||||
*/
|
|
||||||
public static Quaternion computeRotation (
|
|
||||||
Vector3f axis, Vector3f from, Vector3f to, Quaternion target)
|
|
||||||
{
|
|
||||||
float angle = computeAngle(from, to);
|
|
||||||
if (angle == FastMath.PI) { // opposite
|
|
||||||
target.fromAngleAxis(angle, axis);
|
|
||||||
} else if (angle == 0) { // coincident
|
|
||||||
target.x = target.y = target.z = 0;
|
|
||||||
target.w = 1;
|
|
||||||
} else {
|
|
||||||
from.cross(to, _axis);
|
|
||||||
target.fromAngleAxis(angle, _axis);
|
|
||||||
}
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Computes the angle between two arbitrary vectors.
|
|
||||||
*/
|
|
||||||
public static float computeAngle (Vector3f one, Vector3f two)
|
|
||||||
{
|
|
||||||
return FastMath.acos(one.dot(two) / (one.length() * two.length()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Computes the angle between two normalized vectors.
|
|
||||||
*/
|
|
||||||
public static float computeAngleNormal (Vector3f one, Vector3f two)
|
|
||||||
{
|
|
||||||
return FastMath.acos(one.dot(two));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static Vector3f[] _axes = {
|
|
||||||
new Vector3f(), new Vector3f(), new Vector3f() };
|
|
||||||
protected static Vector3f _axis = new Vector3f();
|
|
||||||
}
|
|
||||||
@@ -1,244 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.sprite;
|
|
||||||
|
|
||||||
import com.jme.scene.Node;
|
|
||||||
import com.jme.scene.Spatial;
|
|
||||||
|
|
||||||
import com.samskivert.util.ObserverList;
|
|
||||||
|
|
||||||
import com.threerings.jme.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a visual entity that one controls as a single unit. Sprites
|
|
||||||
* can be made to follow paths which is one of their primary reasons for
|
|
||||||
* existence.
|
|
||||||
*/
|
|
||||||
public class Sprite extends Node
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Walks down the hierarchy provided setting the animation speed on
|
|
||||||
* any controllers found along the way.
|
|
||||||
*/
|
|
||||||
public static void setAnimationSpeed (Spatial spatial, float speed)
|
|
||||||
{
|
|
||||||
for (int ii = 0; ii < spatial.getControllers().size(); ii++) {
|
|
||||||
spatial.getController(ii).setSpeed(speed);
|
|
||||||
}
|
|
||||||
if (spatial instanceof Node) {
|
|
||||||
Node node = (Node)spatial;
|
|
||||||
for (int ii = 0; ii < node.getQuantity(); ii++) {
|
|
||||||
setAnimationSpeed(node.getChild(ii), speed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Walks down the hierarchy provided turning on or off any controllers
|
|
||||||
* found along the way.
|
|
||||||
*/
|
|
||||||
public static void setAnimationActive (Spatial spatial, boolean active)
|
|
||||||
{
|
|
||||||
for (int ii = 0; ii < spatial.getControllers().size(); ii++) {
|
|
||||||
spatial.getController(ii).setActive(active);
|
|
||||||
}
|
|
||||||
if (spatial instanceof Node) {
|
|
||||||
Node node = (Node)spatial;
|
|
||||||
for (int ii = 0; ii < node.getQuantity(); ii++) {
|
|
||||||
setAnimationActive(node.getChild(ii), active);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Walks down the hierarchy provided setting the animation repeat type on
|
|
||||||
* any controllers found along the way.
|
|
||||||
*/
|
|
||||||
public static void setAnimationRepeatType (Spatial spatial, int repeatType)
|
|
||||||
{
|
|
||||||
for (int ii = 0; ii < spatial.getControllers().size(); ii++) {
|
|
||||||
spatial.getController(ii).setRepeatType(repeatType);
|
|
||||||
}
|
|
||||||
if (spatial instanceof Node) {
|
|
||||||
Node node = (Node)spatial;
|
|
||||||
for (int ii = 0; ii < node.getQuantity(); ii++) {
|
|
||||||
setAnimationRepeatType(node.getChild(ii), repeatType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Sprite ()
|
|
||||||
{
|
|
||||||
super("");
|
|
||||||
setName("sprite:" + hashCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an observer to this sprite. Observers are notified when path
|
|
||||||
* related events take place.
|
|
||||||
*/
|
|
||||||
public void addObserver (SpriteObserver obs)
|
|
||||||
{
|
|
||||||
if (_observers == null) {
|
|
||||||
_observers = new ObserverList<SpriteObserver>(
|
|
||||||
ObserverList.SAFE_IN_ORDER_NOTIFY);
|
|
||||||
}
|
|
||||||
_observers.add(obs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the specified observer from this sprite.
|
|
||||||
*/
|
|
||||||
public void removeObserver (SpriteObserver obs)
|
|
||||||
{
|
|
||||||
if (_observers != null) {
|
|
||||||
_observers.remove(obs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if this sprite is moving along a path, false if not.
|
|
||||||
*/
|
|
||||||
public boolean isMoving ()
|
|
||||||
{
|
|
||||||
return _path != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instructs this sprite to move along the specified path. Any
|
|
||||||
* currently executing path will be cancelled.
|
|
||||||
*/
|
|
||||||
public void move (Path path)
|
|
||||||
{
|
|
||||||
// if there's a previous path, let it know that it's going away
|
|
||||||
cancelMove();
|
|
||||||
|
|
||||||
// save off this path
|
|
||||||
_path = path;
|
|
||||||
addController(_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancels any currently executing path. Any registered observers will
|
|
||||||
* be notified of the cancellation.
|
|
||||||
*/
|
|
||||||
public void cancelMove ()
|
|
||||||
{
|
|
||||||
if (_path != null) {
|
|
||||||
Path oldpath = _path;
|
|
||||||
_path = null;
|
|
||||||
oldpath.wasRemoved();
|
|
||||||
if (_observers != null) {
|
|
||||||
_observers.apply(new CancelledOp(this, oldpath));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by the active path when it has completed. <em>Note:</em>
|
|
||||||
* don't call this method unless you are implementing a {@link Path}.
|
|
||||||
*/
|
|
||||||
public void pathCompleted ()
|
|
||||||
{
|
|
||||||
if (_path == null) {
|
|
||||||
Log.warning("pathCompleted() called on pathless sprite " +
|
|
||||||
"(re-completed?) [sprite=" + this + "].");
|
|
||||||
Thread.dumpStack();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Path oldpath = _path;
|
|
||||||
_path = null;
|
|
||||||
removeController(oldpath);
|
|
||||||
oldpath.wasRemoved();
|
|
||||||
if (_observers != null) {
|
|
||||||
_observers.apply(new CompletedOp(this, oldpath));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures the speed of all controllers in our model hierarchy.
|
|
||||||
*/
|
|
||||||
public void setAnimationSpeed (float speed)
|
|
||||||
{
|
|
||||||
setAnimationSpeed(this, speed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures the active state of all controllers in our model hierarchy.
|
|
||||||
*/
|
|
||||||
public void setAnimationActive (boolean active)
|
|
||||||
{
|
|
||||||
setAnimationActive(this, active);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures the repeat type of all controllers in our model hierarchy.
|
|
||||||
*/
|
|
||||||
public void setAnimationRepeatType (int repeatType)
|
|
||||||
{
|
|
||||||
setAnimationRepeatType(this, repeatType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Used to dispatch {@link PathObserver#pathCancelled}. */
|
|
||||||
protected static class CancelledOp
|
|
||||||
implements ObserverList.ObserverOp<SpriteObserver>
|
|
||||||
{
|
|
||||||
public CancelledOp (Sprite sprite, Path path) {
|
|
||||||
_sprite = sprite;
|
|
||||||
_path = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean apply (SpriteObserver observer) {
|
|
||||||
if (observer instanceof PathObserver) {
|
|
||||||
((PathObserver)observer).pathCancelled(_sprite, _path);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Sprite _sprite;
|
|
||||||
protected Path _path;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Used to dispatch {@link PathObserver#pathCompleted}. */
|
|
||||||
protected static class CompletedOp
|
|
||||||
implements ObserverList.ObserverOp<SpriteObserver>
|
|
||||||
{
|
|
||||||
public CompletedOp (Sprite sprite, Path path) {
|
|
||||||
_sprite = sprite;
|
|
||||||
_path = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean apply (SpriteObserver observer) {
|
|
||||||
if (observer instanceof PathObserver) {
|
|
||||||
((PathObserver)observer).pathCompleted(_sprite, _path);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Sprite _sprite;
|
|
||||||
protected Path _path;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ObserverList<SpriteObserver> _observers;
|
|
||||||
protected Path _path;
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.sprite;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The super-interface for all sprite observers.
|
|
||||||
*/
|
|
||||||
public interface SpriteObserver
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
//
|
|
||||||
// $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;
|
|
||||||
}
|
|
||||||
@@ -1,404 +0,0 @@
|
|||||||
//
|
|
||||||
// $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;
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
//
|
|
||||||
// $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;
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
//
|
|
||||||
// $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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.tools;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
import com.jme.math.Quaternion;
|
|
||||||
import com.jme.math.Vector3f;
|
|
||||||
import com.jme.scene.Controller;
|
|
||||||
import com.jme.scene.Spatial;
|
|
||||||
|
|
||||||
import com.threerings.jme.Log;
|
|
||||||
import com.threerings.jme.model.Model;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A basic representation for keyframe animations.
|
|
||||||
*/
|
|
||||||
public class AnimationDef
|
|
||||||
{
|
|
||||||
/** The rate of the animation in frames per second. */
|
|
||||||
public int frameRate;
|
|
||||||
|
|
||||||
/** A single frame of the animation. */
|
|
||||||
public static class FrameDef
|
|
||||||
{
|
|
||||||
/** Transform for affected nodes. */
|
|
||||||
public ArrayList<TransformDef> transforms =
|
|
||||||
new ArrayList<TransformDef>();
|
|
||||||
|
|
||||||
public void addTransform (TransformDef transform)
|
|
||||||
{
|
|
||||||
transforms.add(transform);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Adds all transform targets in this frame to the supplied set. */
|
|
||||||
public void addTransformTargets (
|
|
||||||
HashMap<String, Spatial> nodes, HashSet<Spatial> targets)
|
|
||||||
{
|
|
||||||
for (int ii = 0, nn = transforms.size(); ii < nn; ii++) {
|
|
||||||
String name = transforms.get(ii).name;
|
|
||||||
Spatial target = nodes.get(name);
|
|
||||||
if (target != null) {
|
|
||||||
targets.add(target);
|
|
||||||
} else {
|
|
||||||
Log.debug("Missing animation target [name=" + name +
|
|
||||||
"].");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the array of transforms for this frame. */
|
|
||||||
public Model.Transform[] getTransforms (Spatial[] targets)
|
|
||||||
{
|
|
||||||
Model.Transform[] mtransforms =
|
|
||||||
new Model.Transform[targets.length];
|
|
||||||
for (int ii = 0; ii < targets.length; ii++) {
|
|
||||||
mtransforms[ii] = getTransform(targets[ii]);
|
|
||||||
}
|
|
||||||
return mtransforms;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the transform for the supplied target. */
|
|
||||||
protected Model.Transform getTransform (Spatial target)
|
|
||||||
{
|
|
||||||
String name = target.getName();
|
|
||||||
for (int ii = 0, nn = transforms.size(); ii < nn; ii++) {
|
|
||||||
TransformDef transform = transforms.get(ii);
|
|
||||||
if (name.equals(transform.name)) {
|
|
||||||
return transform.getTransform();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A transform for a single node. */
|
|
||||||
public static class TransformDef
|
|
||||||
{
|
|
||||||
/** The name of the affected node. */
|
|
||||||
public String name;
|
|
||||||
|
|
||||||
/** The transformation parameters. */
|
|
||||||
public float[] translation;
|
|
||||||
public float[] rotation;
|
|
||||||
public float[] scale;
|
|
||||||
|
|
||||||
/** Returns the live transform object. */
|
|
||||||
public Model.Transform getTransform ()
|
|
||||||
{
|
|
||||||
return new Model.Transform(
|
|
||||||
new Vector3f(translation[0], translation[1], translation[2]),
|
|
||||||
new Quaternion(rotation[0], rotation[1], rotation[2],
|
|
||||||
rotation[3]),
|
|
||||||
new Vector3f(scale[0], scale[1], scale[2]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The individual frames of the animation. */
|
|
||||||
public ArrayList<FrameDef> frames = new ArrayList<FrameDef>();
|
|
||||||
|
|
||||||
public void addFrame (FrameDef frame)
|
|
||||||
{
|
|
||||||
frames.add(frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the "live" animation object that will be serialized with the
|
|
||||||
* object.
|
|
||||||
*
|
|
||||||
* @param props the animation properties
|
|
||||||
* @param nodes the nodes in the model, mapped by name
|
|
||||||
*/
|
|
||||||
public Model.Animation createAnimation (
|
|
||||||
Properties props, HashMap<String, Spatial> nodes)
|
|
||||||
{
|
|
||||||
// find all affected nodes
|
|
||||||
HashSet<Spatial> targets = new HashSet<Spatial>();
|
|
||||||
for (int ii = 0, nn = frames.size(); ii < nn; ii++) {
|
|
||||||
frames.get(ii).addTransformTargets(nodes, targets);
|
|
||||||
}
|
|
||||||
|
|
||||||
// create and configure the animation
|
|
||||||
Model.Animation anim = new Model.Animation();
|
|
||||||
anim.frameRate = frameRate;
|
|
||||||
String rtype = props.getProperty("repeat_type", "clamp");
|
|
||||||
if (rtype.equals("cycle")) {
|
|
||||||
anim.repeatType = Controller.RT_CYCLE;
|
|
||||||
} else if (rtype.equals("wrap")) {
|
|
||||||
anim.repeatType = Controller.RT_WRAP;
|
|
||||||
} else {
|
|
||||||
anim.repeatType = Controller.RT_CLAMP;
|
|
||||||
}
|
|
||||||
|
|
||||||
// collect all transforms
|
|
||||||
anim.transformTargets = targets.toArray(new Spatial[targets.size()]);
|
|
||||||
anim.transforms = new Model.Transform[frames.size()][targets.size()];
|
|
||||||
for (int ii = 0; ii < anim.transforms.length; ii++) {
|
|
||||||
anim.transforms[ii] =
|
|
||||||
frames.get(ii).getTransforms(anim.transformTargets);
|
|
||||||
}
|
|
||||||
return anim;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.tools;
|
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
|
||||||
|
|
||||||
import com.jme.image.Texture;
|
|
||||||
import com.jme.math.FastMath;
|
|
||||||
import com.jme.math.Vector3f;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A tool for converting cube maps (sky boxes) to sphere maps.
|
|
||||||
*/
|
|
||||||
public class BuildSphereMap
|
|
||||||
{
|
|
||||||
public static void main (String[] args)
|
|
||||||
{
|
|
||||||
if (args.length < 7) {
|
|
||||||
System.err.println("Usage: BuildSphereMap front.ext back.ext " +
|
|
||||||
"left.ext right.ext up.ext dest.ext size");
|
|
||||||
System.exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
execute(new File(args[0]), new File(args[1]), new File(args[2]),
|
|
||||||
new File(args[3]), new File(args[4]), new File(args[5]),
|
|
||||||
Integer.parseInt(args[6]));
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds the sphere map.
|
|
||||||
*
|
|
||||||
* @param front the file containing the front side of the cube map
|
|
||||||
* @param back the file containing the back side of the cube map
|
|
||||||
* @param left the file containing the left side of the cube map
|
|
||||||
* @param right the file containing the right side of the cube map
|
|
||||||
* @param up the file containing the up side of the cube map
|
|
||||||
* @param target the file to contain the sphere map
|
|
||||||
* @param size the size of the sphere map to generate
|
|
||||||
*/
|
|
||||||
public static void execute (File front, File back, File left, File right,
|
|
||||||
File up, File target, int size)
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
// load up the sides of the cube map
|
|
||||||
BufferedImage[] sides = new BufferedImage[5];
|
|
||||||
sides[FRONT] = ImageIO.read(front);
|
|
||||||
sides[BACK] = ImageIO.read(back);
|
|
||||||
sides[LEFT] = ImageIO.read(left);
|
|
||||||
sides[RIGHT] = ImageIO.read(right);
|
|
||||||
sides[UP] = ImageIO.read(up);
|
|
||||||
|
|
||||||
// compute the pixels
|
|
||||||
int[] rgb = new int[size * size];
|
|
||||||
Vector3f vec = new Vector3f();
|
|
||||||
for (int y = 0, idx = 0; y < size; y++) {
|
|
||||||
for (int x = 0; x < size; x++, idx++) {
|
|
||||||
float vx = x / (size*0.5f) - 1f, vy = y / (size*0.5f) - 1f,
|
|
||||||
d2 = vx*vx + vy*vy;
|
|
||||||
int p = 0;
|
|
||||||
if (d2 <= 1f) {
|
|
||||||
vec.set(vx, vy, FastMath.sqrt(1f - d2));
|
|
||||||
rgb[idx] = getCubeMapPixel(vec, sides);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// create and write the image
|
|
||||||
BufferedImage image = new BufferedImage(size, size,
|
|
||||||
BufferedImage.TYPE_INT_RGB);
|
|
||||||
image.setRGB(0, 0, size, size, rgb, 0, size);
|
|
||||||
String dest = target.toString(),
|
|
||||||
ext = dest.substring(dest.lastIndexOf('.')+1);
|
|
||||||
ImageIO.write(image, ext, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the pixel from the cube map to which the given vector points.
|
|
||||||
*/
|
|
||||||
protected static int getCubeMapPixel (Vector3f vec, BufferedImage[] sides)
|
|
||||||
{
|
|
||||||
int side = getCubeMapSide(vec);
|
|
||||||
|
|
||||||
float s, t;
|
|
||||||
switch (side) {
|
|
||||||
case FRONT:
|
|
||||||
s = -vec.x / vec.y;
|
|
||||||
t = vec.z / vec.y;
|
|
||||||
break;
|
|
||||||
case BACK:
|
|
||||||
s = -vec.x / vec.y;
|
|
||||||
t = -vec.z / vec.y;
|
|
||||||
break;
|
|
||||||
case LEFT:
|
|
||||||
s = vec.y / vec.x;
|
|
||||||
t = -vec.z / vec.x;
|
|
||||||
break;
|
|
||||||
case RIGHT:
|
|
||||||
s = vec.y / vec.x;
|
|
||||||
t = vec.z / vec.x;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
case UP:
|
|
||||||
s = vec.x / vec.z;
|
|
||||||
t = -vec.y / vec.z;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
int width = sides[side].getWidth(), height = sides[side].getHeight();
|
|
||||||
return sides[side].getRGB((int)((width-1) * (s+1f)/2f),
|
|
||||||
(int)((height-1) * (1f-t)/2f));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the side index identifying the face of the cube map to which
|
|
||||||
* the given vector points.
|
|
||||||
*/
|
|
||||||
protected static int getCubeMapSide (Vector3f vec)
|
|
||||||
{
|
|
||||||
if (vec.x > vec.z && vec.x > vec.y && vec.x > -vec.y) {
|
|
||||||
return RIGHT;
|
|
||||||
|
|
||||||
} else if (vec.x < -vec.z && vec.x < -vec.y && vec.x < vec.y) {
|
|
||||||
return LEFT;
|
|
||||||
|
|
||||||
} else if (vec.y > vec.z) {
|
|
||||||
return FRONT;
|
|
||||||
|
|
||||||
} else if (vec.y < -vec.z) {
|
|
||||||
return BACK;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
return UP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The sides of the cube map. */
|
|
||||||
protected static final int FRONT = 0, BACK = 1, LEFT = 2, RIGHT = 3,
|
|
||||||
UP = 4;
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.tools;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import org.apache.tools.ant.BuildException;
|
|
||||||
import org.apache.tools.ant.Task;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An ant task for converting cube maps (sky boxes) to sphere maps.
|
|
||||||
*/
|
|
||||||
public class BuildSphereMapTask extends Task
|
|
||||||
{
|
|
||||||
public void setFront (File front)
|
|
||||||
{
|
|
||||||
_front = front;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBack (File back)
|
|
||||||
{
|
|
||||||
_back = back;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLeft (File left)
|
|
||||||
{
|
|
||||||
_left = left;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRight (File right)
|
|
||||||
{
|
|
||||||
_right = right;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUp (File up)
|
|
||||||
{
|
|
||||||
_up = up;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTarget (File target)
|
|
||||||
{
|
|
||||||
_target = target;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSize (int size)
|
|
||||||
{
|
|
||||||
_size = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void execute () throws BuildException
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
BuildSphereMap.execute(_front, _back, _left, _right, _up, _target,
|
|
||||||
_size);
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new BuildException("Failure building sphere map", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The files representing the sides of the cube map and the target. */
|
|
||||||
protected File _front, _back, _left, _right, _up, _target;
|
|
||||||
|
|
||||||
/** The size of the target image. */
|
|
||||||
protected int _size;
|
|
||||||
}
|
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.tools;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
|
|
||||||
import com.jme.scene.Spatial;
|
|
||||||
import com.jme.util.LoggingSystem;
|
|
||||||
import com.jmex.model.XMLparser.Converters.DummyDisplaySystem;
|
|
||||||
|
|
||||||
import com.samskivert.util.PropertiesUtil;
|
|
||||||
import com.samskivert.util.StringUtil;
|
|
||||||
|
|
||||||
import com.threerings.jme.model.Model;
|
|
||||||
import com.threerings.jme.model.ModelMesh;
|
|
||||||
import com.threerings.jme.model.ModelNode;
|
|
||||||
import com.threerings.jme.model.SkinMesh;
|
|
||||||
import com.threerings.jme.tools.xml.AnimationParser;
|
|
||||||
import com.threerings.jme.tools.xml.ModelParser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An application for compiling 3D models defined in XML to fast-loading binary
|
|
||||||
* files.
|
|
||||||
*/
|
|
||||||
public class CompileModel
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Loads the model described by the given properties file and compiles it
|
|
||||||
* to a <code>.dat</code> file in the same directory.
|
|
||||||
*
|
|
||||||
* @return the loaded model, or <code>null</code> if the compiled version
|
|
||||||
* is up-to-date
|
|
||||||
*/
|
|
||||||
public static Model compile (File source)
|
|
||||||
throws Exception
|
|
||||||
{
|
|
||||||
String spath = source.toString();
|
|
||||||
int didx = spath.lastIndexOf('.');
|
|
||||||
String root = (didx == -1) ? spath : spath.substring(0, didx);
|
|
||||||
File content = new File(root + ".mxml"),
|
|
||||||
target = new File(root + ".dat");
|
|
||||||
boolean needsUpdate = false;
|
|
||||||
if (source.lastModified() >= target.lastModified() ||
|
|
||||||
content.lastModified() >= target.lastModified()) {
|
|
||||||
needsUpdate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// load the model properties
|
|
||||||
Properties props = new Properties();
|
|
||||||
FileInputStream in = new FileInputStream(source);
|
|
||||||
props.load(in);
|
|
||||||
in.close();
|
|
||||||
|
|
||||||
// locate the animations, if any
|
|
||||||
String[] anims =
|
|
||||||
StringUtil.parseStringArray(props.getProperty("animations", ""));
|
|
||||||
File[] afiles = new File[anims.length];
|
|
||||||
File dir = source.getParentFile();
|
|
||||||
for (int ii = 0; ii < anims.length; ii++) {
|
|
||||||
afiles[ii] = new File(dir, anims[ii] + ".mxml");
|
|
||||||
if (afiles[ii].lastModified() >= target.lastModified()) {
|
|
||||||
needsUpdate = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!needsUpdate) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
System.out.println("Compiling " + source.getParent() + "...");
|
|
||||||
|
|
||||||
// load the model content
|
|
||||||
ModelDef mdef = _mparser.parseModel(content.toString());
|
|
||||||
HashMap<String, Spatial> nodes = new HashMap<String, Spatial>();
|
|
||||||
Model model = mdef.createModel(props, nodes);
|
|
||||||
model.initPrototype();
|
|
||||||
|
|
||||||
// load the animations, if any
|
|
||||||
for (int ii = 0; ii < anims.length; ii++) {
|
|
||||||
System.out.println(" Adding " + afiles[ii] + "...");
|
|
||||||
AnimationDef adef = _aparser.parseAnimation(afiles[ii].toString());
|
|
||||||
model.addAnimation(anims[ii], adef.createAnimation(
|
|
||||||
PropertiesUtil.getSubProperties(props, anims[ii]), nodes));
|
|
||||||
}
|
|
||||||
|
|
||||||
// write and return the model
|
|
||||||
model.writeToFile(target);
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main (String[] args)
|
|
||||||
{
|
|
||||||
if (args.length < 1) {
|
|
||||||
System.err.println("Usage: CompileModel source.properties");
|
|
||||||
System.exit(-1);
|
|
||||||
}
|
|
||||||
// create a dummy display system
|
|
||||||
new DummyDisplaySystem();
|
|
||||||
LoggingSystem.getLogger().setLevel(Level.WARNING);
|
|
||||||
|
|
||||||
try {
|
|
||||||
compile(new File(args[0]));
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println("Error compiling model: " + e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A parser for the model definitions. */
|
|
||||||
protected static ModelParser _mparser = new ModelParser();
|
|
||||||
|
|
||||||
/** A parser for the animation definitions. */
|
|
||||||
protected static AnimationParser _aparser = new AnimationParser();
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.tools;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
|
|
||||||
import org.apache.tools.ant.BuildException;
|
|
||||||
import org.apache.tools.ant.DirectoryScanner;
|
|
||||||
import org.apache.tools.ant.Task;
|
|
||||||
import org.apache.tools.ant.types.FileSet;
|
|
||||||
|
|
||||||
import com.jme.util.LoggingSystem;
|
|
||||||
import com.jmex.model.XMLparser.Converters.DummyDisplaySystem;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An ant task for compiling 3D models defined in XML to fast-loading binary
|
|
||||||
* files.
|
|
||||||
*/
|
|
||||||
public class CompileModelTask extends Task
|
|
||||||
{
|
|
||||||
public void addFileset (FileSet set)
|
|
||||||
{
|
|
||||||
_filesets.add(set);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void init () throws BuildException
|
|
||||||
{
|
|
||||||
// create a dummy display system
|
|
||||||
new DummyDisplaySystem();
|
|
||||||
LoggingSystem.getLogger().setLevel(Level.WARNING);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void execute ()
|
|
||||||
throws BuildException
|
|
||||||
{
|
|
||||||
for (int ii = 0, nn = _filesets.size(); ii < nn; ii++) {
|
|
||||||
FileSet fs = _filesets.get(ii);
|
|
||||||
DirectoryScanner ds = fs.getDirectoryScanner(getProject());
|
|
||||||
File fromDir = fs.getDir(getProject());
|
|
||||||
String[] srcFiles = ds.getIncludedFiles();
|
|
||||||
|
|
||||||
for (int f = 0; f < srcFiles.length; f++) {
|
|
||||||
File source = new File(fromDir, srcFiles[f]);
|
|
||||||
try {
|
|
||||||
CompileModel.compile(source);
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println("Error compiling " + source + ": " + e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A list of filesets that contain XML models. */
|
|
||||||
protected ArrayList<FileSet> _filesets = new ArrayList<FileSet>();
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.tools;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.BufferedOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URL;
|
|
||||||
|
|
||||||
import com.jmex.model.XMLparser.Converters.AseToJme;
|
|
||||||
import com.jmex.model.XMLparser.Converters.DummyDisplaySystem;
|
|
||||||
import com.jmex.model.XMLparser.Converters.FormatConverter;
|
|
||||||
import com.jmex.model.XMLparser.Converters.MaxToJme;
|
|
||||||
import com.jmex.model.XMLparser.Converters.Md2ToJme;
|
|
||||||
import com.jmex.model.XMLparser.Converters.Md3ToJme;
|
|
||||||
import com.jmex.model.XMLparser.Converters.ObjToJme;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A tool for converting various 3D model formats into JME's internal
|
|
||||||
* format.
|
|
||||||
*/
|
|
||||||
public class ConvertModel
|
|
||||||
{
|
|
||||||
public static void main (String[] args)
|
|
||||||
{
|
|
||||||
if (args.length < 2) {
|
|
||||||
System.err.println("Usage: ConvertModel source.ext dest.jme");
|
|
||||||
System.exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a dummy display system which the converters need
|
|
||||||
new DummyDisplaySystem();
|
|
||||||
|
|
||||||
ConvertModel app = new ConvertModel();
|
|
||||||
File source = new File(args[0]);
|
|
||||||
File target = new File(args[1]);
|
|
||||||
|
|
||||||
String path = source.getPath().toLowerCase();
|
|
||||||
String type = path.substring(path.lastIndexOf(".") + 1);
|
|
||||||
|
|
||||||
// set up our converter
|
|
||||||
FormatConverter convert = null;
|
|
||||||
if (type.equals("obj")) {
|
|
||||||
convert = new ObjToJme();
|
|
||||||
try {
|
|
||||||
convert.setProperty("mtllib", new URL("file:" + source));
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println("Failed to create material URL: " + e);
|
|
||||||
System.exit(-1);
|
|
||||||
}
|
|
||||||
} else if (type.equals("3ds")) {
|
|
||||||
convert = new MaxToJme();
|
|
||||||
} else if (type.equals("md2")) {
|
|
||||||
convert = new Md2ToJme();
|
|
||||||
} else if (type.equals("md3")) {
|
|
||||||
convert = new Md3ToJme();
|
|
||||||
} else if (type.equals("ase")) {
|
|
||||||
convert = new AseToJme();
|
|
||||||
} else {
|
|
||||||
System.err.println("Unknown model type '" + type + "'.");
|
|
||||||
System.exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// and do the deed
|
|
||||||
try {
|
|
||||||
BufferedOutputStream bout = new BufferedOutputStream(
|
|
||||||
new FileOutputStream(target));
|
|
||||||
BufferedInputStream bin = new BufferedInputStream(
|
|
||||||
new FileInputStream(source));
|
|
||||||
convert.convert(bin, bout);
|
|
||||||
bout.close();
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
System.err.println("Error converting '" + source +
|
|
||||||
"' to '" + target + "'.");
|
|
||||||
ioe.printStackTrace(System.err);
|
|
||||||
System.exit(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.tools;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.BufferedOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
|
|
||||||
import org.apache.tools.ant.BuildException;
|
|
||||||
import org.apache.tools.ant.DirectoryScanner;
|
|
||||||
import org.apache.tools.ant.Task;
|
|
||||||
import org.apache.tools.ant.types.FileSet;
|
|
||||||
|
|
||||||
import com.jme.util.LoggingSystem;
|
|
||||||
import com.jmex.model.XMLparser.Converters.AseToJme;
|
|
||||||
import com.jmex.model.XMLparser.Converters.DummyDisplaySystem;
|
|
||||||
import com.jmex.model.XMLparser.Converters.FormatConverter;
|
|
||||||
import com.jmex.model.XMLparser.Converters.MaxToJme;
|
|
||||||
import com.jmex.model.XMLparser.Converters.Md2ToJme;
|
|
||||||
import com.jmex.model.XMLparser.Converters.Md3ToJme;
|
|
||||||
import com.jmex.model.XMLparser.Converters.ObjToJme;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An ant task for converting various 3D model formats into JME's internal
|
|
||||||
* format.
|
|
||||||
*/
|
|
||||||
public class ConvertModelTask extends Task
|
|
||||||
{
|
|
||||||
public void addFileset (FileSet set)
|
|
||||||
{
|
|
||||||
_filesets.add(set);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void init () throws BuildException
|
|
||||||
{
|
|
||||||
// create a dummy display system which the converters need
|
|
||||||
new DummyDisplaySystem();
|
|
||||||
LoggingSystem.getLogger().setLevel(Level.WARNING);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void execute () throws BuildException
|
|
||||||
{
|
|
||||||
for (int i = 0; i < _filesets.size(); i++) {
|
|
||||||
FileSet fs = (FileSet)_filesets.get(i);
|
|
||||||
DirectoryScanner ds = fs.getDirectoryScanner(getProject());
|
|
||||||
File fromDir = fs.getDir(getProject());
|
|
||||||
String[] srcFiles = ds.getIncludedFiles();
|
|
||||||
|
|
||||||
for (int f = 0; f < srcFiles.length; f++) {
|
|
||||||
File cfile = new File(fromDir, srcFiles[f]);
|
|
||||||
String target = srcFiles[f];
|
|
||||||
int didx = target.lastIndexOf(".");
|
|
||||||
target = (didx == -1) ? target : target.substring(0, didx);
|
|
||||||
target += ".jme";
|
|
||||||
convertModel(cfile, new File(fromDir, target));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void convertModel (File source, File target)
|
|
||||||
{
|
|
||||||
if (source.lastModified() < target.lastModified()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("Converting " + source + "...");
|
|
||||||
String path = source.getPath().toLowerCase();
|
|
||||||
String type = path.substring(path.lastIndexOf(".") + 1);
|
|
||||||
|
|
||||||
// set up our converter
|
|
||||||
FormatConverter convert = null;
|
|
||||||
if (type.equals("obj")) {
|
|
||||||
convert = new ObjToJme();
|
|
||||||
try {
|
|
||||||
convert.setProperty("mtllib", new URL("file:" + source));
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println("Failed to create material URL: " + e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else if (type.equals("3ds")) {
|
|
||||||
convert = new MaxToJme();
|
|
||||||
} else if (type.equals("md2")) {
|
|
||||||
convert = new Md2ToJme();
|
|
||||||
} else if (type.equals("md3")) {
|
|
||||||
convert = new Md3ToJme();
|
|
||||||
} else if (type.equals("ase")) {
|
|
||||||
convert = new AseToJme();
|
|
||||||
} else {
|
|
||||||
System.err.println("Unknown model type '" + type + "'.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// and do the deed
|
|
||||||
try {
|
|
||||||
BufferedOutputStream bout = new BufferedOutputStream(
|
|
||||||
new FileOutputStream(target));
|
|
||||||
BufferedInputStream bin = new BufferedInputStream(
|
|
||||||
new FileInputStream(source));
|
|
||||||
convert.convert(bin, bout);
|
|
||||||
bout.close();
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
System.err.println("Error converting '" + source +
|
|
||||||
"' to '" + target + "'.");
|
|
||||||
ioe.printStackTrace(System.err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A list of filesets that contain tileset bundle definitions. */
|
|
||||||
protected ArrayList _filesets = new ArrayList();
|
|
||||||
}
|
|
||||||
@@ -1,573 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2005 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.tools;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.ByteOrder;
|
|
||||||
import java.nio.FloatBuffer;
|
|
||||||
import java.nio.IntBuffer;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import com.jme.bounding.BoundingBox;
|
|
||||||
import com.jme.bounding.BoundingSphere;
|
|
||||||
import com.jme.math.FastMath;
|
|
||||||
import com.jme.scene.Spatial;
|
|
||||||
import com.jme.util.geom.BufferUtils;
|
|
||||||
|
|
||||||
import com.samskivert.util.PropertiesUtil;
|
|
||||||
import com.samskivert.util.StringUtil;
|
|
||||||
|
|
||||||
import com.threerings.jme.Log;
|
|
||||||
import com.threerings.jme.model.Model;
|
|
||||||
import com.threerings.jme.model.ModelController;
|
|
||||||
import com.threerings.jme.model.ModelMesh;
|
|
||||||
import com.threerings.jme.model.ModelNode;
|
|
||||||
import com.threerings.jme.model.SkinMesh;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An intermediate representation for models used to store data parsed from
|
|
||||||
* XML and convert it into JME nodes.
|
|
||||||
*/
|
|
||||||
public class ModelDef
|
|
||||||
{
|
|
||||||
/** The base class of nodes in the model. */
|
|
||||||
public abstract static class SpatialDef
|
|
||||||
{
|
|
||||||
/** The node's name. */
|
|
||||||
public String name;
|
|
||||||
|
|
||||||
/** The name of the node's parent. */
|
|
||||||
public String parent;
|
|
||||||
|
|
||||||
/** The node's transformation. */
|
|
||||||
public float[] translation;
|
|
||||||
public float[] rotation;
|
|
||||||
public float[] scale;
|
|
||||||
|
|
||||||
/** Returns a JME node for this definition. */
|
|
||||||
public Spatial getSpatial (Properties props)
|
|
||||||
{
|
|
||||||
if (_spatial == null) {
|
|
||||||
_spatial = createSpatial(new NodeProperties(props, name));
|
|
||||||
setTransform();
|
|
||||||
}
|
|
||||||
return _spatial;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Sets the transform of the created node. */
|
|
||||||
protected void setTransform ()
|
|
||||||
{
|
|
||||||
_spatial.getLocalTranslation().set(translation[0], translation[1],
|
|
||||||
translation[2]);
|
|
||||||
_spatial.getLocalRotation().set(rotation[0], rotation[1],
|
|
||||||
rotation[2], rotation[3]);
|
|
||||||
_spatial.getLocalScale().set(scale[0], scale[1], scale[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Creates a JME node for this definition. */
|
|
||||||
public abstract Spatial createSpatial (Properties props);
|
|
||||||
|
|
||||||
/** Resolves any name references using the supplied map. */
|
|
||||||
public void resolveReferences (
|
|
||||||
HashMap<String, Spatial> nodes, HashSet<Spatial> referenced)
|
|
||||||
{
|
|
||||||
Spatial pnode = nodes.get(parent);
|
|
||||||
if (pnode instanceof ModelNode) {
|
|
||||||
((ModelNode)pnode).attachChild(_spatial);
|
|
||||||
|
|
||||||
} else if (parent != null) {
|
|
||||||
Log.warning("Missing or invalid parent node [spatial=" +
|
|
||||||
name + ", parent=" + parent + "].");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The JME node created for this definition. */
|
|
||||||
protected Spatial _spatial;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A rigid triangle mesh. */
|
|
||||||
public static class TriMeshDef extends SpatialDef
|
|
||||||
{
|
|
||||||
/** The geometry offset transform. */
|
|
||||||
public float[] offsetTranslation;
|
|
||||||
public float[] offsetRotation;
|
|
||||||
public float[] offsetScale;
|
|
||||||
|
|
||||||
/** Whether or not the mesh allows back face culling. */
|
|
||||||
public boolean solid;
|
|
||||||
|
|
||||||
/** The texture of the mesh, if any. */
|
|
||||||
public String texture;
|
|
||||||
|
|
||||||
/** Whether or not the mesh is (partially) transparent. */
|
|
||||||
public boolean transparent;
|
|
||||||
|
|
||||||
/** The vertices of the mesh. */
|
|
||||||
public ArrayList<Vertex> vertices = new ArrayList<Vertex>();
|
|
||||||
|
|
||||||
/** The triangle indices. */
|
|
||||||
public ArrayList<Integer> indices = new ArrayList<Integer>();
|
|
||||||
|
|
||||||
/** Whether or not any of the vertices have texture coordinates. */
|
|
||||||
public boolean tcoords;
|
|
||||||
|
|
||||||
public void addVertex (Vertex vertex)
|
|
||||||
{
|
|
||||||
int idx = vertices.indexOf(vertex);
|
|
||||||
if (idx != -1) {
|
|
||||||
indices.add(idx);
|
|
||||||
} else {
|
|
||||||
indices.add(vertices.size());
|
|
||||||
vertices.add(vertex);
|
|
||||||
}
|
|
||||||
tcoords = tcoords || vertex.tcoords != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public Spatial createSpatial (Properties props)
|
|
||||||
{
|
|
||||||
ModelNode node = new ModelNode(name);
|
|
||||||
if (indices.size() > 0) {
|
|
||||||
_mesh = createMesh();
|
|
||||||
configureMesh(props);
|
|
||||||
node.attachChild(_mesh);
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Creates the mesh to attach to the node. */
|
|
||||||
protected ModelMesh createMesh ()
|
|
||||||
{
|
|
||||||
return new ModelMesh("mesh");
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Configures the mesh. */
|
|
||||||
protected void configureMesh (Properties props)
|
|
||||||
{
|
|
||||||
// set the geometry offset
|
|
||||||
if (offsetTranslation != null) {
|
|
||||||
_mesh.getLocalTranslation().set(offsetTranslation[0],
|
|
||||||
offsetTranslation[1], offsetTranslation[2]);
|
|
||||||
}
|
|
||||||
if (offsetRotation != null) {
|
|
||||||
_mesh.getLocalRotation().set(offsetRotation[0],
|
|
||||||
offsetRotation[1], offsetRotation[2], offsetRotation[3]);
|
|
||||||
}
|
|
||||||
if (offsetScale != null) {
|
|
||||||
_mesh.getLocalScale().set(offsetScale[0], offsetScale[1],
|
|
||||||
offsetScale[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure texture is just a filename
|
|
||||||
int sidx = (texture == null) ? -1 :
|
|
||||||
Math.max(texture.lastIndexOf('/'), texture.lastIndexOf('\\'));
|
|
||||||
if (sidx != -1) {
|
|
||||||
texture = texture.substring(sidx + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// configure using properties
|
|
||||||
_mesh.configure(solid, texture, transparent, props);
|
|
||||||
|
|
||||||
// set the various buffers
|
|
||||||
int vsize = vertices.size();
|
|
||||||
ByteOrder no = ByteOrder.nativeOrder();
|
|
||||||
ByteBuffer vbbuf = ByteBuffer.allocateDirect(vsize*3*4).order(no),
|
|
||||||
nbbuf = ByteBuffer.allocateDirect(vsize*3*4).order(no),
|
|
||||||
tbbuf = tcoords ?
|
|
||||||
ByteBuffer.allocateDirect(vsize*2*4).order(no) : null,
|
|
||||||
ibbuf = ByteBuffer.allocateDirect(indices.size()*4).order(no);
|
|
||||||
FloatBuffer vbuf = vbbuf.asFloatBuffer(),
|
|
||||||
nbuf = nbbuf.asFloatBuffer(),
|
|
||||||
tbuf = tcoords ? tbbuf.asFloatBuffer() : null;
|
|
||||||
for (int ii = 0; ii < vsize; ii++) {
|
|
||||||
vertices.get(ii).setInBuffers(vbuf, nbuf, tbuf);
|
|
||||||
}
|
|
||||||
IntBuffer ibuf = ibbuf.asIntBuffer();
|
|
||||||
for (int ii = 0, nn = indices.size(); ii < nn; ii++) {
|
|
||||||
ibuf.put(indices.get(ii));
|
|
||||||
}
|
|
||||||
_mesh.reconstruct(vbbuf, nbbuf, null, tbbuf, ibbuf);
|
|
||||||
|
|
||||||
_mesh.setModelBound("sphere".equals(props.getProperty("bound")) ?
|
|
||||||
new BoundingSphere() : new BoundingBox());
|
|
||||||
_mesh.updateModelBound();
|
|
||||||
|
|
||||||
// set the mesh's origin to the center of its bounding box
|
|
||||||
_mesh.centerVertices();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The mesh that contains the actual geometry. */
|
|
||||||
protected ModelMesh _mesh;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A triangle mesh that deforms according to bone positions. */
|
|
||||||
public static class SkinMeshDef extends TriMeshDef
|
|
||||||
{
|
|
||||||
@Override // documentation inherited
|
|
||||||
protected ModelMesh createMesh ()
|
|
||||||
{
|
|
||||||
return new SkinMesh("mesh");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
public void resolveReferences (
|
|
||||||
HashMap<String, Spatial> nodes, HashSet<Spatial> referenced)
|
|
||||||
{
|
|
||||||
super.resolveReferences(nodes, referenced);
|
|
||||||
if (_mesh == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// create and set the final weight groups
|
|
||||||
SkinMesh.WeightGroup[] wgroups =
|
|
||||||
new SkinMesh.WeightGroup[_groups.size()];
|
|
||||||
HashMap<String, SkinMesh.Bone> bones =
|
|
||||||
new HashMap<String, SkinMesh.Bone>();
|
|
||||||
int ii = 0;
|
|
||||||
for (Map.Entry<Set<String>, WeightGroupDef> entry :
|
|
||||||
_groups.entrySet()) {
|
|
||||||
SkinMesh.WeightGroup wgroup = new SkinMesh.WeightGroup();
|
|
||||||
wgroup.vertexCount = entry.getValue().indices.size();
|
|
||||||
wgroup.bones = new SkinMesh.Bone[entry.getKey().size()];
|
|
||||||
int jj = 0;
|
|
||||||
for (String bname : entry.getKey()) {
|
|
||||||
SkinMesh.Bone bone = bones.get(bname);
|
|
||||||
if (bone == null) {
|
|
||||||
Spatial node = nodes.get(bname);
|
|
||||||
bones.put(bname,
|
|
||||||
bone = new SkinMesh.Bone((ModelNode)node));
|
|
||||||
referenced.add(node);
|
|
||||||
}
|
|
||||||
wgroup.bones[jj++] = bone;
|
|
||||||
}
|
|
||||||
wgroup.weights = toArray(entry.getValue().weights);
|
|
||||||
wgroups[ii++] = wgroup;
|
|
||||||
}
|
|
||||||
((SkinMesh)_mesh).setWeightGroups(wgroups);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
protected void configureMesh (Properties props)
|
|
||||||
{
|
|
||||||
// divide the vertices up by weight groups
|
|
||||||
_groups = new HashMap<Set<String>, WeightGroupDef>();
|
|
||||||
for (int ii = 0, nn = vertices.size(); ii < nn; ii++) {
|
|
||||||
SkinVertex svertex = (SkinVertex)vertices.get(ii);
|
|
||||||
Set<String> bones = svertex.boneWeights.keySet();
|
|
||||||
WeightGroupDef group = _groups.get(bones);
|
|
||||||
if (group == null) {
|
|
||||||
_groups.put(bones, group = new WeightGroupDef());
|
|
||||||
}
|
|
||||||
group.indices.add(ii);
|
|
||||||
for (String bone : bones) {
|
|
||||||
group.weights.add(svertex.boneWeights.get(bone).weight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// reorder the vertices by group
|
|
||||||
ArrayList<Vertex> overts = vertices;
|
|
||||||
vertices = new ArrayList<Vertex>();
|
|
||||||
int[] imap = new int[overts.size()];
|
|
||||||
for (Map.Entry<Set<String>, WeightGroupDef> entry :
|
|
||||||
_groups.entrySet()) {
|
|
||||||
for (int idx : entry.getValue().indices) {
|
|
||||||
imap[idx] = vertices.size();
|
|
||||||
vertices.add(overts.get(idx));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int ii = 0, nn = indices.size(); ii < nn; ii++) {
|
|
||||||
indices.set(ii, imap[indices.get(ii)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
super.configureMesh(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The intermediate weight groups, mapped by bone names. */
|
|
||||||
protected HashMap<Set<String>, WeightGroupDef> _groups;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A generic node. */
|
|
||||||
public static class NodeDef extends SpatialDef
|
|
||||||
{
|
|
||||||
// documentation inherited
|
|
||||||
public Spatial createSpatial (Properties props)
|
|
||||||
{
|
|
||||||
return new ModelNode(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A basic vertex. */
|
|
||||||
public static class Vertex
|
|
||||||
{
|
|
||||||
public float[] location;
|
|
||||||
public float[] normal;
|
|
||||||
public float[] tcoords;
|
|
||||||
|
|
||||||
public void setInBuffers (
|
|
||||||
FloatBuffer vbuf, FloatBuffer nbuf, FloatBuffer tbuf)
|
|
||||||
{
|
|
||||||
vbuf.put(location);
|
|
||||||
nbuf.put(normal);
|
|
||||||
|
|
||||||
if (tbuf != null) {
|
|
||||||
if (tcoords != null) {
|
|
||||||
tbuf.put(tcoords);
|
|
||||||
} else {
|
|
||||||
tbuf.put(0f);
|
|
||||||
tbuf.put(0f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean equals (Object obj)
|
|
||||||
{
|
|
||||||
Vertex overt = (Vertex)obj;
|
|
||||||
return Arrays.equals(location, overt.location) &&
|
|
||||||
Arrays.equals(normal, overt.normal) &&
|
|
||||||
Arrays.equals(tcoords, overt.tcoords);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A vertex influenced by a number of bones. */
|
|
||||||
public static class SkinVertex extends Vertex
|
|
||||||
{
|
|
||||||
/** The bones influencing the vertex, mapped by name. */
|
|
||||||
public HashMap<String, BoneWeight> boneWeights =
|
|
||||||
new HashMap<String, BoneWeight>();
|
|
||||||
|
|
||||||
public void addBoneWeight (BoneWeight weight)
|
|
||||||
{
|
|
||||||
if (weight.weight == 0f) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
BoneWeight bweight = boneWeights.get(weight.bone);
|
|
||||||
if (bweight != null) {
|
|
||||||
bweight.weight += weight.weight;
|
|
||||||
} else {
|
|
||||||
boneWeights.put(weight.bone, weight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Finds the bone nodes influencing this vertex. */
|
|
||||||
public HashSet<ModelNode> getBones (HashMap<String, Spatial> nodes)
|
|
||||||
{
|
|
||||||
HashSet<ModelNode> bones = new HashSet<ModelNode>();
|
|
||||||
for (String bone : boneWeights.keySet()) {
|
|
||||||
Spatial node = nodes.get(bone);
|
|
||||||
if (node instanceof ModelNode) {
|
|
||||||
bones.add((ModelNode)node);
|
|
||||||
} else {
|
|
||||||
Log.warning("Missing or invalid bone for bone weight " +
|
|
||||||
"[bone=" + bone + "].");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bones;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the weight of the given bone. */
|
|
||||||
public float getWeight (ModelNode bone)
|
|
||||||
{
|
|
||||||
BoneWeight bweight = boneWeights.get(bone.getName());
|
|
||||||
return (bweight == null) ? 0f : bweight.weight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The influence of a bone on a vertex. */
|
|
||||||
public static class BoneWeight
|
|
||||||
{
|
|
||||||
/** The name of the influencing bone. */
|
|
||||||
public String bone;
|
|
||||||
|
|
||||||
/** The amount of influence. */
|
|
||||||
public float weight;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A group of vertices influenced by the same bone. */
|
|
||||||
public static class WeightGroupDef
|
|
||||||
{
|
|
||||||
/** The indices of the affected vertex. */
|
|
||||||
public ArrayList<Integer> indices = new ArrayList<Integer>();
|
|
||||||
|
|
||||||
/** The interleaved vertex weights. */
|
|
||||||
public ArrayList<Float> weights = new ArrayList<Float>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The meshes and bones comprising the model. */
|
|
||||||
public ArrayList<SpatialDef> spatials = new ArrayList<SpatialDef>();
|
|
||||||
|
|
||||||
public void addSpatial (SpatialDef spatial)
|
|
||||||
{
|
|
||||||
// put nodes before meshes so that bones are updated before skin
|
|
||||||
spatials.add(spatial instanceof NodeDef ? 0 : spatials.size(),
|
|
||||||
spatial);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the model node defined herein.
|
|
||||||
*
|
|
||||||
* @param props the properties of the model
|
|
||||||
* @param nodes a node map to populate
|
|
||||||
*/
|
|
||||||
public Model createModel (Properties props, HashMap<String, Spatial> nodes)
|
|
||||||
{
|
|
||||||
Model model = new Model(props.getProperty("name", "model"), props);
|
|
||||||
|
|
||||||
// set the overall scale
|
|
||||||
model.setLocalScale(Float.parseFloat(props.getProperty("scale", "1")));
|
|
||||||
|
|
||||||
// start by creating the spatials and mapping them to their names
|
|
||||||
for (int ii = 0, nn = spatials.size(); ii < nn; ii++) {
|
|
||||||
Spatial spatial = spatials.get(ii).getSpatial(props);
|
|
||||||
nodes.put(spatial.getName(), spatial);
|
|
||||||
}
|
|
||||||
|
|
||||||
// then go through again, resolving any name references and attaching
|
|
||||||
// root children
|
|
||||||
HashSet<Spatial> referenced = new HashSet<Spatial>();
|
|
||||||
for (int ii = 0, nn = spatials.size(); ii < nn; ii++) {
|
|
||||||
SpatialDef sdef = spatials.get(ii);
|
|
||||||
sdef.resolveReferences(nodes, referenced);
|
|
||||||
if (sdef.getSpatial(props).getParent() == null) {
|
|
||||||
model.attachChild(sdef.getSpatial(props));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// create any controllers listed
|
|
||||||
String[] controllers = StringUtil.parseStringArray(
|
|
||||||
props.getProperty("controllers", ""));
|
|
||||||
for (int ii = 0; ii < controllers.length; ii++) {
|
|
||||||
Spatial target = nodes.get(controllers[ii]);
|
|
||||||
if (target == null) {
|
|
||||||
Log.warning("Missing controller node [name=" +
|
|
||||||
controllers[ii] + "].");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ModelController ctrl = createController(
|
|
||||||
PropertiesUtil.getSubProperties(props, controllers[ii]),
|
|
||||||
target);
|
|
||||||
if (ctrl != null) {
|
|
||||||
model.addController(ctrl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get rid of any nodes that serve no purpose
|
|
||||||
pruneUnusedNodes(model, nodes, referenced);
|
|
||||||
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Creates, configures, and returns a model controller. */
|
|
||||||
protected ModelController createController (
|
|
||||||
Properties props, Spatial target)
|
|
||||||
{
|
|
||||||
// attempt to create an instance of the controller
|
|
||||||
ModelController ctrl;
|
|
||||||
String cname = props.getProperty("class", "");
|
|
||||||
try {
|
|
||||||
ctrl = (ModelController)Class.forName(cname).newInstance();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.warning("Error instantiating controller [class=" + cname +
|
|
||||||
", error=" + e + "].");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
ctrl.configure(props, target);
|
|
||||||
return ctrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Recursively removes any unused nodes. */
|
|
||||||
protected boolean pruneUnusedNodes (
|
|
||||||
ModelNode node, HashMap<String, Spatial> nodes,
|
|
||||||
HashSet<Spatial> referenced)
|
|
||||||
{
|
|
||||||
boolean hasValidChildren = false;
|
|
||||||
for (int ii = node.getQuantity() - 1; ii >= 0; ii--) {
|
|
||||||
Spatial child = node.getChild(ii);
|
|
||||||
if (!(child instanceof ModelNode) ||
|
|
||||||
pruneUnusedNodes((ModelNode)child, nodes, referenced)) {
|
|
||||||
hasValidChildren = true;
|
|
||||||
} else {
|
|
||||||
node.detachChildAt(ii);
|
|
||||||
nodes.remove(child.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return referenced.contains(node) || hasValidChildren;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Converts a boxed Integer list to an unboxed int array. */
|
|
||||||
protected static int[] toArray (ArrayList<Integer> list)
|
|
||||||
{
|
|
||||||
int[] array = new int[list.size()];
|
|
||||||
for (int ii = 0, nn = list.size(); ii < nn; ii++) {
|
|
||||||
array[ii] = list.get(ii);
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Converts a boxed Float list to an unboxed float array. */
|
|
||||||
protected static float[] toArray (ArrayList<Float> list)
|
|
||||||
{
|
|
||||||
float[] array = new float[list.size()];
|
|
||||||
for (int ii = 0, nn = list.size(); ii < nn; ii++) {
|
|
||||||
array[ii] = list.get(ii);
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A wrapper for the model properties providing access to the properties
|
|
||||||
* of a node within the model. */
|
|
||||||
protected static class NodeProperties extends Properties
|
|
||||||
{
|
|
||||||
public NodeProperties (Properties mprops, String name)
|
|
||||||
{
|
|
||||||
_mprops = mprops;
|
|
||||||
_prefix = name + ".";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
public String getProperty (String key)
|
|
||||||
{
|
|
||||||
return getProperty(key, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
public String getProperty (String key, String defaultValue)
|
|
||||||
{
|
|
||||||
return _mprops.getProperty(_prefix + key,
|
|
||||||
_mprops.getProperty(key, defaultValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The properties of the model. */
|
|
||||||
protected Properties _mprops;
|
|
||||||
|
|
||||||
/** The node prefix. */
|
|
||||||
protected String _prefix;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,88 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id: SceneParser.java 3749 2005-11-09 04:00:16Z 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.tools.xml;
|
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import org.xml.sax.SAXException;
|
|
||||||
import org.apache.commons.digester.Digester;
|
|
||||||
|
|
||||||
import com.samskivert.xml.SetPropertyFieldsRule;
|
|
||||||
|
|
||||||
import com.threerings.jme.tools.AnimationDef;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses XML files containing animations.
|
|
||||||
*/
|
|
||||||
public class AnimationParser
|
|
||||||
{
|
|
||||||
public AnimationParser ()
|
|
||||||
{
|
|
||||||
// create and configure our digester
|
|
||||||
_digester = new Digester();
|
|
||||||
|
|
||||||
// add the rules
|
|
||||||
String anim = "animation";
|
|
||||||
_digester.addObjectCreate(anim, AnimationDef.class.getName());
|
|
||||||
_digester.addRule(anim, new SetPropertyFieldsRule());
|
|
||||||
_digester.addSetNext(anim, "setAnimation",
|
|
||||||
AnimationDef.class.getName());
|
|
||||||
|
|
||||||
String frame = anim + "/frame";
|
|
||||||
_digester.addObjectCreate(frame,
|
|
||||||
AnimationDef.FrameDef.class.getName());
|
|
||||||
_digester.addSetNext(frame, "addFrame",
|
|
||||||
AnimationDef.FrameDef.class.getName());
|
|
||||||
|
|
||||||
String xform = frame + "/transform";
|
|
||||||
_digester.addObjectCreate(xform,
|
|
||||||
AnimationDef.TransformDef.class.getName());
|
|
||||||
_digester.addRule(xform, new SetPropertyFieldsRule());
|
|
||||||
_digester.addSetNext(xform, "addTransform",
|
|
||||||
AnimationDef.TransformDef.class.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses the XML file at the specified path into an animation
|
|
||||||
* definition.
|
|
||||||
*/
|
|
||||||
public AnimationDef parseAnimation (String path)
|
|
||||||
throws IOException, SAXException
|
|
||||||
{
|
|
||||||
_animation = null;
|
|
||||||
_digester.push(this);
|
|
||||||
_digester.parse(new FileInputStream(path));
|
|
||||||
return _animation;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by the parser once the animation is parsed.
|
|
||||||
*/
|
|
||||||
public void setAnimation (AnimationDef animation)
|
|
||||||
{
|
|
||||||
_animation = animation;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Digester _digester;
|
|
||||||
protected AnimationDef _animation;
|
|
||||||
}
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id: SceneParser.java 3749 2005-11-09 04:00:16Z 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.tools.xml;
|
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import org.xml.sax.SAXException;
|
|
||||||
import org.apache.commons.digester.Digester;
|
|
||||||
|
|
||||||
import com.samskivert.xml.SetPropertyFieldsRule;
|
|
||||||
|
|
||||||
import com.threerings.jme.tools.ModelDef;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses XML files containing 3D models.
|
|
||||||
*/
|
|
||||||
public class ModelParser
|
|
||||||
{
|
|
||||||
public ModelParser ()
|
|
||||||
{
|
|
||||||
// create and configure our digester
|
|
||||||
_digester = new Digester();
|
|
||||||
|
|
||||||
// add the rules
|
|
||||||
String model = "model";
|
|
||||||
_digester.addObjectCreate(model, ModelDef.class.getName());
|
|
||||||
_digester.addSetNext(model, "setModel", ModelDef.class.getName());
|
|
||||||
|
|
||||||
String tmesh = model + "/triMesh";
|
|
||||||
_digester.addObjectCreate(tmesh, ModelDef.TriMeshDef.class.getName());
|
|
||||||
_digester.addRule(tmesh, new SetPropertyFieldsRule());
|
|
||||||
_digester.addSetNext(tmesh, "addSpatial",
|
|
||||||
ModelDef.SpatialDef.class.getName());
|
|
||||||
|
|
||||||
String smesh = model + "/skinMesh";
|
|
||||||
_digester.addObjectCreate(smesh,
|
|
||||||
ModelDef.SkinMeshDef.class.getName());
|
|
||||||
_digester.addRule(smesh, new SetPropertyFieldsRule());
|
|
||||||
_digester.addSetNext(smesh, "addSpatial",
|
|
||||||
ModelDef.SpatialDef.class.getName());
|
|
||||||
|
|
||||||
String node = model + "/node";
|
|
||||||
_digester.addObjectCreate(node, ModelDef.NodeDef.class.getName());
|
|
||||||
_digester.addRule(node, new SetPropertyFieldsRule());
|
|
||||||
_digester.addSetNext(node, "addSpatial",
|
|
||||||
ModelDef.SpatialDef.class.getName());
|
|
||||||
|
|
||||||
String vertex = tmesh + "/vertex", svertex = smesh + "/vertex";
|
|
||||||
_digester.addObjectCreate(vertex, ModelDef.Vertex.class.getName());
|
|
||||||
_digester.addObjectCreate(svertex,
|
|
||||||
ModelDef.SkinVertex.class.getName());
|
|
||||||
_digester.addRule(vertex, new SetPropertyFieldsRule());
|
|
||||||
_digester.addRule(svertex, new SetPropertyFieldsRule());
|
|
||||||
_digester.addSetNext(vertex, "addVertex",
|
|
||||||
ModelDef.Vertex.class.getName());
|
|
||||||
_digester.addSetNext(svertex, "addVertex",
|
|
||||||
ModelDef.Vertex.class.getName());
|
|
||||||
|
|
||||||
String bweight = smesh + "/vertex/boneWeight";
|
|
||||||
_digester.addObjectCreate(bweight,
|
|
||||||
ModelDef.BoneWeight.class.getName());
|
|
||||||
_digester.addRule(bweight, new SetPropertyFieldsRule());
|
|
||||||
_digester.addSetNext(bweight, "addBoneWeight",
|
|
||||||
ModelDef.BoneWeight.class.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses the XML file at the specified path into a model definition.
|
|
||||||
*/
|
|
||||||
public ModelDef parseModel (String path)
|
|
||||||
throws IOException, SAXException
|
|
||||||
{
|
|
||||||
_model = null;
|
|
||||||
_digester.push(this);
|
|
||||||
_digester.parse(new FileInputStream(path));
|
|
||||||
return _model;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by the parser once the model is parsed.
|
|
||||||
*/
|
|
||||||
public void setModel (ModelDef model)
|
|
||||||
{
|
|
||||||
_model = model;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Digester _digester;
|
|
||||||
protected ModelDef _model;
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id: LinearTimeFunction.java 3122 2004-09-18 22:57:08Z mdb $
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2006 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.util;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Varies a value linearly with time.
|
|
||||||
*/
|
|
||||||
public class LinearTimeFunction extends TimeFunction
|
|
||||||
{
|
|
||||||
public LinearTimeFunction (float start, float end, float duration)
|
|
||||||
{
|
|
||||||
super(start, end, duration);
|
|
||||||
_range = (end - start);
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
protected float computeValue ()
|
|
||||||
{
|
|
||||||
return (_elapsed * _range / _duration) + _start;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected float _range;
|
|
||||||
}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id: TimeFunction.java 3122 2004-09-18 22:57:08Z mdb $
|
|
||||||
//
|
|
||||||
// Narya library - tools for developing networked games
|
|
||||||
// Copyright (C) 2002-2006 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.util;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to vary a value over time where time is provided at discrete increments
|
|
||||||
* (on the frame tick) and the value is computed appropriately. This can be
|
|
||||||
* used for fades and other effects where different functions (linear, ease-in
|
|
||||||
* ease-out, etc.) should be easy to plug in.
|
|
||||||
*/
|
|
||||||
public abstract class TimeFunction
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Every time function varies a value from some starting value to some
|
|
||||||
* ending value over some duration. The way in which it varies
|
|
||||||
* (linearly, for example) is up to the derived class.
|
|
||||||
*
|
|
||||||
* @param start the starting value.
|
|
||||||
* @param end the ending value.
|
|
||||||
* @param duration the duration in seconds.
|
|
||||||
*/
|
|
||||||
public TimeFunction (float start, float end, float duration)
|
|
||||||
{
|
|
||||||
_start = start;
|
|
||||||
_end = end;
|
|
||||||
_duration = duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current value given the supplied elapsed time. The value
|
|
||||||
* will be bounded to the originally supplied starting and ending values at
|
|
||||||
* times 0 (and below) and {@link #_duration} (and above) respectively.
|
|
||||||
*
|
|
||||||
* @param deltaTime the amount of time that has elapsed since the last call
|
|
||||||
* to this method.
|
|
||||||
*/
|
|
||||||
public float getValue (float deltaTime)
|
|
||||||
{
|
|
||||||
_elapsed += deltaTime;
|
|
||||||
|
|
||||||
if (_elapsed <= 0) {
|
|
||||||
return _start;
|
|
||||||
} else if (_elapsed >= _duration) {
|
|
||||||
return _end;
|
|
||||||
} else {
|
|
||||||
return computeValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if this function has proceeded the full length of its
|
|
||||||
* duration. This should be called after a call to {@link #getValue} has
|
|
||||||
* been made to update our internal elapsed time.
|
|
||||||
*/
|
|
||||||
public boolean isComplete ()
|
|
||||||
{
|
|
||||||
return _elapsed >= _duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Generates a string representation of this instance. */
|
|
||||||
public String toString ()
|
|
||||||
{
|
|
||||||
return _start + " to " + _end + ", " + _elapsed + " of " + _duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This must be implemented by our derived class to compute our value given
|
|
||||||
* the currently stored {@link #_elapsed} time.
|
|
||||||
*/
|
|
||||||
protected abstract float computeValue ();
|
|
||||||
|
|
||||||
/** Our starting and ending values. */
|
|
||||||
protected float _start, _end;
|
|
||||||
|
|
||||||
/** The number of milliseconds over which we vary our value. */
|
|
||||||
protected float _duration;
|
|
||||||
|
|
||||||
/** The number of seconds that have elapsed since we started. */
|
|
||||||
protected float _elapsed;
|
|
||||||
}
|
|
||||||
@@ -1,332 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.media;
|
|
||||||
|
|
||||||
import java.awt.Graphics2D;
|
|
||||||
import java.awt.Rectangle;
|
|
||||||
import java.awt.Shape;
|
|
||||||
|
|
||||||
import java.awt.geom.AffineTransform;
|
|
||||||
import java.awt.geom.PathIterator;
|
|
||||||
import java.awt.geom.Point2D;
|
|
||||||
import java.awt.geom.Rectangle2D;
|
|
||||||
|
|
||||||
import com.samskivert.util.ObserverList;
|
|
||||||
import com.samskivert.util.StringUtil;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Something that can be rendered on the media panel.
|
|
||||||
*/
|
|
||||||
public abstract class AbstractMedia
|
|
||||||
implements Shape
|
|
||||||
{
|
|
||||||
/** A {@link #_renderOrder} value at or above which, indicates that this
|
|
||||||
* media is in the HUD (heads up display) and should not scroll when the
|
|
||||||
* view scrolls. */
|
|
||||||
public static final int HUD_LAYER = 65536;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instantiate an abstract media object.
|
|
||||||
*/
|
|
||||||
public AbstractMedia (Rectangle bounds)
|
|
||||||
{
|
|
||||||
_bounds = bounds;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called periodically by this media's manager to give it
|
|
||||||
* a chance to do its thing.
|
|
||||||
*
|
|
||||||
* @param tickStamp a time stamp associated with this tick.
|
|
||||||
* <em>Note:</em> this is not obtained from a call to {@link
|
|
||||||
* System#currentTimeMillis} and cannot be compared to timestamps
|
|
||||||
* obtained there from.
|
|
||||||
*/
|
|
||||||
public abstract void tick (long tickStamp);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by the appropriate manager to request that the
|
|
||||||
* media render itself with the given graphics context. The
|
|
||||||
* media may wish to inspect the clipping region that has been set
|
|
||||||
* on the graphics context to render itself more efficiently. This
|
|
||||||
* method will only be called after it has been established that this
|
|
||||||
* media's bounds intersect the clipping region.
|
|
||||||
*/
|
|
||||||
public abstract void paint (Graphics2D gfx);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the appropriate media manager has been paused for some
|
|
||||||
* length of time and is then unpaused. Media should adjust any time stamps
|
|
||||||
* that are maintained internally forward by the delta so that time
|
|
||||||
* maintains the illusion of flowing smoothly forward.
|
|
||||||
*/
|
|
||||||
public void fastForward (long timeDelta)
|
|
||||||
{
|
|
||||||
// adjust our first tick stamp
|
|
||||||
_firstTick += timeDelta;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invalidate the media's bounding rectangle for later painting.
|
|
||||||
*/
|
|
||||||
public void invalidate ()
|
|
||||||
{
|
|
||||||
if (_mgr != null) {
|
|
||||||
_mgr.getRegionManager().invalidateRegion(_bounds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the location.
|
|
||||||
*/
|
|
||||||
public void setLocation (int x, int y)
|
|
||||||
{
|
|
||||||
_bounds.x = x;
|
|
||||||
_bounds.y = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a rectangle containing all the pixels rendered by this media.
|
|
||||||
*/
|
|
||||||
public Rectangle getBounds ()
|
|
||||||
{
|
|
||||||
return _bounds;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface Shape
|
|
||||||
public Rectangle2D getBounds2D ()
|
|
||||||
{
|
|
||||||
return _bounds;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface Shape
|
|
||||||
public boolean contains (double x, double y)
|
|
||||||
{
|
|
||||||
return _bounds.contains(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface Shape
|
|
||||||
public boolean contains (Point2D p)
|
|
||||||
{
|
|
||||||
return _bounds.contains(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface Shape
|
|
||||||
public boolean intersects (double x, double y, double w, double h)
|
|
||||||
{
|
|
||||||
return _bounds.intersects(x, y, w, h);
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface Shape
|
|
||||||
public boolean intersects (Rectangle2D r)
|
|
||||||
{
|
|
||||||
return _bounds.intersects(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface Shape
|
|
||||||
public boolean contains (double x, double y, double w, double h)
|
|
||||||
{
|
|
||||||
return _bounds.contains(x, y, w, h);
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface Shape
|
|
||||||
public boolean contains (Rectangle2D r)
|
|
||||||
{
|
|
||||||
return _bounds.contains(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface Shape
|
|
||||||
public PathIterator getPathIterator (AffineTransform at)
|
|
||||||
{
|
|
||||||
return _bounds.getPathIterator(at);
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from interface Shape
|
|
||||||
public PathIterator getPathIterator (AffineTransform at, double flatness)
|
|
||||||
{
|
|
||||||
return _bounds.getPathIterator(at, flatness);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the render order associated with this media. Media
|
|
||||||
* can be rendered in two layers; those with negative render order and
|
|
||||||
* those with positive render order. In the same layer, they
|
|
||||||
* will be rendered according to their render order's cardinal value
|
|
||||||
* (least to greatest). Those with the same render order value will be
|
|
||||||
* rendered in arbitrary order.
|
|
||||||
*
|
|
||||||
* <p>This method may not be called during a tick.
|
|
||||||
*
|
|
||||||
* @see #HUD_LAYER
|
|
||||||
*/
|
|
||||||
public void setRenderOrder (int renderOrder)
|
|
||||||
{
|
|
||||||
if (_renderOrder != renderOrder) {
|
|
||||||
_renderOrder = renderOrder;
|
|
||||||
if (_mgr != null) {
|
|
||||||
_mgr.renderOrderDidChange(this);
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the render order of this media element.
|
|
||||||
*/
|
|
||||||
public int getRenderOrder ()
|
|
||||||
{
|
|
||||||
return _renderOrder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Queues the supplied notification up to be dispatched to this
|
|
||||||
* abstract media's observers.
|
|
||||||
*/
|
|
||||||
public void queueNotification (ObserverList.ObserverOp amop)
|
|
||||||
{
|
|
||||||
if (_observers != null) {
|
|
||||||
if (_mgr != null) {
|
|
||||||
_mgr.queueNotification(_observers, amop);
|
|
||||||
} else {
|
|
||||||
Log.warning("Have no manager, dropping notification " +
|
|
||||||
"[media=" + this + ", op=" + amop + "].");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by the {@link AbstractMediaManager} when we are in a
|
|
||||||
* {@link VirtualMediaPanel} that just scrolled.
|
|
||||||
*/
|
|
||||||
public void viewLocationDidChange (int dx, int dy)
|
|
||||||
{
|
|
||||||
if (_renderOrder >= HUD_LAYER) {
|
|
||||||
setLocation(_bounds.x + dx, _bounds.y + dy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dumps this media to a String object.
|
|
||||||
*/
|
|
||||||
public String toString ()
|
|
||||||
{
|
|
||||||
StringBuilder buf = new StringBuilder();
|
|
||||||
buf.append(StringUtil.shortClassName(this));
|
|
||||||
buf.append("[");
|
|
||||||
toString(buf);
|
|
||||||
return buf.append("]").toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the media.
|
|
||||||
*/
|
|
||||||
protected final void init (AbstractMediaManager manager)
|
|
||||||
{
|
|
||||||
_mgr = manager;
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the media has had its manager set.
|
|
||||||
* Derived classes may override this method, but should be sure to
|
|
||||||
* call <code>super.init()</code>.
|
|
||||||
*/
|
|
||||||
protected void init ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prior to the first call to {@link #tick} on an abstract media, this
|
|
||||||
* method is called by the {@link AbstractMediaManager}. It is called
|
|
||||||
* during the normal tick cycle, immediately prior to the first call
|
|
||||||
* to {@link #tick}.
|
|
||||||
*
|
|
||||||
* <p><em>Note:</em> It is imperative that
|
|
||||||
* <code>super.willStart()</code> is called by any entity that
|
|
||||||
* overrides this method because the {@link AbstractMediaManager}
|
|
||||||
* depends on the setting of the {@link #_firstTick} value to know
|
|
||||||
* whether or not to call this method.
|
|
||||||
*/
|
|
||||||
protected void willStart (long tickStamp)
|
|
||||||
{
|
|
||||||
_firstTick = tickStamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by the media manager after the media is removed from service.
|
|
||||||
* Derived classes may override this method, but should be sure to
|
|
||||||
* call <code>super.shutdown()</code>.
|
|
||||||
*/
|
|
||||||
protected void shutdown ()
|
|
||||||
{
|
|
||||||
invalidate();
|
|
||||||
_mgr = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the specified observer to this media element.
|
|
||||||
*/
|
|
||||||
protected void addObserver (Object obs)
|
|
||||||
{
|
|
||||||
if (_observers == null) {
|
|
||||||
_observers = new ObserverList(ObserverList.FAST_UNSAFE_NOTIFY);
|
|
||||||
}
|
|
||||||
_observers.add(obs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the specified observer from this media element.
|
|
||||||
*/
|
|
||||||
protected void removeObserver (Object obs)
|
|
||||||
{
|
|
||||||
if (_observers != null) {
|
|
||||||
_observers.remove(obs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This should be overridden by derived classes (which should be sure
|
|
||||||
* to call <code>super.toString()</code>) to append the derived class
|
|
||||||
* specific information to the string buffer.
|
|
||||||
*/
|
|
||||||
protected void toString (StringBuilder buf)
|
|
||||||
{
|
|
||||||
buf.append("bounds=").append(StringUtil.toString(_bounds));
|
|
||||||
buf.append(", renderOrder=").append(_renderOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The layer in which to render. */
|
|
||||||
protected int _renderOrder = 0;
|
|
||||||
|
|
||||||
/** The bounds of the media's rendering area. */
|
|
||||||
protected Rectangle _bounds;
|
|
||||||
|
|
||||||
/** Our manager. */
|
|
||||||
protected AbstractMediaManager _mgr;
|
|
||||||
|
|
||||||
/** Our observers. */
|
|
||||||
protected ObserverList _observers = null;
|
|
||||||
|
|
||||||
/** The tick stamp associated with our first call to {@link #tick}.
|
|
||||||
* This is set up automatically in {@link #willStart}. */
|
|
||||||
protected long _firstTick;
|
|
||||||
}
|
|
||||||
@@ -1,335 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.media;
|
|
||||||
|
|
||||||
import java.awt.Graphics2D;
|
|
||||||
import java.awt.Shape;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Comparator;
|
|
||||||
|
|
||||||
import com.samskivert.util.ObserverList.ObserverOp;
|
|
||||||
import com.samskivert.util.ObserverList;
|
|
||||||
import com.samskivert.util.SortableArrayList;
|
|
||||||
import com.samskivert.util.Tuple;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Manages, ticks, and paints {@link AbstractMedia}.
|
|
||||||
*/
|
|
||||||
public abstract class AbstractMediaManager
|
|
||||||
implements MediaConstants
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Default constructor.
|
|
||||||
*/
|
|
||||||
public AbstractMediaManager (MediaPanel panel)
|
|
||||||
{
|
|
||||||
_panel = panel;
|
|
||||||
_remgr = panel.getRegionManager();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns region manager in use by this manager.
|
|
||||||
*/
|
|
||||||
public RegionManager getRegionManager ()
|
|
||||||
{
|
|
||||||
return _remgr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the media panel with which we are coordinating.
|
|
||||||
*/
|
|
||||||
public MediaPanel getMediaPanel ()
|
|
||||||
{
|
|
||||||
return _panel;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Must be called every frame so that the media can be properly
|
|
||||||
* updated.
|
|
||||||
*/
|
|
||||||
public void tick (long tickStamp)
|
|
||||||
{
|
|
||||||
_tickStamp = tickStamp;
|
|
||||||
tickAllMedia(tickStamp);
|
|
||||||
dispatchNotifications();
|
|
||||||
// we clear our tick stamp when we're about to be painted, this
|
|
||||||
// lets us handle situations when yet more media is slipped in
|
|
||||||
// between our being ticked and our being painted
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This will always be called prior to the {@link #paint} calls for a
|
|
||||||
* particular tick. Because it is possible that there will be no dirty
|
|
||||||
* regions and thus no calls to {@link #paint} this method exists so
|
|
||||||
* that the media manager can guarantee that it will be notified when
|
|
||||||
* all ticking is complete and the painting phase has begun.
|
|
||||||
*/
|
|
||||||
public void willPaint ()
|
|
||||||
{
|
|
||||||
// now that we're done ticking, we can safely clear this
|
|
||||||
_tickStamp = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders all registered media in the given layer that intersect
|
|
||||||
* the supplied clipping rectangle to the given graphics context.
|
|
||||||
*
|
|
||||||
* @param layer the layer to render; one of {@link #FRONT}, {@link
|
|
||||||
* #BACK}, or {@link #ALL}. The front layer contains all animations
|
|
||||||
* with a positive render order; the back layer contains all
|
|
||||||
* animations with a negative render order; all, both.
|
|
||||||
*/
|
|
||||||
public void paint (Graphics2D gfx, int layer, Shape clip)
|
|
||||||
{
|
|
||||||
for (int ii = 0, nn = _media.size(); ii < nn; ii++) {
|
|
||||||
AbstractMedia media = (AbstractMedia)_media.get(ii);
|
|
||||||
int order = media.getRenderOrder();
|
|
||||||
try {
|
|
||||||
if (((layer == ALL) ||
|
|
||||||
(layer == FRONT && order >= 0) ||
|
|
||||||
(layer == BACK && order < 0)) &&
|
|
||||||
clip.intersects(media.getBounds())) {
|
|
||||||
media.paint(gfx);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.warning("Failed to render media " +
|
|
||||||
"[media=" + media + ", e=" + e + "].");
|
|
||||||
Log.logStackTrace(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If the manager is paused for some length of time, it should
|
|
||||||
* be fast forwarded by the appropriate number of milliseconds. This
|
|
||||||
* allows media to smoothly pick up where they left off rather than
|
|
||||||
* abruptly jumping into the future, thinking that some outrageous
|
|
||||||
* amount of time passed since their last tick.
|
|
||||||
*/
|
|
||||||
public void fastForward (long timeDelta)
|
|
||||||
{
|
|
||||||
if (_tickStamp > 0) {
|
|
||||||
Log.warning("Egads! Asked to fastForward() during a tick.");
|
|
||||||
Thread.dumpStack();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int ii=0, nn=_media.size(); ii < nn; ii++) {
|
|
||||||
((AbstractMedia) _media.get(ii)).fastForward(timeDelta);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the specified media is being managed by this media
|
|
||||||
* manager.
|
|
||||||
*/
|
|
||||||
public boolean isManaged (AbstractMedia media)
|
|
||||||
{
|
|
||||||
return _media.contains(media);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by a {@link VirtualMediaPanel} when the view that contains our
|
|
||||||
* media is scrolled.
|
|
||||||
*
|
|
||||||
* @param dx the scrolled distance in the x direction (in pixels).
|
|
||||||
* @param dy the scrolled distance in the y direction (in pixels).
|
|
||||||
*/
|
|
||||||
public void viewLocationDidChange (int dx, int dy)
|
|
||||||
{
|
|
||||||
// let our media know
|
|
||||||
for (int ii = 0, ll = _media.size(); ii < ll; ii++) {
|
|
||||||
((AbstractMedia)_media.get(ii)).viewLocationDidChange(dx, dy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by a {@link AbstractMedia} when its render order has changed.
|
|
||||||
*/
|
|
||||||
public void renderOrderDidChange (AbstractMedia media)
|
|
||||||
{
|
|
||||||
if (_tickStamp > 0) {
|
|
||||||
Log.warning("Egads! Render order changed during a tick.");
|
|
||||||
Thread.dumpStack();
|
|
||||||
}
|
|
||||||
|
|
||||||
_media.remove(media);
|
|
||||||
_media.insertSorted(media, RENDER_ORDER);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calls {@link AbstractMedia#tick} on all media to give them a chance
|
|
||||||
* to move about, change their look, generate dirty regions, and so
|
|
||||||
* forth.
|
|
||||||
*/
|
|
||||||
protected void tickAllMedia (long tickStamp)
|
|
||||||
{
|
|
||||||
// we use _tickpos so that it can be adjusted if media is added or
|
|
||||||
// removed during the tick dispatch
|
|
||||||
for (_tickpos = 0; _tickpos < _media.size(); _tickpos++) {
|
|
||||||
tickMedia((AbstractMedia) _media.get(_tickpos), tickStamp);
|
|
||||||
}
|
|
||||||
_tickpos = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts the specified media into this manager, return true on
|
|
||||||
* success.
|
|
||||||
*/
|
|
||||||
protected boolean insertMedia (AbstractMedia media)
|
|
||||||
{
|
|
||||||
if (_media.contains(media)) {
|
|
||||||
Log.warning("Attempt to insert media more than once " +
|
|
||||||
"[media=" + media + "].");
|
|
||||||
Thread.dumpStack();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
media.init(this);
|
|
||||||
int ipos = _media.insertSorted(media, RENDER_ORDER);
|
|
||||||
|
|
||||||
// if we've started our tick but have not yet painted our media,
|
|
||||||
// we need to take care that this newly added media will be ticked
|
|
||||||
// before our upcoming render
|
|
||||||
if (_tickStamp > 0L) {
|
|
||||||
if (_tickpos == -1) {
|
|
||||||
// if we're done with our own call to tick(), we
|
|
||||||
// definitely need to tick this new media
|
|
||||||
tickMedia(media, _tickStamp);
|
|
||||||
} else if (ipos <= _tickpos) {
|
|
||||||
// otherwise, we're in the middle of our call to tick()
|
|
||||||
// and we only need to tick this guy if he's being
|
|
||||||
// inserted before our current tick position (if he's
|
|
||||||
// inserted after our current position, we'll get to him
|
|
||||||
// as part of this tick iteration)
|
|
||||||
_tickpos++;
|
|
||||||
tickMedia(media, _tickStamp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A helper function used to call {@link AbstractMedia#tick}. */
|
|
||||||
protected final void tickMedia (AbstractMedia media, long tickStamp)
|
|
||||||
{
|
|
||||||
if (media._firstTick == 0L) {
|
|
||||||
media.willStart(tickStamp);
|
|
||||||
}
|
|
||||||
media.tick(tickStamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the specified media from this manager, return true on
|
|
||||||
* success.
|
|
||||||
*/
|
|
||||||
protected boolean removeMedia (AbstractMedia media)
|
|
||||||
{
|
|
||||||
int mpos = _media.indexOf(media);
|
|
||||||
if (mpos != -1) {
|
|
||||||
_media.remove(mpos);
|
|
||||||
media.invalidate();
|
|
||||||
media.shutdown();
|
|
||||||
// if we're in the middle of ticking, we need to adjust the
|
|
||||||
// _tickpos if necessary
|
|
||||||
if (mpos <= _tickpos) {
|
|
||||||
_tickpos--;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
Log.warning("Attempt to remove media that wasn't inserted " +
|
|
||||||
"[media=" + media + "].");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears all media from the manager and calls {@link
|
|
||||||
* AbstractMedia#shutdown} on each. This does not invalidate the
|
|
||||||
* media's vacated bounds; it is assumed that it will be ok.
|
|
||||||
*/
|
|
||||||
protected void clearMedia ()
|
|
||||||
{
|
|
||||||
if (_tickStamp > 0) {
|
|
||||||
Log.warning("Egads! Requested to clearMedia() during a tick.");
|
|
||||||
Thread.dumpStack();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int ii=_media.size() - 1; ii >= 0; ii--) {
|
|
||||||
AbstractMedia media = (AbstractMedia) _media.remove(ii);
|
|
||||||
media.shutdown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Queues the notification for dispatching after we've ticked all the
|
|
||||||
* media.
|
|
||||||
*/
|
|
||||||
public void queueNotification (ObserverList observers, ObserverOp event)
|
|
||||||
{
|
|
||||||
_notify.add(new Tuple(observers, event));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dispatches all queued media notifications.
|
|
||||||
*/
|
|
||||||
protected void dispatchNotifications ()
|
|
||||||
{
|
|
||||||
for (int ii = 0, nn = _notify.size(); ii < nn; ii++) {
|
|
||||||
Tuple tuple = (Tuple)_notify.get(ii);
|
|
||||||
((ObserverList)tuple.left).apply((ObserverOp)tuple.right);
|
|
||||||
}
|
|
||||||
_notify.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The media panel we're working with. */
|
|
||||||
protected MediaPanel _panel;
|
|
||||||
|
|
||||||
/** The region manager. */
|
|
||||||
protected RegionManager _remgr;
|
|
||||||
|
|
||||||
/** List of observers to notify at the end of the tick. */
|
|
||||||
protected ArrayList _notify = new ArrayList();
|
|
||||||
|
|
||||||
/** Our render-order sorted list of media. */
|
|
||||||
protected SortableArrayList _media = new SortableArrayList();
|
|
||||||
|
|
||||||
/** The position in our media list that we're ticking in the middle of
|
|
||||||
* a call to {@link #tick} otherwise -1. */
|
|
||||||
protected int _tickpos = -1;
|
|
||||||
|
|
||||||
/** The tick stamp if the manager is in the midst of a call to {@link
|
|
||||||
* #tick}, else <code>0</code>. */
|
|
||||||
protected long _tickStamp;
|
|
||||||
|
|
||||||
/** Used to sort media by render order. */
|
|
||||||
protected static final Comparator RENDER_ORDER = new Comparator() {
|
|
||||||
public int compare (Object o1, Object o2) {
|
|
||||||
int result = (((AbstractMedia)o1)._renderOrder -
|
|
||||||
((AbstractMedia)o2)._renderOrder);
|
|
||||||
return (result != 0) ? result :
|
|
||||||
// find some other way to keep them stable relative to
|
|
||||||
// each other
|
|
||||||
o1.hashCode() - o2.hashCode();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,502 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.media;
|
|
||||||
|
|
||||||
import java.applet.Applet;
|
|
||||||
|
|
||||||
import java.awt.Component;
|
|
||||||
import java.awt.Graphics2D;
|
|
||||||
import java.awt.Graphics;
|
|
||||||
import java.awt.Image;
|
|
||||||
import java.awt.Rectangle;
|
|
||||||
import java.awt.Window;
|
|
||||||
|
|
||||||
import javax.swing.CellRendererPane;
|
|
||||||
import javax.swing.JComponent;
|
|
||||||
import javax.swing.JEditorPane;
|
|
||||||
import javax.swing.JScrollPane;
|
|
||||||
import javax.swing.JTextField;
|
|
||||||
import javax.swing.RepaintManager;
|
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
|
|
||||||
import com.samskivert.util.ListUtil;
|
|
||||||
import com.samskivert.util.RunAnywhere;
|
|
||||||
import com.samskivert.util.StringUtil;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to get Swing's repainting to jive with our active rendering
|
|
||||||
* strategy.
|
|
||||||
*
|
|
||||||
* @see FrameManager
|
|
||||||
*/
|
|
||||||
public class ActiveRepaintManager extends RepaintManager
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Components that are rooted in this component (which must be a {@link
|
|
||||||
* Window} or an {@link Applet}) will be rendered into the offscreen buffer
|
|
||||||
* managed by the frame manager. Other components will be rendered into
|
|
||||||
* separate offscreen buffers and repainted in the normal Swing manner.
|
|
||||||
*/
|
|
||||||
public ActiveRepaintManager (Component root)
|
|
||||||
{
|
|
||||||
_root = root;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public synchronized void addInvalidComponent (JComponent comp)
|
|
||||||
{
|
|
||||||
Component vroot = null;
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.info("Maybe invalidating " + toString(comp) + ".");
|
|
||||||
}
|
|
||||||
|
|
||||||
// locate the validation root for this component
|
|
||||||
for (Component c = comp; c != null; c = c.getParent()) {
|
|
||||||
// if the component is not part of an active widget hierarcy,
|
|
||||||
// we can stop now; if the component is a cell render pane,
|
|
||||||
// we're apparently supposed to ignore it as wel
|
|
||||||
if (!c.isDisplayable() || c instanceof CellRendererPane) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip non-Swing components
|
|
||||||
if (!(c instanceof JComponent)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we find our validate root, we can stop looking; NOTE:
|
|
||||||
// JTextField incorrectly claims to be a validate root thereby
|
|
||||||
// fucking up the program something serious; we jovially
|
|
||||||
// ignore it's claims here and restore order to the universe;
|
|
||||||
// see bug #403550 for more fallout from Sun's fuckup
|
|
||||||
if (!(c instanceof JTextField) &&
|
|
||||||
!(c instanceof JScrollPane) &&
|
|
||||||
((JComponent)c).isValidateRoot()) {
|
|
||||||
vroot = c;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we found no validation root we can abort as this component
|
|
||||||
// is not part of any valid widget hierarchy
|
|
||||||
if (vroot == null) {
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.info("Skipping vrootless component: " + toString(comp));
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure that the component is actually in a window or applet
|
|
||||||
// that is showing
|
|
||||||
if (getRoot(vroot) == null) {
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.info("Skipping rootless component " +
|
|
||||||
"[comp=" + toString(comp) +
|
|
||||||
", vroot=" + toString(vroot) + "].");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add the invalid component to our list and we'll validate it on
|
|
||||||
// the next frame
|
|
||||||
if (!ListUtil.containsRef(_invalid, vroot)) {
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.info("Invalidating " + toString(vroot) + ".");
|
|
||||||
}
|
|
||||||
_invalid = ListUtil.add(_invalid, vroot);
|
|
||||||
|
|
||||||
// on the mac, components frequently do not repaint themselves
|
|
||||||
// after being invalidated so we have to force a repaint from
|
|
||||||
// the validation roon on down
|
|
||||||
if (RunAnywhere.isMacOS() && vroot instanceof JComponent) {
|
|
||||||
addDirtyRegion((JComponent)vroot, 0, 0,
|
|
||||||
vroot.getWidth(), vroot.getHeight());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public synchronized void addDirtyRegion (
|
|
||||||
JComponent comp, int x, int y, int width, int height)
|
|
||||||
{
|
|
||||||
// ignore invalid requests
|
|
||||||
if ((width <= 0) || (height <= 0) || (comp == null) ||
|
|
||||||
(comp.getWidth() <= 0) || (comp.getHeight() <= 0)) {
|
|
||||||
// Log.info("Skipping bogus region " + comp.getClass().getName() +
|
|
||||||
// ", x=" + x + ", y=" + y + ", width=" + width + ", height=" + height + ".");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if this component is already dirty, simply expand their
|
|
||||||
// existing dirty rectangle
|
|
||||||
Rectangle drect = (Rectangle)_dirty.get(comp);
|
|
||||||
if (drect != null) {
|
|
||||||
drect.add(x, y);
|
|
||||||
drect.add(x+width, y+height);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure this component has a valid root
|
|
||||||
if (getRoot(comp) == null) {
|
|
||||||
// Log.info("Skipping rootless repaint " + comp + ".");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.info("Dirtying component [comp=" + toString(comp) +
|
|
||||||
", drect=" + StringUtil.toString(
|
|
||||||
new Rectangle(x, y, width, height)) + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we made it this far, we can queue up a dirty region for this
|
|
||||||
// component to be repainted on the next tick
|
|
||||||
_dirty.put(comp, new Rectangle(x, y, width, height));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the root component for the supplied component or null if it
|
|
||||||
* is not part of a rooted hierarchy or if any parent along the way is
|
|
||||||
* found to be hidden or without a peer.
|
|
||||||
*/
|
|
||||||
protected Component getRoot (Component comp)
|
|
||||||
{
|
|
||||||
for (Component c = comp; c != null; c = c.getParent()) {
|
|
||||||
boolean hidden = !c.isDisplayable();
|
|
||||||
// on the mac, the JRootPane is invalidated before it is
|
|
||||||
// visible and is never again invalidated or repainted, so we
|
|
||||||
// punt and allow all invisible components to be invalidated
|
|
||||||
// and revalidated
|
|
||||||
if (!RunAnywhere.isMacOS()) {
|
|
||||||
hidden |= !c.isVisible();
|
|
||||||
}
|
|
||||||
if (hidden) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (c instanceof Window || c instanceof Applet) {
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public synchronized Rectangle getDirtyRegion (JComponent comp)
|
|
||||||
{
|
|
||||||
Rectangle drect = (Rectangle)_dirty.get(comp);
|
|
||||||
// copy the rectangle if we found one, otherwise create an empty
|
|
||||||
// rectangle because we don't want them leaving empty handed
|
|
||||||
return (drect == null) ?
|
|
||||||
new Rectangle(0, 0, 0, 0) : new Rectangle(drect);
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public synchronized void markCompletelyClean (JComponent comp)
|
|
||||||
{
|
|
||||||
_dirty.remove(comp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates the invalid components that have been queued up since the
|
|
||||||
* last frame tick.
|
|
||||||
*/
|
|
||||||
public void validateComponents ()
|
|
||||||
{
|
|
||||||
// swap out our invalid array
|
|
||||||
Object[] invalid = null;
|
|
||||||
synchronized (this) {
|
|
||||||
invalid = _invalid;
|
|
||||||
_invalid = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if there's nothing to validate, we're home free
|
|
||||||
if (invalid == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate everything therein
|
|
||||||
int icount = invalid.length;
|
|
||||||
for (int ii = 0; ii < icount; ii++) {
|
|
||||||
if (invalid[ii] != null) {
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.info("Validating " + invalid[ii]);
|
|
||||||
}
|
|
||||||
((Component)invalid[ii]).validate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Paints the components that have become dirty since the last tick.
|
|
||||||
*
|
|
||||||
* @return true if any components were painted.
|
|
||||||
*/
|
|
||||||
public boolean paintComponents (Graphics g, FrameManager fmgr)
|
|
||||||
{
|
|
||||||
synchronized (this) {
|
|
||||||
// exit now if there are no dirty rectangles to paint
|
|
||||||
if (_dirty.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise, swap our hashmaps
|
|
||||||
HashMap tmap = _spare;
|
|
||||||
_spare = _dirty;
|
|
||||||
_dirty = tmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
// scan through the list, looking for components for whom a parent
|
|
||||||
// component is also dirty. in such a case, the dirty rectangle
|
|
||||||
// for the parent component is expanded to contain the dirty
|
|
||||||
// rectangle of the child and the child is removed from the
|
|
||||||
// repaint list (painting the parent will repaint the child)
|
|
||||||
Iterator iter = _spare.entrySet().iterator();
|
|
||||||
PRUNE:
|
|
||||||
while (iter.hasNext()) {
|
|
||||||
Entry entry = (Entry)iter.next();
|
|
||||||
JComponent comp = (JComponent)entry.getKey();
|
|
||||||
Rectangle drect = (Rectangle)entry.getValue();
|
|
||||||
int x = comp.getX() + drect.x, y = comp.getY() + drect.y;
|
|
||||||
|
|
||||||
// climb up the parent hierarchy, looking for the first opaque
|
|
||||||
// parent as well as the root component
|
|
||||||
for (Component c = comp.getParent(); c != null; c = c.getParent()) {
|
|
||||||
// stop looking for combinable parents for non-visible or
|
|
||||||
// non-JComponents
|
|
||||||
if (!c.isVisible() || !c.isDisplayable() ||
|
|
||||||
!(c instanceof JComponent)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check to see if this parent is dirty
|
|
||||||
Rectangle prect = (Rectangle)_spare.get(c);
|
|
||||||
if (prect != null) {
|
|
||||||
// that we were going to merge it with its parent and
|
|
||||||
// blow it away
|
|
||||||
drect.x = x;
|
|
||||||
drect.y = y;
|
|
||||||
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.info("Found dirty parent " +
|
|
||||||
"[comp=" + toString(comp) +
|
|
||||||
", drect=" + StringUtil.toString(drect) +
|
|
||||||
", pcomp=" + toString(c) +
|
|
||||||
", prect=" + StringUtil.toString(prect) +
|
|
||||||
"].");
|
|
||||||
}
|
|
||||||
prect.add(drect);
|
|
||||||
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.info("New prect " + StringUtil.toString(prect));
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove the child component and be on our way
|
|
||||||
iter.remove();
|
|
||||||
continue PRUNE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// translate the coordinates into this component's
|
|
||||||
// coordinate system
|
|
||||||
x += c.getX();
|
|
||||||
y += c.getY();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// now paint each of the dirty components, by setting the clipping
|
|
||||||
// rectangle appropriately and calling paint() on the associated
|
|
||||||
// root component
|
|
||||||
iter = _spare.entrySet().iterator();
|
|
||||||
while (iter.hasNext()) {
|
|
||||||
Entry entry = (Entry)iter.next();
|
|
||||||
JComponent comp = (JComponent)entry.getKey();
|
|
||||||
Rectangle drect = (Rectangle)entry.getValue();
|
|
||||||
|
|
||||||
// get the root component, adjust the clipping (dirty)
|
|
||||||
// rectangle and obtain the bounds of the client in absolute
|
|
||||||
// coordinates
|
|
||||||
Component root = null, ocomp = null;
|
|
||||||
|
|
||||||
// start with the components bounds which we'll switch to the
|
|
||||||
// opaque parent component's bounds if and when we find one
|
|
||||||
_cbounds.setBounds(0, 0, comp.getWidth(), comp.getHeight());
|
|
||||||
|
|
||||||
// climb up the parent hierarchy, looking for the first opaque
|
|
||||||
// parent as well as the root component
|
|
||||||
for (Component c = comp; c != null; c = c.getParent()) {
|
|
||||||
if (!c.isVisible() || !c.isDisplayable()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c instanceof JComponent) {
|
|
||||||
// make a note of the first opaque parent we find
|
|
||||||
if (ocomp == null && ((JComponent)c).isOpaque()) {
|
|
||||||
ocomp = c;
|
|
||||||
// we need to obtain the opaque parent's coordinates
|
|
||||||
// in the root coordinate system for when we repaint
|
|
||||||
_cbounds.setBounds(
|
|
||||||
0, 0, ocomp.getWidth(), ocomp.getHeight());
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// oh god the hackery. apparently the fscking
|
|
||||||
// JEditorPane wraps a heavy weight component around
|
|
||||||
// every swing component it uses when doing forms
|
|
||||||
Component tp = c.getParent();
|
|
||||||
if (!(tp instanceof JEditorPane)) {
|
|
||||||
root = c;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// translate the coordinates into this component's
|
|
||||||
// coordinate system
|
|
||||||
drect.x += c.getX();
|
|
||||||
drect.y += c.getY();
|
|
||||||
_cbounds.x += c.getX();
|
|
||||||
_cbounds.y += c.getY();
|
|
||||||
|
|
||||||
// clip the dirty region to the bounds of this component
|
|
||||||
SwingUtilities.computeIntersection(
|
|
||||||
c.getX(), c.getY(), c.getWidth(), c.getHeight(), drect);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we found no opaque parent, just paint the component
|
|
||||||
// itself (this seems to happen with the top-level layered
|
|
||||||
// pane)
|
|
||||||
if (ocomp == null) {
|
|
||||||
ocomp = comp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if this component is rooted in our frame, repaint it into
|
|
||||||
// the supplied graphics instance
|
|
||||||
if (root == _root) {
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.info("Repainting [comp=" + toString(comp) +
|
|
||||||
StringUtil.toString(_cbounds) +
|
|
||||||
", ocomp=" + toString(ocomp) +
|
|
||||||
", drect=" + StringUtil.toString(drect) + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
g.setClip(drect);
|
|
||||||
g.translate(_cbounds.x, _cbounds.y);
|
|
||||||
try {
|
|
||||||
// some components are ill-behaved and may throw an
|
|
||||||
// exception while painting themselves, and so we
|
|
||||||
// needs must deal with these fellows gracefully
|
|
||||||
ocomp.paint(g);
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.warning("Exception while painting component " +
|
|
||||||
"[comp=" + ocomp + ", e=" + e + "].");
|
|
||||||
Log.logStackTrace(e);
|
|
||||||
}
|
|
||||||
g.translate(-_cbounds.x, -_cbounds.y);
|
|
||||||
|
|
||||||
// we also need to repaint any components in this layer
|
|
||||||
// that are above our freshly repainted component
|
|
||||||
fmgr.renderLayers((Graphics2D)g, ocomp, _cbounds, _clipped);
|
|
||||||
|
|
||||||
} else if (root != null) {
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.info("Repainting old-school " +
|
|
||||||
"[comp=" + toString(comp) +
|
|
||||||
", ocomp=" + toString(ocomp) +
|
|
||||||
", root=" + toString(root) +
|
|
||||||
", bounds=" + StringUtil.toString(_cbounds) +
|
|
||||||
"].");
|
|
||||||
dumpHierarchy(comp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise, repaint with standard swing double buffers
|
|
||||||
Image obuf = getOffscreenBuffer(
|
|
||||||
ocomp, _cbounds.width, _cbounds.height);
|
|
||||||
Graphics og = null, cg = null;
|
|
||||||
try {
|
|
||||||
og = obuf.getGraphics();
|
|
||||||
ocomp.paint(og);
|
|
||||||
cg = ocomp.getGraphics();
|
|
||||||
cg.drawImage(obuf, 0, 0, null);
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
if (og != null) {
|
|
||||||
og.dispose();
|
|
||||||
}
|
|
||||||
if (cg != null) {
|
|
||||||
cg.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear out the mapping of dirty components
|
|
||||||
_spare.clear();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to dump a component when debugging.
|
|
||||||
*/
|
|
||||||
protected static String toString (Component comp)
|
|
||||||
{
|
|
||||||
return comp.getClass().getName() +
|
|
||||||
StringUtil.toString(comp.getBounds());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dumps the containment hierarchy for the supplied component.
|
|
||||||
*/
|
|
||||||
protected static void dumpHierarchy (Component comp)
|
|
||||||
{
|
|
||||||
for (String indent = ""; comp != null; indent += " ") {
|
|
||||||
Log.info(indent + toString(comp));
|
|
||||||
comp = comp.getParent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The root of our interface. */
|
|
||||||
protected Component _root;
|
|
||||||
|
|
||||||
/** A list of invalid components. */
|
|
||||||
protected Object[] _invalid;
|
|
||||||
|
|
||||||
/** A mapping of invalid rectangles for each widget that is dirty. */
|
|
||||||
protected HashMap _dirty = new HashMap();
|
|
||||||
|
|
||||||
/** A spare hashmap that we swap in while repainting dirty components
|
|
||||||
* in the old hashmap. */
|
|
||||||
protected HashMap _spare = new HashMap();
|
|
||||||
|
|
||||||
/** Used to compute dirty components' bounds. */
|
|
||||||
protected Rectangle _cbounds = new Rectangle();
|
|
||||||
|
|
||||||
/** Used when rendering "layered" components. */
|
|
||||||
protected boolean[] _clipped = new boolean[] { true };
|
|
||||||
|
|
||||||
/** We debug so much that we have to make it easy to enable and
|
|
||||||
* disable debug logging. Yay! */
|
|
||||||
protected static final boolean DEBUG = false;
|
|
||||||
}
|
|
||||||
@@ -1,148 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.media;
|
|
||||||
|
|
||||||
import java.awt.Graphics2D;
|
|
||||||
import java.awt.GraphicsConfiguration;
|
|
||||||
import java.awt.Rectangle;
|
|
||||||
import java.awt.image.VolatileImage;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link FrameManager} extension that uses a volatile off-screen image
|
|
||||||
* to do its rendering.
|
|
||||||
*/
|
|
||||||
public class BackFrameManager extends FrameManager
|
|
||||||
{
|
|
||||||
// documentation inherited
|
|
||||||
protected void paint (long tickStamp)
|
|
||||||
{
|
|
||||||
// start out assuming we can do an incremental render
|
|
||||||
boolean incremental = true;
|
|
||||||
|
|
||||||
do {
|
|
||||||
GraphicsConfiguration gc = _window.getGraphicsConfiguration();
|
|
||||||
|
|
||||||
// create our off-screen buffer if necessary
|
|
||||||
if (_backimg == null || _backimg.getWidth() != _window.getWidth() ||
|
|
||||||
_backimg.getHeight() != _window.getHeight()) {
|
|
||||||
createBackBuffer(gc);
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure our back buffer hasn't disappeared
|
|
||||||
int valres = _backimg.validate(gc);
|
|
||||||
|
|
||||||
// if we've changed resolutions, recreate the buffer
|
|
||||||
if (valres == VolatileImage.IMAGE_INCOMPATIBLE) {
|
|
||||||
Log.info("Back buffer incompatible, recreating.");
|
|
||||||
createBackBuffer(gc);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the image wasn't A-OK, we need to rerender the whole
|
|
||||||
// business rather than just the dirty parts
|
|
||||||
if (valres != VolatileImage.IMAGE_OK) {
|
|
||||||
// Log.info("Lost back buffer, redrawing " + valres);
|
|
||||||
if (_bgfx != null) {
|
|
||||||
_bgfx.dispose();
|
|
||||||
}
|
|
||||||
_bgfx = (Graphics2D)_backimg.getGraphics();
|
|
||||||
if (_fgfx != null) {
|
|
||||||
_fgfx.dispose();
|
|
||||||
_fgfx = null;
|
|
||||||
}
|
|
||||||
incremental = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// dirty everything if we're not incrementally rendering
|
|
||||||
if (!incremental) {
|
|
||||||
_root.getRootPane().revalidate();
|
|
||||||
_root.getRootPane().repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!paint(_bgfx)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we cache our frame's graphics object so that we can avoid
|
|
||||||
// instantiating a new one on every tick
|
|
||||||
if (_fgfx == null) {
|
|
||||||
_fgfx = (Graphics2D)_window.getGraphics();
|
|
||||||
}
|
|
||||||
_fgfx.drawImage(_backimg, 0, 0, null);
|
|
||||||
|
|
||||||
// if we loop through a second time, we'll need to rerender
|
|
||||||
// everything
|
|
||||||
incremental = false;
|
|
||||||
|
|
||||||
} while (_backimg.contentsLost());
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
protected void restoreFromBack (Rectangle dirty)
|
|
||||||
{
|
|
||||||
if (_fgfx == null || _backimg == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Log.info("Restoring from back " + StringUtil.toString(dirty) + ".");
|
|
||||||
_fgfx.setClip(dirty);
|
|
||||||
_fgfx.drawImage(_backimg, 0, 0, null);
|
|
||||||
_fgfx.setClip(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the off-screen buffer used to perform double buffered
|
|
||||||
* rendering of the animated panel.
|
|
||||||
*/
|
|
||||||
protected void createBackBuffer (GraphicsConfiguration gc)
|
|
||||||
{
|
|
||||||
// if we have an old image, clear it out
|
|
||||||
if (_backimg != null) {
|
|
||||||
_backimg.flush();
|
|
||||||
_bgfx.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
// create the offscreen buffer
|
|
||||||
int width = _window.getWidth(), height = _window.getHeight();
|
|
||||||
_backimg = gc.createCompatibleVolatileImage(width, height);
|
|
||||||
|
|
||||||
// fill the back buffer with white
|
|
||||||
_bgfx = (Graphics2D)_backimg.getGraphics();
|
|
||||||
_bgfx.fillRect(0, 0, width, height);
|
|
||||||
|
|
||||||
// clear out our frame graphics in case that became invalid for
|
|
||||||
// the same reasons our back buffer became invalid
|
|
||||||
if (_fgfx != null) {
|
|
||||||
_fgfx.dispose();
|
|
||||||
_fgfx = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log.info("Created back buffer [" + width + "x" + height + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The image used to render off-screen. */
|
|
||||||
protected VolatileImage _backimg;
|
|
||||||
|
|
||||||
/** The graphics object from our back buffer. */
|
|
||||||
protected Graphics2D _bgfx;
|
|
||||||
|
|
||||||
/** The graphics object from our frame. */
|
|
||||||
protected Graphics2D _fgfx;
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.media;
|
|
||||||
|
|
||||||
import java.awt.AWTException;
|
|
||||||
import java.awt.BufferCapabilities;
|
|
||||||
import java.awt.Graphics2D;
|
|
||||||
import java.awt.ImageCapabilities;
|
|
||||||
import java.awt.Rectangle;
|
|
||||||
import java.awt.image.BufferStrategy;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link FrameManager} extension that uses a flip-buffer (via {@link
|
|
||||||
* BufferStrategy} to do its rendering.
|
|
||||||
*/
|
|
||||||
public class FlipFrameManager extends FrameManager
|
|
||||||
{
|
|
||||||
// documentation inherited
|
|
||||||
protected void paint (long tickStamp)
|
|
||||||
{
|
|
||||||
// create our buffer strategy if we don't already have one
|
|
||||||
if (_bufstrat == null) {
|
|
||||||
BufferCapabilities cap = new BufferCapabilities(
|
|
||||||
new ImageCapabilities(true), new ImageCapabilities(true),
|
|
||||||
BufferCapabilities.FlipContents.COPIED);
|
|
||||||
try {
|
|
||||||
_window.createBufferStrategy(2, cap);
|
|
||||||
} catch (AWTException ae) {
|
|
||||||
Log.warning("Failed creating flip bufstrat: " + ae + ".");
|
|
||||||
// fall back to one without custom capabilities
|
|
||||||
_window.createBufferStrategy(2);
|
|
||||||
}
|
|
||||||
_bufstrat = _window.getBufferStrategy();
|
|
||||||
}
|
|
||||||
|
|
||||||
// start out assuming we can do an incremental render
|
|
||||||
boolean incremental = true;
|
|
||||||
|
|
||||||
do {
|
|
||||||
Graphics2D gfx = null;
|
|
||||||
try {
|
|
||||||
gfx = (Graphics2D)_bufstrat.getDrawGraphics();
|
|
||||||
|
|
||||||
// dirty everything if we're not incrementally rendering
|
|
||||||
if (!incremental) {
|
|
||||||
Log.info("Doing non-incremental render; contents lost " +
|
|
||||||
"[lost=" + _bufstrat.contentsLost() +
|
|
||||||
", rest=" + _bufstrat.contentsRestored() + "].");
|
|
||||||
_root.getRootPane().revalidate();
|
|
||||||
_root.getRootPane().repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
// request to paint our participants and components and bail
|
|
||||||
// if they paint nothing
|
|
||||||
if (!paint(gfx)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// flip our buffer to visible
|
|
||||||
_bufstrat.show();
|
|
||||||
|
|
||||||
// if we loop through a second time, we'll need to rerender
|
|
||||||
// everything
|
|
||||||
incremental = false;
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
if (gfx != null) {
|
|
||||||
gfx.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (_bufstrat.contentsLost());
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
protected void restoreFromBack (Rectangle dirty)
|
|
||||||
{
|
|
||||||
// nothing doing
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The buffer strategy used to do our rendering. */
|
|
||||||
protected BufferStrategy _bufstrat;
|
|
||||||
}
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
package com.threerings.media;
|
|
||||||
|
|
||||||
import java.awt.Component;
|
|
||||||
|
|
||||||
import com.threerings.media.FrameManager;
|
|
||||||
import com.threerings.media.FrameParticipant;
|
|
||||||
|
|
||||||
public abstract class FrameInterval
|
|
||||||
implements FrameParticipant
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Constructor - registers the interval as a frame participant
|
|
||||||
*/
|
|
||||||
public FrameInterval (FrameManager mgr)
|
|
||||||
{
|
|
||||||
_mgr = mgr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inhertied from FrameParticipant
|
|
||||||
public Component getComponent ()
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from FrameParticipant
|
|
||||||
public boolean needsPaint ()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited from FrameParticipant
|
|
||||||
public void tick (long tickStamp)
|
|
||||||
{
|
|
||||||
if (_nextTime == -1) {
|
|
||||||
// First time through
|
|
||||||
_nextTime = tickStamp + _initDelay;
|
|
||||||
} else if (tickStamp >= _nextTime) {
|
|
||||||
|
|
||||||
// If we're repeating, set the next time to run, otherwise, reset
|
|
||||||
if (_repeatDelay != 0L) {
|
|
||||||
_nextTime += _repeatDelay;
|
|
||||||
} else {
|
|
||||||
_nextTime = -1;
|
|
||||||
cancel();
|
|
||||||
}
|
|
||||||
expired();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* The main method where your interval should do its work.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public abstract void expired ();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schedule the interval to execute once, after the specified delay.
|
|
||||||
* Supersedes any previous schedule that this Interval may have had.
|
|
||||||
*/
|
|
||||||
public final void schedule (long delay)
|
|
||||||
{
|
|
||||||
schedule(delay, 0L);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schedule the interval to execute repeatedly, with the same delay.
|
|
||||||
* Supersedes any previous schedule that this Interval may have had.
|
|
||||||
*/
|
|
||||||
public final void schedule (long delay, boolean repeat)
|
|
||||||
{
|
|
||||||
schedule(delay, repeat ? delay : 0L);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schedule the interval to execute repeatedly with the specified
|
|
||||||
* initial delay and repeat delay.
|
|
||||||
* Supersedes any previous schedule that this Interval may have had.
|
|
||||||
*/
|
|
||||||
public final void schedule (long initialDelay, long repeatDelay)
|
|
||||||
{
|
|
||||||
if (!_mgr.isRegisteredFrameParticipant(this)) {
|
|
||||||
_mgr.registerFrameParticipant(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
_repeatDelay = repeatDelay;
|
|
||||||
_initDelay = initialDelay;
|
|
||||||
_nextTime = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancel the current schedule, and ensure that any expirations that
|
|
||||||
* are queued up but have not yet run do not run.
|
|
||||||
*/
|
|
||||||
public final void cancel ()
|
|
||||||
{
|
|
||||||
_mgr.removeFrameParticipant(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Time of the next expiration. */
|
|
||||||
protected long _nextTime;
|
|
||||||
|
|
||||||
/** Time between expirations. */
|
|
||||||
protected long _repeatDelay;
|
|
||||||
|
|
||||||
/** Time between expirations. */
|
|
||||||
protected long _initDelay;
|
|
||||||
|
|
||||||
/** The context whose FrameManager we are using. */
|
|
||||||
protected FrameManager _mgr;
|
|
||||||
}
|
|
||||||
@@ -1,846 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.media;
|
|
||||||
|
|
||||||
import java.applet.Applet;
|
|
||||||
|
|
||||||
import java.awt.Component;
|
|
||||||
import java.awt.Graphics2D;
|
|
||||||
import java.awt.GraphicsDevice;
|
|
||||||
import java.awt.KeyEventDispatcher;
|
|
||||||
import java.awt.KeyboardFocusManager;
|
|
||||||
import java.awt.Rectangle;
|
|
||||||
import java.awt.Window;
|
|
||||||
|
|
||||||
import java.awt.event.ComponentEvent;
|
|
||||||
import java.awt.event.ComponentListener;
|
|
||||||
import java.awt.event.KeyEvent;
|
|
||||||
import java.awt.event.WindowEvent;
|
|
||||||
import java.awt.event.WindowListener;
|
|
||||||
|
|
||||||
import java.awt.EventQueue;
|
|
||||||
|
|
||||||
import javax.swing.JLayeredPane;
|
|
||||||
import javax.swing.RepaintManager;
|
|
||||||
import javax.swing.JRootPane;
|
|
||||||
import javax.swing.RootPaneContainer;
|
|
||||||
import javax.swing.event.AncestorEvent;
|
|
||||||
import javax.swing.event.AncestorListener;
|
|
||||||
|
|
||||||
import com.samskivert.swing.RuntimeAdjust;
|
|
||||||
import com.samskivert.util.ListUtil;
|
|
||||||
import com.samskivert.util.RunAnywhere;
|
|
||||||
import com.samskivert.util.StringUtil;
|
|
||||||
|
|
||||||
import com.threerings.media.timer.MediaTimer;
|
|
||||||
import com.threerings.media.timer.SystemMediaTimer;
|
|
||||||
import com.threerings.media.util.TrailingAverage;
|
|
||||||
import com.threerings.util.unsafe.Unsafe;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides a central point from which the computation for each "frame" or tick
|
|
||||||
* can be dispatched. This assumed that the application structures its activity
|
|
||||||
* around the rendering of each frame, which is a common architecture for
|
|
||||||
* games. The animation and sprite support provided by other classes in this
|
|
||||||
* package are structured for use in an application that uses a frame manager
|
|
||||||
* to tick everything once per frame.
|
|
||||||
*
|
|
||||||
* <p> The frame manager goes through a simple two part procedure every frame:
|
|
||||||
*
|
|
||||||
* <ul>
|
|
||||||
* <li> Ticking all of the frame participants: in {@link
|
|
||||||
* FrameParticipant#tick}, any processing that need be performed during this
|
|
||||||
* frame should be performed. Care should be taken not to execute code that
|
|
||||||
* will take unduly long, instead such processing should be broken up so that
|
|
||||||
* it can be performed in small pieces every frame (or performed on a separate
|
|
||||||
* thread with the results safely communicated back to the frame participants
|
|
||||||
* for incorporation into the rendering loop).
|
|
||||||
*
|
|
||||||
* <li> Painting the user interface hierarchy: the top-level component (the
|
|
||||||
* frame) is painted (via a call to {@link JRootPane#paint}) into a flip buffer
|
|
||||||
* (if supported, an off-screen buffer if not). Updates that were computed
|
|
||||||
* during the tick should be rendered in this call to paint. The paint call
|
|
||||||
* will propagate down to all components in the UI hierarchy, some of which may
|
|
||||||
* be {@link FrameParticipant}s and will have prepared themselves for their
|
|
||||||
* upcoming painting in the previous call to {@link
|
|
||||||
* FrameParticipant#tick}. When the call to paint completes, the flip buffer is
|
|
||||||
* flipped and the process starts all over again. </ul>
|
|
||||||
*
|
|
||||||
* <p> The ticking and rendering takes place on the AWT thread so as to
|
|
||||||
* avoid the need for complicated coordination between AWT event handler
|
|
||||||
* code and frame code. However, this means that all AWT (and Swing) event
|
|
||||||
* handlers <em>must not</em> perform any complicated processing. After
|
|
||||||
* each frame, control of the AWT thread is given back to the AWT which
|
|
||||||
* processes all pending AWT events before giving the frame manager an
|
|
||||||
* opportunity to process the next frame. Thus the convenience of
|
|
||||||
* everything running on the AWT thread comes with the price of requiring
|
|
||||||
* that AWT event handlers not block or perform any intensive processing.
|
|
||||||
* In general, this is a sensible structure for an application anyhow, so
|
|
||||||
* this organization tends to be preferable to an organization where the
|
|
||||||
* AWT and frame threads are separate and must tread lightly so as not to
|
|
||||||
* collide.
|
|
||||||
*
|
|
||||||
* <p> Note: the way that <code>JScrollPane</code> goes about improving
|
|
||||||
* performance when scrolling complicated contents cannot work with active
|
|
||||||
* rendering. If you use a <code>JScrollPane</code> in an application that
|
|
||||||
* uses the frame manager, you should either use the provided {@link
|
|
||||||
* SafeScrollPane} or set your scroll panes' viewports to
|
|
||||||
* <code>SIMPLE_SCROLL_MODE</code>.
|
|
||||||
*/
|
|
||||||
public abstract class FrameManager
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Normally, the frame manager will repaint any component in a {@link
|
|
||||||
* JLayeredPane} layer (popups, overlays, etc.) that overlaps a frame
|
|
||||||
* participant on every tick because the frame participant <em>could</em>
|
|
||||||
* have changed underneath the overlay which would require that the overlay
|
|
||||||
* be repainted. If the application knows that the frame participant
|
|
||||||
* beneath the overlay will never change, it can have its overlay implement
|
|
||||||
* this interface and avoid the expense of forcibly fully repainting the
|
|
||||||
* overlay on every frame.
|
|
||||||
*/
|
|
||||||
public static interface SafeLayerComponent
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a frame manager that will use a {@link SystemMediaTimer} to
|
|
||||||
* obtain timing information, which is available on every platform, but
|
|
||||||
* returns inaccurate time stamps on many platforms.
|
|
||||||
*
|
|
||||||
* @see #newInstance(Window, RootPaneContainer, MediaTimer)
|
|
||||||
*/
|
|
||||||
public static FrameManager newInstance (
|
|
||||||
Window window, RootPaneContainer root)
|
|
||||||
{
|
|
||||||
// first try creating a PerfTimer which is the best if we're using
|
|
||||||
// JDK1.4.2
|
|
||||||
MediaTimer timer = null;
|
|
||||||
try {
|
|
||||||
timer = (MediaTimer)Class.forName(PERF_TIMER).newInstance();
|
|
||||||
} catch (Throwable t) {
|
|
||||||
Log.info("Can't use PerfTimer (" + t + ") reverting to " +
|
|
||||||
"System.currentTimeMillis() based timer.");
|
|
||||||
timer = new SystemMediaTimer();
|
|
||||||
}
|
|
||||||
return newInstance(window, root, timer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a frame manager that will do its rendering to the
|
|
||||||
* supplied frame. It is likely that the caller will want to have put
|
|
||||||
* the frame into full-screen exclusive mode prior to providing it to
|
|
||||||
* the frame manager so that the frame manager can take advantage of
|
|
||||||
* optimizations available in that mode.
|
|
||||||
*
|
|
||||||
* @see GraphicsDevice#setFullScreenWindow
|
|
||||||
*/
|
|
||||||
public static FrameManager newInstance (
|
|
||||||
Window window, RootPaneContainer root, MediaTimer timer)
|
|
||||||
{
|
|
||||||
FrameManager fmgr;
|
|
||||||
if (_useFlip.getValue()) {
|
|
||||||
Log.info("Creating flip frame manager.");
|
|
||||||
fmgr = new FlipFrameManager();
|
|
||||||
} else {
|
|
||||||
Log.info("Creating back frame manager.");
|
|
||||||
fmgr = new BackFrameManager();
|
|
||||||
}
|
|
||||||
fmgr.init(window, root, timer);
|
|
||||||
return fmgr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes this frame manager and prepares it for operation.
|
|
||||||
*/
|
|
||||||
protected void init (
|
|
||||||
Window window, RootPaneContainer root, MediaTimer timer)
|
|
||||||
{
|
|
||||||
_window = window;
|
|
||||||
_root = root;
|
|
||||||
if (window instanceof ManagedJFrame) {
|
|
||||||
((ManagedJFrame)window).init(this);
|
|
||||||
}
|
|
||||||
_timer = timer;
|
|
||||||
|
|
||||||
// set up our custom repaint manager
|
|
||||||
_remgr = new ActiveRepaintManager(_window);
|
|
||||||
RepaintManager.setCurrentManager(_remgr);
|
|
||||||
|
|
||||||
// turn off double buffering for the whole business because we
|
|
||||||
// handle repaints
|
|
||||||
_remgr.setDoubleBufferingEnabled(false);
|
|
||||||
|
|
||||||
if (DEBUG_EVENTS) {
|
|
||||||
addTestListeners();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a variety of listeners to the frame in order to provide
|
|
||||||
* visibility into the various events received by the frame.
|
|
||||||
*/
|
|
||||||
protected void addTestListeners ()
|
|
||||||
{
|
|
||||||
// add a test window listener
|
|
||||||
_window.addWindowListener(new WindowListener() {
|
|
||||||
public void windowActivated (WindowEvent e) {
|
|
||||||
Log.info("Window activated [evt=" + e + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void windowClosed (WindowEvent e) {
|
|
||||||
Log.info("Window closed [evt=" + e + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void windowClosing (WindowEvent e) {
|
|
||||||
Log.info("Window closing [evt=" + e + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void windowDeactivated (WindowEvent e) {
|
|
||||||
Log.info("Window deactivated [evt=" + e + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void windowDeiconified (WindowEvent e) {
|
|
||||||
Log.info("Window deiconified [evt=" + e + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void windowIconified (WindowEvent e) {
|
|
||||||
Log.info("Window iconified [evt=" + e + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void windowOpened (WindowEvent e) {
|
|
||||||
Log.info("Window opened [evt=" + e + "].");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// add a component listener
|
|
||||||
_window.addComponentListener(new ComponentListener() {
|
|
||||||
public void componentHidden (ComponentEvent e) {
|
|
||||||
Log.info("Window component hidden [evt=" + e + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void componentShown (ComponentEvent e) {
|
|
||||||
Log.info("Window component shown [evt=" + e + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void componentMoved (ComponentEvent e) {
|
|
||||||
Log.info("Window component moved [evt=" + e + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void componentResized (ComponentEvent e) {
|
|
||||||
Log.info("Window component resized [evt=" + e + "].");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// add test ancestor focus listener
|
|
||||||
_root.getRootPane().addAncestorListener(
|
|
||||||
new AncestorListener() {
|
|
||||||
public void ancestorAdded (AncestorEvent e) {
|
|
||||||
Log.info("Root pane ancestor added [e=" + e + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ancestorRemoved (AncestorEvent e) {
|
|
||||||
Log.info("Root pane ancestor removed [e=" + e + "].");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ancestorMoved (AncestorEvent e) {
|
|
||||||
Log.info("Root pane ancestor moved [e=" + e + "].");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// add test key event dispatcher
|
|
||||||
KeyboardFocusManager.getCurrentKeyboardFocusManager().
|
|
||||||
addKeyEventDispatcher(new KeyEventDispatcher() {
|
|
||||||
public boolean dispatchKeyEvent (KeyEvent e) {
|
|
||||||
// if ((e.getModifiersEx() & KeyEvent.ALT_DOWN_MASK) != 0 &&
|
|
||||||
// e.getKeyCode() == KeyEvent.VK_TAB) {
|
|
||||||
// Log.info("Detected alt-tab key event " +
|
|
||||||
// "[e=" + e + "].");
|
|
||||||
// // attempt to eat the event so that windows
|
|
||||||
// // doesn't alt-tab into unhappy land
|
|
||||||
// e.consume();
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instructs the frame manager to target the specified number of
|
|
||||||
* frames per second. If the computation and rendering for a frame are
|
|
||||||
* completed with time to spare, the frame manager will wait until the
|
|
||||||
* proper time to begin processing for the next frame. If a frame
|
|
||||||
* takes longer than its alotted time, the frame manager will
|
|
||||||
* immediately begin processing on the next frame.
|
|
||||||
*/
|
|
||||||
public void setTargetFrameRate (int fps)
|
|
||||||
{
|
|
||||||
// compute the number of milliseconds per frame
|
|
||||||
_millisPerFrame = 1000/fps;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a frame participant. The participant will be given the
|
|
||||||
* opportunity to do processing and rendering on each frame.
|
|
||||||
*/
|
|
||||||
public void registerFrameParticipant (FrameParticipant participant)
|
|
||||||
{
|
|
||||||
Object[] nparts = ListUtil.testAndAddRef(_participants, participant);
|
|
||||||
if (nparts == null) {
|
|
||||||
Log.warning("Refusing to add duplicate frame participant! " +
|
|
||||||
participant);
|
|
||||||
} else {
|
|
||||||
_participants = nparts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the specified participant is registered.
|
|
||||||
*/
|
|
||||||
public boolean isRegisteredFrameParticipant (FrameParticipant participant)
|
|
||||||
{
|
|
||||||
return ListUtil.containsRef(_participants, participant);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes a frame participant.
|
|
||||||
*/
|
|
||||||
public void removeFrameParticipant (FrameParticipant participant)
|
|
||||||
{
|
|
||||||
ListUtil.clearRef(_participants, participant);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a millisecond granularity time stamp using the {@link
|
|
||||||
* MediaTimer} with which this frame manager was configured.
|
|
||||||
* <em>Note:</em> this should only be called from the AWT thread.
|
|
||||||
*/
|
|
||||||
public long getTimeStamp ()
|
|
||||||
{
|
|
||||||
return _timer.getElapsedMillis();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts up the per-frame tick
|
|
||||||
*/
|
|
||||||
public void start ()
|
|
||||||
{
|
|
||||||
if (_ticker == null) {
|
|
||||||
_ticker = new Ticker();
|
|
||||||
_ticker.start();
|
|
||||||
_lastTickStamp = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stops the per-frame tick.
|
|
||||||
*/
|
|
||||||
public synchronized void stop ()
|
|
||||||
{
|
|
||||||
if (_ticker != null) {
|
|
||||||
_ticker.cancel();
|
|
||||||
_ticker = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the tick interval is be running (not necessarily at
|
|
||||||
* that instant, but in general).
|
|
||||||
*/
|
|
||||||
public synchronized boolean isRunning ()
|
|
||||||
{
|
|
||||||
return (_ticker != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of ticks executed in the last second.
|
|
||||||
*/
|
|
||||||
public int getPerfTicks ()
|
|
||||||
{
|
|
||||||
return Math.round(_fps[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of ticks requested in the last second.
|
|
||||||
*/
|
|
||||||
public int getPerfTries ()
|
|
||||||
{
|
|
||||||
return Math.round(_fps[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns debug performance metrics.
|
|
||||||
*/
|
|
||||||
public TrailingAverage[] getPerfMetrics ()
|
|
||||||
{
|
|
||||||
if (_metrics == null) {
|
|
||||||
_metrics = new TrailingAverage[] {
|
|
||||||
new TrailingAverage(150),
|
|
||||||
new TrailingAverage(150),
|
|
||||||
new TrailingAverage(150) };
|
|
||||||
}
|
|
||||||
return _metrics;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called to perform the frame processing and rendering.
|
|
||||||
*/
|
|
||||||
protected void tick (long tickStamp)
|
|
||||||
{
|
|
||||||
long start = 0L, paint = 0L;
|
|
||||||
if (MediaPanel._perfDebug.getValue()) {
|
|
||||||
start = paint = _timer.getElapsedMicros();
|
|
||||||
}
|
|
||||||
// if our frame is not showing (or is impossibly sized), don't try
|
|
||||||
// rendering anything
|
|
||||||
if (_window.isShowing() &&
|
|
||||||
_window.getWidth() > 0 && _window.getHeight() > 0) {
|
|
||||||
// tick our participants
|
|
||||||
tickParticipants(tickStamp);
|
|
||||||
paint = _timer.getElapsedMicros();
|
|
||||||
// repaint our participants and components
|
|
||||||
paint(tickStamp);
|
|
||||||
}
|
|
||||||
if (MediaPanel._perfDebug.getValue()) {
|
|
||||||
long end = _timer.getElapsedMicros();
|
|
||||||
getPerfMetrics()[1].record((int)(paint-start)/100);
|
|
||||||
getPerfMetrics()[2].record((int)(end-paint)/100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called once per frame to invoke {@link FrameParticipant#tick} on
|
|
||||||
* all of our frame participants.
|
|
||||||
*/
|
|
||||||
protected void tickParticipants (long tickStamp)
|
|
||||||
{
|
|
||||||
long gap = tickStamp - _lastTickStamp;
|
|
||||||
if (_lastTickStamp != 0 && gap > (HANG_DEBUG ? HANG_GAP : BIG_GAP)) {
|
|
||||||
Log.debug("Long tick delay [delay=" + gap + "ms].");
|
|
||||||
}
|
|
||||||
_lastTickStamp = tickStamp;
|
|
||||||
|
|
||||||
// validate any invalid components
|
|
||||||
try {
|
|
||||||
_remgr.validateComponents();
|
|
||||||
} catch (Throwable t) {
|
|
||||||
Log.warning("Failure validating components.");
|
|
||||||
Log.logStackTrace(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
// tick all of our frame participants
|
|
||||||
for (int ii = 0; ii < _participants.length; ii++) {
|
|
||||||
FrameParticipant part = (FrameParticipant)_participants[ii];
|
|
||||||
if (part == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
long start = 0L;
|
|
||||||
if (HANG_DEBUG) {
|
|
||||||
start = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
|
|
||||||
part.tick(tickStamp);
|
|
||||||
|
|
||||||
if (HANG_DEBUG) {
|
|
||||||
long delay = (System.currentTimeMillis() - start);
|
|
||||||
if (delay > HANG_GAP) {
|
|
||||||
Log.info("Whoa nelly! Ticker took a long time " +
|
|
||||||
"[part=" + part + ", time=" + delay + "ms].");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Throwable t) {
|
|
||||||
Log.warning("Frame participant choked during tick " +
|
|
||||||
"[part=" + StringUtil.safeToString(part) + "].");
|
|
||||||
Log.logStackTrace(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called once per frame to invoke {@link Component#paint} on all of
|
|
||||||
* our frame participants' components and all dirty components managed
|
|
||||||
* by our {@link ActiveRepaintManager}.
|
|
||||||
*/
|
|
||||||
protected abstract void paint (long tickStamp);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Paints our frame participants and any dirty components via the
|
|
||||||
* repaint manager.
|
|
||||||
*
|
|
||||||
* @return true if anything was painted, false if not.
|
|
||||||
*/
|
|
||||||
protected boolean paint (Graphics2D gfx)
|
|
||||||
{
|
|
||||||
// paint our frame participants (which want to be handled
|
|
||||||
// specially)
|
|
||||||
int painted = 0;
|
|
||||||
for (int ii = 0; ii < _participants.length; ii++) {
|
|
||||||
FrameParticipant part = (FrameParticipant)_participants[ii];
|
|
||||||
if (part == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Component pcomp = part.getComponent();
|
|
||||||
if (pcomp == null || !part.needsPaint()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
long start = 0L;
|
|
||||||
if (HANG_DEBUG) {
|
|
||||||
start = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the bounds of this component
|
|
||||||
pcomp.getBounds(_tbounds);
|
|
||||||
|
|
||||||
// the bounds adjustment we're about to call will add in the
|
|
||||||
// components initial bounds offsets, so we remove them here
|
|
||||||
_tbounds.setLocation(0, 0);
|
|
||||||
|
|
||||||
// convert them into top-level coordinates; also note that if
|
|
||||||
// this component does not have a valid or visible root, we
|
|
||||||
// don't want to paint it either
|
|
||||||
if (getRoot(pcomp, _tbounds) == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// render this participant; we don't set the clip because
|
|
||||||
// frame participants are expected to handle clipping
|
|
||||||
// themselves; otherwise we might pointlessly set the clip
|
|
||||||
// here, creating a few Rectangle objects in the process,
|
|
||||||
// only to have the frame participant immediately set the
|
|
||||||
// clip to something more sensible
|
|
||||||
gfx.translate(_tbounds.x, _tbounds.y);
|
|
||||||
pcomp.paint(gfx);
|
|
||||||
gfx.translate(-_tbounds.x, -_tbounds.y);
|
|
||||||
painted++;
|
|
||||||
|
|
||||||
} catch (Throwable t) {
|
|
||||||
String ptos = StringUtil.safeToString(part);
|
|
||||||
Log.warning("Frame participant choked during paint " +
|
|
||||||
"[part=" + ptos + "].");
|
|
||||||
Log.logStackTrace(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
// render any components in our layered pane that are not in
|
|
||||||
// the default layer
|
|
||||||
_clipped[0] = false;
|
|
||||||
renderLayers(gfx, pcomp, _tbounds, _clipped);
|
|
||||||
|
|
||||||
if (HANG_DEBUG) {
|
|
||||||
long delay = (System.currentTimeMillis() - start);
|
|
||||||
if (delay > HANG_GAP) {
|
|
||||||
Log.warning("Whoa nelly! Painter took a long time " +
|
|
||||||
"[part=" + part + ", time=" + delay + "ms].");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// repaint any widgets that have declared they need to be
|
|
||||||
// repainted since the last tick
|
|
||||||
boolean pcomp = _remgr.paintComponents(gfx, this);
|
|
||||||
|
|
||||||
// let the caller know if anybody painted anything
|
|
||||||
return ((painted > 0) || pcomp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by the {@link ManagedJFrame} when our window was hidden and
|
|
||||||
* reexposed.
|
|
||||||
*/
|
|
||||||
protected abstract void restoreFromBack (Rectangle dirty);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders all components in all {@link JLayeredPane} layers that
|
|
||||||
* intersect the supplied bounds.
|
|
||||||
*/
|
|
||||||
protected void renderLayers (Graphics2D g, Component pcomp,
|
|
||||||
Rectangle bounds, boolean[] clipped)
|
|
||||||
{
|
|
||||||
JLayeredPane lpane =
|
|
||||||
JLayeredPane.getLayeredPaneAbove(pcomp);
|
|
||||||
if (lpane != null) {
|
|
||||||
renderLayer(g, bounds, lpane, clipped, JLayeredPane.PALETTE_LAYER);
|
|
||||||
renderLayer(g, bounds, lpane, clipped, JLayeredPane.MODAL_LAYER);
|
|
||||||
renderLayer(g, bounds, lpane, clipped, JLayeredPane.POPUP_LAYER);
|
|
||||||
renderLayer(g, bounds, lpane, clipped, JLayeredPane.DRAG_LAYER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders all components in the specified layer of the supplied
|
|
||||||
* layered pane that intersect the supplied bounds.
|
|
||||||
*/
|
|
||||||
protected void renderLayer (Graphics2D g, Rectangle bounds,
|
|
||||||
JLayeredPane pane, boolean[] clipped,
|
|
||||||
Integer layer)
|
|
||||||
{
|
|
||||||
// stop now if there are no components in that layer
|
|
||||||
int ccount = pane.getComponentCountInLayer(layer.intValue());
|
|
||||||
if (ccount == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// render them up
|
|
||||||
Component[] comps = pane.getComponentsInLayer(layer.intValue());
|
|
||||||
for (int ii = 0; ii < ccount; ii++) {
|
|
||||||
Component comp = comps[ii];
|
|
||||||
if (!comp.isVisible() || comp instanceof SafeLayerComponent) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if this overlay does not intersect the component we just
|
|
||||||
// rendered, we don't need to repaint it
|
|
||||||
_tbounds.setBounds(0, 0, comp.getWidth(), comp.getHeight());
|
|
||||||
getRoot(comp, _tbounds);
|
|
||||||
if (!_tbounds.intersects(bounds)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the clipping region has not yet been set during this
|
|
||||||
// render pass, the time has come to do so
|
|
||||||
if (!clipped[0]) {
|
|
||||||
g.setClip(bounds);
|
|
||||||
clipped[0] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// translate into the components coordinate system and render
|
|
||||||
g.translate(_tbounds.x, _tbounds.y);
|
|
||||||
try {
|
|
||||||
comp.paint(g);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.warning("Component choked while rendering.");
|
|
||||||
Log.logStackTrace(e);
|
|
||||||
}
|
|
||||||
g.translate(-_tbounds.x, -_tbounds.y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public void checkpoint (String name, int ticks)
|
|
||||||
{
|
|
||||||
Log.info("Frames in last second: " + ticks);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the root component for the supplied component or null if it
|
|
||||||
* is not part of a rooted hierarchy or if any parent along the way is
|
|
||||||
* found to be hidden or without a peer. Along the way, it adjusts the
|
|
||||||
* supplied component-relative rectangle to be relative to the
|
|
||||||
* returned root component.
|
|
||||||
*/
|
|
||||||
public static Component getRoot (Component comp, Rectangle rect)
|
|
||||||
{
|
|
||||||
for (Component c = comp; c != null; c = c.getParent()) {
|
|
||||||
if (!c.isVisible() || !c.isDisplayable()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (c instanceof Window || c instanceof Applet) {
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
rect.x += c.getX();
|
|
||||||
rect.y += c.getY();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Used to effect periodic calls to {@link #tick}. */
|
|
||||||
protected class Ticker extends Thread
|
|
||||||
{
|
|
||||||
public void run ()
|
|
||||||
{
|
|
||||||
Log.info("Frame manager ticker running " +
|
|
||||||
"[sleepGran=" + _sleepGranularity.getValue() + "].");
|
|
||||||
while (_running) {
|
|
||||||
long start = 0L;
|
|
||||||
if (MediaPanel._perfDebug.getValue()) {
|
|
||||||
start = _timer.getElapsedMicros();
|
|
||||||
}
|
|
||||||
Unsafe.sleep(_sleepGranularity.getValue());
|
|
||||||
|
|
||||||
long woke = _timer.getElapsedMicros();
|
|
||||||
if (start > 0L) {
|
|
||||||
getPerfMetrics()[0].record((int)(woke-start)/100);
|
|
||||||
int elapsed = (int)(woke-start);
|
|
||||||
if (elapsed > _sleepGranularity.getValue()*1500) {
|
|
||||||
Log.warning("Long tick [elapsed=" + elapsed + "us].");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// work around sketchy bug on WinXP that causes the clock
|
|
||||||
// to leap into the past from time to time
|
|
||||||
if (woke < _lastAttempt) {
|
|
||||||
Log.warning("Zoiks! We've leapt into the past, coping " +
|
|
||||||
"as best we can [dt=" +
|
|
||||||
(woke - _lastAttempt) + "].");
|
|
||||||
_lastAttempt = woke;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (woke - _lastAttempt >= _millisPerFrame * 1000) {
|
|
||||||
_lastAttempt = woke;
|
|
||||||
if (testAndSet()) {
|
|
||||||
EventQueue.invokeLater(_awtTicker);
|
|
||||||
}
|
|
||||||
// else: drop the frame
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void cancel ()
|
|
||||||
{
|
|
||||||
_running = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final synchronized boolean testAndSet ()
|
|
||||||
{
|
|
||||||
_tries++;
|
|
||||||
if (!_ticking) {
|
|
||||||
_ticking = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final synchronized void clearTicking (long elapsed)
|
|
||||||
{
|
|
||||||
if (++_ticks == 100) {
|
|
||||||
long time = (elapsed - _lastTick);
|
|
||||||
_fps[0] = _tries * 1000f / time;
|
|
||||||
_fps[1] = _ticks * 1000f / time;
|
|
||||||
_lastTick = elapsed;
|
|
||||||
_ticks = _tries = 0;
|
|
||||||
}
|
|
||||||
_ticking = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Used to invoke the call to {@link #tick} on the AWT event
|
|
||||||
* queue thread. */
|
|
||||||
protected Runnable _awtTicker = new Runnable ()
|
|
||||||
{
|
|
||||||
public void run ()
|
|
||||||
{
|
|
||||||
long elapsed = _timer.getElapsedMillis();
|
|
||||||
try {
|
|
||||||
tick(elapsed);
|
|
||||||
} finally {
|
|
||||||
clearTicking(elapsed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Used to stick a fork in our ticker when desired. */
|
|
||||||
protected transient boolean _running = true;
|
|
||||||
|
|
||||||
/** Used to detect when we need to drop frames. */
|
|
||||||
protected boolean _ticking;
|
|
||||||
|
|
||||||
/** The time at which we last attempted to tick. */
|
|
||||||
protected long _lastAttempt;
|
|
||||||
|
|
||||||
/** Used to compute metrics. */
|
|
||||||
protected int _tries, _ticks, _time;
|
|
||||||
|
|
||||||
/** Used to compute metrics. */
|
|
||||||
protected long _lastTick;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** The window into which we do our rendering. */
|
|
||||||
protected Window _window;
|
|
||||||
|
|
||||||
/** Provides access to our Swing bits. */
|
|
||||||
protected RootPaneContainer _root;
|
|
||||||
|
|
||||||
/** Used to obtain timing measurements. */
|
|
||||||
protected MediaTimer _timer;
|
|
||||||
|
|
||||||
/** Our custom repaint manager. */
|
|
||||||
protected ActiveRepaintManager _remgr;
|
|
||||||
|
|
||||||
/** The number of milliseconds per frame (14 by default, which gives
|
|
||||||
* an fps of ~71). */
|
|
||||||
protected long _millisPerFrame = 14;
|
|
||||||
|
|
||||||
/** Used to track big delays in calls to our tick method. */
|
|
||||||
protected long _lastTickStamp;
|
|
||||||
|
|
||||||
/** The thread that dispatches our frame ticks. */
|
|
||||||
protected Ticker _ticker;
|
|
||||||
|
|
||||||
/** Used to track and report frames per second. */
|
|
||||||
protected float[] _fps = new float[2];
|
|
||||||
|
|
||||||
/** Used to track performance metrics. */
|
|
||||||
protected TrailingAverage[] _metrics;
|
|
||||||
|
|
||||||
/** A temporary bounds rectangle used to avoid lots of object creation. */
|
|
||||||
protected Rectangle _tbounds = new Rectangle();
|
|
||||||
|
|
||||||
/** Used to lazily set the clip when painting popups and other
|
|
||||||
* "layered" components. */
|
|
||||||
protected boolean[] _clipped = new boolean[1];
|
|
||||||
|
|
||||||
/** The entites that are ticked each frame. */
|
|
||||||
protected Object[] _participants = new Object[4];
|
|
||||||
|
|
||||||
/** If we don't get ticked for 500ms, that's worth complaining about. */
|
|
||||||
protected static final long BIG_GAP = 500L;
|
|
||||||
|
|
||||||
/** If we don't get ticked for 100ms and we're hang debugging,
|
|
||||||
* complain. */
|
|
||||||
protected static final long HANG_GAP = 100L;
|
|
||||||
|
|
||||||
/** Enable this to log warnings when ticking or painting takes too
|
|
||||||
* long. */
|
|
||||||
protected static final boolean HANG_DEBUG = false;
|
|
||||||
|
|
||||||
/** A debug hook that toggles debug rendering of sprite paths. */
|
|
||||||
protected static RuntimeAdjust.BooleanAdjust _useFlip =
|
|
||||||
new RuntimeAdjust.BooleanAdjust(
|
|
||||||
"When active a flip-buffer will be used to manage our " +
|
|
||||||
"rendering, otherwise a volatile back buffer is used " +
|
|
||||||
"[requires restart]", "narya.media.frame",
|
|
||||||
// back buffer rendering doesn't work on the Mac, so we
|
|
||||||
// default to flip buffer on that platform; we still allow it
|
|
||||||
// to be toggled so that we can easily test things when they
|
|
||||||
// release new JVMs
|
|
||||||
MediaPrefs.config, RunAnywhere.isMacOS());
|
|
||||||
|
|
||||||
/** Allows us to tweak the sleep granularity. */
|
|
||||||
protected static RuntimeAdjust.IntAdjust _sleepGranularity =
|
|
||||||
new RuntimeAdjust.IntAdjust(
|
|
||||||
"The number of milliseconds slept before checking to see if " +
|
|
||||||
"it's time to queue up a new frame tick.", "narya.media.sleep_gran",
|
|
||||||
MediaPrefs.config, RunAnywhere.isWindows() ? 10 : 7);
|
|
||||||
|
|
||||||
/** Whether to enable AWT event debugging for the frame. */
|
|
||||||
protected static final boolean DEBUG_EVENTS = false;
|
|
||||||
|
|
||||||
/** The name of the high-performance timer class we attempt to load. */
|
|
||||||
protected static final String PERF_TIMER =
|
|
||||||
"com.threerings.media.timer.PerfTimer";
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id$
|
|
||||||
//
|
|
||||||
// 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.media;
|
|
||||||
|
|
||||||
import java.awt.Component;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides a mechanism for participating in the frame tick managed by the
|
|
||||||
* {@link FrameManager}.
|
|
||||||
*/
|
|
||||||
public interface FrameParticipant
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* This is called on all registered frame participants, one for every
|
|
||||||
* frame. Following the tick the interface will be rendered, so
|
|
||||||
* participants can prepare themselves for their upcoming render in
|
|
||||||
* this method (making use of the timestamp provided for the frame if
|
|
||||||
* choreography is desired between different participants).
|
|
||||||
*/
|
|
||||||
public void tick (long tickStamp);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called immediately prior to {@link #getComponent} and then {@link
|
|
||||||
* Component#paint} on said component, to determine whether or not
|
|
||||||
* this frame participant needs to be painted.
|
|
||||||
*/
|
|
||||||
public boolean needsPaint ();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If a frame participant wishes also to be actively rendered every
|
|
||||||
* frame rather than use passive rendering (which for Swing, at least,
|
|
||||||
* is hijacked when using the frame manager such that we take care of
|
|
||||||
* repainting dirty Swing components every frame into our off-screen
|
|
||||||
* buffer), it can return a component here which will have {@link
|
|
||||||
* Component#paint} called on it once per frame with a translated but
|
|
||||||
* unclipped graphics object.
|
|
||||||
*
|
|
||||||
* <p> Because clipping is expensive in terms of rectangle object
|
|
||||||
* allocation, frame participants are given the opportunity to do
|
|
||||||
* their own clipping because they are likely to want to clip to a
|
|
||||||
* more fine grained region than their entire bounds. If a particpant
|
|
||||||
* does not wish to be actively rendered, it can safely return null.
|
|
||||||
*/
|
|
||||||
public Component getComponent ();
|
|
||||||
}
|
|
||||||
@@ -1,175 +0,0 @@
|
|||||||
//
|
|
||||||
// $Id: HourglassView.java 18862 2005-01-27 00:34:51Z tedv $
|
|
||||||
|
|
||||||
package com.threerings.media;
|
|
||||||
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Graphics2D;
|
|
||||||
import java.awt.Point;
|
|
||||||
import java.awt.Rectangle;
|
|
||||||
import java.awt.Shape;
|
|
||||||
|
|
||||||
import javax.swing.JComponent;
|
|
||||||
|
|
||||||
import com.samskivert.util.Interval;
|
|
||||||
import com.samskivert.util.ResultListener;
|
|
||||||
|
|
||||||
import com.threerings.media.image.Mirage;
|
|
||||||
import com.threerings.media.util.MultiFrameImage;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Displays an hourglass with the sand level representing the amount of
|
|
||||||
* time remaining.
|
|
||||||
*/
|
|
||||||
public class HourglassView extends TimerView
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Constructs an hourglass view.
|
|
||||||
*/
|
|
||||||
public HourglassView (FrameManager fmgr, JComponent host, int x, int y,
|
|
||||||
Mirage glassImage, Mirage topImage, Rectangle topRect,
|
|
||||||
Mirage botImage, Rectangle botRect,
|
|
||||||
MultiFrameImage sandTrickle)
|
|
||||||
{
|
|
||||||
this(fmgr, host, x, y, glassImage, topImage, topRect, new Point(0, 0),
|
|
||||||
botImage, botRect, new Point(0, 0), sandTrickle);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs an hourglass view.
|
|
||||||
*/
|
|
||||||
public HourglassView (
|
|
||||||
FrameManager fmgr, JComponent host, int x, int y, Mirage glassImage,
|
|
||||||
Mirage topImage, Rectangle topRect, Point topOff,
|
|
||||||
Mirage botImage, Rectangle botRect, Point botOff,
|
|
||||||
MultiFrameImage sandTrickle)
|
|
||||||
{
|
|
||||||
super(fmgr, host, new Rectangle(x, y, glassImage.getWidth(),
|
|
||||||
glassImage.getHeight()));
|
|
||||||
|
|
||||||
// Store relevant coordinate data
|
|
||||||
_topRect = topRect;
|
|
||||||
_topOff = topOff;
|
|
||||||
_botRect = botRect;
|
|
||||||
_botOff = botOff;
|
|
||||||
|
|
||||||
// Save hourglass images
|
|
||||||
_hourglass = glassImage;
|
|
||||||
_sandTop = topImage;
|
|
||||||
_sandBottom = botImage;
|
|
||||||
_sandTrickle = sandTrickle;
|
|
||||||
|
|
||||||
// Initialize the falling grain of sand
|
|
||||||
_sandY = 0;
|
|
||||||
|
|
||||||
// Percentage changes smaller than one pixel in the hourglass
|
|
||||||
// itself definitely won't be noticed
|
|
||||||
_changeThreshold = 1.0f / _bounds.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public void changeComplete (float complete)
|
|
||||||
{
|
|
||||||
super.changeComplete(complete);
|
|
||||||
setSandTrickleY();
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public void tick (long tickStamp)
|
|
||||||
{
|
|
||||||
// Let the parent handle its stuff
|
|
||||||
super.tick(tickStamp);
|
|
||||||
|
|
||||||
// check if the sand should be updated
|
|
||||||
if (_enabled && _running && tickStamp > _sandStamp) {
|
|
||||||
|
|
||||||
// update the sand frame
|
|
||||||
setSandTrickleY();
|
|
||||||
_sandFrame = (_sandFrame + 1) % _sandTrickle.getFrameCount();
|
|
||||||
_sandStamp = tickStamp + SAND_RATE;
|
|
||||||
|
|
||||||
// Make sure the timer gets repainted
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// documentation inherited
|
|
||||||
public void paint (Graphics2D gfx, float completed)
|
|
||||||
{
|
|
||||||
// Handle processing from parent class
|
|
||||||
super.paint(gfx, completed);
|
|
||||||
|
|
||||||
// Paint the hourglass
|
|
||||||
gfx.translate(_bounds.x, _bounds.y);
|
|
||||||
_hourglass.paint(gfx, 0, 0);
|
|
||||||
|
|
||||||
// Paint the remaining top sand level
|
|
||||||
Shape oclip = gfx.getClip();
|
|
||||||
int top = _topRect.y + (int)(_topRect.height * completed);
|
|
||||||
gfx.clipRect(_topRect.x, top, _topRect.width,
|
|
||||||
_topRect.height - (top-_topRect.y));
|
|
||||||
_sandTop.paint(gfx, _topOff.x, _topOff.y);
|
|
||||||
|
|
||||||
// paint the current sand frame
|
|
||||||
gfx.setClip(oclip);
|
|
||||||
int sandtop = _topRect.y + _topRect.height;
|
|
||||||
if (_sandY < _botRect.height) {
|
|
||||||
gfx.clipRect(_botRect.x, sandtop, _botRect.width, _sandY);
|
|
||||||
}
|
|
||||||
_sandTrickle.paintFrame(gfx, _sandFrame,
|
|
||||||
_botRect.x + (_botRect.width - _sandTrickle.getWidth(_sandFrame))/2,
|
|
||||||
sandtop);
|
|
||||||
gfx.setClip(oclip);
|
|
||||||
|
|
||||||
// Paint the current bottom sand level
|
|
||||||
top = getSandBottomTop(completed);
|
|
||||||
gfx.clipRect(_botRect.x, top, _botRect.width,
|
|
||||||
_botRect.height-(top-_botRect.y));
|
|
||||||
_sandBottom.paint(gfx, _botOff.x, _botOff.y);
|
|
||||||
gfx.setClip(oclip);
|
|
||||||
|
|
||||||
// untranslate
|
|
||||||
gfx.translate(-_bounds.x, -_bounds.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the y position of the trickle.
|
|
||||||
*/
|
|
||||||
protected void setSandTrickleY ()
|
|
||||||
{
|
|
||||||
_sandY = (int) (_botRect.height * Math.min(1f, (_complete / .025)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current top coordinate of the bottom sand pile.
|
|
||||||
*/
|
|
||||||
protected int getSandBottomTop (float completed)
|
|
||||||
{
|
|
||||||
return _botRect.y + _botRect.height -
|
|
||||||
(int)(_botRect.height * completed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The bounds of the sand within the top and bottom sand images. */
|
|
||||||
protected Rectangle _topRect, _botRect;
|
|
||||||
|
|
||||||
/** Offsets at which to render the sand images. */
|
|
||||||
protected Point _topOff, _botOff;
|
|
||||||
|
|
||||||
/** Our images. */
|
|
||||||
protected Mirage _hourglass, _sandTop, _sandBottom;
|
|
||||||
|
|
||||||
/** The sand trickle. */
|
|
||||||
protected MultiFrameImage _sandTrickle;
|
|
||||||
|
|
||||||
/** The last time the sand updated moved. */
|
|
||||||
protected long _sandStamp;
|
|
||||||
|
|
||||||
/** The next sand frame to be painted. */
|
|
||||||
protected int _sandFrame;
|
|
||||||
|
|
||||||
/** The position of the top of the sand. */
|
|
||||||
protected int _sandY;
|
|
||||||
|
|
||||||
/** How often the sand frame updates. */
|
|
||||||
protected static final long SAND_RATE = 80;
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user