more utils

This commit is contained in:
Caspian Rychlik-Prince 2004-04-21 19:46:04 +00:00
parent c08c972bce
commit e871fefb02
11 changed files with 363 additions and 36 deletions

View File

@ -29,7 +29,7 @@
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.lwjgl.util.model.renderer;
package org.lwjgl.util;
/**
* $Id$

View File

@ -0,0 +1,140 @@
/*
* Copyright (c) 2002 Shaven Puppy Ltd
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'Shaven Puppy' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.lwjgl.util;
import org.lwjgl.Sys;
/**
* $Id$
*
* A hires timer. This measures time in seconds as floating point values.
* All Timers created are updated simultaneously by calling the static method
* tick(). This ensures that within a single iteration of a game loop that
* all timers are updated consistently with each other.
*
* @author cix_foo <cix_foo@users.sourceforge.net>
* @version $Revision$
*/
public class Timer {
// Record the timer resolution on classload
private static final long resolution = Sys.getTimerResolution();
// Globally keeps track of time for all instances of Timer
private static long currentTime;
// When the timer was started
private long startTime;
// The last time recorded by getTime()
private long lastTime;
// Whether the timer is paused
private boolean paused;
/**
* Constructs a timer. The timer will be reset to 0.0 and resumed immediately.
*/
public Timer() {
reset();
resume();
}
/**
* @return the time in seconds, as a float
*/
public float getTime() {
if (!paused)
lastTime = currentTime - startTime;
return (float) ((double) lastTime / (double) resolution);
}
/**
* @return whether this timer is paused
*/
public boolean isPaused() {
return paused;
}
/**
* Pause the timer. Whilst paused the time will not change for this timer
* when tick() is called.
*
* @see #resume()
*/
public void pause() {
paused = true;
}
/**
* Reset the timer. Equivalent to set(0.0f);
* @see #set()
*/
public void reset() {
set(0.0f);
}
/**
* Resume the timer.
* @see #pause()
*/
public void resume() {
paused = false;
startTime = currentTime - lastTime;
}
/**
* Set the time of this timer
* @param newTime the new time, in seconds
*/
public void set(float newTime) {
long newTimeInTicks = (long) ((double) newTime * (double) resolution);
startTime = currentTime - newTimeInTicks;
lastTime = newTimeInTicks;
}
/**
* Get the next time update from the system's hires timer. This method should
* be called once per main loop iteration; all timers are updated simultaneously
* from it.
*/
public static void tick() {
currentTime = Sys.getTime();
}
/**
* Debug output.
*/
public String toString() {
return "Timer[Time=" + getTime() + ", Paused=" + paused + "]";
}
}

View File

@ -49,10 +49,11 @@ public class BoneFrame extends Frame {
/**
* C'tor
* @param time
* @param action
* @param bone[]
*/
public BoneFrame(float time, Matrix4f[] bone) {
super(time);
public BoneFrame(float time, String action, Matrix4f[] bone) {
super(time, action);
this.bone = bone;
}

View File

@ -65,8 +65,8 @@ public class BonedModel extends Model {
* @param animation
* @param vertex
*/
public BonedModel(String material, Triangle[] triangle, Vector2f[] skin, Color[] color, Map animation, BonedVertex[] vertex) {
super(material, triangle, skin, color, animation);
public BonedModel(String name, String material, Triangle[] triangle, Vector2f[] skin, Color[] color, Map animation, BonedVertex[] vertex) {
super(name, material, triangle, skin, color, animation);
this.vertex = vertex;
}

View File

@ -39,19 +39,24 @@ import java.io.Serializable;
* @author $Author$
* @version $Revision$
*/
public abstract class Frame implements Serializable {
public abstract class Frame implements Serializable, Comparable {
public static final long serialVersionUID = 1L;
/** Frame time */
private final float time;
/** User-defined action to occur after this frame has been used. May be null */
private final String action;
/**
* C'tor
* @param time
* @param action
*/
public Frame(float time) {
public Frame(float time, String action) {
this.time = time;
this.action = action;
}
/**
@ -60,4 +65,35 @@ public abstract class Frame implements Serializable {
public final float getTime() {
return time;
}
/* (non-Javadoc)
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(Object o) {
if (o == null) {
return 0;
}
if (! (o instanceof Frame)) {
return 0;
}
Frame f = (Frame) o;
if (f.time == time) {
return 0;
} else if (f.time > time) {
return 1;
} else {
return -1;
}
}
/**
* Gets the user-defined animation action. This can be processed by whatever
* is animating the model to perform some special action after the frame is
* used. For example, you could use "stop" to stop the animation, or "rewind"
* to repeat the animation ad infinitum.
* @return String, or null, for no action
*/
public final String getAction() {
return action;
}
}

View File

@ -48,10 +48,11 @@ public class MeshFrame extends Frame {
/**
* C'tor
* @param time
* @param action
* @param bone[]
*/
public MeshFrame(float time, Vertex[] vertex) {
super(time);
public MeshFrame(float time, String action, Vertex[] vertex) {
super(time, action);
this.vertex = vertex;
}

View File

@ -61,8 +61,8 @@ public class MeshedModel extends Model {
* @param color[]
* @param animation
*/
public MeshedModel(String material, Triangle[] triangle, Vector2f[] skin, Color[] color, Map animation) {
super(material, triangle, skin, color, animation);
public MeshedModel(String name, String material, Triangle[] triangle, Vector2f[] skin, Color[] color, Map animation) {
super(name, material, triangle, skin, color, animation);
}
}

View File

@ -52,6 +52,9 @@ public abstract class Model implements Serializable {
public static final long serialVersionUID = 1L;
/** Model name */
private final String name;
/** Material */
private final String material;
@ -69,13 +72,15 @@ public abstract class Model implements Serializable {
/**
* C'tor
* @param name
* @param material
* @param triangle
* @param skin[]
* @param color[]
* @param animation
*/
public Model(String material, Triangle[] triangle, Vector2f[] skin, Color[] color, Map animation) {
public Model(String name, String material, Triangle[] triangle, Vector2f[] skin, Color[] color, Map animation) {
this.name = name;
this.material = material;
this.triangle = triangle;
this.skin = skin;
@ -88,8 +93,8 @@ public abstract class Model implements Serializable {
* @param name The name of the animation
* @return the Frames of an animation (or null, if no such animation exists)
*/
public final BoneFrame[] getAnimation(String name) {
return (BoneFrame[]) animation.get(name);
public final Frame[] getAnimation(String name) {
return (Frame[]) animation.get(name);
}
/**
@ -120,5 +125,17 @@ public abstract class Model implements Serializable {
return color;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
public String toString() {
return "Model["+name+"]";
}
/**
* @return Returns the name.
*/
public final String getName() {
return name;
}
}

View File

@ -29,8 +29,9 @@
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.lwjgl.util.model.loader;
package org.lwjgl.util.model.loaders;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@ -60,7 +61,7 @@ import org.w3c.dom.Element;
* @author $Author$
* @version $Revision$
*/
public class Loader {
public class XMLLoader {
/** The source document */
private final Document src;
@ -74,7 +75,7 @@ public class Loader {
/**
* C'tor
*/
public Loader(Document src) {
public XMLLoader(Document src) {
this.src = src;
}
@ -84,12 +85,14 @@ public class Loader {
* @throws Exception
*/
public Model load() throws Exception {
String name = XMLUtil.getString(src.getDocumentElement(), "name");
String material = XMLUtil.getString(src.getDocumentElement(), "material");
numVertices = XMLUtil.getInt(src.getDocumentElement(), "vertices");
if (XMLUtil.getString(src.getDocumentElement(), "type").equals("boned")) {
// It's a boned model
numBones = XMLUtil.getInt(src.getDocumentElement(), "bones", 0);
return new BonedModel(
name,
material,
loadTriangles(),
loadSkin(),
@ -100,6 +103,7 @@ public class Loader {
} else if (XMLUtil.getString(src.getDocumentElement(), "type").equals("meshed")) {
// It's a mesh keyframe model
return new MeshedModel(
name,
material,
loadTriangles(),
loadSkin(),
@ -371,6 +375,7 @@ public class Loader {
Element frameElement = (Element) i.next();
frames[frameCount++] = loadBoneFrame(frameElement);
}
Arrays.sort(frames);
return frames;
}
@ -388,6 +393,7 @@ public class Loader {
Element frameElement = (Element) i.next();
frames[frameCount++] = loadMeshFrame(frameElement);
}
Arrays.sort(frames);
return frames;
}
@ -409,7 +415,8 @@ public class Loader {
bones[boneCount++] = loadBone(boneElement);
}
return new BoneFrame(
XMLUtil.getFloat(element, "time", 0.0f),
XMLUtil.getFloat(element, "time"),
XMLUtil.getString(element, "action", null),
bones
);
}
@ -432,7 +439,8 @@ public class Loader {
vertices[vertexCount++] = loadMeshVertex(vertexElement);
}
return new MeshFrame(
XMLUtil.getFloat(element, "time", 0.0f),
XMLUtil.getFloat(element, "time"),
XMLUtil.getString(element, "action", null),
vertices
);
}

View File

@ -29,7 +29,7 @@
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.lwjgl.util.model.loader;
package org.lwjgl.util.model.loaders;
import java.util.ArrayList;
import java.util.List;

View File

@ -31,11 +31,15 @@
*/
package org.lwjgl.util.model.renderer;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.lwjgl.util.model.BoneFrame;
import org.lwjgl.util.model.BonedModel;
import org.lwjgl.util.Renderable;
import org.lwjgl.util.Timer;
import org.lwjgl.util.model.Frame;
import org.lwjgl.util.model.Model;
/**
* $Id$
@ -46,14 +50,35 @@ import org.lwjgl.util.model.BonedModel;
* Material lookups are performed by mapping the material name to a Renderable thing. You must
* suppy appropriate Renderables - typically something that binds a 2D texture and sets up some
* GL state.
* <p>
* To animate things, you will need to call Timer.tick() every frame to update the timers in your
* Renderables. Then they'll just animate themselves. Hurrah!
*
* @author $Author$
* @version $Revision$
*/
public class Renderer {
public class Renderer implements Renderable {
/** Material map: String name->Renderable */
private final Map materials = new HashMap();
private static final Map materials = new HashMap();
/** The model we're rendering */
private Model model;
/** The current material */
private Renderable material;
/** The animation currently being animated */
private Frame[] frame;
/** The current time */
private final Timer timer = new Timer();
/** Last frame rendered */
private Frame lastFrame;
/** Visibility */
private boolean visible;
/**
* C'tor
@ -62,36 +87,135 @@ public class Renderer {
}
/**
* Render a Model
* @param model The model to render
* @param animation The name of the animation
* @param time The time for the animation
* @param model The model to set.
*/
public void render(BonedModel model, String animation, float time) {
public void setModel(Model model) {
if (this.model == model) {
return;
}
this.model = model;
material = (Renderable) materials.get(model.getMaterial());
frame = null;
}
/**
* @return the Model we're rendering with this Renderer
*/
public Model getModel() {
return model;
}
/**
* Set the animation
* @param animation
*/
public void setAnimation(String animation) {
if (model == null) {
return;
}
frame = model.getAnimation(animation);
timer.reset();
lastFrame = null;
}
/**
* Render a Model
*/
public void render() {
// Don't do anything if there's no model or no animation
if (model == null || frame == null) {
return;
}
// 1. Set up GL state from the Model's material
Renderable material = (Renderable) materials.get(model.getMaterial());
if (material != null) {
material.render();
}
// 2. Get the animation
BoneFrame[] frame = model.getAnimation(animation);
if (frame == null) {
return;
// 2. Work out what frame to show
Frame frame = findFrame();
if (frame != lastFrame) {
lastFrame = frame;
processFrame();
}
// 3. Work out what the nearest frame is to the specified time
// 3. Render the processed frame
}
/**
* Find the nearest frame to the current time
* @return the Frame nearest the current time
*/
private Frame findFrame() {
float time = timer.getTime();
// Use a binary search to find the frame
int a = 0, b =
}
/**
* Add a material
* @param name The material's name
* @param renderable The renderable object
*/
public void putMaterial(String name, Renderable renderable) {
public static void putMaterial(String name, Renderable renderable) {
materials.put(name, renderable);
}
/**
* Remove a material
* @param name The material's name
* @return a Renderable
*/
public static Renderable removeMaterial(String name) {
return (Renderable) materials.remove(name);
}
/**
* Determine if this Renderer is visible
* @return boolean
*/
public boolean isVisible() {
return visible;
}
/**
* Sets the visibility of this Renderer
* @param visible
*/
public void setVisible(boolean visible) {
this.visible = visible;
}
/**
* Determines if the animation is paused
* @return boolean
*/
public boolean isPaused() {
return timer.isPaused();
}
/**
* Pause the animation
*/
public void pause() {
timer.pause();
}
/**
* Rewind the animation
*/
public void rewind() {
timer.reset();
}
/**
* Resume a paused animation
*/
public void resume() {
timer.resume();
}
}