com.jme3.app.ChaseCameraAppState Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jme3-core Show documentation
Show all versions of jme3-core Show documentation
jMonkeyEngine is a 3-D game engine for adventurous Java developers
/*
* Copyright (c) 2009-2021 jMonkeyEngine
* 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 'jMonkeyEngine' 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 com.jme3.app;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.input.CameraInput;
import com.jme3.input.InputManager;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.MouseAxisTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.input.controls.Trigger;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.CameraNode;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.CameraControl;
import com.jme3.util.TempVars;
/**
* This class is a camera controller that allow the camera to follow a target
* Spatial.
*
* @author Nehon
*/
public class ChaseCameraAppState extends AbstractAppState implements ActionListener, AnalogListener {
protected Spatial spatial;
protected Node target;
protected CameraNode camNode;
protected InputManager inputManager;
protected boolean invertYaxis = false;
protected boolean invertXaxis = false;
protected boolean hideCursorOnRotate = true;
protected boolean canRotate;
protected boolean dragToRotate = true;
protected float rotationSpeed = 1.0f;
protected float zoomSpeed = 2.0f;
//protected boolean zoomin;
protected float minDistance = 1.0f;
protected float maxDistance = 40.0f;
protected float distance = 20;
protected float maxVerticalRotation = 1.4f;
protected float verticalRotation = 0f;
protected float minVerticalRotation = 0f;
protected float horizontalRotation = 0f;
//protected float distanceLerpFactor = 0;
protected Vector3f upVector = new Vector3f();
protected Vector3f leftVector = new Vector3f();
protected Trigger[] zoomOutTrigger = {new MouseAxisTrigger(MouseInput.AXIS_WHEEL, true)};
protected Trigger[] zoomInTrigger = {new MouseAxisTrigger(MouseInput.AXIS_WHEEL, false)};
protected Trigger[] toggleRotateTrigger = {new MouseButtonTrigger(MouseInput.BUTTON_LEFT), new MouseButtonTrigger(MouseInput.BUTTON_RIGHT)};
//
// protected boolean rotating = false;
// protected float rotation = 0;
// protected float targetRotation = rotation;
public ChaseCameraAppState() {
camNode = new CameraNode("ChaseCameraNode", new CameraControl());
}
@Override
public void initialize(AppStateManager stateManager, Application app) {
super.initialize(stateManager, app);
this.inputManager = app.getInputManager();
target = new Node("ChaseCamTarget");
camNode.setCamera(app.getCamera());
camNode.setControlDir(CameraControl.ControlDirection.SpatialToCamera);
target.attachChild(camNode);
camNode.setLocalTranslation(0, 0, distance);
upVector = app.getCamera().getUp().clone();
leftVector = app.getCamera().getLeft().clone();
registerWithInput();
rotateCamera();
}
/**
* Registers inputs with the input manager
*
*/
public final void registerWithInput() {
String[] inputs = {CameraInput.CHASECAM_TOGGLEROTATE,
CameraInput.CHASECAM_DOWN,
CameraInput.CHASECAM_UP,
CameraInput.CHASECAM_MOVELEFT,
CameraInput.CHASECAM_MOVERIGHT,
CameraInput.CHASECAM_ZOOMIN,
CameraInput.CHASECAM_ZOOMOUT};
initVerticalAxisInputs();
initZoomInput();
initHorizontalAxisInput();
initToggleRotateInput();
inputManager.addListener(this, inputs);
inputManager.setCursorVisible(dragToRotate);
}
@Override
public void onAction(String name, boolean keyPressed, float tpf) {
if (isEnabled()) {
if (dragToRotate) {
if (name.equals(CameraInput.CHASECAM_TOGGLEROTATE) && isEnabled()) {
if (keyPressed) {
canRotate = true;
if (hideCursorOnRotate) {
inputManager.setCursorVisible(false);
}
} else {
canRotate = false;
if (hideCursorOnRotate) {
inputManager.setCursorVisible(true);
}
}
}
}
}
}
@Override
public void onAnalog(String name, float value, float tpf) {
if (isEnabled()) {
if (canRotate) {
if (name.equals(CameraInput.CHASECAM_MOVELEFT)) {
horizontalRotation -= value * rotationSpeed;
rotateCamera();
} else if (name.equals(CameraInput.CHASECAM_MOVERIGHT)) {
horizontalRotation += value * rotationSpeed;
rotateCamera();
} else if (name.equals(CameraInput.CHASECAM_UP)) {
verticalRotation += value * rotationSpeed;
rotateCamera();
} else if (name.equals(CameraInput.CHASECAM_DOWN)) {
verticalRotation -= value * rotationSpeed;
rotateCamera();
}
}
if (name.equals(CameraInput.CHASECAM_ZOOMIN)) {
zoomCamera(-value * zoomSpeed);
} else if (name.equals(CameraInput.CHASECAM_ZOOMOUT)) {
zoomCamera(+value * zoomSpeed);
}
}
}
/**
* rotate the camera around the target
*/
protected void rotateCamera() {
verticalRotation = FastMath.clamp(verticalRotation, minVerticalRotation, maxVerticalRotation);
TempVars vars = TempVars.get();
Quaternion rot = vars.quat1;
Quaternion rot2 = vars.quat2;
rot.fromAngleNormalAxis(verticalRotation, leftVector);
rot2.fromAngleNormalAxis(horizontalRotation, upVector);
rot2.multLocal(rot);
target.setLocalRotation(rot2);
vars.release();
}
/**
* move the camera toward or away the target
*
* @param value the distance to move
*/
protected void zoomCamera(float value) {
distance = FastMath.clamp(distance + value, minDistance, maxDistance);
camNode.setLocalTranslation(new Vector3f(0, 0, distance));
}
public void setTarget(Spatial targetSpatial) {
spatial = targetSpatial;
}
@Override
public void update(float tpf) {
if (spatial == null) {
throw new IllegalArgumentException("The spatial to follow is null, please use the setTarget method");
}
target.setLocalTranslation(spatial.getWorldTranslation());
camNode.lookAt(target.getWorldTranslation(), upVector);
target.updateLogicalState(tpf);
target.updateGeometricState();
}
/**
* Sets custom triggers for toggling the rotation of the cam default are
* new MouseButtonTrigger(MouseInput.BUTTON_LEFT) left mouse button new
* MouseButtonTrigger(MouseInput.BUTTON_RIGHT) right mouse button
*
* @param triggers the desired triggers
*/
public void setToggleRotationTrigger(Trigger... triggers) {
toggleRotateTrigger = triggers;
if (inputManager != null) {
inputManager.deleteMapping(CameraInput.CHASECAM_TOGGLEROTATE);
initToggleRotateInput();
inputManager.addListener(this, CameraInput.CHASECAM_TOGGLEROTATE);
}
}
/**
* Sets custom triggers for zooming in the cam default is new
* MouseAxisTrigger(MouseInput.AXIS_WHEEL, true) mouse wheel up
*
* @param triggers the desired triggers
*/
public void setZoomInTrigger(Trigger... triggers) {
zoomInTrigger = triggers;
if (inputManager != null) {
inputManager.deleteMapping(CameraInput.CHASECAM_ZOOMIN);
inputManager.addMapping(CameraInput.CHASECAM_ZOOMIN, zoomInTrigger);
inputManager.addListener(this, CameraInput.CHASECAM_ZOOMIN);
}
}
/**
* Sets custom triggers for zooming out the cam default is new
* MouseAxisTrigger(MouseInput.AXIS_WHEEL, false) mouse wheel down
*
* @param triggers the desired triggers
*/
public void setZoomOutTrigger(Trigger... triggers) {
zoomOutTrigger = triggers;
if (inputManager != null) {
inputManager.deleteMapping(CameraInput.CHASECAM_ZOOMOUT);
inputManager.addMapping(CameraInput.CHASECAM_ZOOMOUT, zoomOutTrigger);
inputManager.addListener(this, CameraInput.CHASECAM_ZOOMOUT);
}
}
/**
* Returns the max zoom distance of the camera (default is 40)
*
* @return maxDistance
*/
public float getMaxDistance() {
return maxDistance;
}
/**
* Sets the max zoom distance of the camera (default is 40)
*
* @param maxDistance the desired maximum distance (in world units,
* default=40)
*/
public void setMaxDistance(float maxDistance) {
this.maxDistance = maxDistance;
if (initialized) {
zoomCamera(distance);
}
}
/**
* Returns the min zoom distance of the camera (default is 1)
*
* @return the minimum distance (in world units)
*/
public float getMinDistance() {
return minDistance;
}
/**
* Sets the min zoom distance of the camera (default is 1)
*
* @param minDistance the desired minimum distance (in world units,
* default=1)
*/
public void setMinDistance(float minDistance) {
this.minDistance = minDistance;
if (initialized) {
zoomCamera(distance);
}
}
/**
* @return The maximal vertical rotation angle in radian of the camera
* around the target
*/
public float getMaxVerticalRotation() {
return maxVerticalRotation;
}
/**
* Sets the maximal vertical rotation angle in radian of the camera around
* the target. Default is Pi/2;
*
* @param maxVerticalRotation the desired maximum angle (in radians,
* default=Pi/2)
*/
public void setMaxVerticalRotation(float maxVerticalRotation) {
this.maxVerticalRotation = maxVerticalRotation;
if (initialized) {
rotateCamera();
}
}
/**
*
* @return The minimal vertical rotation angle in radian of the camera
* around the target
*/
public float getMinVerticalRotation() {
return minVerticalRotation;
}
/**
* Sets the minimal vertical rotation angle in radian of the camera around
* the target default is 0;
*
* @param minHeight the desired minimum angle (in radians, default=0)
*/
public void setMinVerticalRotation(float minHeight) {
this.minVerticalRotation = minHeight;
if (initialized) {
rotateCamera();
}
}
/**
* returns the zoom speed
*
* @return the speed
*/
public float getZoomSpeed() {
return zoomSpeed;
}
/**
* Sets the zoom speed, the lower the value, the slower the camera will zoom
* in and out. default is 2.
*
* @param zoomSpeed the speed
*/
public void setZoomSpeed(float zoomSpeed) {
this.zoomSpeed = zoomSpeed;
}
/**
* Returns the rotation speed when the mouse is moved.
*
* @return the rotation speed when the mouse is moved.
*/
public float getRotationSpeed() {
return rotationSpeed;
}
/**
* Sets the rotate amount when user moves his mouse. The lower the value,
* the slower the camera will rotate. Default is 1.
*
* @param rotationSpeed Rotation speed on mouse movement, default is 1.
*/
public void setRotationSpeed(float rotationSpeed) {
this.rotationSpeed = rotationSpeed;
}
/**
* Sets the default distance at start of application
*
* @param defaultDistance the desired distance (in world units, default=20)
*/
public void setDefaultDistance(float defaultDistance) {
distance = defaultDistance;
}
/**
* sets the default horizontal rotation in radian of the camera at start of
* the application
*
* @param angleInRad the desired rotation (in radians, default=0)
*/
public void setDefaultHorizontalRotation(float angleInRad) {
horizontalRotation = angleInRad;
}
/**
* sets the default vertical rotation in radian of the camera at start of
* the application
*
* @param angleInRad the desired rotation (in radians, default=0)
*/
public void setDefaultVerticalRotation(float angleInRad) {
verticalRotation = angleInRad;
}
/**
* @return If drag to rotate feature is enabled.
*
* @see #setDragToRotate(boolean)
*/
public boolean isDragToRotate() {
return dragToRotate;
}
/**
* @param dragToRotate When true, the user must hold the mouse button and
* drag over the screen to rotate the camera, and the cursor is visible
* until dragged. Otherwise, the cursor is invisible at all times and
* holding the mouse button is not needed to rotate the camera. This feature
* is disabled by default.
*/
public void setDragToRotate(boolean dragToRotate) {
this.dragToRotate = dragToRotate;
this.canRotate = !dragToRotate;
if (inputManager != null) {
inputManager.setCursorVisible(dragToRotate);
}
}
/**
* invert the vertical axis movement of the mouse
*
* @param invertYaxis true→inverted, false→not inverted
*/
public void setInvertVerticalAxis(boolean invertYaxis) {
this.invertYaxis = invertYaxis;
if (inputManager != null) {
inputManager.deleteMapping(CameraInput.CHASECAM_DOWN);
inputManager.deleteMapping(CameraInput.CHASECAM_UP);
initVerticalAxisInputs();
inputManager.addListener(this, CameraInput.CHASECAM_DOWN, CameraInput.CHASECAM_UP);
}
}
/**
* invert the Horizontal axis movement of the mouse
*
* @param invertXaxis true→inverted, false→not inverted
*/
public void setInvertHorizontalAxis(boolean invertXaxis) {
this.invertXaxis = invertXaxis;
if (inputManager != null) {
inputManager.deleteMapping(CameraInput.CHASECAM_MOVELEFT);
inputManager.deleteMapping(CameraInput.CHASECAM_MOVERIGHT);
initHorizontalAxisInput();
inputManager.addListener(this, CameraInput.CHASECAM_MOVELEFT, CameraInput.CHASECAM_MOVERIGHT);
}
}
private void initVerticalAxisInputs() {
if (!invertYaxis) {
inputManager.addMapping(CameraInput.CHASECAM_DOWN, new MouseAxisTrigger(MouseInput.AXIS_Y, true));
inputManager.addMapping(CameraInput.CHASECAM_UP, new MouseAxisTrigger(MouseInput.AXIS_Y, false));
} else {
inputManager.addMapping(CameraInput.CHASECAM_DOWN, new MouseAxisTrigger(MouseInput.AXIS_Y, false));
inputManager.addMapping(CameraInput.CHASECAM_UP, new MouseAxisTrigger(MouseInput.AXIS_Y, true));
}
}
private void initHorizontalAxisInput() {
if (!invertXaxis) {
inputManager.addMapping(CameraInput.CHASECAM_MOVELEFT, new MouseAxisTrigger(MouseInput.AXIS_X, true));
inputManager.addMapping(CameraInput.CHASECAM_MOVERIGHT, new MouseAxisTrigger(MouseInput.AXIS_X, false));
} else {
inputManager.addMapping(CameraInput.CHASECAM_MOVELEFT, new MouseAxisTrigger(MouseInput.AXIS_X, false));
inputManager.addMapping(CameraInput.CHASECAM_MOVERIGHT, new MouseAxisTrigger(MouseInput.AXIS_X, true));
}
}
private void initZoomInput() {
inputManager.addMapping(CameraInput.CHASECAM_ZOOMIN, zoomInTrigger);
inputManager.addMapping(CameraInput.CHASECAM_ZOOMOUT, zoomOutTrigger);
}
private void initToggleRotateInput() {
inputManager.addMapping(CameraInput.CHASECAM_TOGGLEROTATE, toggleRotateTrigger);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy