Behold, Nenya, Ring of Water and repository for our media and animation related
goodies, both Java 2D and LWJGL/JME 3D. git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@1 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
@@ -0,0 +1,412 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.camera;
|
||||
|
||||
import com.jme.math.FastMath;
|
||||
import com.jme.math.Matrix3f;
|
||||
import com.jme.math.Plane;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.renderer.Camera;
|
||||
|
||||
import com.samskivert.util.ObserverList;
|
||||
|
||||
/**
|
||||
* Provides various useful mechanisms for manipulating the camera.
|
||||
*/
|
||||
public class CameraHandler
|
||||
{
|
||||
/**
|
||||
* Creates a new camera handler. The camera begins life at the origin,
|
||||
* facing in the negative z direction (pointing at the ground).
|
||||
*/
|
||||
public CameraHandler (Camera camera)
|
||||
{
|
||||
_camera = camera;
|
||||
resetAxes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the camera orientation to its initial state.
|
||||
*/
|
||||
public void resetAxes ()
|
||||
{
|
||||
_camera.getDirection().set(0, 0, -1);
|
||||
_camera.getLeft().set(-1, 0, 0);
|
||||
_camera.getUp().set(0, 1, 0);
|
||||
_camera.update();
|
||||
|
||||
_rxdir.set(1, 0, 0);
|
||||
_rydir.set(0, 1, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures limits on the camera tilt.
|
||||
*/
|
||||
public void setTiltLimits (float minAngle, float maxAngle)
|
||||
{
|
||||
_minTilt = minAngle;
|
||||
_maxTilt = maxAngle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures limits on the distance the camera can be panned.
|
||||
*
|
||||
* @param boundViaFrustum if true instead of bounding the camera's
|
||||
* position, we will compute the intersections of the view frustum with the
|
||||
* ground plan and bound that rectangle into the specified bounds.
|
||||
* <em>Note:</em> the camera must generally be pointing down at the ground
|
||||
* (up to perhaps 45 degrees or so) for this to work. At higher angles the
|
||||
* back of the view frustum will intersect the ground plane at or near
|
||||
* infinity.
|
||||
*/
|
||||
public void setPanLimits (float minX, float minY, float maxX, float maxY,
|
||||
boolean boundViaFrustum)
|
||||
{
|
||||
_minX = minX;
|
||||
_minY = minY;
|
||||
_maxX = maxX;
|
||||
_maxY = maxY;
|
||||
_boundViaFrustum = boundViaFrustum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the minimum and maximum zoom values allowed for the
|
||||
* camera.
|
||||
*/
|
||||
public void setZoomLimits (float minZoom, float maxZoom)
|
||||
{
|
||||
_minZoom = minZoom;
|
||||
_maxZoom = maxZoom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables the current set of limits.
|
||||
*/
|
||||
public void setLimitsEnabled (boolean enabled)
|
||||
{
|
||||
_limitsEnabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the camera zoom level to a value between zero (zoomed in maximally)
|
||||
* and 1 (zoomed out maximally). Zoom limits must have already been set up
|
||||
* via a call to {@link #setZoomLimits}.
|
||||
*/
|
||||
public void setZoomLevel (float level)
|
||||
{
|
||||
// Log.info("Zoom " + level + " " + _camera.getLocation());
|
||||
level = Math.max(0f, Math.min(level, 1f));
|
||||
_camera.getLocation().z = _minZ + (_maxZ - _minZ) * level;
|
||||
_camera.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current camera zoom level.
|
||||
*/
|
||||
public float getZoomLevel ()
|
||||
{
|
||||
return (_camera.getLocation().z - _minZ) / (_maxZ - _minZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a camera path observer.
|
||||
*/
|
||||
public void addCameraObserver (CameraPath.Observer camobs)
|
||||
{
|
||||
_campathobs.add(camobs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a camera path observer.
|
||||
*/
|
||||
public void removeCameraObserver (CameraPath.Observer camobs)
|
||||
{
|
||||
_campathobs.remove(camobs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the camera moving along a path which will be updated every tick
|
||||
* until it is complete.
|
||||
*/
|
||||
public void moveCamera (CameraPath path)
|
||||
{
|
||||
if (_campath != null) {
|
||||
_campath.abort();
|
||||
_campathobs.apply(new CompletedOp(_campath));
|
||||
}
|
||||
_campath = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the camera is currently animating along a path, false if
|
||||
* it is not.
|
||||
*/
|
||||
public boolean cameraIsMoving ()
|
||||
{
|
||||
return (_campath != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips immediately to the end of the current camera path.
|
||||
*/
|
||||
public void skipPath ()
|
||||
{
|
||||
// fake an update far enough into the future to trick the camera path
|
||||
// into thinking it's done
|
||||
update(100);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called by the {@link JmeApp} on every frame to allow the handler
|
||||
* to update the camera as necessary.
|
||||
*/
|
||||
public void update (float frameTime)
|
||||
{
|
||||
if (_campath != null) {
|
||||
if (_campath.tick(frameTime)) {
|
||||
CameraPath opath = _campath;
|
||||
_campath = null;
|
||||
_campathobs.apply(new CompletedOp(opath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the camera being manipulated by this handler.
|
||||
*/
|
||||
public Camera getCamera ()
|
||||
{
|
||||
return _camera;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the camera's location. The specified location will be bounded
|
||||
* within the current pan and zoom limits.
|
||||
*/
|
||||
public void setLocation (Vector3f location)
|
||||
{
|
||||
_camera.setLocation(bound(location));
|
||||
_camera.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pans the camera the specified distance in the x and y directions. These
|
||||
* distances will be multiplied by the current "view" x and y axes and
|
||||
* added to the camera's position.
|
||||
*/
|
||||
public void panCamera (float x, float y)
|
||||
{
|
||||
Vector3f loc = _camera.getLocation();
|
||||
loc.addLocal(_rxdir.mult(x, _temp));
|
||||
loc.addLocal(_rydir.mult(y, _temp));
|
||||
setLocation(loc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zooms the camera in (distance < 0) and out (distance > 0) by the
|
||||
* specified amount. The distance is multiplied by the camera's current
|
||||
* direction and added to its current position, which is then bounded into
|
||||
* the pan and zoom volume.
|
||||
*/
|
||||
public void zoomCamera (float distance)
|
||||
{
|
||||
Vector3f loc = _camera.getLocation();
|
||||
float dist = getGroundPoint().distance(loc),
|
||||
ndist = Math.min(Math.max(dist + distance, _minZoom), _maxZoom);
|
||||
if ((distance = ndist - dist) == 0f) {
|
||||
return;
|
||||
}
|
||||
loc.subtractLocal(_camera.getDirection().mult(distance, _temp));
|
||||
setLocation(loc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates the point on the ground at which the camera is "looking" and
|
||||
* rotates the camera from that point around the specified vector by the
|
||||
* specified angle. Additionally zooms the camera in (deltaZoom < 0) or out
|
||||
* (deltaZoom > 0) along its direction of view by the specified amount.
|
||||
*/
|
||||
public void rotateCamera (
|
||||
Vector3f spot, Vector3f axis, float deltaAngle, float deltaZoom)
|
||||
{
|
||||
// get a vector from the camera's current position to the point around
|
||||
// which we're going to orbit
|
||||
Vector3f direction = _camera.getLocation().subtract(spot);
|
||||
|
||||
// if we're rotating around the left vector, impose tilt limits
|
||||
if (axis == _camera.getLeft()) {
|
||||
float angle = FastMath.asin(_ground.normal.dot(direction) /
|
||||
direction.length());
|
||||
float nangle = Math.min(Math.max(angle + deltaAngle, _minTilt),
|
||||
_maxTilt);
|
||||
if ((deltaAngle = nangle - angle) == 0f) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// create a rotation matrix
|
||||
_rotm.fromAxisAngle(axis, deltaAngle);
|
||||
|
||||
// rotate the direction vector and the camera itself
|
||||
_rotm.mult(direction, direction);
|
||||
_rotm.mult(_camera.getUp(), _camera.getUp());
|
||||
_rotm.mult(_camera.getLeft(), _camera.getLeft());
|
||||
_rotm.mult(_camera.getDirection(), _camera.getDirection());
|
||||
|
||||
// if we're rotating around the ground normal, we need to update our
|
||||
// notion of side-to-side and forward for panning
|
||||
if (axis == _ground.normal) {
|
||||
_rotm.mult(_rxdir, _rxdir);
|
||||
_rotm.mult(_rydir, _rydir);
|
||||
}
|
||||
|
||||
// finally move the camera to its new location, zooming in or out in
|
||||
// the process
|
||||
float scale = 1 + (deltaZoom / direction.length());
|
||||
direction.scaleAdd(scale, spot);
|
||||
setLocation(direction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Swings the camera perpendicular to the ground normal, around the point
|
||||
* on the ground at which it is looking.
|
||||
*/
|
||||
public void orbitCamera (float deltaAngle)
|
||||
{
|
||||
rotateCamera(getGroundPoint(), _ground.normal, deltaAngle, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Swings the camera perpendicular to its left vector around the point on
|
||||
* the ground at which it is looking.
|
||||
*/
|
||||
public void tiltCamera (float deltaAngle)
|
||||
{
|
||||
rotateCamera(getGroundPoint(), _camera.getLeft(), deltaAngle, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the point on the ground (z = 0) at which the camera is looking.
|
||||
*/
|
||||
public Vector3f getGroundPoint ()
|
||||
{
|
||||
float dist = -1f * _ground.normal.dot(_camera.getLocation()) /
|
||||
_ground.normal.dot(_camera.getDirection());
|
||||
return _camera.getLocation().add(_camera.getDirection().mult(dist));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ground normal. Z is the default ground plane and the normal
|
||||
* points in the positive z direction. <em>Do not modify this value.</em>
|
||||
*/
|
||||
public Vector3f getGroundNormal ()
|
||||
{
|
||||
return _ground.normal;
|
||||
}
|
||||
|
||||
protected Vector3f bound (Vector3f loc)
|
||||
{
|
||||
if (!_limitsEnabled) {
|
||||
return loc;
|
||||
}
|
||||
if (_boundViaFrustum) {
|
||||
bound(_camera.getFrustumLeft(), _camera.getFrustumTop(), loc);
|
||||
bound(_camera.getFrustumLeft(), _camera.getFrustumBottom(), loc);
|
||||
bound(_camera.getFrustumRight(), _camera.getFrustumTop(), loc);
|
||||
bound(_camera.getFrustumRight(), _camera.getFrustumBottom(), loc);
|
||||
} else {
|
||||
loc.x = Math.max(Math.min(loc.x, _maxX), _minX);
|
||||
loc.y = Math.max(Math.min(loc.y, _maxY), _minY);
|
||||
}
|
||||
loc.z = Math.max(Math.min(loc.z, _maxZ), _minZ);
|
||||
return loc;
|
||||
}
|
||||
|
||||
protected void bound (float left, float up, Vector3f loc)
|
||||
{
|
||||
// start with the location of the camera moved out into the near
|
||||
// frustum plane
|
||||
_temp.set(loc);
|
||||
_temp.scaleAdd(_camera.getFrustumNear(), _camera.getDirection(), loc);
|
||||
|
||||
// then slide it over to a corner of the near frustum rectangle
|
||||
_temp.scaleAdd(left, _camera.getLeft(), _temp);
|
||||
_temp.scaleAdd(up, _camera.getUp(), _temp);
|
||||
|
||||
// turn this into a vector with origin at the camera's location
|
||||
_temp.subtractLocal(loc);
|
||||
_temp.normalizeLocal();
|
||||
|
||||
// determine the intersection of said vector with the ground plane
|
||||
float dist = -1f * _ground.normal.dot(loc) / _ground.normal.dot(_temp);
|
||||
_temp.scaleAdd(dist, _temp, loc);
|
||||
|
||||
// we then assume that if the corner of the "viewable ground rectangle"
|
||||
// is outside our bounds that we can simply adjust the camera location
|
||||
// by the amount it is out of bounds
|
||||
if (_temp.x > _maxX) {
|
||||
loc.x -= (_temp.x - _maxX);
|
||||
} else if (_temp.x < _minX) {
|
||||
loc.x += (_minX - _temp.x);
|
||||
}
|
||||
if (_temp.y > _maxY) {
|
||||
loc.y -= (_temp.y - _maxY);
|
||||
} else if (_temp.y < _minY) {
|
||||
loc.y += (_minY - _temp.y);
|
||||
}
|
||||
}
|
||||
|
||||
/** Used to dispatch {@link CameraPath.Observer#pathCompleted}. */
|
||||
protected static class CompletedOp implements ObserverList.ObserverOp
|
||||
{
|
||||
public CompletedOp (CameraPath path) {
|
||||
_path = path;
|
||||
}
|
||||
public boolean apply (Object observer) {
|
||||
return ((CameraPath.Observer)observer).pathCompleted(_path);
|
||||
}
|
||||
protected CameraPath _path;
|
||||
}
|
||||
|
||||
protected Camera _camera;
|
||||
protected CameraPath _campath;
|
||||
protected ObserverList _campathobs =
|
||||
new ObserverList(ObserverList.SAFE_IN_ORDER_NOTIFY);
|
||||
|
||||
protected Matrix3f _rotm = new Matrix3f();
|
||||
protected Vector3f _temp = new Vector3f();
|
||||
|
||||
protected boolean _boundViaFrustum, _limitsEnabled = true;
|
||||
protected float _minX = -Float.MAX_VALUE, _maxX = Float.MAX_VALUE;
|
||||
protected float _minY = -Float.MAX_VALUE, _maxY = Float.MAX_VALUE;
|
||||
protected float _minZ = -Float.MAX_VALUE, _maxZ = Float.MAX_VALUE;
|
||||
protected float _minZoom = 0f, _maxZoom = Float.MAX_VALUE;
|
||||
protected float _minTilt = -Float.MAX_VALUE, _maxTilt = Float.MAX_VALUE;
|
||||
|
||||
protected Vector3f _rxdir = new Vector3f(1, 0, 0);
|
||||
protected Vector3f _rydir = new Vector3f(0, 1, 0);
|
||||
|
||||
protected static final Vector3f _xdir = new Vector3f(1, 0, 0);
|
||||
protected static final Vector3f _ydir = new Vector3f(0, 1, 0);
|
||||
|
||||
protected static final Plane _ground = new Plane(new Vector3f(0, 0, 1), 0);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.camera;
|
||||
|
||||
import com.jme.renderer.Camera;
|
||||
|
||||
/**
|
||||
* Used to move the camera along a particular path.
|
||||
*/
|
||||
public abstract class CameraPath
|
||||
{
|
||||
/** Used to inform observers when a camera path is completed or aborted. */
|
||||
public interface Observer
|
||||
{
|
||||
/**
|
||||
* Called when this path is finished (potentially early because another
|
||||
* path was set before this path completed).
|
||||
*
|
||||
* @param path the path that was completed.
|
||||
*
|
||||
* @return true if the observer should remain in the list, false if it
|
||||
* should be removed following this notification.
|
||||
*/
|
||||
public boolean pathCompleted (CameraPath path);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called on every frame to allow the path to adjust the position
|
||||
* of the camera.
|
||||
*
|
||||
* @return true if the path is completed and can be disposed, false if it
|
||||
* is not yet completed.
|
||||
*/
|
||||
public abstract boolean tick (float secondsSince);
|
||||
|
||||
/**
|
||||
* Called if this path is aborted prior to completion due to being replaced
|
||||
* by a new camera path.
|
||||
*/
|
||||
public void abort ()
|
||||
{
|
||||
}
|
||||
|
||||
protected CameraPath (CameraHandler camhand)
|
||||
{
|
||||
_camhand = camhand;
|
||||
}
|
||||
|
||||
protected CameraHandler _camhand;
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.jme.camera;
|
||||
|
||||
import com.jme.math.FastMath;
|
||||
import com.jme.math.Matrix3f;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.renderer.Camera;
|
||||
|
||||
import com.jme.input.InputHandler;
|
||||
import com.jme.input.KeyBindingManager;
|
||||
import com.jme.input.KeyInput;
|
||||
import com.jme.input.RelativeMouse;
|
||||
import com.jme.input.action.*;
|
||||
import com.jme.input.action.InputActionEvent;
|
||||
|
||||
import com.threerings.jme.JmeApp;
|
||||
import com.threerings.jme.Log;
|
||||
|
||||
/**
|
||||
* Sets up camera controls for moving around from a top-down perspective,
|
||||
* suitable for strategy games and their ilk. The "ground" is assumed to be the
|
||||
* XY plane.
|
||||
*/
|
||||
public class GodViewHandler extends InputHandler
|
||||
{
|
||||
/**
|
||||
* Creates the handler.
|
||||
*
|
||||
* @param cam The camera to move with this handler.
|
||||
*/
|
||||
public GodViewHandler (CameraHandler camhand)
|
||||
{
|
||||
_camhand = camhand;
|
||||
setKeyBindings();
|
||||
addActions();
|
||||
}
|
||||
|
||||
protected void setKeyBindings ()
|
||||
{
|
||||
KeyBindingManager keyboard = KeyBindingManager.getKeyBindingManager();
|
||||
|
||||
// the key bindings for the pan actions
|
||||
keyboard.set("forward", KeyInput.KEY_W);
|
||||
keyboard.set("arrow_forward", KeyInput.KEY_UP);
|
||||
keyboard.set("backward", KeyInput.KEY_S);
|
||||
keyboard.set("arrow_backward", KeyInput.KEY_DOWN);
|
||||
keyboard.set("left", KeyInput.KEY_A);
|
||||
keyboard.set("arrow_left", KeyInput.KEY_LEFT);
|
||||
keyboard.set("right", KeyInput.KEY_D);
|
||||
keyboard.set("arrow_right", KeyInput.KEY_RIGHT);
|
||||
|
||||
// the key bindings for the zoom actions
|
||||
keyboard.set("zoomIn", KeyInput.KEY_UP);
|
||||
keyboard.set("zoomOut", KeyInput.KEY_DOWN);
|
||||
|
||||
// the key bindings for the orbit actions
|
||||
keyboard.set("turnRight", KeyInput.KEY_RIGHT);
|
||||
keyboard.set("turnLeft", KeyInput.KEY_LEFT);
|
||||
|
||||
// the key bindings for the tilt actions
|
||||
keyboard.set("tiltForward", KeyInput.KEY_HOME);
|
||||
keyboard.set("tiltBack", KeyInput.KEY_END);
|
||||
|
||||
keyboard.set("screenshot", KeyInput.KEY_F12);
|
||||
}
|
||||
|
||||
protected void addActions ()
|
||||
{
|
||||
addAction(new KeyScreenShotAction(), "screenshot", false);
|
||||
addPanActions();
|
||||
addZoomActions();
|
||||
addOrbitActions();
|
||||
addTiltActions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds actions for panning the camera around the scene.
|
||||
*/
|
||||
protected void addPanActions ()
|
||||
{
|
||||
InputAction forward = new InputAction() {
|
||||
public void performAction (InputActionEvent evt) {
|
||||
_camhand.panCamera(0, speed * evt.getTime());
|
||||
}
|
||||
};
|
||||
forward.setSpeed(0.5f);
|
||||
addAction(forward, "forward", true);
|
||||
addAction(forward, "arrow_forward", true);
|
||||
|
||||
InputAction backward = new InputAction() {
|
||||
public void performAction (InputActionEvent evt) {
|
||||
_camhand.panCamera(0, -speed * evt.getTime());
|
||||
}
|
||||
};
|
||||
backward.setSpeed(0.5f);
|
||||
addAction(backward, "backward", true);
|
||||
addAction(backward, "arrow_backward", true);
|
||||
|
||||
InputAction left = new InputAction() {
|
||||
public void performAction (InputActionEvent evt) {
|
||||
_camhand.panCamera(-speed * evt.getTime(), 0);
|
||||
}
|
||||
};
|
||||
left.setSpeed(0.5f);
|
||||
addAction(left, "left", true);
|
||||
addAction(left, "arrow_left", true);
|
||||
|
||||
InputAction right = new InputAction() {
|
||||
public void performAction (InputActionEvent evt) {
|
||||
_camhand.panCamera(speed * evt.getTime(), 0);
|
||||
}
|
||||
};
|
||||
right.setSpeed(0.5f);
|
||||
addAction(right, "right", true);
|
||||
addAction(right, "arrow_right", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds actions for zooming the camaera in and out.
|
||||
*/
|
||||
protected void addZoomActions ()
|
||||
{
|
||||
InputAction zoomIn = new InputAction() {
|
||||
public void performAction (InputActionEvent evt) {
|
||||
_camhand.zoomCamera(-speed * evt.getTime());
|
||||
}
|
||||
};
|
||||
zoomIn.setSpeed(0.5f);
|
||||
addAction(zoomIn, "zoomIn", true);
|
||||
|
||||
InputAction zoomOut = new InputAction() {
|
||||
public void performAction (InputActionEvent evt) {
|
||||
_camhand.zoomCamera(speed * evt.getTime());
|
||||
}
|
||||
};
|
||||
zoomOut.setSpeed(0.5f);
|
||||
addAction(zoomOut, "zoomOut", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds actions for orbiting the camera around the viewpoint.
|
||||
*/
|
||||
protected void addOrbitActions ()
|
||||
{
|
||||
addAction(new OrbitAction(-FastMath.PI / 2), "turnRight", true);
|
||||
addAction(new OrbitAction(FastMath.PI / 2), "turnLeft", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds actions for tilting the camera (rotating around the yaw axis).
|
||||
*/
|
||||
protected void addTiltActions ()
|
||||
{
|
||||
addAction(new TiltAction(-FastMath.PI / 2), "tiltForward", true);
|
||||
addAction(new TiltAction(FastMath.PI / 2), "tiltBack", true);
|
||||
}
|
||||
|
||||
protected class OrbitAction extends InputAction
|
||||
{
|
||||
public OrbitAction (float radPerSec)
|
||||
{
|
||||
_radPerSec = radPerSec;
|
||||
}
|
||||
|
||||
public void performAction (InputActionEvent evt)
|
||||
{
|
||||
_camhand.orbitCamera(_radPerSec * evt.getTime());
|
||||
}
|
||||
|
||||
protected float _radPerSec;
|
||||
}
|
||||
|
||||
protected class TiltAction extends InputAction
|
||||
{
|
||||
public TiltAction (float radPerSec)
|
||||
{
|
||||
_radPerSec = radPerSec;
|
||||
}
|
||||
|
||||
public void performAction (InputActionEvent evt)
|
||||
{
|
||||
_camhand.tiltCamera(_radPerSec * evt.getTime());
|
||||
}
|
||||
|
||||
protected float _radPerSec;
|
||||
}
|
||||
|
||||
protected CameraHandler _camhand;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.camera;
|
||||
|
||||
import com.jme.math.Quaternion;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.renderer.Camera;
|
||||
|
||||
/**
|
||||
* Pans the camera to the specified location in the specified amount of time.
|
||||
*/
|
||||
public class PanPath extends CameraPath
|
||||
{
|
||||
/**
|
||||
* Creates a panning path for the specified camera that will retain the
|
||||
* camera's current orientation.
|
||||
*
|
||||
* @param target the target position for the camera.
|
||||
* @param duration the number of seconds in which to pan the camera.
|
||||
*/
|
||||
public PanPath (CameraHandler camhand, Vector3f target, float duration)
|
||||
{
|
||||
this(camhand, target, null, duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a panning path for the specified camera.
|
||||
*
|
||||
* @param target the target position for the camera.
|
||||
* @param trot the target rotation for the camera (or <code>null</code> to
|
||||
* keep its current orientation)
|
||||
* @param duration the number of seconds in which to pan the camera.
|
||||
*/
|
||||
public PanPath (CameraHandler camhand, Vector3f target, Quaternion trot,
|
||||
float duration)
|
||||
{
|
||||
super(camhand);
|
||||
|
||||
Camera cam = camhand.getCamera();
|
||||
_start = new Vector3f(cam.getLocation());
|
||||
_velocity = target.subtract(_start);
|
||||
_velocity.divideLocal(duration);
|
||||
if (trot != null) {
|
||||
_irot = new Quaternion();
|
||||
_irot.fromAxes(cam.getLeft(), cam.getUp(), cam.getDirection());
|
||||
_trot = trot;
|
||||
}
|
||||
|
||||
_duration = duration;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public boolean tick (float secondsSince)
|
||||
{
|
||||
_elapsed = Math.min(_elapsed + secondsSince, _duration);
|
||||
_camloc.scaleAdd(_elapsed, _velocity, _start);
|
||||
_camhand.setLocation(_camloc);
|
||||
if (_irot != null) {
|
||||
_camhand.getCamera().setAxes(_axes.slerp(_irot, _trot,
|
||||
_elapsed / _duration));
|
||||
}
|
||||
return (_elapsed >= _duration);
|
||||
}
|
||||
|
||||
protected Vector3f _start, _velocity, _camloc = new Vector3f();
|
||||
protected Quaternion _irot, _trot, _axes = new Quaternion();
|
||||
protected float _elapsed, _duration;
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.camera;
|
||||
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.renderer.Camera;
|
||||
|
||||
import com.threerings.jme.Log;
|
||||
|
||||
/**
|
||||
* Moves the camera along a cubic Hermite spline path defined by the start and
|
||||
* end locations and directions. Spline formulas obtained from
|
||||
* <a href="http://en.wikipedia.org/wiki/Cubic_Hermite_spline">Wikipedia</a>.
|
||||
*/
|
||||
public class SplinePath extends CameraPath
|
||||
{
|
||||
/**
|
||||
* Creates a cubic spline path for the camera to follow.
|
||||
*
|
||||
* @param tloc the target location
|
||||
* @param tdir the target direction
|
||||
* @param axis the heading axis (typically {@link Vector3f#UNIT_Z})
|
||||
* @param duration the duration of the path
|
||||
* @param tension the tension parameter, which can range from zero to one:
|
||||
* higher tension values create a more direct path with a sharper turn,
|
||||
* lower values create a rounder path with a smoother turn
|
||||
*/
|
||||
public SplinePath (CameraHandler camhand, Vector3f tloc,
|
||||
Vector3f tdir, Vector3f axis, float duration, float tension)
|
||||
{
|
||||
super(camhand);
|
||||
|
||||
// get the spline function coefficients
|
||||
Camera cam = camhand.getCamera();
|
||||
_p0 = new Vector3f(cam.getLocation());
|
||||
_p1 = new Vector3f(tloc);
|
||||
float tscale = (1f - tension) * _p0.distance(_p1);
|
||||
_m0 = cam.getDirection().mult(tscale);
|
||||
_m1 = tdir.mult(tscale);
|
||||
|
||||
_axis = axis;
|
||||
_duration = duration;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public boolean tick (float secondsSince)
|
||||
{
|
||||
_elapsed = Math.min(_elapsed + secondsSince, _duration);
|
||||
float t = _elapsed / _duration, t2 = t*t, t3 = t2*t,
|
||||
h00 = 2*t3 - 3*t2 + 1,
|
||||
h00p = 6*t2 - 6*t,
|
||||
h10 = t3 - 2*t2 + t,
|
||||
h10p = 3*t2 - 4*t + 1,
|
||||
h01 = -2*t3 + 3*t2,
|
||||
h01p = -6*t2 + 6*t,
|
||||
h11 = t3 - t2,
|
||||
h11p = 3*t2 - 2*t;
|
||||
|
||||
// take the derivative to find the direction
|
||||
_p0.mult(h00p, _dir);
|
||||
_dir.scaleAdd(h10p, _m0, _dir);
|
||||
_dir.scaleAdd(h01p, _p1, _dir);
|
||||
_dir.scaleAdd(h11p, _m1, _dir);
|
||||
_dir.normalizeLocal();
|
||||
|
||||
// evaluate the spline function to find the location
|
||||
_p0.mult(h00, _loc);
|
||||
_loc.scaleAdd(h10, _m0, _loc);
|
||||
_loc.scaleAdd(h01, _p1, _loc);
|
||||
_loc.scaleAdd(h11, _m1, _loc);
|
||||
|
||||
// compute the left and up vectors using the direction and
|
||||
// axis vectors
|
||||
Camera cam = _camhand.getCamera();
|
||||
_axis.cross(_dir, _left);
|
||||
_left.normalizeLocal();
|
||||
_dir.cross(_left, _up);
|
||||
|
||||
// update the camera's location and orientation
|
||||
cam.setFrame(_loc, _left, _up, _dir);
|
||||
|
||||
return _elapsed >= _duration;
|
||||
}
|
||||
|
||||
/** The parameters of the spline. */
|
||||
Vector3f _p0, _m0, _p1, _m1, _axis;
|
||||
|
||||
/** Working vectors. */
|
||||
Vector3f _loc = new Vector3f(), _left = new Vector3f(),
|
||||
_up = new Vector3f(), _dir = new Vector3f();
|
||||
|
||||
/** The elapsed time and total duration. */
|
||||
float _elapsed, _duration;
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.jme.camera;
|
||||
|
||||
import com.jme.math.FastMath;
|
||||
import com.jme.math.Quaternion;
|
||||
import com.jme.math.Vector3f;
|
||||
import com.jme.renderer.Camera;
|
||||
|
||||
import com.threerings.jme.Log;
|
||||
|
||||
/**
|
||||
* Swings the camera around a point of interest (which should be somewhere
|
||||
* along the camera's view vector). Also optionally pans the camera and/or
|
||||
* zooms it in or out (moves it along its view vector) in the process.
|
||||
*
|
||||
* <p align="center"><img src="rotate_zoom.png">
|
||||
*/
|
||||
public class SwingPath extends CameraPath
|
||||
{
|
||||
/**
|
||||
* Creates a rotating, zooming path for the specified camera.
|
||||
*
|
||||
* @param spot the point of interest around which to swing the camera.
|
||||
* @param axis the axis around which to rotate the camera.
|
||||
* @param angle the angle through which to rotate the camera.
|
||||
* @param angvel the (absolute value of the) velocity at which to rotate
|
||||
* the camera (in radians per second).
|
||||
* @param zoom the distance to zoom along the camera's view axis (negative
|
||||
* = in, positive = out).
|
||||
*/
|
||||
public SwingPath (CameraHandler camhand, Vector3f spot, Vector3f axis,
|
||||
float angle, float angvel, float zoom)
|
||||
{
|
||||
this(camhand, spot, axis, angle, angvel, null, 0f, null, zoom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a rotating, panning, zooming path for the specified camera.
|
||||
*
|
||||
* @param spot the point of interest around which to swing the camera.
|
||||
* @param paxis the primary axis around which to rotate the camera.
|
||||
* @param pangle the angle through which to rotate the camera about the
|
||||
* primary axis.
|
||||
* @param angvel the (absolute value of the) velocity at which to rotate
|
||||
* the camera (in radians per second) about the primary angle.
|
||||
* @param saxis the secondary axis around which to rotate the camera, or
|
||||
* <code>null</code> for none
|
||||
* @param sangle the angle through which to rotate the camera about the
|
||||
* secondary axis
|
||||
* @param pan the amount to pan the camera, or <code>null</code> for none
|
||||
* @param zoom the distance to zoom along the camera's view axis (negative
|
||||
* = in, positive = out).
|
||||
*/
|
||||
public SwingPath (CameraHandler camhand, Vector3f spot, Vector3f paxis,
|
||||
float pangle, float angvel, Vector3f saxis, float sangle,
|
||||
Vector3f pan, float zoom)
|
||||
{
|
||||
super(camhand);
|
||||
|
||||
if (pangle == 0) {
|
||||
Log.warning("Requested to swing camera through zero degrees " +
|
||||
"[spot=" + spot + ", paxis=" + paxis +
|
||||
", angvel=" + angvel + ", zoom=" + zoom + "].");
|
||||
pangle = 0.0001f;
|
||||
}
|
||||
if (angvel <= 0) {
|
||||
Log.warning("Requested to swing camera with invalid velocity " +
|
||||
"[spot=" + spot + ", paxis=" + paxis + ", pangle=" +
|
||||
pangle + ", angvel=" + angvel + ", zoom=" + zoom +
|
||||
"].");
|
||||
angvel = FastMath.PI;
|
||||
}
|
||||
|
||||
_spot = new Vector3f(spot);
|
||||
_paxis = paxis;
|
||||
_pangle = pangle;
|
||||
_pangvel = (pangle > 0) ? angvel : -1 * angvel;
|
||||
_saxis = (saxis == null) ? null : new Vector3f(saxis);
|
||||
_sangle = sangle;
|
||||
_sangvel = _sangle * _pangvel / _pangle;
|
||||
_pan = pan;
|
||||
if (_pan != null) {
|
||||
_panvel = _pan.mult(_pangvel / _pangle);
|
||||
_panned = new Vector3f();
|
||||
}
|
||||
_zoom = zoom;
|
||||
_zoomvel = _zoom * _pangvel / _pangle;
|
||||
|
||||
// Log.info("Swinging camera [angle=" + _angle + ", angvel=" + _angvel +
|
||||
// ", zoom=" + _zoom + ", zoomvel=" + _zoomvel + "].");
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public boolean tick (float secondsSince)
|
||||
{
|
||||
float deltaPAngle = (secondsSince * _pangvel);
|
||||
float deltaSAngle = (secondsSince * _sangvel);
|
||||
float deltaZoom = (secondsSince * _zoomvel);
|
||||
_protated += deltaPAngle;
|
||||
_srotated += deltaSAngle;
|
||||
_zoomed += deltaZoom;
|
||||
if (_pan != null) {
|
||||
_panvel.mult(secondsSince, _deltaPan);
|
||||
_panned.addLocal(_deltaPan);
|
||||
}
|
||||
|
||||
// clamp our rotation at the target angle and determine whether or not
|
||||
// we're done
|
||||
boolean done = false;
|
||||
if (_pangle > 0 && _protated > _pangle ||
|
||||
_pangle < 0 && _protated < _pangle) {
|
||||
deltaPAngle -= (_protated - _pangle);
|
||||
deltaSAngle -= (_srotated - _sangle);
|
||||
deltaZoom -= (_zoomed - _zoom);
|
||||
if (_pan != null) {
|
||||
_deltaPan.subtractLocal(_panned).addLocal(_pan);
|
||||
}
|
||||
_protated = _pangle;
|
||||
_srotated = _sangle;
|
||||
_panned = _pan;
|
||||
_zoomed = _zoom;
|
||||
done = true;
|
||||
}
|
||||
|
||||
// have the camera handler do the necessary rotating, panning, zooming
|
||||
if (_pan != null) {
|
||||
_camhand.setLocation(
|
||||
_camhand.getCamera().getLocation().addLocal(_deltaPan));
|
||||
_spot.addLocal(_deltaPan);
|
||||
}
|
||||
_camhand.rotateCamera(_spot, _paxis, deltaPAngle, deltaZoom);
|
||||
if (_saxis != null) {
|
||||
_rot.fromAngleAxis(deltaPAngle, _paxis).multLocal(_saxis);
|
||||
_camhand.rotateCamera(_spot, _saxis, deltaSAngle, 0f);
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
protected Vector3f _spot, _paxis, _saxis;
|
||||
protected float _pangle, _pangvel, _protated;
|
||||
protected float _sangle, _sangvel, _srotated;
|
||||
protected Vector3f _pan, _panvel, _panned;
|
||||
protected float _zoom, _zoomvel, _zoomed;
|
||||
|
||||
protected Quaternion _rot = new Quaternion();
|
||||
protected Vector3f _deltaPan = new Vector3f();
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
Reference in New Issue
Block a user