JME is kind of an evolutionary dead end, so we're going to move all the JME
code into the Bang project directly so as to confine the dependencies on JME libraries to the only thing that will ever need them. git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@1035 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
+4
-4
@@ -1,14 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry excluding="**/.svn/|com/threerings/jme/" kind="src" output="dist/classes" path="src/java"/>
|
||||
<classpathentry excluding="**/.svn/|com/threerings/jme/" kind="src" output="tests/dist/classes" path="tests/src/java"/>
|
||||
<classpathentry excluding="**/.svn/**" kind="src" output="dist/classes" path="src/java"/>
|
||||
<classpathentry excluding="**/.svn/**" kind="src" output="tests/dist/classes" path="tests/src/java"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry combineaccessrules="false" exported="true" kind="src" path="/narya"/>
|
||||
<classpathentry exported="true" kind="var" path="EXT_LIBS_DIR/xml-writer.jar"/>
|
||||
<classpathentry exported="true" kind="var" path="EXT_LIBS_DIR/jogg-0.0.7.jar"/>
|
||||
<classpathentry exported="true" kind="var" path="EXT_LIBS_DIR/jorbis-0.0.15.jar"/>
|
||||
<classpathentry exported="true" kind="var" path="EXT_LIBS_DIR/lwjgl/lwjgl.jar"/>
|
||||
<classpathentry exported="true" kind="var" path="EXT_LIBS_DIR/jl1.0.jar"/>
|
||||
<classpathentry exported="true" kind="var" path="EXT_LIBS_DIR/lwjgl/lwjgl.jar"/>
|
||||
<classpathentry exported="true" kind="var" path="EXT_LIBS_DIR/lwjgl/lwjgl_util.jar"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
<classpathentry combineaccessrules="false" exported="true" kind="src" path="/narya"/>
|
||||
</classpath>
|
||||
|
||||
@@ -73,7 +73,6 @@
|
||||
<javac srcdir="src/java" destdir="${classes.dir}" includeAntRuntime="false"
|
||||
debug="on" optimize="${build.optimize}" deprecation="on" source="1.5" target="1.5">
|
||||
<classpath refid="classpath"/>
|
||||
<exclude name="com/threerings/jme/**" unless="build.jme"/>
|
||||
<exclude name="com/threerings/openal/**" unless="build.openal"/>
|
||||
<exclude name="com/threerings/**/tools/xml/**" unless="build.xml"/>
|
||||
<exclude name="**/OggPlayer.java"/>
|
||||
@@ -219,10 +218,6 @@
|
||||
<jar destfile="${deploy.dir}/${lib.name}-chat.jar">
|
||||
<fileset dir="${classes.dir}" includes="com/threerings/chat/**"/>
|
||||
</jar>
|
||||
<jar destfile="${deploy.dir}/${lib.name}-jme.jar">
|
||||
<fileset dir="${classes.dir}" includes="com/threerings/jme/**"/>
|
||||
<fileset dir="${classes.dir}" includes="rsrc/**/jme/**"/>
|
||||
</jar>
|
||||
<jar destfile="${deploy.dir}/${lib.name}-openal.jar">
|
||||
<fileset dir="${classes.dir}" includes="com/threerings/openal/**"/>
|
||||
</jar>
|
||||
|
||||
@@ -9,14 +9,6 @@
|
||||
classname="org.lwjgl.LWJGLException" classpathref="classpath"/>
|
||||
<echo level="info" message="Have LWJGL: ${lwjgl.present}"/>
|
||||
|
||||
<available property="jme.present"
|
||||
classname="com.jme.scene.Node" classpathref="classpath"/>
|
||||
<echo level="info" message="Have JME: ${jme.present}"/>
|
||||
|
||||
<available property="jme-bui.present"
|
||||
classname="com.jmex.bui.BWindow" classpathref="classpath"/>
|
||||
<echo level="info" message="Have JME-BUI: ${jme-bui.present}"/>
|
||||
|
||||
<available property="jorbis.present"
|
||||
classname="com.jcraft.jorbis.Info" classpathref="classpath"/>
|
||||
<echo level="info" message="Have JOrbis: ${jorbis.present}"/>
|
||||
@@ -29,13 +21,6 @@
|
||||
classname="com.megginson.sax.DataWriter" classpathref="classpath"/>
|
||||
<echo level="info" message="Have XML Writer: ${xml-writer.present}"/>
|
||||
|
||||
<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"/>
|
||||
|
||||
+1
-8
@@ -18,16 +18,9 @@
|
||||
<include name="samskivert.jar"/>
|
||||
<include name="narya-base*.jar"/>
|
||||
<include name="narya-distrib*.jar"/>
|
||||
<!-- these LWJGL, JME and JME-BUI libraries are optional -->
|
||||
<!-- these LWJGL (for OpenAL) and codec libraries are optional -->
|
||||
<include name="lwjgl.jar"/>
|
||||
<include name="lwjgl_util.jar"/>
|
||||
<include name="jme-awt.jar"/>
|
||||
<include name="jme-effects.jar"/>
|
||||
<include name="jme-model.jar"/>
|
||||
<include name="jme-sound.jar"/>
|
||||
<include name="jme-terrain.jar"/>
|
||||
<include name="jme.jar"/>
|
||||
<include name="jme-bui.jar"/>
|
||||
<include name="jogg-0.0.7.jar"/>
|
||||
<include name="jorbis-0.0.15.jar"/>
|
||||
<include name="jl1.0.jar"/>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 192 KiB |
@@ -1,56 +0,0 @@
|
||||
//
|
||||
// $Id: ImageCache.java 158 2007-02-24 00:38:17Z mdb $
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/nenya/
|
||||
//
|
||||
// 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
|
||||
|
||||
/** The diffuse texture map. */
|
||||
uniform sampler2D diffuseMap;
|
||||
|
||||
/** The emissive texture map. */
|
||||
#ifdef EMISSIVE_MAPPED
|
||||
uniform sampler2D emissiveMap;
|
||||
#endif
|
||||
|
||||
/** The amount of fog. */
|
||||
#ifdef FOG
|
||||
varying float fogAlpha;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Fragment shader for skinned meshes.
|
||||
*/
|
||||
void main ()
|
||||
{
|
||||
// start with the diffuse color
|
||||
vec4 fragColor = texture2D(diffuseMap, gl_TexCoord[0].st);
|
||||
|
||||
// modulate by the light color
|
||||
#ifdef EMISSIVE_MAPPED
|
||||
fragColor *= (gl_Color + vec4(texture2D(emissiveMap, gl_TexCoord[0].st).rgb, 0.0));
|
||||
#else
|
||||
fragColor *= gl_Color;
|
||||
#endif
|
||||
|
||||
// blend between the computed color and the fog color
|
||||
#ifdef FOG
|
||||
gl_FragColor = mix(gl_Fog.color, fragColor, fogAlpha);
|
||||
#else
|
||||
gl_FragColor = fragColor;
|
||||
#endif
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
//
|
||||
// $Id: ImageCache.java 158 2007-02-24 00:38:17Z mdb $
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/nenya/
|
||||
//
|
||||
// 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
|
||||
|
||||
/** The bone transforms. */
|
||||
uniform mat4 boneTransforms[MAX_BONE_COUNT];
|
||||
|
||||
/** The bone indices. */
|
||||
attribute vec4 boneIndices;
|
||||
|
||||
/** The bone weights. */
|
||||
attribute vec4 boneWeights;
|
||||
|
||||
/** The amount of fog. */
|
||||
#ifdef FOG
|
||||
varying float fogAlpha;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Vertex shader for skinned meshes.
|
||||
*/
|
||||
void main ()
|
||||
{
|
||||
vec4 normal4 = vec4(gl_Normal, 0.0);
|
||||
|
||||
// add up the vertex as transformed by each bone and scaled by each weight
|
||||
vec4 modelVertex = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
vec4 modelNormal = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
for (int ii = 0; ii < 4; ii++) {
|
||||
mat4 boneTransform = boneTransforms[int(boneIndices[ii])];
|
||||
modelVertex += boneTransform * gl_Vertex * boneWeights[ii];
|
||||
modelNormal += boneTransform * normal4 * boneWeights[ii];
|
||||
}
|
||||
|
||||
// apply the standard transformation
|
||||
gl_Position = gl_ModelViewProjectionMatrix * modelVertex;
|
||||
|
||||
// transform the vertex and normal into eye space
|
||||
vec3 eyeVertex = vec3(gl_ModelViewMatrix * modelVertex);
|
||||
vec3 eyeNormal = normalize(vec3(gl_ModelViewMatrixInverseTranspose * modelNormal));
|
||||
|
||||
// set gl_FrontColor based on vertex, normal and light parameters
|
||||
SET_FRONT_COLOR
|
||||
|
||||
// set the varying texture coordinates
|
||||
SET_TEX_COORDS
|
||||
|
||||
// set the fog alpha based on the eye space vertex
|
||||
SET_FOG_ALPHA
|
||||
}
|
||||
@@ -1,600 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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 java.util.Arrays;
|
||||
|
||||
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.RenderState;
|
||||
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.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;
|
||||
|
||||
import static com.threerings.jme.Log.log;
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
try {
|
||||
// render the current frame
|
||||
long frameStart = processFrame();
|
||||
|
||||
// and process events until it's time to render the next
|
||||
PROCESS_EVENTS:
|
||||
do {
|
||||
Runnable r = _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.warning(t);
|
||||
// stick a fork in things if we fail too many times in a row
|
||||
if (++_failures > MAX_SUCCESSIVE_FAILURES) {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
if (_display.isClosing()) {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
cleanup();
|
||||
} catch (Throwable t) {
|
||||
log.warning(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;
|
||||
}
|
||||
|
||||
// from interface RunQueue
|
||||
public void postRunnable (Runnable r)
|
||||
{
|
||||
_evqueue.append(r);
|
||||
}
|
||||
|
||||
// from interface RunQueue
|
||||
public boolean isDispatchThread ()
|
||||
{
|
||||
return Thread.currentThread() == _dispatchThread;
|
||||
}
|
||||
|
||||
// from interface RunQueue
|
||||
public boolean isRunning ()
|
||||
{
|
||||
return !_finished;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the display configuration and creates the display. This must
|
||||
* fill in {@link #_api} as a side-effect.
|
||||
*/
|
||||
protected DisplaySystem createDisplay ()
|
||||
throws JmeException
|
||||
{
|
||||
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);
|
||||
|
||||
// enable all of the "quick compares," which means that states will
|
||||
// be refreshed only when necessary
|
||||
Arrays.fill(RenderState.QUICK_COMPARE, true);
|
||||
|
||||
// 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.warning(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<Runnable> _evqueue = Queue.newQueue();
|
||||
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,202 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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.AWTEvent;
|
||||
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.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;
|
||||
|
||||
import static com.threerings.jme.Log.log;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
// block mouse and keyboard events until the context is valid.
|
||||
return new LWJGLCanvas() {
|
||||
public void addNotify () {
|
||||
super.addNotify();
|
||||
disableEvents(MOUSE_KEY_EVENT_MASK);
|
||||
}
|
||||
public void update (Graphics g) {
|
||||
super.update(g);
|
||||
try {
|
||||
makeCurrent();
|
||||
} catch (LWJGLException e) {
|
||||
log.warning("Failed to make context current [error=" +
|
||||
e + "].");
|
||||
}
|
||||
}
|
||||
protected void initGL () {
|
||||
super.initGL();
|
||||
enableEvents(MOUSE_KEY_EVENT_MASK);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 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);
|
||||
_display.getCurrentContext().setupRecords(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.warning(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;
|
||||
|
||||
protected static final long MOUSE_KEY_EVENT_MASK =
|
||||
AWTEvent.MOUSE_EVENT_MASK |
|
||||
AWTEvent.MOUSE_MOTION_EVENT_MASK |
|
||||
AWTEvent.MOUSE_WHEEL_EVENT_MASK |
|
||||
AWTEvent.KEY_EVENT_MASK;
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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,33 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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.samskivert.util.Logger;
|
||||
|
||||
/**
|
||||
* Contains a reference to the log object used by this package.
|
||||
*/
|
||||
public class Log
|
||||
{
|
||||
/** We dispatch our log messages through this logger. */
|
||||
public static Logger log = Logger.getLogger("com.threerings.nenya.jme");
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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.toString());
|
||||
}
|
||||
|
||||
protected Text _text;
|
||||
protected StringBuilder _stats = new StringBuilder();
|
||||
protected StringBuffer _temp = new StringBuffer();
|
||||
|
||||
protected static final String DEFAULT_JME_FONT =
|
||||
"rsrc/media/jme/defaultfont.tga";
|
||||
}
|
||||
@@ -1,412 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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;
|
||||
import com.threerings.jme.JmeApp;
|
||||
|
||||
/**
|
||||
* 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.fromAngleAxis(deltaAngle, axis);
|
||||
|
||||
// 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<CameraPath.Observer>
|
||||
{
|
||||
public CompletedOp (CameraPath path) {
|
||||
_path = path;
|
||||
}
|
||||
public boolean apply (CameraPath.Observer observer) {
|
||||
return observer.pathCompleted(_path);
|
||||
}
|
||||
protected CameraPath _path;
|
||||
}
|
||||
|
||||
protected Camera _camera;
|
||||
protected CameraPath _campath;
|
||||
protected ObserverList<CameraPath.Observer> _campathobs = ObserverList.newSafeInOrder();
|
||||
|
||||
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,67 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* 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,202 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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.input.InputHandler;
|
||||
import com.jme.input.KeyBindingManager;
|
||||
import com.jme.input.KeyInput;
|
||||
import com.jme.input.action.*;
|
||||
import com.jme.input.action.InputActionEvent;
|
||||
|
||||
/**
|
||||
* 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 camhand 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$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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,111 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* 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,167 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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 static com.threerings.jme.Log.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: 12 KiB |
@@ -1,203 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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 com.jme.renderer.ColorRGBA;
|
||||
|
||||
import com.jmex.bui.BButton;
|
||||
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.jmex.bui.layout.GroupLayout;
|
||||
|
||||
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 static com.threerings.jme.Log.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);
|
||||
_incont = new BContainer(GroupLayout.makeHStretch());
|
||||
add(_incont, BorderLayout.SOUTH);
|
||||
_incont.add(_input = new BTextField());
|
||||
_input.addListener(_inlist);
|
||||
}
|
||||
|
||||
public void willEnterPlace (PlaceObject plobj)
|
||||
{
|
||||
setSpeakService(plobj.speakService, null);
|
||||
}
|
||||
|
||||
public void didLeavePlace (PlaceObject plobj)
|
||||
{
|
||||
clearSpeakService();
|
||||
}
|
||||
|
||||
public void setSpeakService (SpeakService spsvc, String localType)
|
||||
{
|
||||
if (spsvc != null) {
|
||||
_spsvc = spsvc;
|
||||
_localType = localType;
|
||||
_chatdtr.addChatDisplay(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearSpeakService ()
|
||||
{
|
||||
if (_spsvc != null) {
|
||||
_chatdtr.removeChatDisplay(this);
|
||||
_spsvc = null;
|
||||
_localType = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setChatButton (BButton button)
|
||||
{
|
||||
_inBtn = button;
|
||||
_incont.add(_inBtn, GroupLayout.FIXED);
|
||||
_inBtn.addListener(_inlist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs our chat input field to request focus.
|
||||
*/
|
||||
public void requestFocus ()
|
||||
{
|
||||
_input.requestFocus();
|
||||
}
|
||||
|
||||
// documentation inherited from interface ChatDisplay
|
||||
public void clear ()
|
||||
{
|
||||
if (_input.hasFocus() || _inBtn.hasFocus()) {
|
||||
_text.clearText();
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface ChatDisplay
|
||||
public boolean displayMessage (ChatMessage msg, boolean alreadyDisplayed)
|
||||
{
|
||||
// we may be restricted in the chat types we handle
|
||||
if (!handlesType(msg.localtype)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (msg instanceof UserMessage) {
|
||||
UserMessage umsg = (UserMessage) msg;
|
||||
if (ChatCodes.USER_CHAT_TYPE.equals(umsg.localtype)) {
|
||||
append("[" + umsg.speaker + " whispers] ", ColorRGBA.green);
|
||||
append(umsg.message + "\n");
|
||||
} else {
|
||||
append("<" + umsg.speaker + "> ", ColorRGBA.blue);
|
||||
append(umsg.message + "\n");
|
||||
}
|
||||
return true;
|
||||
|
||||
} else if (msg instanceof SystemMessage) {
|
||||
append(msg.message + "\n", ColorRGBA.red);
|
||||
return true;
|
||||
|
||||
} else {
|
||||
log.warning("Received unknown message type: " + msg + ".");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 _localType == null || _localType.equals(localType);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/** Used to trigger sending chat (on return key or button press). */
|
||||
protected ActionListener _inlist = new ActionListener() {
|
||||
public void actionPerformed (ActionEvent event) {
|
||||
if (handleInput(_input.getText())) {
|
||||
_input.setText("");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
protected ChatDirector _chatdtr;
|
||||
protected SpeakService _spsvc;
|
||||
protected String _localType;
|
||||
|
||||
protected BTextArea _text;
|
||||
protected BContainer _incont;
|
||||
protected BTextField _input;
|
||||
protected BButton _inBtn;
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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.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,157 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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.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
|
||||
{
|
||||
// slides from the specified location into the center of the screen
|
||||
public static final int FROM_TOP = 0;
|
||||
public static final int FROM_RIGHT = 1;
|
||||
|
||||
// slides from the center of the screen off screen in the specified direction
|
||||
public static final int TO_TOP = 2;
|
||||
public static final int TO_RIGHT = 3;
|
||||
|
||||
// slides down from the specified location stopping when whole window is visible (window
|
||||
// remains "stuck" to the edge from which it slid in)
|
||||
public static final int FROM_TOP_STICKY = 4;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
case FROM_TOP_STICKY:
|
||||
start = sheight+wheight;
|
||||
end = sheight-wheight + dy;
|
||||
window.setLocation((swidth-wwidth)/2 + dx, start);
|
||||
}
|
||||
|
||||
_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,177 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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.util.Properties;
|
||||
|
||||
import com.jme.math.FastMath;
|
||||
import com.jme.math.Quaternion;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.renderer.Camera;
|
||||
import com.jme.scene.Controller;
|
||||
import com.jme.scene.Spatial;
|
||||
import com.jme.system.DisplaySystem;
|
||||
import com.jme.util.export.JMEExporter;
|
||||
import com.jme.util.export.JMEImporter;
|
||||
import com.jme.util.export.InputCapsule;
|
||||
import com.jme.util.export.OutputCapsule;
|
||||
|
||||
/**
|
||||
* Orients its target towards the camera plane.
|
||||
*/
|
||||
public class BillboardController extends ModelController
|
||||
{
|
||||
/** Determines how the billboard is rotated. */
|
||||
public enum Alignment {
|
||||
DIR_X_POS_Z(false, true),
|
||||
DIR_X_DIR_Z(false, false),
|
||||
POS_X_POS_Z(true, true),
|
||||
DIR_Z(false),
|
||||
POS_Z(true);
|
||||
|
||||
/** Determines whether rotation is limited to the Z axis. */
|
||||
public boolean isAxial () {
|
||||
return _axial;
|
||||
}
|
||||
|
||||
/** Determines whether to tilt in the direction of the vector from the eyepoint to the
|
||||
* target's origin (the eye vector) as opposed to the camera direction. */
|
||||
public boolean isEyeRelativeX () {
|
||||
return _eyeRelativeX;
|
||||
}
|
||||
|
||||
/** Determines whether to swivel in the direction of the eye vector as opposed to the
|
||||
* camera direction. */
|
||||
public boolean isEyeRelativeZ () {
|
||||
return _eyeRelativeZ;
|
||||
}
|
||||
|
||||
Alignment (boolean eyeRelativeX, boolean eyeRelativeZ) {
|
||||
_eyeRelativeX = eyeRelativeX;
|
||||
_eyeRelativeZ = eyeRelativeZ;
|
||||
}
|
||||
|
||||
Alignment (boolean eyeRelativeZ) {
|
||||
_axial = true;
|
||||
_eyeRelativeZ = eyeRelativeZ;
|
||||
}
|
||||
|
||||
protected boolean _axial, _eyeRelativeX, _eyeRelativeZ;
|
||||
};
|
||||
|
||||
@Override
|
||||
public void configure (Properties props, Spatial target)
|
||||
{
|
||||
super.configure(props, target);
|
||||
_alignment = Enum.valueOf(Alignment.class,
|
||||
props.getProperty("alignment", "dir_x_pos_z").toUpperCase());
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void update (float time)
|
||||
{
|
||||
Camera cam = DisplaySystem.getDisplaySystem().getRenderer().getCamera();
|
||||
if (!isActive() || cam == null) {
|
||||
return;
|
||||
}
|
||||
if (_alignment.isEyeRelativeZ()) {
|
||||
_target.getWorldTranslation().subtract(cam.getLocation(), _yvec);
|
||||
} else {
|
||||
_yvec.set(cam.getDirection());
|
||||
}
|
||||
_zvec.set(_alignment.isAxial() ? Vector3f.UNIT_Z : cam.getUp());
|
||||
if (_alignment.isEyeRelativeX()) {
|
||||
// use the forward vector as-is
|
||||
_yvec.normalizeLocal().cross(_zvec, _xvec);
|
||||
if (_xvec.length() < FastMath.FLT_EPSILON) {
|
||||
return;
|
||||
}
|
||||
_xvec.normalizeLocal().cross(_yvec, _zvec);
|
||||
|
||||
} else {
|
||||
// project the forward vector onto the up plane
|
||||
_yvec.scaleAdd(-_zvec.dot(_yvec), _zvec, _yvec);
|
||||
if (_yvec.length() < FastMath.FLT_EPSILON) {
|
||||
return;
|
||||
}
|
||||
_yvec.normalizeLocal().cross(_zvec, _xvec);
|
||||
}
|
||||
// compute the world rotation with the axes and rotate into
|
||||
// parent's coordinate system
|
||||
Quaternion lrot = _target.getLocalRotation();
|
||||
lrot.fromAxes(_xvec, _yvec, _zvec);
|
||||
Spatial parent = _target.getParent();
|
||||
if (parent == null) {
|
||||
_rot.loadIdentity();
|
||||
} else {
|
||||
_rot.set(parent.getWorldRotation()).inverseLocal();
|
||||
}
|
||||
_rot.mult(lrot, lrot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Controller putClone (
|
||||
Controller store, Model.CloneCreator properties)
|
||||
{
|
||||
BillboardController bstore;
|
||||
if (store == null) {
|
||||
bstore = new BillboardController();
|
||||
} else {
|
||||
bstore = (BillboardController)store;
|
||||
}
|
||||
super.putClone(bstore, properties);
|
||||
bstore._alignment = _alignment;
|
||||
return bstore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read (JMEImporter im)
|
||||
throws IOException
|
||||
{
|
||||
super.read(im);
|
||||
InputCapsule capsule = im.getCapsule(this);
|
||||
_alignment = Enum.valueOf(Alignment.class,
|
||||
capsule.readString("alignment", "DIR_X_POS_Z"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write (JMEExporter ex)
|
||||
throws IOException
|
||||
{
|
||||
super.write(ex);
|
||||
OutputCapsule capsule = ex.getCapsule(this);
|
||||
capsule.write(_alignment.name(), "alignment", "DIR_X_POS_Z");
|
||||
}
|
||||
|
||||
/** The alignment mode. */
|
||||
protected Alignment _alignment;
|
||||
|
||||
/** A temporary quaternion. */
|
||||
protected Quaternion _rot = new Quaternion();
|
||||
|
||||
/** Temporary axis vectors. */
|
||||
protected Vector3f _xvec = new Vector3f(), _yvec = new Vector3f(), _zvec = new Vector3f();
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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.util.Properties;
|
||||
|
||||
import com.jme.scene.Controller;
|
||||
import com.jme.scene.Spatial;
|
||||
import com.jme.util.export.JMEExporter;
|
||||
import com.jme.util.export.JMEImporter;
|
||||
import com.jme.util.export.InputCapsule;
|
||||
import com.jme.util.export.OutputCapsule;
|
||||
|
||||
/**
|
||||
* A model controller whose target represents an emitter.
|
||||
*/
|
||||
public abstract class EmissionController extends ModelController
|
||||
{
|
||||
@Override
|
||||
public void configure (Properties props, Spatial target)
|
||||
{
|
||||
super.configure(props, target);
|
||||
_hideTarget = Boolean.parseBoolean(
|
||||
props.getProperty("hide_target", "true"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init (Model model)
|
||||
{
|
||||
super.init(model);
|
||||
if (_hideTarget) {
|
||||
_target.setCullMode(Spatial.CULL_ALWAYS);
|
||||
if (_target instanceof ModelNode) {
|
||||
// make sure the node isn't turned back on by an
|
||||
// animation
|
||||
((ModelNode)_target).setForceCull(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Controller putClone (
|
||||
Controller store, Model.CloneCreator properties)
|
||||
{
|
||||
if (store == null) {
|
||||
return null;
|
||||
}
|
||||
EmissionController estore = (EmissionController)store;
|
||||
super.putClone(estore, properties);
|
||||
estore._hideTarget = _hideTarget;
|
||||
return estore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read (JMEImporter im)
|
||||
throws IOException
|
||||
{
|
||||
super.read(im);
|
||||
InputCapsule capsule = im.getCapsule(this);
|
||||
_hideTarget = capsule.readBoolean("hideTarget", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write (JMEExporter ex)
|
||||
throws IOException
|
||||
{
|
||||
super.write(ex);
|
||||
OutputCapsule capsule = ex.getCapsule(this);
|
||||
capsule.write(_hideTarget, "hideTarget", true);
|
||||
}
|
||||
|
||||
/** Whether or not the target should be hidden from view. */
|
||||
protected boolean _hideTarget;
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,188 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Properties;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import com.jme.scene.Controller;
|
||||
import com.jme.scene.Spatial;
|
||||
import com.jme.util.export.JMEExporter;
|
||||
import com.jme.util.export.JMEImporter;
|
||||
import com.jme.util.export.InputCapsule;
|
||||
import com.jme.util.export.OutputCapsule;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
/**
|
||||
* The superclass of procedural animation controllers for models.
|
||||
*/
|
||||
public abstract class ModelController extends Controller
|
||||
{
|
||||
/**
|
||||
* 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 = Sets.newHashSet();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the controller updates should include time
|
||||
* accumulated while the model was out of sight. Accumulating will
|
||||
* be turned off only if all controllers are non-accumulating.
|
||||
*/
|
||||
public boolean shouldAccumulate ()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read (JMEImporter im)
|
||||
throws IOException
|
||||
{
|
||||
InputCapsule capsule = im.getCapsule(this);
|
||||
_target = (Spatial)capsule.readSavable("target", null);
|
||||
String[] anims = capsule.readStringArray("animations", null);
|
||||
if (anims != null) {
|
||||
_animations = Sets.newHashSet();
|
||||
Collections.addAll(_animations, anims);
|
||||
} else {
|
||||
_animations = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write (JMEExporter ex)
|
||||
throws IOException
|
||||
{
|
||||
OutputCapsule capsule = ex.getCapsule(this);
|
||||
capsule.write(_target, "target", null);
|
||||
capsule.write((_animations == null) ?
|
||||
null : _animations.toArray(new String[_animations.size()]),
|
||||
"animations", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,904 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Properties;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import com.jme.bounding.BoundingBox;
|
||||
import com.jme.bounding.BoundingSphere;
|
||||
import com.jme.bounding.BoundingVolume;
|
||||
import com.jme.image.Image;
|
||||
import com.jme.image.Texture;
|
||||
import com.jme.math.FastMath;
|
||||
import com.jme.math.Quaternion;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.renderer.ColorRGBA;
|
||||
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.TriangleBatch;
|
||||
import com.jme.scene.state.AlphaState;
|
||||
import com.jme.scene.state.CullState;
|
||||
import com.jme.scene.state.FogState;
|
||||
import com.jme.scene.state.LightState;
|
||||
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.export.JMEExporter;
|
||||
import com.jme.util.export.JMEImporter;
|
||||
import com.jme.util.export.InputCapsule;
|
||||
import com.jme.util.export.OutputCapsule;
|
||||
import com.jme.util.geom.BufferUtils;
|
||||
|
||||
import com.samskivert.util.PropertiesUtil;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.jme.util.ShaderCache;
|
||||
|
||||
/**
|
||||
* A {@link TriMesh} with a serialization mechanism tailored to stored models.
|
||||
*/
|
||||
public class ModelMesh extends TriMesh
|
||||
implements ModelSpatial
|
||||
{
|
||||
/**
|
||||
* No-arg constructor for deserialization.
|
||||
*/
|
||||
public ModelMesh ()
|
||||
{
|
||||
super("mesh");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a mesh with no vertex data.
|
||||
*/
|
||||
public ModelMesh (String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode ()
|
||||
{
|
||||
// hash on the name rather than the identity for consistent ordering
|
||||
return getName().hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconfigures this model with a new set of (sub-)properties. Textures
|
||||
* must be (re-)resolved after calling this method.
|
||||
*/
|
||||
public void reconfigure (Properties props)
|
||||
{
|
||||
configure(_solid, _textureKey, _transparent, props);
|
||||
setRenderStates();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
_textureKey = texture;
|
||||
_textures = (texture == null) ? null : StringUtil.parseStringArray(
|
||||
props.getProperty(texture, texture));
|
||||
Properties tprops = PropertiesUtil.getFilteredProperties(
|
||||
props, texture);
|
||||
_sphereMapped = Boolean.parseBoolean(tprops.getProperty("sphere_map"));
|
||||
_filterMode = "nearest".equals(tprops.getProperty("filter")) ?
|
||||
Texture.FM_NEAREST : Texture.FM_LINEAR;
|
||||
_mipMapMode = getMipMapMode(tprops.getProperty("mipmap"));
|
||||
_compress = Boolean.parseBoolean(tprops.getProperty("compress", "true"));
|
||||
String emissive = tprops.getProperty("emissive");
|
||||
if (Boolean.parseBoolean(emissive)) {
|
||||
_emissive = true;
|
||||
} else {
|
||||
_emissiveMap = emissive;
|
||||
}
|
||||
_additive = Boolean.parseBoolean(tprops.getProperty("additive"));
|
||||
_solid = solid;
|
||||
_transparent = transparent;
|
||||
String threshold = tprops.getProperty("alpha_threshold");
|
||||
_alphaThreshold = (threshold == null) ?
|
||||
DEFAULT_ALPHA_THRESHOLD : Float.parseFloat(threshold);
|
||||
_translucent = _transparent &&
|
||||
Boolean.parseBoolean(tprops.getProperty("translucent"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
storeOriginalBuffers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an overlay layer to this mesh. After the mesh is rendered with
|
||||
* its configured states, these states will be applied and the mesh will
|
||||
* be rendered again.
|
||||
*/
|
||||
public void addOverlay (RenderState[] overlay)
|
||||
{
|
||||
if (_overlays == null) {
|
||||
_overlays = Lists.newArrayListWithCapacity(1);
|
||||
}
|
||||
_overlays.add(overlay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a layer from this mesh.
|
||||
*/
|
||||
public void removeOverlay (RenderState[] overlay)
|
||||
{
|
||||
if (_overlays != null) {
|
||||
_overlays.remove(overlay);
|
||||
if (_overlays.isEmpty()) {
|
||||
_overlays = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reconstruct (
|
||||
FloatBuffer vertices, FloatBuffer normals, FloatBuffer colors,
|
||||
FloatBuffer textures, IntBuffer indices)
|
||||
{
|
||||
super.reconstruct(vertices, normals, colors, textures, indices);
|
||||
for (int ii = 1, nn = getTextureCount(); ii < nn; ii++) {
|
||||
setTextureBuffer(0, getTextureBuffer(0, 0), ii);
|
||||
}
|
||||
|
||||
// store any buffers that will be manipulated on a per-instance basis
|
||||
storeOriginalBuffers();
|
||||
|
||||
// initialize the model if we're displaying
|
||||
setRenderStates();
|
||||
}
|
||||
|
||||
// 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 = getBatch(0), mbatch = 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") && !_translucent) ?
|
||||
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));
|
||||
}
|
||||
mstore._textureKey = _textureKey;
|
||||
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;
|
||||
}
|
||||
mstore._sphereMapped = _sphereMapped;
|
||||
mstore._filterMode = _filterMode;
|
||||
mstore._mipMapMode = _mipMapMode;
|
||||
mstore._compress = _compress;
|
||||
mstore._emissiveMap = _emissiveMap;
|
||||
mstore._emissive = _emissive;
|
||||
mstore._additive = _additive;
|
||||
mstore._solid = _solid;
|
||||
mstore._transparent = _transparent;
|
||||
mstore._alphaThreshold = _alphaThreshold;
|
||||
mstore._translucent = _translucent;
|
||||
mstore._oibuf = _oibuf;
|
||||
mstore._vbuf = _vbuf;
|
||||
return mstore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateWorldVectors ()
|
||||
{
|
||||
if (!_transformLocked) {
|
||||
super.updateWorldVectors();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read (JMEImporter im)
|
||||
throws IOException
|
||||
{
|
||||
InputCapsule capsule = im.getCapsule(this);
|
||||
setName(capsule.readString("name", null));
|
||||
setLocalTranslation((Vector3f)capsule.readSavable(
|
||||
"localTranslation", null));
|
||||
setLocalRotation((Quaternion)capsule.readSavable(
|
||||
"localRotation", null));
|
||||
setLocalScale((Vector3f)capsule.readSavable(
|
||||
"localScale", null));
|
||||
TriangleBatch batch = getBatch(0);
|
||||
batch.setModelBound((BoundingVolume)capsule.readSavable(
|
||||
"modelBound", null));
|
||||
_textureKey = capsule.readString("textureKey", null);
|
||||
_textures = capsule.readStringArray("textures", null);
|
||||
_sphereMapped = capsule.readBoolean("sphereMapped", false);
|
||||
_filterMode = capsule.readInt("filterMode", Texture.FM_LINEAR);
|
||||
_mipMapMode = capsule.readInt("mipMapMode", Texture.MM_LINEAR_LINEAR);
|
||||
_compress = capsule.readBoolean("compress", true);
|
||||
_emissiveMap = capsule.readString("emissiveMap", null);
|
||||
_emissive = capsule.readBoolean("emissive", false);
|
||||
_additive = capsule.readBoolean("additive", false);
|
||||
_solid = capsule.readBoolean("solid", true);
|
||||
_transparent = capsule.readBoolean("transparent", false);
|
||||
_alphaThreshold = capsule.readFloat("alphaThreshold",
|
||||
DEFAULT_ALPHA_THRESHOLD);
|
||||
_translucent = capsule.readBoolean("translucent", false);
|
||||
|
||||
reconstruct(capsule.readFloatBuffer("vertexBuffer", null),
|
||||
capsule.readFloatBuffer("normalBuffer", null), null,
|
||||
capsule.readFloatBuffer("textureBuffer", null),
|
||||
capsule.readIntBuffer("indexBuffer", null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write (JMEExporter ex)
|
||||
throws IOException
|
||||
{
|
||||
OutputCapsule capsule = ex.getCapsule(this);
|
||||
capsule.write(getName(), "name", null);
|
||||
capsule.write(getLocalTranslation(), "localTranslation", null);
|
||||
capsule.write(getLocalRotation(), "localRotation", null);
|
||||
capsule.write(getLocalScale(), "localScale", null);
|
||||
capsule.write(getBatch(0).getModelBound(), "modelBound", null);
|
||||
capsule.write(getVertexBuffer(0), "vertexBuffer", null);
|
||||
capsule.write(getNormalBuffer(0), "normalBuffer", null);
|
||||
capsule.write(getTextureBuffer(0, 0), "textureBuffer", null);
|
||||
capsule.write(getIndexBuffer(0), "indexBuffer", null);
|
||||
capsule.write(_textureKey, "textureKey", null);
|
||||
capsule.write(_textures, "textures", null);
|
||||
capsule.write(_sphereMapped, "sphereMapped", false);
|
||||
capsule.write(_filterMode, "filterMode", Texture.FM_LINEAR);
|
||||
capsule.write(_mipMapMode, "mipMapMode", Texture.MM_LINEAR_LINEAR);
|
||||
capsule.write(_compress, "compress", true);
|
||||
capsule.write(_emissiveMap, "emissiveMap", null);
|
||||
capsule.write(_emissive, "emissive", false);
|
||||
capsule.write(_additive, "additive", false);
|
||||
capsule.write(_solid, "solid", true);
|
||||
capsule.write(_transparent, "transparent", false);
|
||||
capsule.write(_alphaThreshold, "alphaThreshold",
|
||||
DEFAULT_ALPHA_THRESHOLD);
|
||||
capsule.write(_translucent, "translucent", false);
|
||||
}
|
||||
|
||||
// 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(!_translucent);
|
||||
setVBOInfo(vboinfo);
|
||||
|
||||
} else if (useDisplayLists && !_translucent) {
|
||||
lockMeshes(renderer);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void resolveTextures (TextureProvider tprov)
|
||||
{
|
||||
if (_textures == null) {
|
||||
return;
|
||||
}
|
||||
Texture emissiveTex = null;
|
||||
if (_emissiveMap != null && TextureState.getNumberOfFixedUnits() >= 2) {
|
||||
TextureState tstate = tprov.getTexture(_emissiveMap);
|
||||
emissiveTex = tstate.getTexture();
|
||||
emissiveTex.setApply(Texture.AM_BLEND);
|
||||
emissiveTex.getBlendColor().set(ColorRGBA.white);
|
||||
}
|
||||
_tstates = new TextureState[_textures.length];
|
||||
for (int ii = 0; ii < _textures.length; ii++) {
|
||||
_tstates[ii] = tprov.getTexture(_textures[ii]);
|
||||
Texture tex = _tstates[ii].getTexture();
|
||||
if (_sphereMapped) {
|
||||
tex.setEnvironmentalMapMode(Texture.EM_SPHERE);
|
||||
}
|
||||
tex.setFilter(_filterMode);
|
||||
tex.setMipmapState(_mipMapMode);
|
||||
if (_compress && _tstates[ii].isS3TCAvailable()) {
|
||||
Image image = tex.getImage();
|
||||
int type = image.getType();
|
||||
if (type == Image.RGB888) {
|
||||
image.setType(Image.RGB888_DXT1);
|
||||
} else if (type == Image.RGBA8888) {
|
||||
image.setType(Image.RGBA8888_DXT5);
|
||||
}
|
||||
}
|
||||
if (emissiveTex != null) {
|
||||
_tstates[ii] = DisplaySystem.getDisplaySystem().
|
||||
getRenderer().createTextureState();
|
||||
_tstates[ii].setTexture(emissiveTex, 0);
|
||||
_tstates[ii].setTexture(tex, 1);
|
||||
}
|
||||
}
|
||||
if (_tstates[0] != null) {
|
||||
setRenderState(_tstates[0]);
|
||||
} else {
|
||||
clearRenderState(RenderState.RS_TEXTURE);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface ModelSpatial
|
||||
public void configureShaders (ShaderCache scache)
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setupBatchList ()
|
||||
{
|
||||
batchList = Lists.newArrayListWithCapacity(1);
|
||||
TriangleBatch batch = createModelBatch();
|
||||
batch.setParentGeom(this);
|
||||
batchList.add(batch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a batch for this mesh.
|
||||
*/
|
||||
protected ModelBatch createModelBatch ()
|
||||
{
|
||||
return new ModelBatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of textures this mesh uses (they must all share the
|
||||
* same texture coordinates).
|
||||
*/
|
||||
protected int getTextureCount ()
|
||||
{
|
||||
return (_emissiveMap == null || TextureState.getNumberOfFixedUnits() < 2) ? 1 : 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* For buffers that must be manipulated in some fashion, this method stores
|
||||
* the originals.
|
||||
*/
|
||||
protected void storeOriginalBuffers ()
|
||||
{
|
||||
if (!_translucent) {
|
||||
return;
|
||||
}
|
||||
IntBuffer ibuf = getIndexBuffer(0);
|
||||
ibuf.rewind();
|
||||
IntBuffer.wrap(_oibuf = new int[ibuf.capacity()]).put(ibuf);
|
||||
|
||||
FloatBuffer vbuf = getVertexBuffer(0);
|
||||
vbuf.rewind();
|
||||
FloatBuffer.wrap(_vbuf = new float[vbuf.capacity()]).put(vbuf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the model's render states (excluding the texture state, which is
|
||||
* set by {@link #resolveTextures}) according to its configuration.
|
||||
*/
|
||||
protected void setRenderStates ()
|
||||
{
|
||||
if (DisplaySystem.getDisplaySystem() == null) {
|
||||
return;
|
||||
}
|
||||
if (_backCull == null) {
|
||||
initSharedStates();
|
||||
}
|
||||
if (_emissive) {
|
||||
setRenderState(_emissiveLight);
|
||||
}
|
||||
if (_solid) {
|
||||
setRenderState(_backCull);
|
||||
}
|
||||
if (_additive) {
|
||||
setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);
|
||||
setRenderState(_addAlpha);
|
||||
setRenderState(_overlayZBuffer);
|
||||
setRenderState(_noFog);
|
||||
} else if (_transparent) {
|
||||
setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);
|
||||
if (_translucent) {
|
||||
setRenderState(_blendAlpha);
|
||||
setRenderState(_overlayZBuffer);
|
||||
} else if (_alphaThreshold == DEFAULT_ALPHA_THRESHOLD) {
|
||||
setRenderState(_defaultTestAlpha);
|
||||
} else {
|
||||
setRenderState(createTestAlpha(_alphaThreshold));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks the transform and bounds of this mesh on the assumption that its
|
||||
* position will not change.
|
||||
*/
|
||||
protected void lockInstance ()
|
||||
{
|
||||
lockBounds();
|
||||
_transformLocked = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mip-map mode corresponding to the given string
|
||||
* (defaulting to {@link Texture#MM_LINEAR_LINEAR}).
|
||||
*/
|
||||
protected static int getMipMapMode (String mmode)
|
||||
{
|
||||
if ("none".equals(mmode)) {
|
||||
return Texture.MM_NONE;
|
||||
} else if ("nearest".equals(mmode)) {
|
||||
return Texture.MM_NEAREST;
|
||||
} else if ("linear".equals(mmode)) {
|
||||
return Texture.MM_LINEAR;
|
||||
} else if ("nearest_nearest".equals(mmode)) {
|
||||
return Texture.MM_NEAREST_NEAREST;
|
||||
} else if ("nearest_linear".equals(mmode)) {
|
||||
return Texture.MM_NEAREST_LINEAR;
|
||||
} else if ("linear_nearest".equals(mmode)) {
|
||||
return Texture.MM_LINEAR_NEAREST;
|
||||
} else {
|
||||
return Texture.MM_LINEAR_LINEAR;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
_addAlpha = renderer.createAlphaState();
|
||||
_addAlpha.setBlendEnabled(true);
|
||||
_addAlpha.setDstFunction(AlphaState.DB_ONE);
|
||||
_defaultTestAlpha = createTestAlpha(DEFAULT_ALPHA_THRESHOLD);
|
||||
_overlayZBuffer = renderer.createZBufferState();
|
||||
_overlayZBuffer.setFunction(ZBufferState.CF_LEQUAL);
|
||||
_overlayZBuffer.setWritable(false);
|
||||
_emissiveLight = renderer.createLightState();
|
||||
_emissiveLight.setGlobalAmbient(ColorRGBA.white);
|
||||
_noFog = renderer.createFogState();
|
||||
_noFog.setEnabled(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an alpha state what will throw away fragments with alpha
|
||||
* values less than or equal to the given threshold.
|
||||
*/
|
||||
protected static AlphaState createTestAlpha (float threshold)
|
||||
{
|
||||
AlphaState astate = DisplaySystem.getDisplaySystem().
|
||||
getRenderer().createAlphaState();
|
||||
astate.setBlendEnabled(true);
|
||||
astate.setTestEnabled(true);
|
||||
astate.setTestFunction(AlphaState.TF_GREATER);
|
||||
astate.setReference(threshold);
|
||||
return astate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the encoded triangle index/distance pairs in {@link #_tcodes}
|
||||
* using a two-pass (16 bit) radix sort (as described by
|
||||
* <a href="http://codercorner.com/RadixSortRevisited.htm">Pierre
|
||||
* Terdiman</a>. {@link #_bcounts} is assumed to be initialized to the
|
||||
* counts for the first radix.
|
||||
*/
|
||||
protected static void sortTriangleCodes (int tcount)
|
||||
{
|
||||
// initialize the offsets for the first radix (LSB) and clear
|
||||
// the counts
|
||||
initByteOffsets();
|
||||
|
||||
// sort by the first radix and get the counts for the second
|
||||
// (swapping directions in the hope of using the cache more
|
||||
// effectively)
|
||||
if (_stcodes == null || _stcodes.length < tcount) {
|
||||
_stcodes = new int[tcount];
|
||||
}
|
||||
int tcode;
|
||||
for (int ii = tcount - 1; ii >= 0; ii--) {
|
||||
tcode = _tcodes[ii];
|
||||
_stcodes[_boffsets[tcode & 0xFF]++] = tcode;
|
||||
_bcounts[(tcode >> 8) & 0xFF]++;
|
||||
}
|
||||
|
||||
// initialize offsets for the second radix, clear counts, and
|
||||
// sort by the second radix
|
||||
initByteOffsets();
|
||||
for (int ii = 0; ii < tcount; ii++) {
|
||||
tcode = _stcodes[ii];
|
||||
_tcodes[_boffsets[(tcode >> 8) & 0xFF]++] = tcode;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initial byte offsets used to place bytes within the sorted
|
||||
* array using the byte counts, clearing the counts in the process.
|
||||
*/
|
||||
protected static void initByteOffsets ()
|
||||
{
|
||||
_boffsets[0] = 0;
|
||||
for (int ii = 1; ii < 256; ii++) {
|
||||
_boffsets[ii] = _boffsets[ii - 1] + _bcounts[ii - 1];
|
||||
_bcounts[ii - 1] = 0;
|
||||
}
|
||||
_bcounts[255] = 0;
|
||||
}
|
||||
|
||||
/** Sorts triangles for transparent meshes and renders overlays as well as
|
||||
* the base layer. */
|
||||
protected class ModelBatch extends TriangleBatch
|
||||
{
|
||||
@Override
|
||||
public void draw (Renderer r)
|
||||
{
|
||||
boolean drawing = (isEnabled() && r.isProcessingQueue());
|
||||
if (drawing) {
|
||||
if (_translucent) {
|
||||
sortTriangles(r);
|
||||
}
|
||||
preDraw();
|
||||
}
|
||||
super.draw(r);
|
||||
if (_overlays != null && drawing) {
|
||||
for (int ii = 0, nn = _overlays.size(); ii < nn; ii++) {
|
||||
preDrawOverlay(ii);
|
||||
r.draw(this);
|
||||
postDrawOverlay(ii);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives derived classes a chance to update states immediately before drawing.
|
||||
*/
|
||||
protected void preDraw ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the batch's states with those of the identified overlay.
|
||||
*/
|
||||
protected void preDrawOverlay (int oidx)
|
||||
{
|
||||
RenderState[] overlay = _overlays.get(oidx);
|
||||
for (RenderState rstate : overlay) {
|
||||
int idx = rstate.getType();
|
||||
_ostates[idx] = states[idx];
|
||||
states[idx] = rstate;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the batch's original states after drawing an overlay.
|
||||
*/
|
||||
protected void postDrawOverlay (int oidx)
|
||||
{
|
||||
RenderState[] overlay = _overlays.get(oidx);
|
||||
for (RenderState rstate : overlay) {
|
||||
int idx = rstate.getType();
|
||||
states[idx] = _ostates[idx];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the batch's triangles by their distance to the camera.
|
||||
*/
|
||||
protected void sortTriangles (Renderer r)
|
||||
{
|
||||
// using the camera's direction in model space and the position
|
||||
// and size of the model bound, find a set of plane parameters
|
||||
// that determine the distance to a camera-aligned plane
|
||||
// that touches the near edge of the bounding volume, as well
|
||||
// as a scaling factor that brings the distance into a 16-bit
|
||||
// integer range
|
||||
getParentGeom().getWorldRotation().inverse().mult(
|
||||
r.getCamera().getDirection(), _cdir);
|
||||
BoundingVolume mbound = getModelBound();
|
||||
Vector3f mc = mbound.getCenter();
|
||||
float radius;
|
||||
if (mbound instanceof BoundingSphere) {
|
||||
radius = ((BoundingSphere)mbound).getRadius();
|
||||
} else { // mbound instanceof BoundingBox
|
||||
BoundingBox bbox = (BoundingBox)mbound;
|
||||
radius = FastMath.sqrt(3f) * Math.max(bbox.xExtent,
|
||||
Math.max(bbox.yExtent, bbox.zExtent));
|
||||
}
|
||||
float a = _cdir.x, b = _cdir.y, c = _cdir.z,
|
||||
d = radius - a*mc.x - b*mc.y - c*mc.z,
|
||||
dscale = 65535f / (radius * 2);
|
||||
|
||||
// premultiply the scale and averaging factor
|
||||
a *= dscale / 3f;
|
||||
b *= dscale / 3f;
|
||||
c *= dscale / 3f;
|
||||
d *= dscale;
|
||||
|
||||
// encode the model's triangles into integers such that the
|
||||
// high 16 bits represent the original triangle index and the
|
||||
// low 16 bits represent the distance to the plane. also
|
||||
// increment the byte counts used for radix sorting
|
||||
int tcount = getTriangleCount(), idist;
|
||||
if (_tcodes == null || _tcodes.length < tcount) {
|
||||
_tcodes = new int[tcount];
|
||||
}
|
||||
int i1, i2, i3;
|
||||
for (int ii = 0, idx = 0; ii < tcount; ii++) {
|
||||
i1 = _oibuf[idx++] * 3;
|
||||
i2 = _oibuf[idx++] * 3;
|
||||
i3 = _oibuf[idx++] * 3;
|
||||
idist = (int)(
|
||||
a * (_vbuf[i1++] + _vbuf[i2++] + _vbuf[i3++]) +
|
||||
b * (_vbuf[i1++] + _vbuf[i2++] + _vbuf[i3++]) +
|
||||
c * (_vbuf[i1++] + _vbuf[i2++] + _vbuf[i3++]) + d);
|
||||
_tcodes[ii] = (ii << 16) | idist;
|
||||
_bcounts[idist & 0xFF]++;
|
||||
}
|
||||
|
||||
// sort the encoded triangles by increasing distance
|
||||
sortTriangleCodes(tcount);
|
||||
|
||||
// reorder the triangles as dictated by the sorted codes, furthest
|
||||
// triangles first
|
||||
int icount = tcount * 3, idx;
|
||||
if (_sibuf == null || _sibuf.length < icount) {
|
||||
_sibuf = new int[icount];
|
||||
}
|
||||
for (int ii = tcount - 1, sidx = 0; ii >= 0; ii--) {
|
||||
idx = ((_tcodes[ii] >> 16) & 0xFFFF) * 3;
|
||||
_sibuf[sidx++] = _oibuf[idx++];
|
||||
_sibuf[sidx++] = _oibuf[idx++];
|
||||
_sibuf[sidx++] = _oibuf[idx];
|
||||
}
|
||||
|
||||
// copy the indices to the buffer
|
||||
IntBuffer ibuf = getIndexBuffer();
|
||||
ibuf.rewind();
|
||||
ibuf.put(_sibuf, 0, icount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives derived classes a chance to update the states immediately before drawing.
|
||||
*
|
||||
* @param oidx the index of the overlay being rendered, or -1 for the base layer.
|
||||
*/
|
||||
protected void processStates (int oidx)
|
||||
{
|
||||
}
|
||||
|
||||
/** Temporarily stores the original states. */
|
||||
protected RenderState[] _ostates =
|
||||
new RenderState[RenderState.RS_MAX_STATE];
|
||||
}
|
||||
|
||||
/** The name of the texture specified in the model file, which acts as a
|
||||
* property key and a default value. */
|
||||
protected String _textureKey;
|
||||
|
||||
/** The name of this model's textures, or <code>null</code> for none. */
|
||||
protected String[] _textures;
|
||||
|
||||
/** Whether or not to use sphere mapping on this model's textures. */
|
||||
protected boolean _sphereMapped;
|
||||
|
||||
/** The filter mode to use on magnification. */
|
||||
protected int _filterMode;
|
||||
|
||||
/** The mipmap mode to use on minification. */
|
||||
protected int _mipMapMode;
|
||||
|
||||
/** Whether or not to use compressed textures, if available. */
|
||||
protected boolean _compress;
|
||||
|
||||
/** The emissive map, if specified. */
|
||||
protected String _emissiveMap;
|
||||
|
||||
/** Whether or not this mesh is completely emissive. */
|
||||
protected boolean _emissive;
|
||||
|
||||
/** Whether or not this mesh should be rendered with additive blending. */
|
||||
protected boolean _additive;
|
||||
|
||||
/** 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;
|
||||
|
||||
/** The alpha threshold below which fragments are discarded. */
|
||||
protected float _alphaThreshold;
|
||||
|
||||
/** Whether or not the triangles of this mesh should be depth-sorted before
|
||||
* rendering. */
|
||||
protected boolean _translucent;
|
||||
|
||||
/** If non-null, additional layers to render over the base layer. */
|
||||
protected ArrayList<RenderState[]> _overlays;
|
||||
|
||||
/** 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;
|
||||
|
||||
/** For depth-sorted and skinned meshes, the array of vertices. */
|
||||
protected float[] _vbuf;
|
||||
|
||||
/** For depth-sorted meshes, the original array of indices. */
|
||||
protected int[] _oibuf;
|
||||
|
||||
/** 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 additive blending. */
|
||||
protected static AlphaState _addAlpha;
|
||||
|
||||
/** The shared state for alpha testing with the default threshold. */
|
||||
protected static AlphaState _defaultTestAlpha;
|
||||
|
||||
/** The shared state for checking, but not writing to, the z buffer. */
|
||||
protected static ZBufferState _overlayZBuffer;
|
||||
|
||||
/** A light state that simulates emissivity. */
|
||||
protected static LightState _emissiveLight;
|
||||
|
||||
/** A fog state that disables fog. */
|
||||
protected static FogState _noFog;
|
||||
|
||||
/** Work vector to store the camera direction. */
|
||||
protected static Vector3f _cdir = new Vector3f();
|
||||
|
||||
/** Work arrays used to sort triangles. */
|
||||
protected static int[] _tcodes, _stcodes;
|
||||
|
||||
/** Holds counts of each byte and array offsets for radix sorting. */
|
||||
protected static int[] _bcounts = new int[256], _boffsets = new int[256];
|
||||
|
||||
/** Work array used to hold indices of sorted triangles. */
|
||||
protected static int[] _sibuf;
|
||||
|
||||
/** The default alpha threshold. */
|
||||
protected static final float DEFAULT_ALPHA_THRESHOLD = 0.5f;
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
@@ -1,414 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import com.jme.math.Matrix4f;
|
||||
import com.jme.math.Quaternion;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.renderer.Renderer;
|
||||
import com.jme.scene.Node;
|
||||
import com.jme.scene.Spatial;
|
||||
import com.jme.scene.state.RenderState;
|
||||
import com.jme.util.export.JMEExporter;
|
||||
import com.jme.util.export.JMEImporter;
|
||||
import com.jme.util.export.InputCapsule;
|
||||
import com.jme.util.export.OutputCapsule;
|
||||
|
||||
import com.threerings.jme.util.JmeUtil;
|
||||
import com.threerings.jme.util.ShaderCache;
|
||||
|
||||
/**
|
||||
* A {@link Node} with a serialization mechanism tailored to stored models.
|
||||
*/
|
||||
public class ModelNode extends Node
|
||||
implements ModelSpatial
|
||||
{
|
||||
/**
|
||||
* No-arg constructor for deserialization.
|
||||
*/
|
||||
public ModelNode ()
|
||||
{
|
||||
super("node");
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard constructor.
|
||||
*/
|
||||
public ModelNode (String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode ()
|
||||
{
|
||||
// hash on the name rather than the identity for consistent ordering
|
||||
return getName().hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
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
|
||||
public void updateWorldBound ()
|
||||
{
|
||||
// don't bother updating if we know there are no visible descendants
|
||||
if (_hasVisibleDescendants) {
|
||||
super.updateWorldBound();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateWorldVectors ()
|
||||
{
|
||||
super.updateWorldVectors();
|
||||
if (parent instanceof ModelNode) {
|
||||
JmeUtil.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));
|
||||
}
|
||||
}
|
||||
mstore._hasVisibleDescendants = _hasVisibleDescendants;
|
||||
return mstore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read (JMEImporter im)
|
||||
throws IOException
|
||||
{
|
||||
InputCapsule capsule = im.getCapsule(this);
|
||||
setName(capsule.readString("name", null));
|
||||
setLocalTranslation((Vector3f)capsule.readSavable(
|
||||
"localTranslation", null));
|
||||
setLocalRotation((Quaternion)capsule.readSavable(
|
||||
"localRotation", null));
|
||||
setLocalScale((Vector3f)capsule.readSavable(
|
||||
"localScale", null));
|
||||
List<?> children = capsule.readSavableArrayList("children", null);
|
||||
if (children != null) {
|
||||
for (Object child : children) {
|
||||
attachChild((Spatial)child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write (JMEExporter ex)
|
||||
throws IOException
|
||||
{
|
||||
OutputCapsule capsule = ex.getCapsule(this);
|
||||
capsule.write(getName(), "name", null);
|
||||
capsule.write(getLocalTranslation(), "localTranslation", null);
|
||||
capsule.write(getLocalRotation(), "localRotation", null);
|
||||
capsule.write(getLocalScale(), "localScale", null);
|
||||
capsule.writeSavableArrayList(getChildren(), "children", null);
|
||||
}
|
||||
|
||||
// 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 configureShaders (ShaderCache scache)
|
||||
{
|
||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
||||
Spatial child = getChild(ii);
|
||||
if (child instanceof ModelSpatial) {
|
||||
((ModelSpatial)child).configureShaders(scache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether this node should be culled even if it has mesh
|
||||
* descendants.
|
||||
*/
|
||||
public void setForceCull (boolean force)
|
||||
{
|
||||
_forceCull = force;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether this node should be culled even if it has mesh
|
||||
* descendants.
|
||||
*/
|
||||
public boolean getForceCull ()
|
||||
{
|
||||
return _forceCull;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ()
|
||||
{
|
||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
||||
Spatial child = getChild(ii);
|
||||
if (!(child instanceof ModelNode) ||
|
||||
((ModelNode)child).cullInvisibleNodes()) {
|
||||
_hasVisibleDescendants = true;
|
||||
}
|
||||
}
|
||||
updateCullMode();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively culls all model nodes under this one in preparation for
|
||||
* activating the ones listed in an animation.
|
||||
*/
|
||||
protected void cullModelNodes ()
|
||||
{
|
||||
for (int ii = 0, nn = getQuantity(); ii < nn; ii++) {
|
||||
Spatial child = getChild(ii);
|
||||
if (child instanceof ModelNode) {
|
||||
child.setCullMode(CULL_ALWAYS);
|
||||
((ModelNode)child).cullModelNodes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes this node visible if and only if it has visible descendants and
|
||||
* has not been specifically culled.
|
||||
*/
|
||||
protected void updateCullMode ()
|
||||
{
|
||||
setCullMode((_hasVisibleDescendants && !_forceCull) ?
|
||||
CULL_INHERIT : CULL_ALWAYS);
|
||||
}
|
||||
|
||||
/** The node's transform in local and model space. */
|
||||
protected Matrix4f _localTransform = new Matrix4f(),
|
||||
_modelTransform = new Matrix4f();
|
||||
|
||||
/** Whether or not this node has mesh descendants. */
|
||||
protected boolean _hasVisibleDescendants;
|
||||
|
||||
/** If true, cull the node even if it has mesh descendants. */
|
||||
protected boolean _forceCull;
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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.renderer.Renderer;
|
||||
import com.jme.scene.Spatial;
|
||||
|
||||
import com.threerings.jme.util.ShaderCache;
|
||||
|
||||
/**
|
||||
* 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 creates or reconfigures any shaders used by the model using the supplied cache.
|
||||
*/
|
||||
public void configureShaders (ShaderCache scache);
|
||||
|
||||
/**
|
||||
* 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 #blendMeshFrames} as opposed to {@link #setMeshFrame}
|
||||
*/
|
||||
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);
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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.util.Properties;
|
||||
|
||||
import com.jme.math.Quaternion;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.scene.Controller;
|
||||
import com.jme.scene.Spatial;
|
||||
import com.jme.util.export.JMEExporter;
|
||||
import com.jme.util.export.JMEImporter;
|
||||
import com.jme.util.export.InputCapsule;
|
||||
import com.jme.util.export.OutputCapsule;
|
||||
|
||||
import com.threerings.jme.util.JmeUtil;
|
||||
|
||||
/**
|
||||
* A procedural animation that rotates a node around at a constant angular
|
||||
* velocity.
|
||||
*/
|
||||
public class Rotator extends ModelController
|
||||
{
|
||||
@Override
|
||||
public void configure (Properties props, Spatial target)
|
||||
{
|
||||
super.configure(props, target);
|
||||
_axis = JmeUtil.parseAxis(props.getProperty("axis", "x"));
|
||||
_velocity = Float.parseFloat(props.getProperty("velocity", "3.14"));
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void update (float time)
|
||||
{
|
||||
if (!isActive()) {
|
||||
return;
|
||||
}
|
||||
_rot.fromAngleNormalAxis(time * _velocity, _axis);
|
||||
_target.getLocalRotation().multLocal(_rot);
|
||||
}
|
||||
|
||||
@Override
|
||||
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._velocity = _velocity;
|
||||
return rstore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read (JMEImporter im)
|
||||
throws IOException
|
||||
{
|
||||
super.read(im);
|
||||
InputCapsule capsule = im.getCapsule(this);
|
||||
_axis = (Vector3f)capsule.readSavable("axis", null);
|
||||
_velocity = capsule.readFloat("velocity", 0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write (JMEExporter ex)
|
||||
throws IOException
|
||||
{
|
||||
super.write(ex);
|
||||
OutputCapsule capsule = ex.getCapsule(this);
|
||||
capsule.write(_axis, "axis", null);
|
||||
capsule.write(_velocity, "velocity", 0f);
|
||||
}
|
||||
|
||||
/** The axis about which to rotate. */
|
||||
protected Vector3f _axis;
|
||||
|
||||
/** The velocity at which to rotate in radians per second. */
|
||||
protected float _velocity;
|
||||
|
||||
/** A temporary quaternion. */
|
||||
protected Quaternion _rot = new Quaternion();
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
@@ -1,760 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import org.lwjgl.opengl.GLContext;
|
||||
|
||||
import com.jme.bounding.BoundingVolume;
|
||||
import com.jme.math.Matrix4f;
|
||||
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.scene.state.GLSLShaderObjectsState;
|
||||
import com.jme.scene.state.RenderState;
|
||||
import com.jme.system.DisplaySystem;
|
||||
import com.jme.util.ShaderAttribute;
|
||||
import com.jme.util.export.JMEExporter;
|
||||
import com.jme.util.export.JMEImporter;
|
||||
import com.jme.util.export.InputCapsule;
|
||||
import com.jme.util.export.OutputCapsule;
|
||||
import com.jme.util.export.Savable;
|
||||
import com.jme.util.geom.BufferUtils;
|
||||
|
||||
import com.samskivert.util.ArrayUtil;
|
||||
import com.samskivert.util.HashIntMap;
|
||||
import com.samskivert.util.ListUtil;
|
||||
|
||||
import com.threerings.jme.util.JmeUtil;
|
||||
import com.threerings.jme.util.ShaderCache;
|
||||
import com.threerings.jme.util.ShaderConfig;
|
||||
|
||||
/**
|
||||
* A triangle mesh that deforms according to a bone hierarchy.
|
||||
*/
|
||||
public class SkinMesh extends ModelMesh
|
||||
{
|
||||
/** The maximum number of bone matrices that we can use for hardware skinning. */
|
||||
public static final int MAX_SHADER_BONE_COUNT = 31;
|
||||
|
||||
/** The maximum number of bones influencing a single vertex for hardware skinning. */
|
||||
public static final int MAX_SHADER_BONES_PER_VERTEX = 4;
|
||||
|
||||
/** Represents the vertex weights of a group of vertices influenced by the
|
||||
* same set of bones. */
|
||||
public static class WeightGroup
|
||||
implements Savable
|
||||
{
|
||||
/** 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 (Map<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;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public Class<?> getClassTag ()
|
||||
{
|
||||
return getClass();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void read (JMEImporter im)
|
||||
throws IOException
|
||||
{
|
||||
InputCapsule capsule = im.getCapsule(this);
|
||||
vertexCount = capsule.readInt("vertexCount", 0);
|
||||
bones = ArrayUtil.copy(capsule.readSavableArray("bones", null),
|
||||
new Bone[0]);
|
||||
weights = capsule.readFloatArray("weights", null);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void write (JMEExporter ex)
|
||||
throws IOException
|
||||
{
|
||||
OutputCapsule capsule = ex.getCapsule(this);
|
||||
capsule.write(vertexCount, "vertexCount", 0);
|
||||
capsule.write(bones, "bones", null);
|
||||
capsule.write(weights, "weights", null);
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
|
||||
/** Represents a bone that influences the mesh. */
|
||||
public static class Bone
|
||||
implements Savable
|
||||
{
|
||||
/** 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();
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
public Bone ()
|
||||
{
|
||||
transform = new Matrix4f();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebinds this bone for a prototype instance.
|
||||
*
|
||||
* @param pnodes a mapping from prototype nodes to instance nodes
|
||||
*/
|
||||
public Bone rebind (Map<ModelSpatial, ModelSpatial> pnodes)
|
||||
{
|
||||
Bone bone = new Bone((ModelNode)pnodes.get(node));
|
||||
bone.invRefTransform = invRefTransform;
|
||||
bone.transform = new Matrix4f();
|
||||
return bone;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public Class<?> getClassTag ()
|
||||
{
|
||||
return getClass();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void read (JMEImporter im)
|
||||
throws IOException
|
||||
{
|
||||
InputCapsule capsule = im.getCapsule(this);
|
||||
node = (ModelNode)capsule.readSavable("node", null);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void write (JMEExporter ex)
|
||||
throws IOException
|
||||
{
|
||||
OutputCapsule capsule = ex.getCapsule(this);
|
||||
capsule.write(node, "node", null);
|
||||
}
|
||||
|
||||
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 = Sets.newHashSet();
|
||||
for (WeightGroup group : weightGroups) {
|
||||
Collections.addAll(bones, group.bones);
|
||||
}
|
||||
_bones = bones.toArray(new Bone[bones.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOverlay (RenderState[] overlay)
|
||||
{
|
||||
// add a cloned state config (with same uniforms) for the overlay
|
||||
super.addOverlay(overlay);
|
||||
if (_sconfig == null) {
|
||||
return;
|
||||
}
|
||||
if (_osconfigs == null) {
|
||||
_osconfigs = Lists.newArrayListWithCapacity(1);
|
||||
}
|
||||
SkinShaderConfig osconfig = (SkinShaderConfig)_sconfig.clone();
|
||||
osconfig.getState().uniforms = _sconfig.getState().uniforms;
|
||||
_osconfigs.add(osconfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeOverlay (RenderState[] overlay)
|
||||
{
|
||||
// remove the corresponding state config
|
||||
int idx = (_overlays == null) ? -1 : _overlays.indexOf(overlay);
|
||||
super.removeOverlay(overlay);
|
||||
if (_osconfigs != null && idx >= 0) {
|
||||
_osconfigs.remove(idx);
|
||||
if (_osconfigs.isEmpty()) {
|
||||
_osconfigs = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reconstruct (
|
||||
FloatBuffer vertices, FloatBuffer normals, FloatBuffer colors,
|
||||
FloatBuffer textures, IntBuffer indices)
|
||||
{
|
||||
super.reconstruct(vertices, normals, colors, textures, indices);
|
||||
|
||||
// initialize the quantized frame table
|
||||
_frames = new HashIntMap<Object>();
|
||||
}
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
GLSLShaderObjectsState sstate = (GLSLShaderObjectsState)getRenderState(
|
||||
RenderState.RS_GLSL_SHADER_OBJECTS);
|
||||
if (sstate == null) {
|
||||
// vertices and normals must be cloned if not using a shader
|
||||
properties.removeProperty("vertices");
|
||||
properties.removeProperty("normals");
|
||||
}
|
||||
properties.removeProperty("displaylistid");
|
||||
super.putClone(mstore, properties);
|
||||
if (sstate == null) {
|
||||
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 = Maps.newHashMap();
|
||||
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 = (sstate == null) ? new float[_vbuf.length] : _vbuf;
|
||||
mstore._nbuf = (sstate == null) ? new float[_nbuf.length] : _nbuf;
|
||||
if (_sconfig != null) {
|
||||
mstore._sconfig = (SkinShaderConfig)_sconfig.clone();
|
||||
mstore.setRenderState(mstore._sconfig.getState());
|
||||
}
|
||||
return mstore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read (JMEImporter im)
|
||||
throws IOException
|
||||
{
|
||||
super.read(im);
|
||||
InputCapsule capsule = im.getCapsule(this);
|
||||
setWeightGroups(ArrayUtil.copy(capsule.readSavableArray(
|
||||
"weightGroups", null), new WeightGroup[0]));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write (JMEExporter ex)
|
||||
throws IOException
|
||||
{
|
||||
super.write(ex);
|
||||
OutputCapsule capsule = ex.getCapsule(this);
|
||||
capsule.write(_weightGroups, "weightGroups", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void expandModelBounds ()
|
||||
{
|
||||
BoundingVolume obound = getBatch(0).getModelBound().clone(null);
|
||||
updateModelBound();
|
||||
getBatch(0).getModelBound().mergeLocal(obound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReferenceTransforms ()
|
||||
{
|
||||
_invRefTransform = new Matrix4f();
|
||||
if (parent instanceof ModelNode) {
|
||||
Matrix4f transform = new Matrix4f();
|
||||
JmeUtil.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
|
||||
public void lockStaticMeshes (
|
||||
Renderer renderer, boolean useVBOs, boolean useDisplayLists)
|
||||
{
|
||||
// we can use VBOs for color, texture, and indices if not using shaders
|
||||
if (useVBOs && renderer.supportsVBO()) {
|
||||
// use VBOs for shader attributes
|
||||
GLSLShaderObjectsState sstate = (GLSLShaderObjectsState)getRenderState(
|
||||
RenderState.RS_GLSL_SHADER_OBJECTS);
|
||||
if (sstate != null) {
|
||||
for (ShaderAttribute attrib : sstate.attribs.values()) {
|
||||
attrib.useVBO = true;
|
||||
}
|
||||
}
|
||||
|
||||
VBOInfo vboinfo = new VBOInfo(true);
|
||||
if (sstate == null) {
|
||||
vboinfo.setVBOVertexEnabled(false);
|
||||
vboinfo.setVBONormalEnabled(false);
|
||||
}
|
||||
vboinfo.setVBOIndexEnabled(!_translucent);
|
||||
setVBOInfo(vboinfo);
|
||||
}
|
||||
_useDisplayLists = useDisplayLists && !_translucent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureShaders (ShaderCache scache)
|
||||
{
|
||||
if (_disableShaders || !GLContext.getCapabilities().GL_ARB_vertex_shader ||
|
||||
_bones.length > MAX_SHADER_BONE_COUNT) {
|
||||
return;
|
||||
}
|
||||
int bonesPerVertex = 0;
|
||||
for (WeightGroup group : _weightGroups) {
|
||||
bonesPerVertex = Math.max(group.bones.length, bonesPerVertex);
|
||||
}
|
||||
if (bonesPerVertex > MAX_SHADER_BONES_PER_VERTEX) {
|
||||
return;
|
||||
}
|
||||
_sconfig = new SkinShaderConfig(scache, _emissiveMap != null);
|
||||
if (_sconfig.update(getBatch(0).states)) {
|
||||
setShaderAttributes();
|
||||
setRenderState(_sconfig.getState());
|
||||
} else {
|
||||
_sconfig = null;
|
||||
_disableShaders = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeMeshFrame (int frameId, boolean blend)
|
||||
{
|
||||
_storeFrameId = frameId;
|
||||
_storeBlend = blend;
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
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
|
||||
public void updateWorldData (float time)
|
||||
{
|
||||
super.updateWorldData(time);
|
||||
if (_weightGroups == null || _storeFrameId == -1 ||
|
||||
(_storeFrameId == 0 && getCullMode() == CULL_ALWAYS)) {
|
||||
return;
|
||||
}
|
||||
// update the bone transforms
|
||||
for (Bone bone : _bones) {
|
||||
_invRefTransform.mult(bone.node.getModelTransform(),
|
||||
bone.transform);
|
||||
bone.transform.multLocal(bone.invRefTransform);
|
||||
}
|
||||
|
||||
// if we're using shaders, initialize the uniform variables with the bone transforms
|
||||
GLSLShaderObjectsState sstate = (GLSLShaderObjectsState)getRenderState(
|
||||
RenderState.RS_GLSL_SHADER_OBJECTS);
|
||||
if (sstate != null) {
|
||||
for (int ii = 0; ii < _bones.length; ii++) {
|
||||
sstate.setUniform("boneTransforms[" + ii + "]", _bones[ii].transform, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 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(_vbuf.clone(), _nbuf.clone()));
|
||||
} else {
|
||||
TriangleBatch batch = getBatch(0), tbatch = new TriangleBatch();
|
||||
tbatch.setParentGeom(DUMMY_MESH);
|
||||
tbatch.setColorBuffer(batch.getColorBuffer());
|
||||
int nunits = batch.getNumberOfUnits();
|
||||
for (int ii = 0; ii < nunits; ii++) {
|
||||
tbatch.setTextureBuffer(batch.getTextureBuffer(ii), ii);
|
||||
}
|
||||
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(!_translucent);
|
||||
vboinfo.setVBOColorID(ovboinfo.getVBOColorID());
|
||||
for (int ii = 0; ii < nunits; ii++) {
|
||||
vboinfo.setVBOTextureID(ii, ovboinfo.getVBOTextureID(ii));
|
||||
}
|
||||
vboinfo.setVBOIndexID(ovboinfo.getVBOIndexID());
|
||||
tbatch.setVBOInfo(vboinfo);
|
||||
} else if (_useDisplayLists) {
|
||||
tbatch.lockMeshes(
|
||||
DisplaySystem.getDisplaySystem().getRenderer());
|
||||
}
|
||||
_frames.put(_storeFrameId, tbatch);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ModelBatch createModelBatch ()
|
||||
{
|
||||
// update the shader configs immediately before drawing
|
||||
return new ModelBatch() {
|
||||
protected void preDraw () {
|
||||
if (_sconfig != null) {
|
||||
_sconfig.update(states);
|
||||
}
|
||||
}
|
||||
protected void preDrawOverlay (int oidx) {
|
||||
super.preDrawOverlay(oidx);
|
||||
if (_osconfigs != null) {
|
||||
_ostates[RenderState.RS_GLSL_SHADER_OBJECTS] =
|
||||
states[RenderState.RS_GLSL_SHADER_OBJECTS];
|
||||
SkinShaderConfig osconfig = _osconfigs.get(oidx);
|
||||
states[RenderState.RS_GLSL_SHADER_OBJECTS] = osconfig.getState();
|
||||
_osconfigs.get(oidx).update(states);
|
||||
}
|
||||
}
|
||||
protected void postDrawOverlay (int oidx) {
|
||||
super.postDrawOverlay(oidx);
|
||||
if (_osconfigs != null) {
|
||||
states[RenderState.RS_GLSL_SHADER_OBJECTS] =
|
||||
_ostates[RenderState.RS_GLSL_SHADER_OBJECTS];
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void storeOriginalBuffers ()
|
||||
{
|
||||
super.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];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the skin shader attributes (bone indices and weights) in the supplied state.
|
||||
*/
|
||||
protected void setShaderAttributes ()
|
||||
{
|
||||
int size = getBatch(0).getVertexCount() * 4;
|
||||
ByteBuffer bibuf = BufferUtils.createByteBuffer(size);
|
||||
FloatBuffer bwbuf = BufferUtils.createFloatBuffer(size);
|
||||
|
||||
for (WeightGroup group : _weightGroups) {
|
||||
byte[] indices = new byte[4];
|
||||
for (int ii = 0; ii < 4; ii++) {
|
||||
indices[ii] = (byte)((ii < group.bones.length) ?
|
||||
ListUtil.indexOf(_bones, group.bones[ii]) : 0);
|
||||
}
|
||||
for (int ii = 0, widx = 0; ii < group.vertexCount; ii++) {
|
||||
bibuf.put(indices);
|
||||
for (int jj = 0; jj < 4; jj++) {
|
||||
bwbuf.put((jj < group.bones.length) ? group.weights[widx++] : 0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
bibuf.rewind();
|
||||
bwbuf.rewind();
|
||||
|
||||
GLSLShaderObjectsState sstate = _sconfig.getState();
|
||||
sstate.setAttributePointer("boneIndices", 4, false, false, 0, bibuf);
|
||||
sstate.setAttributePointer("boneWeights", 4, false, 0, bwbuf);
|
||||
}
|
||||
|
||||
/** Tracks the configuration of a skin shader. */
|
||||
protected static class SkinShaderConfig extends ShaderConfig
|
||||
{
|
||||
public SkinShaderConfig (ShaderCache scache, boolean emissiveMapped)
|
||||
{
|
||||
super(scache);
|
||||
_emissiveMapped = emissiveMapped;
|
||||
|
||||
// set bindings from texture units to samplers
|
||||
if (emissiveMapped) {
|
||||
_state.setUniform("diffuseMap", 1);
|
||||
_state.setUniform("emissiveMap", 0);
|
||||
} else {
|
||||
_state.setUniform("diffuseMap", 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getVertexShader ()
|
||||
{
|
||||
return "media/jme/skin.vert";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFragmentShader ()
|
||||
{
|
||||
return "media/jme/skin.frag";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getDefinitions (ArrayList<String> defs)
|
||||
{
|
||||
super.getDefinitions(defs);
|
||||
if (_emissiveMapped) {
|
||||
defs.add("EMISSIVE_MAPPED");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getDerivedDefinitions (ArrayList<String> ddefs)
|
||||
{
|
||||
super.getDerivedDefinitions(ddefs);
|
||||
ddefs.add("MAX_BONE_COUNT " + MAX_SHADER_BONE_COUNT);
|
||||
}
|
||||
|
||||
protected boolean _emissiveMapped;
|
||||
}
|
||||
|
||||
/** 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[] _onbuf, _ovbuf, _nbuf;
|
||||
|
||||
/** The primary skin shader configuration. */
|
||||
protected SkinShaderConfig _sconfig;
|
||||
|
||||
/** Skin shader configurations for each overlay. */
|
||||
protected ArrayList<SkinShaderConfig> _osconfigs;
|
||||
|
||||
/** 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;
|
||||
|
||||
/** Set if we determine that our shaders don't compile to prevent us from trying again. */
|
||||
protected static boolean _disableShaders;
|
||||
|
||||
/** A dummy mesh that simply hold transformation values. */
|
||||
protected static final TriMesh DUMMY_MESH = new TriMesh();
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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.util.Properties;
|
||||
|
||||
import com.jme.image.Texture;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.scene.Controller;
|
||||
import com.jme.scene.Spatial;
|
||||
import com.jme.util.export.JMEExporter;
|
||||
import com.jme.util.export.JMEImporter;
|
||||
import com.jme.util.export.InputCapsule;
|
||||
import com.jme.util.export.OutputCapsule;
|
||||
|
||||
import com.threerings.jme.util.JmeUtil;
|
||||
import com.threerings.jme.util.JmeUtil.FrameState;
|
||||
|
||||
/**
|
||||
* Animates a model's textures by flipping between different parts.
|
||||
*/
|
||||
public class TextureAnimator extends TextureController
|
||||
{
|
||||
@Override
|
||||
public void configure (Properties props, Spatial target)
|
||||
{
|
||||
super.configure(props, target);
|
||||
_frameWidth = Float.valueOf(props.getProperty("frame_width", "0.5"));
|
||||
_frameHeight = Float.valueOf(props.getProperty("frame_height", "0.5"));
|
||||
_frameCount = Integer.valueOf(props.getProperty("frame_count", "4"));
|
||||
_frameRate = Float.valueOf(props.getProperty("frame_rate", "1"));
|
||||
_repeatType = JmeUtil.parseRepeatType(props.getProperty("repeat_type"),
|
||||
Controller.RT_WRAP);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void update (float time)
|
||||
{
|
||||
super.update(time);
|
||||
if (!isActive()) {
|
||||
return;
|
||||
}
|
||||
_fstate.update(time, _frameRate, _frameCount, _repeatType);
|
||||
_translation.set(
|
||||
(_fstate.idx % _hframes) * _frameWidth,
|
||||
-(_fstate.idx / _hframes) * _frameHeight,
|
||||
0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Controller putClone (
|
||||
Controller store, Model.CloneCreator properties)
|
||||
{
|
||||
TextureAnimator tstore;
|
||||
if (store == null) {
|
||||
tstore = new TextureAnimator();
|
||||
} else {
|
||||
tstore = (TextureAnimator)store;
|
||||
}
|
||||
super.putClone(tstore, properties);
|
||||
tstore._frameWidth = _frameWidth;
|
||||
tstore._frameHeight = _frameHeight;
|
||||
tstore._frameCount = _frameCount;
|
||||
tstore._frameRate = _frameRate;
|
||||
tstore._repeatType = _repeatType;
|
||||
return tstore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read (JMEImporter im)
|
||||
throws IOException
|
||||
{
|
||||
super.read(im);
|
||||
InputCapsule capsule = im.getCapsule(this);
|
||||
_frameWidth = capsule.readFloat("frameWidth", 0.5f);
|
||||
_frameHeight = capsule.readFloat("frameHeight", 0.5f);
|
||||
_frameCount = capsule.readInt("frameCount", 4);
|
||||
_frameRate = capsule.readFloat("frameRate", 1f);
|
||||
_repeatType = capsule.readInt("repeatType", Controller.RT_WRAP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write (JMEExporter ex)
|
||||
throws IOException
|
||||
{
|
||||
super.write(ex);
|
||||
OutputCapsule capsule = ex.getCapsule(this);
|
||||
capsule.write(_frameWidth, "frameWidth", 0.5f);
|
||||
capsule.write(_frameHeight, "frameHeight", 0.5f);
|
||||
capsule.write(_frameCount, "frameCount", 4);
|
||||
capsule.write(_frameRate, "frameRate", 1f);
|
||||
capsule.write(_repeatType, "repeatType", Controller.RT_WRAP);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initTextures ()
|
||||
{
|
||||
super.initTextures();
|
||||
|
||||
// compute derived values
|
||||
_hframes = (int)Math.floor(1f / _frameWidth);
|
||||
_vframes = (int)Math.floor(1f / _frameHeight);
|
||||
|
||||
// use the same translation vector for all textures
|
||||
_translation = new Vector3f();
|
||||
for (Texture texture : _textures) {
|
||||
texture.setTranslation(_translation);
|
||||
}
|
||||
}
|
||||
|
||||
/** The width of the frames in texture units. */
|
||||
protected float _frameWidth;
|
||||
|
||||
/** The height of the frames in texture units. */
|
||||
protected float _frameHeight;
|
||||
|
||||
/** The number of frames in the texture. */
|
||||
protected int _frameCount;
|
||||
|
||||
/** The rate at which to display the frames (frames per second). */
|
||||
protected float _frameRate;
|
||||
|
||||
/** The repeat type (one of the constants in {@link Controller}). */
|
||||
protected int _repeatType;
|
||||
|
||||
/** The number of frames on the horizontal and vertical axes. */
|
||||
protected transient int _hframes, _vframes;
|
||||
|
||||
/** The animation position. */
|
||||
protected transient FrameState _fstate = new FrameState();
|
||||
|
||||
/** The shared texture translation. */
|
||||
protected transient Vector3f _translation;
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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.HashMap;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import com.jme.image.Texture;
|
||||
import com.jme.scene.state.RenderState;
|
||||
import com.jme.scene.state.TextureState;
|
||||
import com.jme.system.DisplaySystem;
|
||||
|
||||
import com.threerings.jme.util.SpatialVisitor;
|
||||
|
||||
/**
|
||||
* Base class for controllers that affect a model's textures.
|
||||
*/
|
||||
public abstract class TextureController extends ModelController
|
||||
{
|
||||
@Override
|
||||
public void resolveTextures (TextureProvider tprov)
|
||||
{
|
||||
// reinitialize the cloned textures if we re-resolve
|
||||
super.resolveTextures(tprov);
|
||||
_textures = null;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void update (float time)
|
||||
{
|
||||
// initialize the textures before the first update
|
||||
if (_textures == null) {
|
||||
initTextures();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs any per-instance initialization required for textures.
|
||||
*/
|
||||
protected void initTextures ()
|
||||
{
|
||||
// find and clone all textures under the target
|
||||
final HashMap<Texture, Texture> clones = Maps.newHashMap();
|
||||
new SpatialVisitor<ModelMesh>(ModelMesh.class) {
|
||||
protected void visit (ModelMesh mesh) {
|
||||
TextureState otstate = (TextureState)mesh.getRenderState(RenderState.RS_TEXTURE);
|
||||
if (otstate == null) {
|
||||
return;
|
||||
}
|
||||
TextureState ntstate =
|
||||
DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
|
||||
for (int ii = 0, nn = otstate.getNumberOfSetTextures(); ii < nn; ii++) {
|
||||
Texture tex = otstate.getTexture(ii), ctex = clones.get(tex);
|
||||
if (ctex == null) {
|
||||
if (tex.getTextureId() == 0) {
|
||||
otstate.apply(); // load before cloning
|
||||
}
|
||||
clones.put(tex, ctex = tex.createSimpleClone());
|
||||
}
|
||||
ntstate.setTexture(ctex, ii);
|
||||
}
|
||||
mesh.setRenderState(ntstate);
|
||||
}
|
||||
}.traverse(_target);
|
||||
_target.updateRenderState();
|
||||
|
||||
// remember them for updates
|
||||
_textures = clones.values().toArray(new Texture[0]);
|
||||
}
|
||||
|
||||
/** The cloned textures to manipulate. */
|
||||
protected transient Texture[] _textures;
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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,122 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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.util.Properties;
|
||||
|
||||
import com.jme.image.Texture;
|
||||
import com.jme.math.Vector2f;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.scene.Controller;
|
||||
import com.jme.scene.Spatial;
|
||||
import com.jme.util.export.JMEExporter;
|
||||
import com.jme.util.export.JMEImporter;
|
||||
import com.jme.util.export.InputCapsule;
|
||||
import com.jme.util.export.OutputCapsule;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import static com.threerings.jme.Log.log;
|
||||
|
||||
/**
|
||||
* A procedural animation that translates the model's textures at a constant velocity.
|
||||
*/
|
||||
public class TextureTranslator extends TextureController
|
||||
{
|
||||
@Override
|
||||
public void configure (Properties props, Spatial target)
|
||||
{
|
||||
super.configure(props, target);
|
||||
String velstr = props.getProperty("velocity", "1, 0");
|
||||
float[] vel = StringUtil.parseFloatArray(velstr);
|
||||
if (vel != null && vel.length == 2) {
|
||||
_velocity = new Vector2f(vel[0], vel[1]);
|
||||
} else {
|
||||
log.warning("Invalid velocity [velocity=" + velstr + "].");
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void update (float time)
|
||||
{
|
||||
super.update(time);
|
||||
if (!isActive()) {
|
||||
return;
|
||||
}
|
||||
_translation.addLocal(_velocity.x * time, _velocity.y * time, 0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Controller putClone (
|
||||
Controller store, Model.CloneCreator properties)
|
||||
{
|
||||
TextureTranslator tstore;
|
||||
if (store == null) {
|
||||
tstore = new TextureTranslator();
|
||||
} else {
|
||||
tstore = (TextureTranslator)store;
|
||||
}
|
||||
super.putClone(tstore, properties);
|
||||
tstore._velocity = _velocity;
|
||||
return tstore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read (JMEImporter im)
|
||||
throws IOException
|
||||
{
|
||||
super.read(im);
|
||||
InputCapsule capsule = im.getCapsule(this);
|
||||
_velocity = (Vector2f)capsule.readSavable("velocity", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write (JMEExporter ex)
|
||||
throws IOException
|
||||
{
|
||||
super.write(ex);
|
||||
OutputCapsule capsule = ex.getCapsule(this);
|
||||
capsule.write(_velocity, "velocity", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initTextures ()
|
||||
{
|
||||
super.initTextures();
|
||||
|
||||
// use the same translation vector for all textures
|
||||
_translation = new Vector3f();
|
||||
for (Texture texture : _textures) {
|
||||
texture.setTranslation(_translation);
|
||||
}
|
||||
}
|
||||
|
||||
/** The velocity at which to translate the texture. */
|
||||
protected Vector2f _velocity;
|
||||
|
||||
/** The shared translation vector. */
|
||||
protected transient Vector3f _translation;
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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.util.Properties;
|
||||
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.scene.Controller;
|
||||
import com.jme.scene.Spatial;
|
||||
import com.jme.util.export.JMEExporter;
|
||||
import com.jme.util.export.JMEImporter;
|
||||
import com.jme.util.export.InputCapsule;
|
||||
import com.jme.util.export.OutputCapsule;
|
||||
|
||||
import com.threerings.jme.util.JmeUtil;
|
||||
|
||||
/**
|
||||
* A procedural animation that moves a node along a straight line at a constant velocity (then
|
||||
* either repeats or moves it in the other direction).
|
||||
*/
|
||||
public class Translator extends ModelController
|
||||
{
|
||||
@Override
|
||||
public void configure (Properties props, Spatial target)
|
||||
{
|
||||
super.configure(props, target);
|
||||
_from = JmeUtil.parseVector3f(props.getProperty("from", "0, 0, 0"));
|
||||
_to = JmeUtil.parseVector3f(props.getProperty("to", "10, 0, 0"));
|
||||
_duration = Float.parseFloat(props.getProperty("duration", "1"));
|
||||
_repeatType = JmeUtil.parseRepeatType(props.getProperty("repeat_type"),
|
||||
Controller.RT_WRAP);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void update (float time)
|
||||
{
|
||||
if (!isActive()) {
|
||||
return;
|
||||
}
|
||||
_elapsed += (time / _duration);
|
||||
float alpha;
|
||||
if (_repeatType == Controller.RT_CLAMP) {
|
||||
alpha = Math.min(_elapsed, 1f);
|
||||
} else if (_repeatType == Controller.RT_WRAP) {
|
||||
alpha = (_elapsed - (int)_elapsed);
|
||||
} else { // _repeatType == Controller.RT_CYCLE
|
||||
int ipart = (int)_elapsed;
|
||||
float fpart = (_elapsed - ipart);
|
||||
alpha = (ipart % 2 == 0) ? fpart : (1f - fpart);
|
||||
}
|
||||
_target.getLocalTranslation().interpolate(_from, _to, alpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Controller putClone (
|
||||
Controller store, Model.CloneCreator properties)
|
||||
{
|
||||
Translator tstore;
|
||||
if (store == null) {
|
||||
tstore = new Translator();
|
||||
} else {
|
||||
tstore = (Translator)store;
|
||||
}
|
||||
super.putClone(tstore, properties);
|
||||
tstore._from = _from;
|
||||
tstore._to = _to;
|
||||
tstore._duration = _duration;
|
||||
tstore._repeatType = _repeatType;
|
||||
return tstore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read (JMEImporter im)
|
||||
throws IOException
|
||||
{
|
||||
super.read(im);
|
||||
InputCapsule capsule = im.getCapsule(this);
|
||||
_from = (Vector3f)capsule.readSavable("from", null);
|
||||
_to = (Vector3f)capsule.readSavable("to", null);
|
||||
_duration = capsule.readFloat("duration", 1f);
|
||||
_repeatType = capsule.readInt("repeatType", Controller.RT_WRAP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write (JMEExporter ex)
|
||||
throws IOException
|
||||
{
|
||||
super.write(ex);
|
||||
OutputCapsule capsule = ex.getCapsule(this);
|
||||
capsule.write(_from, "from", null);
|
||||
capsule.write(_to, "to", null);
|
||||
capsule.write(_duration, "duration", 1f);
|
||||
capsule.write(_repeatType, "repeatType", Controller.RT_WRAP);
|
||||
}
|
||||
|
||||
/** The beginning and end of the path. */
|
||||
protected Vector3f _from, _to;
|
||||
|
||||
/** The duration of the path. */
|
||||
protected float _duration;
|
||||
|
||||
/** What to do after reaching the destination. */
|
||||
protected int _repeatType;
|
||||
|
||||
/** The accumulated amount of time elapsed. */
|
||||
protected transient float _elapsed;
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
}
|
||||
@@ -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$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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,188 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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 an interpolated spline based on a series of control
|
||||
* points.
|
||||
*/
|
||||
public class CubicSplinePath extends Path
|
||||
{
|
||||
/**
|
||||
* Creates a path for the supplied sprite traversing the supplied
|
||||
* series of points with the specified duration between points. The
|
||||
* path assumes that all the control points exist on either the same
|
||||
* x or y axis. The non-fixed x/y axis must be in order (either ascending
|
||||
* or descending), and the z values will be interpolated.
|
||||
*
|
||||
* @param points a list of points to interpolate between. Currently
|
||||
* all the points along one dimension must be equal.
|
||||
* @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 CubicSplinePath (
|
||||
Sprite sprite, Vector3f[] points, float[] durations)
|
||||
{
|
||||
super(sprite);
|
||||
_isX = (points[0].x != points[1].x);
|
||||
_x = new float[points.length];
|
||||
_y = new float[points.length];
|
||||
_z = (_isX ? points[0].y : points[0].x);
|
||||
for (int ii = 0; ii < points.length; ii++) {
|
||||
_x[ii] = (_isX ? points[ii].x : points[ii].y);
|
||||
_y[ii] = points[ii].z;
|
||||
}
|
||||
_end = points[points.length - 1];
|
||||
_durations = durations;
|
||||
calculateDerivatives();
|
||||
}
|
||||
|
||||
// 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];
|
||||
_current++;
|
||||
}
|
||||
|
||||
// if we have completed out path, move the sprite to the final
|
||||
// position and wrap everything up
|
||||
if (_current >= _durations.length) {
|
||||
_sprite.setLocalTranslation(_end);
|
||||
_sprite.pathCompleted();
|
||||
return;
|
||||
}
|
||||
|
||||
// move the sprite to the appropriate position between points
|
||||
_sprite.getLocalTranslation().set(interpolate(0f));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the second derivative values for all the control points
|
||||
* along the path.
|
||||
*/
|
||||
protected void calculateDerivatives ()
|
||||
{
|
||||
float[] u = new float[_x.length];
|
||||
_y2 = new float[_x.length];
|
||||
for (int ii = 1; ii < _x.length - 1; ii++) {
|
||||
float sig = (_x[ii] - _x[ii-1])/(_x[ii+1] - _x[ii-1]);
|
||||
float p = sig * _y2[ii-1] + 2;
|
||||
_y2[ii] = (sig - 1f) / p;
|
||||
u[ii] = (6f * ((_y[ii+1] - _y[ii]) / (_x[ii+1] - _x[ii]) -
|
||||
(_y[ii] - _y[ii-1]) / (_x[ii] - _x[ii-1])) /
|
||||
(_x[ii+1] - _x[ii-1]) - sig * u[ii-1]) / p;
|
||||
}
|
||||
for (int ii = _x.length - 2; ii >= 0; ii--) {
|
||||
_y2[ii] = _y2[ii] * _y2[ii + 1] + u[ii];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpolates the z value based on the current time point on the path.
|
||||
*
|
||||
* @param delta is the difference from the current time point to
|
||||
* interpolate the point.
|
||||
*/
|
||||
protected Vector3f interpolate (float delta)
|
||||
{
|
||||
int idx = _current;
|
||||
// no change, just use the current value
|
||||
if (delta == 0f) {
|
||||
delta = _accum;
|
||||
|
||||
// find the control point after the current one for interpolating
|
||||
} else if (delta + _accum > _durations[idx]) {
|
||||
delta -= _durations[idx] - _accum;
|
||||
while (idx < _durations.length - 1) {
|
||||
idx++;
|
||||
if (delta < _durations[idx]) {
|
||||
break;
|
||||
}
|
||||
delta -= _durations[idx];
|
||||
}
|
||||
if (delta > _durations[idx] || idx == _durations.length - 1) {
|
||||
return _end;
|
||||
}
|
||||
|
||||
// find the control point before the current one for interpolating
|
||||
} else if (_accum + delta < 0) {
|
||||
delta += _accum;
|
||||
while (idx > 0) {
|
||||
idx--;
|
||||
delta += _durations[idx];
|
||||
if (delta > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (delta < 0) {
|
||||
delta = 0;
|
||||
}
|
||||
|
||||
// we're using the same control point, simply adjust the time point
|
||||
} else {
|
||||
delta += _accum;
|
||||
}
|
||||
|
||||
float h = _x[idx + 1] - _x[idx];
|
||||
// no different between these points, just return the current value
|
||||
if (h == 0) {
|
||||
if (_sprite != null) {
|
||||
return new Vector3f(_sprite.getLocalTranslation());
|
||||
}
|
||||
return new Vector3f();
|
||||
}
|
||||
|
||||
float x = _x[idx] + h * (delta / _durations[idx]);
|
||||
float a = (_x[idx + 1] - x) / h;
|
||||
float b = (x - _x[idx]) / h;
|
||||
float a3 = a * a * a, b3 = b * b * b;
|
||||
float y = a * _y[idx] + b * _y[idx + 1] +
|
||||
((a3 - a) * _y2[idx] + (b3 - b) * _y2[idx + 1]) *
|
||||
(h * h) / 6;
|
||||
float z = y;
|
||||
if (_isX) {
|
||||
y = _z;
|
||||
} else {
|
||||
y = x;
|
||||
x = _z;
|
||||
}
|
||||
return new Vector3f(x, y, z);
|
||||
}
|
||||
|
||||
/** The final path point */
|
||||
protected Vector3f _end;
|
||||
/** The array of control points, derivatives and durations. */
|
||||
protected float[] _x, _y, _y2, _durations;
|
||||
|
||||
protected float _accum, _z;
|
||||
protected int _current;
|
||||
protected boolean _isX;
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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 static com.threerings.jme.Log.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;
|
||||
removeController(oldpath);
|
||||
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 + "].", new Exception());
|
||||
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$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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,163 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import static com.threerings.jme.Log.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 List<FringeRecord> fringes = Lists.newArrayList();
|
||||
|
||||
/** 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 = _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 = _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 = _trecs.get(type);
|
||||
return t.fringes.get(hashValue % t.fringes.size());
|
||||
}
|
||||
|
||||
/** The mapping from tile type to tile record. */
|
||||
protected Map<String, TileRecord> _trecs = Maps.newHashMap();
|
||||
|
||||
/** 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,406 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import com.samskivert.util.QuickSort;
|
||||
|
||||
import com.threerings.media.image.ImageUtil;
|
||||
import com.threerings.media.tile.TileUtil;
|
||||
|
||||
import static com.threerings.jme.Log.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, Map<String, BufferedImage> 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, Map<String, BufferedImage> 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,
|
||||
Map<String, BufferedImage> 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 = 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];
|
||||
}
|
||||
|
||||
List<Integer> indexes = Lists.newArrayList();
|
||||
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] = 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<FringerRec>
|
||||
{
|
||||
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 (FringerRec o) {
|
||||
return priority - 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,75 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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.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,82 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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.xml.SetPropertyFieldsRule;
|
||||
|
||||
import com.threerings.tools.xml.CompiledConfigParser;
|
||||
|
||||
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,212 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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.Iterator;
|
||||
import java.util.Properties;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import com.jme.math.Matrix4f;
|
||||
import com.jme.math.Quaternion;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.scene.Controller;
|
||||
import com.jme.scene.Node;
|
||||
import com.jme.scene.Spatial;
|
||||
|
||||
import com.samskivert.util.Tuple;
|
||||
|
||||
import com.threerings.jme.model.Model;
|
||||
import com.threerings.jme.util.JmeUtil;
|
||||
|
||||
import com.threerings.jme.tools.ModelDef.TransformNode;
|
||||
|
||||
/**
|
||||
* 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 = Lists.newArrayList();
|
||||
|
||||
public void addTransform (TransformDef transform)
|
||||
{
|
||||
transforms.add(transform);
|
||||
}
|
||||
|
||||
/** Adds all transform targets in this frame to the supplied sets. */
|
||||
public void addTransformTargets (
|
||||
HashMap<String, Spatial> nodes, HashMap<String, TransformNode> tnodes,
|
||||
HashSet<Spatial> staticTargets, HashSet<Spatial> transformTargets)
|
||||
{
|
||||
for (int ii = 0, nn = transforms.size(); ii < nn; ii++) {
|
||||
String name = transforms.get(ii).name;
|
||||
Spatial target = nodes.get(name);
|
||||
if (target == null) {
|
||||
continue;
|
||||
}
|
||||
TransformNode tnode = tnodes.get(name);
|
||||
if (tnode.baseLocalTransform != null) {
|
||||
staticTargets.add(target);
|
||||
} else {
|
||||
transformTargets.add(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 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 = Lists.newArrayList();
|
||||
|
||||
public void addFrame (FrameDef frame)
|
||||
{
|
||||
frames.add(frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs through each frame of the animation, updating the transforms of the preprocessing
|
||||
* node to keep track of which transforms diverge from their original states.
|
||||
*/
|
||||
public void filterTransforms (Node root, HashMap<String, TransformNode> nodes)
|
||||
{
|
||||
// clear the nodes' transformed flags
|
||||
for (TransformNode node : nodes.values()) {
|
||||
node.transformed = false;
|
||||
}
|
||||
|
||||
// run through all animation frames
|
||||
for (FrameDef frame : frames) {
|
||||
for (TransformDef transform : frame.transforms) {
|
||||
TransformNode node = nodes.get(transform.name);
|
||||
if (node != null) {
|
||||
node.setLocalTransform(
|
||||
transform.translation, transform.rotation, transform.scale);
|
||||
node.transformed = true;
|
||||
}
|
||||
}
|
||||
root.updateGeometricState(0f, true);
|
||||
for (TransformNode node : nodes.values()) {
|
||||
node.cullDivergentTransforms();
|
||||
}
|
||||
}
|
||||
|
||||
// remove from merge candidates any pairs where one is visible and the other isn't
|
||||
for (TransformNode node : nodes.values()) {
|
||||
for (Iterator<Tuple<TransformNode, Matrix4f>> it = node.relativeTransforms.iterator();
|
||||
it.hasNext(); ) {
|
||||
if (it.next().left.transformed != node.transformed) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, HashMap<String, TransformNode> tnodes)
|
||||
{
|
||||
// find all affected nodes
|
||||
HashSet<Spatial> staticTargets = Sets.newHashSet(),
|
||||
transformTargets = Sets.newHashSet();
|
||||
for (int ii = 0, nn = frames.size(); ii < nn; ii++) {
|
||||
frames.get(ii).addTransformTargets(nodes, tnodes, staticTargets, transformTargets);
|
||||
}
|
||||
|
||||
// create and configure the animation
|
||||
Model.Animation anim = new Model.Animation();
|
||||
anim.frameRate = frameRate;
|
||||
anim.repeatType = JmeUtil.parseRepeatType(props.getProperty("repeat_type"),
|
||||
Controller.RT_CLAMP);
|
||||
|
||||
// collect all transforms
|
||||
anim.staticTargets = staticTargets.toArray(new Spatial[staticTargets.size()]);
|
||||
anim.transformTargets = transformTargets.toArray(new Spatial[transformTargets.size()]);
|
||||
anim.transforms = new Model.Transform[frames.size()][transformTargets.size()];
|
||||
for (int ii = 0; ii < anim.transforms.length; ii++) {
|
||||
anim.transforms[ii] =
|
||||
frames.get(ii).getTransforms(anim.transformTargets);
|
||||
}
|
||||
|
||||
return anim;
|
||||
}
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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.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;
|
||||
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$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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,167 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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.util.HashMap;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import com.jme.scene.Node;
|
||||
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.tools.ModelDef.TransformNode;
|
||||
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
|
||||
{
|
||||
return compile(source, source.getParentFile());
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the model described by the given properties file and compiles it into a
|
||||
* <code>.dat</code> file in the specified directory.
|
||||
*
|
||||
* @return the loaded model, or <code>null</code> if the compiled version is up-to-date
|
||||
*/
|
||||
public static Model compile (File source, File targetDir)
|
||||
throws Exception
|
||||
{
|
||||
String sname = source.getName();
|
||||
int didx = sname.lastIndexOf('.');
|
||||
String root = (didx == -1) ? sname : sname.substring(0, didx);
|
||||
File target = new File(targetDir, root + ".dat");
|
||||
File content = new File(source.getParentFile(), root + ".mxml");
|
||||
|
||||
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 to " + target + "...");
|
||||
|
||||
// parse the model and animations
|
||||
ModelDef mdef = _mparser.parseModel(content.toString());
|
||||
AnimationDef[] adefs = new AnimationDef[anims.length];
|
||||
for (int ii = 0; ii < adefs.length; ii++) {
|
||||
System.out.println(" Adding " + afiles[ii] + "...");
|
||||
adefs[ii] = _aparser.parseAnimation(afiles[ii].toString());
|
||||
}
|
||||
|
||||
// preprocess the model and animations to determine which nodes never move, which never
|
||||
// move within an animation, and which never move with respect to others
|
||||
HashMap<String, TransformNode> tnodes = Maps.newHashMap();
|
||||
Node troot = mdef.createTransformTree(props, tnodes);
|
||||
for (AnimationDef adef : adefs) {
|
||||
adef.filterTransforms(troot, tnodes);
|
||||
}
|
||||
mdef.mergeSpatials(tnodes);
|
||||
|
||||
// load the model content
|
||||
HashMap<String, Spatial> nodes = Maps.newHashMap();
|
||||
Model model = mdef.createModel(props, nodes);
|
||||
model.initPrototype();
|
||||
|
||||
// load the animations, if any
|
||||
for (int ii = 0; ii < anims.length; ii++) {
|
||||
model.addAnimation(anims[ii], adefs[ii].createAnimation(
|
||||
PropertiesUtil.getSubProperties(props, anims[ii]), nodes, tnodes));
|
||||
}
|
||||
|
||||
// write and return the model
|
||||
File parent = target.getParentFile();
|
||||
if (!parent.isDirectory() && !parent.mkdirs()) {
|
||||
System.err.println("Unable to create target directory '" + parent + "'.");
|
||||
} else {
|
||||
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,87 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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.google.common.collect.Lists;
|
||||
|
||||
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 setDest (File dest)
|
||||
{
|
||||
_dest = dest;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
String baseDir = getProject().getBaseDir().getPath();
|
||||
for (FileSet fs : _filesets) {
|
||||
DirectoryScanner ds = fs.getDirectoryScanner(getProject());
|
||||
File fromDir = fs.getDir(getProject());
|
||||
for (String file : ds.getIncludedFiles()) {
|
||||
File source = new File(fromDir, file);
|
||||
File destDir = (_dest == null) ? source.getParentFile() :
|
||||
new File(source.getParent().replaceAll(baseDir, _dest.getPath()));
|
||||
try {
|
||||
CompileModel.compile(source, destDir);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error compiling " + source + ": " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** The directory in which we will generate our model output (in a directory tree mirroring the
|
||||
* source files. */
|
||||
protected File _dest;
|
||||
|
||||
/** A list of filesets that contain XML models. */
|
||||
protected ArrayList<FileSet> _filesets = Lists.newArrayList();
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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();
|
||||
|
||||
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,136 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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.google.common.collect.Lists;
|
||||
|
||||
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 (FileSet fs : _filesets) {
|
||||
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<FileSet> _filesets = Lists.newArrayList();
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,88 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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,49 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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;
|
||||
|
||||
import com.jme.scene.Geometry;
|
||||
import com.jme.scene.batch.GeomBatch;
|
||||
|
||||
/**
|
||||
* Visits all of the {@link GeomBatch}es in a scene.
|
||||
*/
|
||||
public abstract class BatchVisitor extends SpatialVisitor<Geometry>
|
||||
{
|
||||
public BatchVisitor ()
|
||||
{
|
||||
super(Geometry.class);
|
||||
}
|
||||
|
||||
// documentation inherited from SpatialVisitor
|
||||
protected void visit (Geometry geom)
|
||||
{
|
||||
for (int ii = 0, nn = geom.getBatchCount(); ii < nn; ii++) {
|
||||
visit(geom.getBatch(ii));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once for each {@link GeomBatch} in the scene graph.
|
||||
*/
|
||||
protected abstract void visit (GeomBatch batch);
|
||||
}
|
||||
@@ -1,410 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.HashMap;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.image.BandCombineOp;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ComponentColorModel;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.DataBufferByte;
|
||||
import java.awt.image.Raster;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import com.jme.image.Image;
|
||||
import com.jmex.bui.BImage;
|
||||
|
||||
import com.threerings.media.image.Colorization;
|
||||
import com.threerings.media.image.ImageUtil;
|
||||
|
||||
import com.threerings.resource.ResourceManager;
|
||||
|
||||
import static com.threerings.jme.Log.log;
|
||||
|
||||
/**
|
||||
* Manages a weak cache of image data to make life simpler for callers that don't want to worry
|
||||
* about coordinating shared use of the same images.
|
||||
*/
|
||||
public class ImageCache
|
||||
{
|
||||
/**
|
||||
* Creates a JME-compatible image from the supplied buffered image.
|
||||
*/
|
||||
public static Image createImage (BufferedImage bufimg, boolean flip)
|
||||
{
|
||||
return createImage(bufimg, 1f, flip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Colorizes the supplied buffered image (which must be an 8-bit colormapped image), then
|
||||
* converts the colorized image into a form that JME can display.
|
||||
*/
|
||||
public static Image createImage (BufferedImage bufimg, Colorization[] zations, boolean flip)
|
||||
{
|
||||
return createImage(bufimg, zations, 1f, flip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Colorizes the supplied buffered image (which must be an 8-bit colormapped image), then
|
||||
* converts the colorized image into a form that JME can display.
|
||||
*/
|
||||
public static Image createImage (BufferedImage bufimg, Colorization[] zations, float scale,
|
||||
boolean flip)
|
||||
{
|
||||
return createImage(ImageUtil.recolorImage(bufimg, zations), scale, flip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JME-compatible image from the supplied buffered image.
|
||||
*/
|
||||
public static Image createImage (BufferedImage bufimg, float scale, boolean flip)
|
||||
{
|
||||
// make sure images are square powers of two
|
||||
int width = (int)(bufimg.getWidth() * scale);
|
||||
int height = (int)(bufimg.getHeight() * scale);
|
||||
int tsize = nextPOT(Math.max(width, height));
|
||||
|
||||
// convert the the image to the format that OpenGL prefers
|
||||
BufferedImage dispimg = createCompatibleImage(
|
||||
tsize, tsize, bufimg.getColorModel().hasAlpha());
|
||||
|
||||
// flip the image to convert into OpenGL's coordinate system
|
||||
AffineTransform tx = null;
|
||||
if (flip) {
|
||||
tx = AffineTransform.getScaleInstance(scale, -scale);
|
||||
tx.translate((tsize - width) / 2, (height - tsize) / 2 - bufimg.getHeight());
|
||||
}
|
||||
|
||||
// "convert" the image by rendering the old into the new
|
||||
Graphics2D gfx = (Graphics2D)dispimg.getGraphics();
|
||||
gfx.drawImage(bufimg, tx, null);
|
||||
gfx.dispose();
|
||||
|
||||
// now extract the image data into a JME image
|
||||
return convertImage(dispimg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a buffered image in a format compatible with LWJGL with the specified dimensions.
|
||||
*
|
||||
* @param transparent if true, the image will be four bytes per pixel (RGBA), if false it will
|
||||
* be three (and have no alpha channel).
|
||||
*/
|
||||
public static BufferedImage createCompatibleImage (int width, int height, boolean transparent)
|
||||
{
|
||||
if (transparent) {
|
||||
return new BufferedImage(GL_ALPHA_MODEL, Raster.createInterleavedRaster(
|
||||
DataBuffer.TYPE_BYTE, width, height, 4, null),
|
||||
false, null);
|
||||
} else {
|
||||
return new BufferedImage(GL_OPAQUE_MODEL, Raster.createInterleavedRaster(
|
||||
DataBuffer.TYPE_BYTE, width, height, 3, null),
|
||||
false, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an image that was created with {@link #createCompatibleImage} into a JME {@link
|
||||
* Image}. The data is assumed to have already been "flipped".
|
||||
*/
|
||||
public static Image convertImage (BufferedImage bufimg)
|
||||
{
|
||||
Image image = new Image();
|
||||
image.setType(bufimg.getColorModel().hasAlpha() ? Image.RGBA8888 : Image.RGB888);
|
||||
image.setWidth(bufimg.getWidth());
|
||||
image.setHeight(bufimg.getHeight());
|
||||
image.setData(convertImage(bufimg, null));
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the supplied image (which must have been created with {@link
|
||||
* #createCompatibleImage}) into a {@link ByteBuffer} that can be passed to {@link
|
||||
* Image#setData}.
|
||||
*
|
||||
* @param target the results of a previous call to {@link #convertImage} that will be
|
||||
* overwritten or null if a new buffer should be allocated. Of course a reused buffer must be
|
||||
* used with the same image or one with the exact same configuration.
|
||||
*/
|
||||
public static ByteBuffer convertImage (BufferedImage image, ByteBuffer target)
|
||||
{
|
||||
DataBufferByte dbuf = (DataBufferByte)image.getRaster().getDataBuffer();
|
||||
byte[] data = dbuf.getData();
|
||||
if (target == null) {
|
||||
target = ByteBuffer.allocateDirect(data.length);
|
||||
target.order(ByteOrder.nativeOrder());
|
||||
}
|
||||
target.clear();
|
||||
target.put(data);
|
||||
target.flip();
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an image cache that will obtain its image data from the supplied resource manager.
|
||||
*/
|
||||
public ImageCache (ResourceManager rsrcmgr)
|
||||
{
|
||||
_rsrcmgr = rsrcmgr;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link #getImage(String,boolean)}.
|
||||
*/
|
||||
public Image getImage (String rsrcPath)
|
||||
{
|
||||
return getImage(rsrcPath, 1f, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link #getImage(String,float,boolean)}.
|
||||
*/
|
||||
public Image getImage (String rsrcPath, float scale)
|
||||
{
|
||||
return getImage(rsrcPath, scale, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads up an image from the cache if possible or from the resource manager otherwise, in
|
||||
* which case it is prepared for use by JME and OpenGL. <em>Note:</em> these images are cached
|
||||
* separately from the {@link BImage} and {@link BufferedImage} caches.
|
||||
*
|
||||
* @param flip whether or not to convert the image from normal computer coordinates into OpenGL
|
||||
* coordinates when loading. <em>Note:</em> this information is not cached, an image must
|
||||
* <em>always</em> be requested as flipped or not flipped.
|
||||
*/
|
||||
public Image getImage (String rsrcPath, boolean flip)
|
||||
{
|
||||
return getImage(rsrcPath, 1f, flip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads up an image from the cache if possible or from the resource manager otherwise, in
|
||||
* which case it is prepared for use by JME and OpenGL. <em>Note:</em> these images are cached
|
||||
* separately from the {@link BImage} and {@link BufferedImage} caches.
|
||||
*
|
||||
* @param flip whether or not to convert the image from normal computer coordinates into OpenGL
|
||||
* coordinates when loading. <em>Note:</em> this information is not cached, an image must
|
||||
* <em>always</em> be requested as flipped or not flipped.
|
||||
* @param scale a scale factor to apply to the image. As with the flip setting, the scale
|
||||
* factor is not cached.
|
||||
*/
|
||||
public Image getImage (String rsrcPath, float scale, boolean flip)
|
||||
{
|
||||
// first check the cache
|
||||
WeakReference<Image> iref = _imgcache.get(rsrcPath);
|
||||
Image image;
|
||||
if (iref != null && (image = iref.get()) != null) {
|
||||
return image;
|
||||
}
|
||||
|
||||
// load the image data from the resource manager
|
||||
BufferedImage bufimg;
|
||||
try {
|
||||
bufimg = _rsrcmgr.getImageResource(rsrcPath);
|
||||
} catch (Throwable t) {
|
||||
log.warning("Unable to load image resource " +
|
||||
"[path=" + rsrcPath + "].", t);
|
||||
// cope; return an error image of abitrary size
|
||||
bufimg = ImageUtil.createErrorImage(64, 64);
|
||||
}
|
||||
|
||||
// create and cache a new JME image with the appropriate data
|
||||
image = createImage(bufimg, scale, flip);
|
||||
_imgcache.put(rsrcPath, new WeakReference<Image>(image));
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads up an image from the cache if possible or from the resource manager otherwise, in
|
||||
* which case it is prepared for use by BUI. <em>Note:</em> these images are cached separately
|
||||
* from the {@link Image} and {@link BufferedImage} caches.
|
||||
*/
|
||||
public BImage getBImage (String rsrcPath)
|
||||
{
|
||||
return getBImage(rsrcPath, 1f, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads up an image from the cache if possible or from the resource manager otherwise, in
|
||||
* which case it is prepared for use by BUI. <em>Note:</em> these images are cached separately
|
||||
* from the {@link Image} and {@link BufferedImage} caches.
|
||||
*
|
||||
* @param scale If none-unity the image will be scaled by the supplied factor before being
|
||||
* converted into a {@link BImage}.
|
||||
* @param returnNull If set to true, will return a null instead of generating an error image on
|
||||
* failure
|
||||
*/
|
||||
public BImage getBImage (String rsrcPath, float scale, boolean returnNull)
|
||||
{
|
||||
// first check the cache
|
||||
String key = rsrcPath + ":" + scale;
|
||||
WeakReference<BImage> iref = _buicache.get(key);
|
||||
BImage image;
|
||||
if (iref != null && (image = iref.get()) != null) {
|
||||
return image;
|
||||
}
|
||||
|
||||
// load the image data from the resource manager
|
||||
BufferedImage bufimg;
|
||||
try {
|
||||
bufimg = _rsrcmgr.getImageResource(rsrcPath);
|
||||
} catch (Throwable t) {
|
||||
if (returnNull) {
|
||||
return null;
|
||||
}
|
||||
log.warning("Unable to load image resource [path=" + rsrcPath + "].", t);
|
||||
// cope; return an error image of abitrary size
|
||||
bufimg = ImageUtil.createErrorImage(64, 64);
|
||||
}
|
||||
|
||||
// create and cache a new BUI image with the appropriate (scaled if necessary) data
|
||||
image = new BImage(scale == 1f ? bufimg :
|
||||
bufimg.getScaledInstance(Math.round(bufimg.getWidth()*scale),
|
||||
Math.round(bufimg.getHeight()*scale),
|
||||
BufferedImage.SCALE_SMOOTH), true);
|
||||
_buicache.put(key, new WeakReference<BImage>(image));
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads up a silhouette image (in which all non-transparent pixels are set to black) from the
|
||||
* cache if possible or from the resource manager otherwise, in which case it is prepared for
|
||||
* use by BUI.
|
||||
*/
|
||||
public BImage getSilhouetteBImage (String rsrcPath, boolean returnNull)
|
||||
{
|
||||
// first check the cache
|
||||
String key = "silhouette:" + rsrcPath;
|
||||
WeakReference<BImage> iref = _buicache.get(key);
|
||||
BImage image;
|
||||
if (iref != null && (image = iref.get()) != null) {
|
||||
return image;
|
||||
}
|
||||
|
||||
// load the image data from the resource manager
|
||||
BufferedImage bufimg, silimg;
|
||||
try {
|
||||
bufimg = _rsrcmgr.getImageResource(rsrcPath);
|
||||
|
||||
// now turn it into a silhouette
|
||||
silimg = new BufferedImage(bufimg.getWidth(), bufimg.getHeight(),
|
||||
BufferedImage.TYPE_4BYTE_ABGR);
|
||||
new BandCombineOp(NON_ALPHA_TO_BROWN, null).filter(
|
||||
bufimg.getRaster(), silimg.getRaster());
|
||||
|
||||
} catch (Throwable t) {
|
||||
if (returnNull) {
|
||||
return null;
|
||||
}
|
||||
log.warning("Unable to load image resource [path=" + rsrcPath + "].", t);
|
||||
// cope; return an error image of abitrary size
|
||||
silimg = ImageUtil.createErrorImage(64, 64);
|
||||
}
|
||||
|
||||
// create and cache a new BUI image with the appropriate data
|
||||
image = new BImage(silimg, true);
|
||||
_buicache.put(key, new WeakReference<BImage>(image));
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Colorizes the image with supplied path (which must be an 8-bit colormapped image), then
|
||||
* converts the colorized image into a form that JME can display.
|
||||
*/
|
||||
public BImage createColorizedBImage (String path, Colorization[] zations, boolean flip)
|
||||
{
|
||||
return new BImage(ImageUtil.recolorImage(getBufferedImage(path), zations), flip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads up a buffered image from the cache if possible or from the resource manager
|
||||
* otherwise. <em>Note:</em> these images are cached separately from the {@link Image} and
|
||||
* {@link BImage} caches.
|
||||
*/
|
||||
public BufferedImage getBufferedImage (String rsrcPath)
|
||||
{
|
||||
// first check the cache
|
||||
WeakReference<BufferedImage> iref = _bufcache.get(rsrcPath);
|
||||
BufferedImage image;
|
||||
if (iref != null && (image = iref.get()) != null) {
|
||||
return image;
|
||||
}
|
||||
|
||||
// load the image data from the resource manager
|
||||
try {
|
||||
image = _rsrcmgr.getImageResource(rsrcPath);
|
||||
} catch (Throwable t) {
|
||||
log.warning("Unable to load image resource [path=" + rsrcPath + "].", t);
|
||||
// cope; return an error image of abitrary size
|
||||
image = ImageUtil.createErrorImage(64, 64);
|
||||
}
|
||||
|
||||
_bufcache.put(rsrcPath, new WeakReference<BufferedImage>(image));
|
||||
return image;
|
||||
}
|
||||
|
||||
/** Rounds the supplied value up to a power of two. */
|
||||
protected static int nextPOT (int value)
|
||||
{
|
||||
return (Integer.bitCount(value) > 1) ? (Integer.highestOneBit(value) << 1) : value;
|
||||
}
|
||||
|
||||
/** Provides access to our image data. */
|
||||
protected ResourceManager _rsrcmgr;
|
||||
|
||||
/** A cache of {@link Image} instances. */
|
||||
protected HashMap<String,WeakReference<Image>> _imgcache = Maps.newHashMap();
|
||||
|
||||
/** A cache of {@link BImage} instances. */
|
||||
protected HashMap<String,WeakReference<BImage>> _buicache = Maps.newHashMap();
|
||||
|
||||
/** A cache of {@link BufferedImage} instances. */
|
||||
protected HashMap<String,WeakReference<BufferedImage>> _bufcache = Maps.newHashMap();
|
||||
|
||||
/** Used to create buffered images in a format compatible with OpenGL. */
|
||||
protected static ComponentColorModel GL_ALPHA_MODEL = new ComponentColorModel(
|
||||
ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] { 8, 8, 8, 8 },
|
||||
true, false, ComponentColorModel.TRANSLUCENT, DataBuffer.TYPE_BYTE);
|
||||
|
||||
/** Used to create buffered images in a format compatible with OpenGL. */
|
||||
protected static ComponentColorModel GL_OPAQUE_MODEL = new ComponentColorModel(
|
||||
ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] { 8, 8, 8, 0 },
|
||||
false, false, ComponentColorModel.OPAQUE, DataBuffer.TYPE_BYTE);
|
||||
|
||||
/** An operation that converts all channels except alpha to brown. */
|
||||
protected static final float[][] NON_ALPHA_TO_BROWN = {
|
||||
{ 0, 0, 0, 0x66/255f },
|
||||
{ 0, 0, 0, 0x57/255f },
|
||||
{ 0, 0, 0, 0x34/255f },
|
||||
{ 0, 0, 0, 0.5f }
|
||||
};
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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;
|
||||
|
||||
import com.jme.math.Matrix4f;
|
||||
import com.jme.math.Quaternion;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.scene.Controller;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import static com.threerings.jme.Log.log;
|
||||
|
||||
/**
|
||||
* Some static classes and methods of general utility to applications using JME.
|
||||
*/
|
||||
public class JmeUtil
|
||||
{
|
||||
/**
|
||||
* Represents the current position and direction in an animation composed of a fixed number of
|
||||
* discrete frames using one of the repeat types defined in {@link Controller}.
|
||||
*/
|
||||
public static class FrameState
|
||||
{
|
||||
/** The index of the current frame. */
|
||||
public int idx;
|
||||
|
||||
/** The current direction of animation. */
|
||||
public int dir = +1;
|
||||
|
||||
/** The fractional progress towards the next frame. */
|
||||
public float accum;
|
||||
|
||||
/**
|
||||
* Resets the state back to the beginning.
|
||||
*/
|
||||
public void reset ()
|
||||
{
|
||||
set(0, +1, 0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entire frame state.
|
||||
*/
|
||||
public void set (int idx, int dir, float accum)
|
||||
{
|
||||
this.idx = idx;
|
||||
this.dir = dir;
|
||||
this.accum = accum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the frame state after some amount of time has elapsed.
|
||||
*/
|
||||
public void update (float elapsed, float frameRate, int frameCount, int repeatType)
|
||||
{
|
||||
float frames = elapsed * frameRate;
|
||||
for (accum += frames; accum >= 1f; accum -= 1f) {
|
||||
advance(frameCount, repeatType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances the position by one frame.
|
||||
*/
|
||||
public void advance (int frameCount, int repeatType)
|
||||
{
|
||||
if ((idx += dir) >= frameCount) {
|
||||
if (repeatType == Controller.RT_CLAMP) {
|
||||
idx = frameCount - 1;
|
||||
dir = 0;
|
||||
} else if (repeatType == Controller.RT_WRAP) {
|
||||
idx = 0;
|
||||
} else { // repeatType == Controller.RT_CYCLE
|
||||
idx = frameCount - 2;
|
||||
dir = -1;
|
||||
}
|
||||
} else if (idx < 0) {
|
||||
idx = 1;
|
||||
dir = +1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a matrix to the transform defined by the given translation,
|
||||
* rotation, and scale values.
|
||||
*/
|
||||
public static Matrix4f setTransform (
|
||||
Vector3f translation, Quaternion rotation, Vector3f scale, Matrix4f result)
|
||||
{
|
||||
result.setRotationQuaternion(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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to parse a string containing an axis: either "x", "y", or "z", or three
|
||||
* comma-delimited values representing an axis vector. The value returned may be one of
|
||||
* JME's "constant" vectors (for example, {@link Vector3f#UNIT_X}), so don't modify it.
|
||||
*/
|
||||
public static Vector3f parseAxis (String axis)
|
||||
{
|
||||
if ("x".equalsIgnoreCase(axis)) {
|
||||
return Vector3f.UNIT_X;
|
||||
} else if ("y".equalsIgnoreCase(axis)) {
|
||||
return Vector3f.UNIT_Y;
|
||||
} else if ("z".equalsIgnoreCase(axis)) {
|
||||
return Vector3f.UNIT_Z;
|
||||
} else {
|
||||
Vector3f vector = parseVector3f(axis);
|
||||
if (vector != null) {
|
||||
vector.normalizeLocal();
|
||||
}
|
||||
return vector;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to parse a string containing three comma-delimited values and return a
|
||||
* {@link Vector3f}.
|
||||
*/
|
||||
public static Vector3f parseVector3f (String vector)
|
||||
{
|
||||
if (vector != null) {
|
||||
float[] vals = StringUtil.parseFloatArray(vector);
|
||||
if (vals != null && vals.length == 3) {
|
||||
return new Vector3f(vals[0], vals[1], vals[2]);
|
||||
} else {
|
||||
log.warning("Invalid vector [vector=" + vector + "].");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to parse a string describing one of the repeat types defined in {@link Controller}:
|
||||
* "clamp", "cycle", or "wrap". Will return the specified default if the type is
|
||||
* <code>null</code> or invalid.
|
||||
*/
|
||||
public static int parseRepeatType (String type, int defaultType)
|
||||
{
|
||||
if ("clamp".equals(type)) {
|
||||
return Controller.RT_CLAMP;
|
||||
} else if ("cycle".equals(type)) {
|
||||
return Controller.RT_CYCLE;
|
||||
} else if ("wrap".equals(type)) {
|
||||
return Controller.RT_WRAP;
|
||||
} else if (type != null) {
|
||||
log.warning("Invalid repeat type [type=" + type + "].");
|
||||
}
|
||||
return defaultType;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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,263 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.lwjgl.opengl.ARBShaderObjects;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import com.jme.scene.state.GLSLShaderObjectsState;
|
||||
import com.jme.system.DisplaySystem;
|
||||
import com.jme.util.ShaderAttribute;
|
||||
import com.jme.util.ShaderUniform;
|
||||
import com.jme.util.geom.BufferUtils;
|
||||
|
||||
import com.samskivert.util.ArrayUtil;
|
||||
|
||||
import com.threerings.resource.ResourceManager;
|
||||
|
||||
import static com.threerings.jme.Log.*;
|
||||
|
||||
/**
|
||||
* Caches shaders under their names and preprocessor definitions, ensuring that identical shaders
|
||||
* are only compiled once.
|
||||
*/
|
||||
public class ShaderCache
|
||||
{
|
||||
/**
|
||||
* Create a shader cache that will obtain shader source from the supplied resource manager.
|
||||
*/
|
||||
public ShaderCache (ResourceManager rsrcmgr)
|
||||
{
|
||||
_rsrcmgr = rsrcmgr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new shader state with the supplied vertex shader, fragment shader,
|
||||
* and preprocessor definitions. If a program with the given parameters has already been
|
||||
* compiled, the state will use the program id of the existing shader.
|
||||
*
|
||||
* @return the newly created state, or <code>null</code> if there was an error and the
|
||||
* program could not be compiled.
|
||||
*/
|
||||
public GLSLShaderObjectsState createState (String vert, String frag, String... defs)
|
||||
{
|
||||
GLSLShaderObjectsState sstate =
|
||||
DisplaySystem.getDisplaySystem().getRenderer().createGLSLShaderObjectsState();
|
||||
return (configureState(sstate, vert, frag, defs) ? sstate : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* (Re)configures an existing shader state with the supplied parameters.
|
||||
*
|
||||
* @return true if the shader was successfully configured, false if the shader could not
|
||||
* be compiled.
|
||||
*/
|
||||
public boolean configureState (
|
||||
GLSLShaderObjectsState sstate, String vert, String frag, String... defs)
|
||||
{
|
||||
return configureState(sstate, vert, frag, defs, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* (Re)configures an existing shader state with the supplied parameters.
|
||||
*
|
||||
* @param ddefs an optional array of derived preprocessor definitions that, unlike the
|
||||
* principal definitions, need not be compared when differentiating between cached shaders.
|
||||
* @return true if the shader was successfully configured, false if the shader could not
|
||||
* be compiled.
|
||||
*/
|
||||
public boolean configureState (
|
||||
GLSLShaderObjectsState sstate, String vert, String frag, String[] defs, String[] ddefs)
|
||||
{
|
||||
ShaderKey key = new ShaderKey(vert, frag, defs);
|
||||
Integer programId = _programIds.get(key);
|
||||
if (programId == null) {
|
||||
if (ddefs != null) {
|
||||
defs = ArrayUtil.concatenate(defs, ddefs);
|
||||
}
|
||||
GLSLShaderObjectsState pstate = loadShaders(vert, frag, defs);
|
||||
if (pstate == null) {
|
||||
return false;
|
||||
}
|
||||
_programIds.put(key, programId = pstate.getProgramID());
|
||||
}
|
||||
if (sstate.getProgramID() == programId) {
|
||||
return true;
|
||||
}
|
||||
sstate.setProgramID(programId);
|
||||
for (ShaderAttribute attrib : sstate.attribs.values()) {
|
||||
attrib.attributeID = -1;
|
||||
}
|
||||
for (ShaderUniform uniform : sstate.uniforms.values()) {
|
||||
uniform.uniformID = -1;
|
||||
}
|
||||
sstate.setNeedsRefresh(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the specified shader is already loaded. This is useful in order to avoid
|
||||
* generating complex derived definitions when they won't be needed.
|
||||
*/
|
||||
public boolean isLoaded (String vert, String frag, String... defs)
|
||||
{
|
||||
return _programIds.containsKey(new ShaderKey(vert, frag, defs));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the specified shaders (prepending the supplied preprocessor definitions)
|
||||
* and returns a GLSL shader state. One of the supplied names may be <code>null</code>
|
||||
* in order to use the fixed-function pipeline for that part. The method returns
|
||||
* <code>null</code> if the shaders fail to compile (JME will log an error).
|
||||
*
|
||||
* @param defs a number of preprocessor definitions to be #defined in both shaders
|
||||
* (e.g., "ENABLE_FOG", "NUM_LIGHTS 2").
|
||||
*/
|
||||
protected GLSLShaderObjectsState loadShaders (String vert, String frag, String[] defs)
|
||||
{
|
||||
GLSLShaderObjectsState sstate =
|
||||
DisplaySystem.getDisplaySystem().getRenderer().createGLSLShaderObjectsState();
|
||||
sstate.load(
|
||||
(vert == null) ? null : getSource(vert, defs),
|
||||
(frag == null) ? null : getSource(frag, defs));
|
||||
|
||||
// check its link status
|
||||
IntBuffer ibuf = BufferUtils.createIntBuffer(1);
|
||||
ARBShaderObjects.glGetObjectParameterARB(sstate.getProgramID(),
|
||||
ARBShaderObjects.GL_OBJECT_LINK_STATUS_ARB, ibuf);
|
||||
if (ibuf.get(0) == 0) {
|
||||
return null; // failed to link
|
||||
}
|
||||
|
||||
// check the info log
|
||||
ARBShaderObjects.glGetObjectParameterARB(sstate.getProgramID(),
|
||||
ARBShaderObjects.GL_OBJECT_INFO_LOG_LENGTH_ARB, ibuf);
|
||||
ByteBuffer bbuf = BufferUtils.createByteBuffer(ibuf.get(0));
|
||||
ARBShaderObjects.glGetInfoLogARB(sstate.getProgramID(), ibuf, bbuf);
|
||||
String log = Charset.forName("US-ASCII").decode(bbuf).toString();
|
||||
|
||||
// if it runs in software mode, that counts as a failure
|
||||
return (log.indexOf("software") == -1) ? sstate : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the source code to a shader as a single {@link String}, either by fetching it from
|
||||
* the cache or loading it from the resource manager (and caching it). Returns
|
||||
* <code>null</code> (after logging a warning) if the shader couldn't be found.
|
||||
*
|
||||
* @param defs an array of definitions to prepend to the result.
|
||||
*/
|
||||
protected String getSource (String shader, String[] defs)
|
||||
{
|
||||
// fetch the shader source
|
||||
String source = _sources.get(shader);
|
||||
if (source == null) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
try {
|
||||
BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(_rsrcmgr.getResource(shader)));
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
buf.append(line).append('\n');
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.warning("Failed to load shader [name=" + shader + ", error=" + e + "].");
|
||||
return null;
|
||||
}
|
||||
_sources.put(shader, (source = buf.toString()));
|
||||
}
|
||||
|
||||
// prepend the definitions (the version directive comes before anything else)
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("#version 110\n");
|
||||
for (String def : defs) {
|
||||
buf.append("#define ").append(def).append('\n');
|
||||
}
|
||||
buf.append(source);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/** Identifies a cached shader. */
|
||||
protected static class ShaderKey
|
||||
{
|
||||
/** The name of the vertex shader (or <code>null</code> for none). */
|
||||
public String vert;
|
||||
|
||||
/** The name of the fragment shader (or <code>null</code> for none). */
|
||||
public String frag;
|
||||
|
||||
/** The set of preprocessor definitions. */
|
||||
public HashSet<String> defs = Sets.newHashSet();
|
||||
|
||||
public ShaderKey (String vert, String frag, String[] defs)
|
||||
{
|
||||
this.vert = vert;
|
||||
this.frag = frag;
|
||||
Collections.addAll(this.defs, defs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode ()
|
||||
{
|
||||
return (vert == null ? 0 : vert.hashCode()) + (frag == null ? 0 : frag.hashCode()) +
|
||||
defs.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals (Object obj)
|
||||
{
|
||||
ShaderKey okey = (ShaderKey)obj;
|
||||
return Objects.equal(vert, okey.vert) && Objects.equal(frag, okey.frag) &&
|
||||
defs.equals(okey.defs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString ()
|
||||
{
|
||||
return "[vert=" + vert + ", frag=" + frag + ", defs=" + defs + "]";
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides access to shader source. */
|
||||
protected ResourceManager _rsrcmgr;
|
||||
|
||||
/** Maps shader names to source strings. */
|
||||
protected HashMap<String, String> _sources = Maps.newHashMap();
|
||||
|
||||
/** Maps shader keys to linked program ids. */
|
||||
protected HashMap<ShaderKey, Integer> _programIds = Maps.newHashMap();
|
||||
}
|
||||
@@ -1,394 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import com.jme.image.Texture;
|
||||
import com.jme.light.Light;
|
||||
import com.jme.scene.state.FogState;
|
||||
import com.jme.scene.state.GLSLShaderObjectsState;
|
||||
import com.jme.scene.state.LightState;
|
||||
import com.jme.scene.state.RenderState;
|
||||
import com.jme.scene.state.TextureState;
|
||||
import com.jme.system.DisplaySystem;
|
||||
import com.jme.util.ShaderUniform;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Tracks the configuration of a shader, which depends on (among other things) a set of
|
||||
* {@link RenderState}s. When the state set changes, the shader must be reconfigured
|
||||
* (recompiled or refetched from the {@link ShaderCache}).
|
||||
*/
|
||||
public abstract class ShaderConfig
|
||||
implements Cloneable
|
||||
{
|
||||
public ShaderConfig (ShaderCache scache)
|
||||
{
|
||||
_scache = scache;
|
||||
_state = DisplaySystem.getDisplaySystem().getRenderer().createGLSLShaderObjectsState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the render state controlled by this configuration.
|
||||
*/
|
||||
public GLSLShaderObjectsState getState ()
|
||||
{
|
||||
return _state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the configuration according to the provided state set. If the configuration has
|
||||
* changed, the shader will be recompiled or refetched from the cache in order to reflect the
|
||||
* new configuration.
|
||||
*
|
||||
* @return true if all went all, false if the shader could not be compiled.
|
||||
*/
|
||||
public boolean update (RenderState[] states)
|
||||
{
|
||||
// update the configurations to determine if we must reconfigure
|
||||
if (!updateConfigs(states) && _state.getProgramID() > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// reconfigure the shader state, generating the derived definitions only if the
|
||||
// required configuration isn't in the cache
|
||||
String vert = getVertexShader(), frag = getFragmentShader();
|
||||
ArrayList<String> defs = Lists.newArrayList();
|
||||
getDefinitions(defs);
|
||||
String[] darray = defs.toArray(new String[defs.size()]), ddarray = null;
|
||||
if (!_scache.isLoaded(vert, frag, darray)) {
|
||||
ArrayList<String> ddefs = Lists.newArrayList();
|
||||
getDerivedDefinitions(ddefs);
|
||||
ddarray = ddefs.toArray(new String[ddefs.size()]);
|
||||
}
|
||||
return _scache.configureState(_state, vert, frag, darray, ddarray);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderConfig clone ()
|
||||
{
|
||||
try {
|
||||
ShaderConfig other = (ShaderConfig)super.clone();
|
||||
other._state =
|
||||
DisplaySystem.getDisplaySystem().getRenderer().createGLSLShaderObjectsState();
|
||||
other._state.setProgramID(_state.getProgramID());
|
||||
other._state.attribs = _state.attribs;
|
||||
for (ShaderUniform uniform : _state.uniforms.values()) {
|
||||
other._state.uniforms.put(uniform.name, (ShaderUniform)uniform.clone());
|
||||
}
|
||||
if (_lights != null) {
|
||||
other._lights = _lights.clone();
|
||||
for (int ii = 0; ii < _lights.length; ii++) {
|
||||
other._lights[ii] = _lights[ii].clone();
|
||||
}
|
||||
}
|
||||
if (_textures != null) {
|
||||
other._textures = _textures.clone();
|
||||
for (int ii = 0; ii < _textures.length; ii++) {
|
||||
other._textures[ii] = _textures[ii].clone();
|
||||
}
|
||||
}
|
||||
return other;
|
||||
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the component configurations according to the supplied state set, returning
|
||||
* <code>true</code> if they have changed and the shader must be reconfigured.
|
||||
*/
|
||||
protected boolean updateConfigs (RenderState[] states)
|
||||
{
|
||||
// this is one place where we don't want short-circuit evaluation
|
||||
boolean lchanged = updateLightConfigs((LightState)states[RenderState.RS_LIGHT]);
|
||||
boolean tchanged = updateTextureConfigs((TextureState)states[RenderState.RS_TEXTURE]);
|
||||
boolean fchanged = updateFogConfig((FogState)states[RenderState.RS_FOG]);
|
||||
return lchanged || tchanged || fchanged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the light configurations, returning <code>true</code> if they have changed.
|
||||
*/
|
||||
protected boolean updateLightConfigs (LightState lstate)
|
||||
{
|
||||
if (lstate == null || !lstate.isEnabled()) {
|
||||
LightConfig[] olights = _lights;
|
||||
_lights = null;
|
||||
return (olights != null);
|
||||
}
|
||||
int lcount = Math.min(lstate.getQuantity(), MAX_LIGHTS);
|
||||
if (_lights == null || _lights.length != lcount) {
|
||||
_lights = new LightConfig[lcount];
|
||||
for (int ii = 0; ii < lcount; ii++) {
|
||||
_lights[ii] = new LightConfig();
|
||||
_lights[ii].update(lstate.get(ii));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
boolean changed = false;
|
||||
for (int ii = 0; ii < lcount; ii++) {
|
||||
changed |= _lights[ii].update(lstate.get(ii));
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the texture configurations, returning <code>true</code> if they have changed.
|
||||
*/
|
||||
protected boolean updateTextureConfigs (TextureState tstate)
|
||||
{
|
||||
if (tstate == null || !tstate.isEnabled()) {
|
||||
TextureConfig[] otextures = _textures;
|
||||
_textures = null;
|
||||
return (otextures != null);
|
||||
}
|
||||
int tcount = tstate.getNumberOfSetTextures();
|
||||
if (_textures == null || _textures.length != tcount) {
|
||||
_textures = new TextureConfig[tcount];
|
||||
for (int ii = 0; ii < tcount; ii++) {
|
||||
_textures[ii] = new TextureConfig();
|
||||
_textures[ii].update(tstate.getTexture(ii));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
boolean changed = false;
|
||||
for (int ii = 0; ii < tcount; ii++) {
|
||||
changed |= _textures[ii].update(tstate.getTexture(ii));
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the fog configuration, returning <code>true</code> if it has changed.
|
||||
*/
|
||||
protected boolean updateFogConfig (FogState fstate)
|
||||
{
|
||||
int ofunc = _fogDensityFunc;
|
||||
_fogDensityFunc = (fstate == null || !fstate.isEnabled()) ?
|
||||
-1 : fstate.getDensityFunction();
|
||||
return (ofunc != _fogDensityFunc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resource name of the vertex shader (or <code>null</code> for none).
|
||||
*/
|
||||
protected String getVertexShader ()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resource name of the fragment shader (or <code>null</code> for none).
|
||||
*/
|
||||
protected String getFragmentShader ()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the preprocessor definitions that this configuration requires for its shader to the
|
||||
* supplied list.
|
||||
*/
|
||||
protected void getDefinitions (ArrayList<String> defs)
|
||||
{
|
||||
// the distinguishing definitions are just keys
|
||||
if (_lights != null) {
|
||||
defs.add("LIGHTS " + StringUtil.join(_lights, "/"));
|
||||
}
|
||||
if (_textures != null) {
|
||||
defs.add("TEXTURES " + StringUtil.join(_textures, "/"));
|
||||
}
|
||||
if (_fogDensityFunc != -1) {
|
||||
defs.add("FOG " + _fogDensityFunc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the derived preprocessor definitions that this configuration requires to the supplied
|
||||
* list. The derived definitions are not used to distinguish between cached shaders.
|
||||
*/
|
||||
protected void getDerivedDefinitions (ArrayList<String> ddefs)
|
||||
{
|
||||
// add the def that sets the front color based on the light types
|
||||
StringBuilder buf = new StringBuilder("SET_FRONT_COLOR ");
|
||||
if (_lights != null) {
|
||||
// start with the "scene color," which combines scene ambient, emissivity, etc.
|
||||
buf.append("vec3 frontColor = gl_FrontLightModelProduct.sceneColor.rgb; ");
|
||||
|
||||
// add snippets for each of the lights
|
||||
for (int ii = 0; ii < _lights.length; ii++) {
|
||||
String snippet = getLightSnippet(_lights[ii].type);
|
||||
buf.append(snippet.replace("%", Integer.toString(ii)));
|
||||
}
|
||||
|
||||
// the alpha value comes from the diffuse color in the material
|
||||
buf.append("gl_FrontColor = vec4(frontColor, gl_FrontMaterial.diffuse.a);");
|
||||
} else {
|
||||
buf.append("gl_FrontColor = vec4(1.0, 1.0, 1.0, 1.0);");
|
||||
}
|
||||
ddefs.add(buf.toString());
|
||||
|
||||
// add the def that sets the texture coordinates based on the env map modes
|
||||
buf = new StringBuilder("SET_TEX_COORDS");
|
||||
if (_textures != null) {
|
||||
for (int ii = 0; ii < _textures.length; ii++) {
|
||||
TextureConfig texture = _textures[ii];
|
||||
if (texture.envMapMode == -1) {
|
||||
continue;
|
||||
}
|
||||
buf.append(" gl_TexCoord[" + ii + "] = ");
|
||||
if (texture.envMapMode == Texture.EM_SPHERE) {
|
||||
buf.append("vec4(eyeNormal.xy * 0.5 + vec2(0.5, 0.5), 0.0, 1.0);");
|
||||
} else {
|
||||
buf.append("gl_MultiTexCoord" + ii + ";");
|
||||
}
|
||||
}
|
||||
}
|
||||
ddefs.add(buf.toString());
|
||||
|
||||
// add the definition that sets the fog alpha based on the density function
|
||||
buf = new StringBuilder("SET_FOG_ALPHA");
|
||||
if (_fogDensityFunc == FogState.DF_EXP) {
|
||||
buf.append(" fogAlpha = exp(gl_Fog.density * eyeVertex.z);");
|
||||
}
|
||||
ddefs.add(buf.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a code snippet that adds the influence of a light of the specified type (after
|
||||
* replacing '%' with the light index).
|
||||
*/
|
||||
protected String getLightSnippet (int type)
|
||||
{
|
||||
if (type == Light.LT_POINT) {
|
||||
return POINT_LIGHT_SNIPPET;
|
||||
} else if (type == Light.LT_DIRECTIONAL) {
|
||||
return DIRECTIONAL_LIGHT_SNIPPET;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/** The configuration of a single light in a {@link LightState}. */
|
||||
protected static class LightConfig
|
||||
implements Cloneable
|
||||
{
|
||||
/** The type of light (see {@link Light#getType}). */
|
||||
public int type = -1;
|
||||
|
||||
public boolean update (Light light)
|
||||
{
|
||||
int otype = type;
|
||||
type = (light == null) ? -1 : light.getType();
|
||||
return (otype != type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LightConfig clone ()
|
||||
{
|
||||
try {
|
||||
return (LightConfig) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString ()
|
||||
{
|
||||
return Integer.toString(type);
|
||||
}
|
||||
}
|
||||
|
||||
/** The configuration of a single texture in a {@link TextureState}. */
|
||||
protected static class TextureConfig
|
||||
implements Cloneable
|
||||
{
|
||||
/** The environment map mode (see {@link Texture#getEnvironmentalMapMode}). */
|
||||
public int envMapMode = -1;
|
||||
|
||||
public boolean update (Texture texture)
|
||||
{
|
||||
int omode = envMapMode;
|
||||
envMapMode = (texture == null) ? -1 : texture.getEnvironmentalMapMode();
|
||||
return (omode != envMapMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureConfig clone ()
|
||||
{
|
||||
try {
|
||||
return (TextureConfig) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString ()
|
||||
{
|
||||
return Integer.toString(envMapMode);
|
||||
}
|
||||
}
|
||||
|
||||
/** The cache used to reconfigure shaders. */
|
||||
protected ShaderCache _scache;
|
||||
|
||||
/** The state object to reconfigure. */
|
||||
protected GLSLShaderObjectsState _state;
|
||||
|
||||
/** The current light configurations (or <code>null</code> if lighting is disabled). */
|
||||
protected LightConfig[] _lights;
|
||||
|
||||
/** The current texture configurations (or <code>null</code> if texturing is disabled). */
|
||||
protected TextureConfig[] _textures;
|
||||
|
||||
/** The density function of the fog in the scene (or -1 for none). */
|
||||
protected int _fogDensityFunc = -1;
|
||||
|
||||
/** To keep things sane, let's limit the total number of lights (OpenGL allows at least
|
||||
* eight). */
|
||||
protected static final int MAX_LIGHTS = 4;
|
||||
|
||||
/** A code snippet for adding the influence of a point light. */
|
||||
protected static final String POINT_LIGHT_SNIPPET =
|
||||
"vec3 lvec% = gl_LightSource[%].position.xyz - eyeVertex; " +
|
||||
"float ldist% = length(lvec%); " +
|
||||
"frontColor += (gl_FrontLightProduct[%].ambient.rgb + " +
|
||||
"gl_FrontLightProduct[%].diffuse.rgb * " +
|
||||
"max(dot(eyeNormal, normalize(lvec%)), 0.0)) / " +
|
||||
"(gl_LightSource[%].constantAttenuation + " +
|
||||
"ldist% * gl_LightSource[%].linearAttenuation + " +
|
||||
"ldist% * ldist% * gl_LightSource[%].quadraticAttenuation);";
|
||||
|
||||
/** A code snippet for adding the influence of a directional light. */
|
||||
protected static final String DIRECTIONAL_LIGHT_SNIPPET =
|
||||
"frontColor += gl_FrontLightProduct[%].ambient.rgb + " +
|
||||
"gl_FrontLightProduct[%].diffuse.rgb * " +
|
||||
"max(dot(eyeNormal, gl_LightSource[%].position.xyz), 0.0);";
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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;
|
||||
|
||||
import com.jme.scene.Node;
|
||||
import com.jme.scene.Spatial;
|
||||
|
||||
/**
|
||||
* Performs a depth-first scene graph traversal looking for {@link Spatial}s
|
||||
* of a given class.
|
||||
*/
|
||||
public abstract class SpatialVisitor<T extends Spatial>
|
||||
{
|
||||
public SpatialVisitor (Class<T> type)
|
||||
{
|
||||
_type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverses the given node in depth-first order, calling {@link #visit}
|
||||
* for each {@link Spatial} of the configured class encountered.
|
||||
*/
|
||||
public void traverse (Spatial spatial)
|
||||
{
|
||||
if (_type.isInstance(spatial)) {
|
||||
visit(_type.cast(spatial));
|
||||
}
|
||||
if (spatial instanceof Node) {
|
||||
Node node = (Node)spatial;
|
||||
for (int ii = 0, nn = node.getQuantity(); ii < nn; ii++) {
|
||||
traverse(node.getChild(ii));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called once for each {@link Spatial} of the configured class in the
|
||||
* scene graph.
|
||||
*/
|
||||
protected abstract void visit (T child);
|
||||
|
||||
/** The type of spatial of interest. */
|
||||
protected Class<T> _type;
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Nenya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/nenya/
|
||||
//
|
||||
// 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,62 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.jme;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFrame;
|
||||
|
||||
import com.jme.bounding.BoundingBox;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.scene.shape.Box;
|
||||
|
||||
/**
|
||||
* Tests the JME/AWT integration bits.
|
||||
*/
|
||||
public class JmeCanvasTestApp extends JmeCanvasApp
|
||||
{
|
||||
public static void main (String[] args)
|
||||
{
|
||||
final JmeCanvasTestApp app = new JmeCanvasTestApp();
|
||||
JFrame frame = new JFrame("JmeCanvasTest");
|
||||
frame.getContentPane().add(app.getCanvas(), BorderLayout.CENTER);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.setSize(800, 600);
|
||||
frame.setVisible(true);
|
||||
|
||||
JButton button = new JButton("Create box");
|
||||
frame.getContentPane().add(button, BorderLayout.SOUTH);
|
||||
button.addActionListener(new ActionListener() {
|
||||
public void actionPerformed (ActionEvent event) {
|
||||
app.addBox();
|
||||
}
|
||||
});
|
||||
app.run();
|
||||
}
|
||||
|
||||
public void addBox ()
|
||||
{
|
||||
Vector3f max = new Vector3f(5, 5, 5);
|
||||
Vector3f min = new Vector3f(-5, -5, -5);
|
||||
|
||||
Box box = new Box("Box", min, max);
|
||||
box.setModelBound(new BoundingBox());
|
||||
box.updateModelBound();
|
||||
box.setLocalTranslation(new Vector3f(0, 0, -10));
|
||||
_geom.attachChild(box);
|
||||
_geom.updateRenderState();
|
||||
}
|
||||
|
||||
protected JmeCanvasTestApp ()
|
||||
{
|
||||
super(800, 600);
|
||||
}
|
||||
|
||||
protected void initRoot ()
|
||||
{
|
||||
super.initRoot();
|
||||
}
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
//
|
||||
// $Id: JabberApp.java 3098 2004-08-27 02:12:55Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 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.client;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import com.jme.bounding.BoundingBox;
|
||||
import com.jme.math.FastMath;
|
||||
import com.jme.math.Matrix3f;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.renderer.ColorRGBA;
|
||||
import com.jme.scene.shape.Box;
|
||||
import com.jme.util.LoggingSystem;
|
||||
import com.jme.util.geom.BufferUtils;
|
||||
import com.jmex.bui.BStyleSheet;
|
||||
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.net.UsernamePasswordCreds;
|
||||
|
||||
import com.threerings.jme.JmeApp;
|
||||
|
||||
import static com.threerings.jme.Log.log;
|
||||
|
||||
/**
|
||||
* The main point of entry for the Jabber client application. It creates
|
||||
* and initializes the myriad components of the client and sets all the
|
||||
* proper wheels in motion.
|
||||
*/
|
||||
public class JabberApp extends JmeApp
|
||||
{
|
||||
/** Used to configure our user interface. */
|
||||
public static BStyleSheet stylesheet;
|
||||
|
||||
// documentation inherited
|
||||
public boolean init ()
|
||||
{
|
||||
if (!super.init()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// initialize our client instance
|
||||
_client = new JabberClient();
|
||||
_client.init(this);
|
||||
|
||||
// add some simple geometry for kicks
|
||||
Vector3f max = new Vector3f(15, 15, 15);
|
||||
Vector3f min = new Vector3f(5, 5, 5);
|
||||
|
||||
Box t = new Box("Box", min, max);
|
||||
t.setModelBound(new BoundingBox());
|
||||
t.updateModelBound();
|
||||
t.setLocalTranslation(new Vector3f(0, 0, -15));
|
||||
ColorRGBA[] colors = new ColorRGBA[24];
|
||||
for (int i = 0; i < 24; i++) {
|
||||
colors[i] = ColorRGBA.randomColor();
|
||||
}
|
||||
t.setColorBuffer(0, BufferUtils.createFloatBuffer(colors));
|
||||
_root.attachChild(t);
|
||||
_root.updateRenderState();
|
||||
|
||||
// set up the camera
|
||||
Vector3f loc = new Vector3f(0, -200, 200);
|
||||
_camera.setLocation(loc);
|
||||
Matrix3f rotm = new Matrix3f();
|
||||
rotm.fromAngleAxis(-FastMath.PI/5, _camera.getLeft());
|
||||
rotm.mult(_camera.getDirection(), _camera.getDirection());
|
||||
rotm.mult(_camera.getUp(), _camera.getUp());
|
||||
rotm.mult(_camera.getLeft(), _camera.getLeft());
|
||||
_camera.update();
|
||||
|
||||
// speed up key input
|
||||
_input.setActionSpeed(100f);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void run (String server, String username, String password)
|
||||
{
|
||||
Client client = _client.getContext().getClient();
|
||||
|
||||
// pass them on to the client
|
||||
log.info("Using [server=" + server + ".");
|
||||
client.setServer(server, Client.DEFAULT_SERVER_PORTS);
|
||||
|
||||
// configure the client with some credentials and logon
|
||||
if (username != null && password != null) {
|
||||
// create and set our credentials
|
||||
client.setCredentials(
|
||||
new UsernamePasswordCreds(new Name(username), password));
|
||||
client.logon();
|
||||
}
|
||||
|
||||
// now start up the main event loop
|
||||
run();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void stop ()
|
||||
{
|
||||
// log off before we shutdown
|
||||
Client client = _client.getContext().getClient();
|
||||
if (client.isLoggedOn()) {
|
||||
client.logoff(false);
|
||||
}
|
||||
log.info("Stopping.");
|
||||
super.stop();
|
||||
}
|
||||
|
||||
public static void main (String[] args)
|
||||
{
|
||||
LoggingSystem.getLogger().setLevel(java.util.logging.Level.OFF);
|
||||
|
||||
String server = "localhost";
|
||||
if (args.length > 0) {
|
||||
server = args[0];
|
||||
}
|
||||
|
||||
// load up the default BUI stylesheet
|
||||
try {
|
||||
InputStream stin = JabberApp.class.getClassLoader().
|
||||
getResourceAsStream("rsrc/style.bss");
|
||||
stylesheet = new BStyleSheet(
|
||||
new InputStreamReader(stin),
|
||||
new BStyleSheet.DefaultResourceProvider());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(System.err);
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
String username = (args.length > 1) ? args[1] : null;
|
||||
String password = (args.length > 2) ? args[2] : null;
|
||||
|
||||
JabberApp app = new JabberApp();
|
||||
app.init();
|
||||
app.run(server, username, password);
|
||||
}
|
||||
|
||||
protected JabberClient _client;
|
||||
}
|
||||
@@ -1,238 +0,0 @@
|
||||
//
|
||||
// $Id: JabberClient.java 3283 2004-12-22 19:23:00Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 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.client;
|
||||
|
||||
import com.jmex.bui.BRootNode;
|
||||
import com.jme.input.InputHandler;
|
||||
import com.jme.renderer.Renderer;
|
||||
import com.jme.scene.Node;
|
||||
import com.jme.system.DisplaySystem;
|
||||
|
||||
import com.samskivert.util.Config;
|
||||
import com.threerings.util.MessageManager;
|
||||
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.client.SessionObserver;
|
||||
import com.threerings.presents.dobj.DObjectManager;
|
||||
|
||||
import com.threerings.crowd.chat.client.ChatDirector;
|
||||
import com.threerings.crowd.client.LocationDirector;
|
||||
import com.threerings.crowd.client.OccupantDirector;
|
||||
import com.threerings.crowd.client.PlaceView;
|
||||
import com.threerings.crowd.util.CrowdContext;
|
||||
|
||||
import com.threerings.jme.JmeContext;
|
||||
import com.threerings.jme.camera.CameraHandler;
|
||||
|
||||
/**
|
||||
* The Jabber client takes care of instantiating all of the proper
|
||||
* managers and loading up all of the necessary configuration and getting
|
||||
* the client bootstrapped. It can be extended by games that require an
|
||||
* extended context implementation.
|
||||
*/
|
||||
public class JabberClient
|
||||
implements SessionObserver
|
||||
{
|
||||
/**
|
||||
* Initializes a new client and provides it with a frame in which to
|
||||
* display everything.
|
||||
*/
|
||||
public void init (JabberApp app)
|
||||
{
|
||||
_app = app;
|
||||
|
||||
// create our context
|
||||
_ctx = createContextImpl();
|
||||
|
||||
// create the directors/managers/etc. provided by the context
|
||||
createContextServices(app);
|
||||
|
||||
// for test purposes, hardcode the server info
|
||||
_client.setServer("localhost", Client.DEFAULT_SERVER_PORTS);
|
||||
_client.addClientObserver(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the context in effect for this client. This
|
||||
* reference is valid for the lifetime of the application.
|
||||
*/
|
||||
public CrowdContext getContext ()
|
||||
{
|
||||
return _ctx;
|
||||
}
|
||||
|
||||
// documentation inherited from interface SessionObserver
|
||||
public void clientWillLogon (Client client)
|
||||
{
|
||||
// nada
|
||||
}
|
||||
|
||||
// documentation inherited from interface SessionObserver
|
||||
public void clientDidLogon (Client client)
|
||||
{
|
||||
// enter the one and only chat room; giant hack warning: we know
|
||||
// that the place we're headed to is ID 2 because there's only one
|
||||
// place on the whole server; a normal client would either issue
|
||||
// an invocation service request at this point or look at
|
||||
// something in the BootstrapData to figure out what to do
|
||||
_ctx.getLocationDirector().moveTo(2);
|
||||
}
|
||||
|
||||
// documentation inherited from interface SessionObserver
|
||||
public void clientObjectDidChange (Client client)
|
||||
{
|
||||
// nada
|
||||
}
|
||||
|
||||
// documentation inherited from interface SessionObserver
|
||||
public void clientDidLogoff (Client client)
|
||||
{
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@link JabberContext} implementation that will be
|
||||
* passed around to all of the client code. Derived classes may wish
|
||||
* to override this and create some extended context implementation.
|
||||
*/
|
||||
protected CrowdContext createContextImpl ()
|
||||
{
|
||||
return new JabberContextImpl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and initializes the various services that are provided by
|
||||
* the context. Derived classes that provide an extended context
|
||||
* should override this method and create their own extended
|
||||
* services. They should be sure to call
|
||||
* <code>super.createContextServices</code>.
|
||||
*/
|
||||
protected void createContextServices (JabberApp app)
|
||||
{
|
||||
// create the handles on our various services
|
||||
_client = new Client(null, app);
|
||||
|
||||
// create our managers and directors
|
||||
_locdir = new LocationDirector(_ctx);
|
||||
_occdir = new OccupantDirector(_ctx);
|
||||
_msgmgr = new MessageManager(MESSAGE_MANAGER_PREFIX);
|
||||
_chatdir = new ChatDirector(_ctx, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* The context implementation. This provides access to all of the
|
||||
* objects and services that are needed by the operating client.
|
||||
*/
|
||||
protected class JabberContextImpl implements CrowdContext, JmeContext
|
||||
{
|
||||
/**
|
||||
* Apparently the default constructor has default access, rather
|
||||
* than protected access, even though this class is declared to be
|
||||
* protected. Why, I don't know, but we need to be able to extend
|
||||
* this class elsewhere, so we need this.
|
||||
*/
|
||||
protected JabberContextImpl () {
|
||||
}
|
||||
|
||||
public Client getClient () {
|
||||
return _client;
|
||||
}
|
||||
|
||||
public DObjectManager getDObjectManager () {
|
||||
return _client.getDObjectManager();
|
||||
}
|
||||
|
||||
public Config getConfig () {
|
||||
return _config;
|
||||
}
|
||||
|
||||
public LocationDirector getLocationDirector () {
|
||||
return _locdir;
|
||||
}
|
||||
|
||||
public OccupantDirector getOccupantDirector () {
|
||||
return _occdir;
|
||||
}
|
||||
|
||||
public ChatDirector getChatDirector () {
|
||||
return _chatdir;
|
||||
}
|
||||
|
||||
public MessageManager getMessageManager () {
|
||||
return _msgmgr;
|
||||
}
|
||||
|
||||
public void setPlaceView (PlaceView view) {
|
||||
// TBD
|
||||
}
|
||||
|
||||
public void clearPlaceView (PlaceView view) {
|
||||
// we'll just let the next place view replace our old one
|
||||
}
|
||||
|
||||
public Renderer getRenderer () {
|
||||
return _app.getContext().getRenderer();
|
||||
}
|
||||
|
||||
public DisplaySystem getDisplay () {
|
||||
return _app.getContext().getDisplay();
|
||||
}
|
||||
|
||||
public CameraHandler getCameraHandler () {
|
||||
return _app.getContext().getCameraHandler();
|
||||
}
|
||||
|
||||
public Node getGeometry () {
|
||||
return _app.getContext().getGeometry();
|
||||
}
|
||||
|
||||
public Node getInterface () {
|
||||
return _app.getContext().getInterface();
|
||||
}
|
||||
|
||||
public InputHandler getInputHandler () {
|
||||
return _app.getContext().getInputHandler();
|
||||
}
|
||||
|
||||
// public InputHandler getBufferedInputHandler () {
|
||||
// return _app.getContext().getBufferedInputHandler();
|
||||
// }
|
||||
|
||||
public BRootNode getRootNode () {
|
||||
return _app.getContext().getRootNode();
|
||||
}
|
||||
}
|
||||
|
||||
protected CrowdContext _ctx;
|
||||
protected JabberApp _app;
|
||||
protected Config _config = new Config("jabber");
|
||||
|
||||
protected Client _client;
|
||||
protected LocationDirector _locdir;
|
||||
protected OccupantDirector _occdir;
|
||||
protected ChatDirector _chatdir;
|
||||
protected MessageManager _msgmgr;
|
||||
|
||||
/** The prefix prepended to localization bundle names before looking
|
||||
* them up in the classpath. */
|
||||
protected static final String MESSAGE_MANAGER_PREFIX = "rsrc.i18n";
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.jme.client;
|
||||
|
||||
import com.threerings.crowd.client.PlaceController;
|
||||
import com.threerings.crowd.client.PlaceView;
|
||||
import com.threerings.crowd.util.CrowdContext;
|
||||
|
||||
/**
|
||||
* Handles the basic bits for our simple chat room.
|
||||
*/
|
||||
public class JabberController extends PlaceController
|
||||
{
|
||||
protected PlaceView createPlaceView (CrowdContext ctx)
|
||||
{
|
||||
return new JabberView(ctx);
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.jme.client;
|
||||
|
||||
import com.jmex.bui.BWindow;
|
||||
import com.jmex.bui.layout.BorderLayout;
|
||||
|
||||
import com.threerings.crowd.client.PlaceView;
|
||||
import com.threerings.crowd.data.PlaceObject;
|
||||
import com.threerings.crowd.util.CrowdContext;
|
||||
|
||||
import com.threerings.jme.JmeContext;
|
||||
import com.threerings.jme.chat.ChatView;
|
||||
|
||||
/**
|
||||
* Manages the "view" when we're in the chat room.
|
||||
*/
|
||||
public class JabberView extends BWindow
|
||||
implements PlaceView
|
||||
{
|
||||
public JabberView (CrowdContext ctx)
|
||||
{
|
||||
super(JabberApp.stylesheet, new BorderLayout());
|
||||
|
||||
_ctx = ctx;
|
||||
_jctx = (JmeContext)ctx;
|
||||
_chat = new ChatView(_jctx, _ctx.getChatDirector());
|
||||
add(_chat, BorderLayout.CENTER);
|
||||
|
||||
_jctx.getRootNode().addWindow(this);
|
||||
setBounds(0, 40, 640, 240);
|
||||
}
|
||||
|
||||
// documentation inherited from interface PlaceView
|
||||
public void willEnterPlace (PlaceObject plobj)
|
||||
{
|
||||
_chat.willEnterPlace(plobj);
|
||||
}
|
||||
|
||||
// documentation inherited from interface PlaceView
|
||||
public void didLeavePlace (PlaceObject plobj)
|
||||
{
|
||||
_chat.didLeavePlace(plobj);
|
||||
}
|
||||
|
||||
protected CrowdContext _ctx;
|
||||
protected JmeContext _jctx;
|
||||
protected ChatView _chat;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.jme.data;
|
||||
|
||||
import com.threerings.crowd.client.PlaceController;
|
||||
import com.threerings.crowd.data.PlaceConfig;
|
||||
import com.threerings.jme.client.JabberController;
|
||||
|
||||
/**
|
||||
* Defines the necessary bits for our chat room.
|
||||
*/
|
||||
public class JabberConfig extends PlaceConfig
|
||||
{
|
||||
// documentation inherited
|
||||
public PlaceController createController ()
|
||||
{
|
||||
return new JabberController();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public String getManagerClassName ()
|
||||
{
|
||||
// nothing special needed on the server side
|
||||
return "com.threerings.crowd.server.PlaceManager";
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.jme.server;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
import com.threerings.crowd.server.CrowdServer;
|
||||
import com.threerings.crowd.server.PlaceManager;
|
||||
|
||||
import com.threerings.jme.data.JabberConfig;
|
||||
|
||||
import static com.threerings.crowd.Log.log;
|
||||
|
||||
/**
|
||||
* A basic server that creates a single room and sticks everyone in it where they can chat with one
|
||||
* another.
|
||||
*/
|
||||
public class JabberServer extends CrowdServer
|
||||
{
|
||||
@Override // from CrowdServer
|
||||
public void init (Injector injector)
|
||||
throws Exception
|
||||
{
|
||||
super.init(injector);
|
||||
|
||||
// create a single location
|
||||
_place = _plreg.createPlace(new JabberConfig());
|
||||
log.info("Created chat room " + _place.where() + ".");
|
||||
}
|
||||
|
||||
public static void main (String[] args)
|
||||
{
|
||||
Injector injector = Guice.createInjector(new Module());
|
||||
JabberServer server = injector.getInstance(JabberServer.class);
|
||||
try {
|
||||
server.init(injector);
|
||||
server.run();
|
||||
} catch (Exception e) {
|
||||
log.warning("Unable to initialize server.", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected PlaceManager _place;
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.jme.sprite;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import com.jme.math.FastMath;
|
||||
import com.jme.math.Matrix3f;
|
||||
import com.jme.math.Quaternion;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.renderer.ColorRGBA;
|
||||
import com.jme.scene.shape.Box;
|
||||
import com.jme.scene.shape.Disk;
|
||||
import com.jme.scene.shape.Quad;
|
||||
import com.jme.scene.state.LightState;
|
||||
import com.jme.util.LoggingSystem;
|
||||
|
||||
import com.threerings.jme.JmeApp;
|
||||
import com.threerings.jme.sprite.LineSegmentPath;
|
||||
|
||||
/**
|
||||
* Used for testing paths.
|
||||
*/
|
||||
public class PathTestApp extends JmeApp
|
||||
{
|
||||
protected void initRoot ()
|
||||
{
|
||||
super.initRoot();
|
||||
|
||||
float dist = 10;
|
||||
|
||||
// set up the camera
|
||||
Vector3f loc = new Vector3f(0, -dist, 10);
|
||||
_camera.setLocation(loc);
|
||||
Matrix3f rotm = new Matrix3f();
|
||||
rotm.fromAngleAxis(-FastMath.PI/4, _camera.getLeft());
|
||||
rotm.multLocal(_camera.getDirection());
|
||||
rotm.multLocal(_camera.getUp());
|
||||
rotm.multLocal(_camera.getLeft());
|
||||
_camera.update();
|
||||
|
||||
for (int yy = -1; yy < 1; yy++) {
|
||||
for (int xx = -1; xx < 1; xx++) {
|
||||
Quad quad = new Quad("ground", 10, 10);
|
||||
quad.setLightCombineMode(LightState.OFF);
|
||||
int index = (yy+1)*2 + (xx+1);
|
||||
quad.setSolidColor(COLORS[index]);
|
||||
quad.setLocalTranslation(
|
||||
new Vector3f(xx * 10 + 5, yy * 10 + 5, -1));
|
||||
_geom.attachChild(quad);
|
||||
}
|
||||
}
|
||||
|
||||
for (int yy = -1; yy <= 1; yy++) {
|
||||
for (int xx = -1; xx <= 1; xx++) {
|
||||
if (xx != 0 || yy != 0) {
|
||||
setup(xx * dist, yy * dist);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void setup (float x, float y)
|
||||
{
|
||||
Box target = new Box("target", new Vector3f(-.1f, -.1f, -.1f),
|
||||
new Vector3f(.1f, .1f, .1f));
|
||||
target.setLocalTranslation(new Vector3f(x, y, 0));
|
||||
_geom.attachChild(target);
|
||||
|
||||
Box box = new Box("box", new Vector3f(-1, -0.5f, -0.25f),
|
||||
new Vector3f(1, 0.5f, 0.25f));
|
||||
Disk disk = new Disk("dot", 10, 10, 0.5f);
|
||||
disk.setLightCombineMode(LightState.OFF);
|
||||
disk.setLocalTranslation(new Vector3f(0.5f, 0f, 0.3f));
|
||||
Sprite shot = new Sprite();
|
||||
shot.attachChild(box);
|
||||
shot.attachChild(disk);
|
||||
_geom.attachChild(shot);
|
||||
|
||||
testBallistic(shot, target);
|
||||
// testLineSegment(shot);
|
||||
}
|
||||
|
||||
protected void testBallistic (Sprite shot, Box target)
|
||||
{
|
||||
// start with the "cannon" pointed along the x axis
|
||||
Vector3f diff = target.getLocalTranslation().subtract(
|
||||
shot.getLocalTranslation());
|
||||
Vector3f vel = diff.normalize();
|
||||
float range = diff.length(), angle = -FastMath.PI/4;
|
||||
|
||||
Vector3f axis = UP.cross(vel);
|
||||
axis.normalizeLocal();
|
||||
|
||||
// rotate it up (around the y axis) the necessary elevation
|
||||
Quaternion rot = new Quaternion();
|
||||
rot.fromAngleAxis(angle, axis);
|
||||
rot.multLocal(vel);
|
||||
|
||||
// give the cannon its muzzle velocity
|
||||
float muzvel = FastMath.sqrt(range * BallisticPath.G /
|
||||
FastMath.sin(2*angle));
|
||||
vel.multLocal(muzvel);
|
||||
|
||||
Vector3f start = new Vector3f(0, 0, 0);
|
||||
float time = BallisticPath.computeFlightTime(range, muzvel, angle);
|
||||
shot.move(new OrientingBallisticPath(
|
||||
shot, new Vector3f(1, 0, 0), start, vel, GRAVITY, time));
|
||||
|
||||
System.out.println("Range: " + range);
|
||||
System.out.println("Muzzle velocity: " + muzvel);
|
||||
System.out.println("Rotation axis: " + axis);
|
||||
System.out.println("Angle: " + angle + " (" +
|
||||
(angle * 180 / FastMath.PI) + ")");
|
||||
System.out.println("Flight time: " + time);
|
||||
System.out.println("Velocity: " + vel);
|
||||
}
|
||||
|
||||
protected void testLineSegment (Sprite shot)
|
||||
{
|
||||
Vector3f[] points = new Vector3f[] {
|
||||
new Vector3f(0, 0, 0), new Vector3f(2, 0, 0), new Vector3f(3, 0, 0),
|
||||
new Vector3f(3, 2, 0), new Vector3f(5, 2, 0), new Vector3f(5, -2, 0),
|
||||
new Vector3f(-4, -2, 0)
|
||||
};
|
||||
float[] durations = new float[points.length];
|
||||
Arrays.fill(durations, 1f);
|
||||
Vector3f up = new Vector3f(0, 0, 1);
|
||||
Vector3f orient = new Vector3f(1, 0, 0);
|
||||
shot.move(new LineSegmentPath(shot, up, orient, points, durations));
|
||||
}
|
||||
|
||||
public static void main (String[] args)
|
||||
{
|
||||
LoggingSystem.getLogger().setLevel(Level.OFF);
|
||||
PathTestApp test = new PathTestApp();
|
||||
if (!test.init()) {
|
||||
System.exit(-1);
|
||||
}
|
||||
test.run();
|
||||
}
|
||||
|
||||
protected static final Vector3f GRAVITY = new Vector3f(0, 0, -9.8f);
|
||||
protected static final Vector3f UP = new Vector3f(0, 0, 1);
|
||||
protected static final ColorRGBA[] COLORS = {
|
||||
ColorRGBA.red, ColorRGBA.green, ColorRGBA.blue, ColorRGBA.gray };
|
||||
}
|
||||
Reference in New Issue
Block a user