Created
March 31, 2020 04:23
-
-
Save Ali-RS/6e9760d4f06c2d958a192c7fecbabc71 to your computer and use it in GitHub Desktop.
JME3.2.1 Smash Cam Class
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2009-2012 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. | |
*/ | |
import com.jme3.app.Application; | |
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. | |
* | |
* Link to thread: https://hub.jmonkeyengine.org/t/super-smash-bros-camera/41250 | |
* | |
* @author Nehon | |
* @author JDC | |
*/ | |
public class SmashCameraAppState extends AbstractAppState implements ActionListener, AnalogListener { | |
protected Spatial spatial; | |
protected Node target; | |
protected CameraNode camNode; | |
private InputManager inputManager; | |
protected boolean invertYaxis = false; | |
protected boolean invertXaxis = false; | |
protected boolean hideCursorOnRotate = true; | |
protected boolean canRotate; | |
protected boolean dragToRotate = false;//defualt is true. | |
protected boolean smoothMotion = true;//default is 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 distanceTarget = 0f; | |
protected float chasingSensitivity = 5f; | |
protected float distanceLerpFactor = 0f; | |
//protected float distanceLerpFactor = 0; | |
protected Vector3f upVector = new Vector3f(); | |
protected Vector3f leftVector = new Vector3f(); | |
protected Vector3f lerpDistance = new Vector3f(); | |
protected Vector3f targetTranslation = new Vector3f(); | |
protected Vector3f spatialTranslation = 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)}; | |
public SmashCameraAppState() { | |
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(); //in my case, camera should have no inputs as it is tracking. | |
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(); | |
initTogleRotateInput(); | |
inputManager.addListener(this, inputs); | |
inputManager.setCursorVisible(dragToRotate); | |
} | |
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); | |
} | |
} | |
} | |
} | |
} | |
} | |
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 | |
*/ | |
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"); | |
} | |
spatialTranslation = spatial.getWorldTranslation(); | |
targetTranslation = target.getWorldTranslation(); | |
distanceTarget = FastMath.abs(spatialTranslation.subtract(targetTranslation).length()); | |
if (smoothMotion){ | |
distanceLerpFactor = Math.min(distanceLerpFactor + (tpf * tpf * chasingSensitivity * 0.05f), 1); | |
lerpDistance = FastMath.interpolateLinear(distanceLerpFactor, targetTranslation, spatialTranslation); | |
target.setLocalTranslation(lerpDistance); | |
if (distanceTarget <= 0.0005f) { | |
distanceLerpFactor = 0f; | |
} | |
} else { | |
target.setLocalTranslation(spatialTranslation); | |
} | |
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 | |
*/ | |
public void setToggleRotationTrigger(Trigger... triggers) { | |
toggleRotateTrigger = triggers; | |
if (inputManager != null) { | |
inputManager.deleteMapping(CameraInput.CHASECAM_TOGGLEROTATE); | |
initTogleRotateInput(); | |
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 | |
*/ | |
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 | |
*/ | |
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 | |
*/ | |
public void setMaxDistance(float maxDistance) { | |
this.maxDistance = maxDistance; | |
if(initialized){ | |
zoomCamera(distance); | |
} | |
} | |
/** | |
* Returns the min zoom distance of the camera (default is 1) | |
* | |
* @return minDistance | |
*/ | |
public float getMinDistance() { | |
return minDistance; | |
} | |
/** | |
* Sets the min zoom distance of the camera (default is 1) | |
* | |
* @param minDistance | |
*/ | |
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 | |
*/ | |
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 | |
*/ | |
public void setMinVerticalRotation(float minHeight) { | |
this.minVerticalRotation = minHeight; | |
if(initialized){ | |
rotateCamera(); | |
} | |
} | |
/** | |
* Enagles smooth motion for this camera. | |
* @param smoothMotion | |
*/ | |
public void setSmoothMotion(boolean smoothMotion) { | |
this.smoothMotion = smoothMotion; | |
} | |
/** | |
* @return True is smooth motion is enabled for this camera | |
*/ | |
public boolean isSmoothMotion() { | |
return smoothMotion; | |
} | |
/** | |
* returns the chasing sensitivity | |
* @return | |
*/ | |
public float getChasingSensitivity() { | |
return chasingSensitivity; | |
} | |
/** | |
* | |
* Sets the chasing sensitivity, the lower the value the slower the camera will follow the target when it moves | |
* default is 5 | |
* Only has an effect if smoothMotion is set to true and trailing is enabled | |
* @param chasingSensitivity | |
*/ | |
public void setChasingSensitivity(float chasingSensitivity) { | |
this.chasingSensitivity = chasingSensitivity; | |
} | |
/** | |
* returns the zoom speed | |
* | |
* @return | |
*/ | |
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 | |
*/ | |
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 | |
*/ | |
public void setDefaultDistance(float defaultDistance) { | |
distance = defaultDistance; | |
} | |
/** | |
* sets the default horizontal rotation in radian of the camera at start of | |
* the application | |
* | |
* @param angleInRad | |
*/ | |
public void setDefaultHorizontalRotation(float angleInRad) { | |
horizontalRotation = angleInRad; | |
} | |
/** | |
* sets the default vertical rotation in radian of the camera at start of | |
* the application | |
* | |
* @param angleInRad | |
*/ | |
public void setDefaultVerticalRotation(float angleInRad) { | |
verticalRotation = angleInRad; | |
} | |
/** | |
* @return If drag to rotate feature is enabled. | |
* | |
* @see FlyByCamera#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 | |
*/ | |
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 | |
*/ | |
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 initTogleRotateInput() { | |
inputManager.addMapping(CameraInput.CHASECAM_TOGGLEROTATE, toggleRotateTrigger); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment