Merge remote-tracking branch 'origin/master' into in-pass-shadows
This commit is contained in:
commit
5108f52ebf
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2009-2012 jMonkeyEngine
|
* Copyright (c) 2009-2017 jMonkeyEngine
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -32,7 +32,11 @@
|
|||||||
package com.jme3.input;
|
package com.jme3.input;
|
||||||
|
|
||||||
import com.jme3.collision.MotionAllowedListener;
|
import com.jme3.collision.MotionAllowedListener;
|
||||||
import com.jme3.input.controls.*;
|
import com.jme3.input.controls.ActionListener;
|
||||||
|
import com.jme3.input.controls.AnalogListener;
|
||||||
|
import com.jme3.input.controls.KeyTrigger;
|
||||||
|
import com.jme3.input.controls.MouseAxisTrigger;
|
||||||
|
import com.jme3.input.controls.MouseButtonTrigger;
|
||||||
import com.jme3.math.FastMath;
|
import com.jme3.math.FastMath;
|
||||||
import com.jme3.math.Matrix3f;
|
import com.jme3.math.Matrix3f;
|
||||||
import com.jme3.math.Quaternion;
|
import com.jme3.math.Quaternion;
|
||||||
@ -40,12 +44,13 @@ import com.jme3.math.Vector3f;
|
|||||||
import com.jme3.renderer.Camera;
|
import com.jme3.renderer.Camera;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A first person view camera controller.
|
* A first-person camera controller.
|
||||||
* After creation, you must register the camera controller with the
|
*
|
||||||
* dispatcher using #registerWithDispatcher().
|
* After creation, you (or FlyCamAppState) must register the controller using
|
||||||
|
* {@link #registerWithInput(com.jme3.input.InputManager)}.
|
||||||
*
|
*
|
||||||
* Controls:
|
* Controls:
|
||||||
* - Move the mouse to rotate the camera
|
* - Move (or, in drag-to-rotate mode, drag) the mouse to rotate the camera
|
||||||
* - Mouse wheel for zooming in or out
|
* - Mouse wheel for zooming in or out
|
||||||
* - WASD keys for moving forward/backward and strafing
|
* - WASD keys for moving forward/backward and strafing
|
||||||
* - QZ keys raise or lower the camera
|
* - QZ keys raise or lower the camera
|
||||||
@ -53,41 +58,62 @@ import com.jme3.renderer.Camera;
|
|||||||
public class FlyByCamera implements AnalogListener, ActionListener {
|
public class FlyByCamera implements AnalogListener, ActionListener {
|
||||||
|
|
||||||
private static String[] mappings = new String[]{
|
private static String[] mappings = new String[]{
|
||||||
CameraInput.FLYCAM_LEFT,
|
CameraInput.FLYCAM_LEFT,
|
||||||
CameraInput.FLYCAM_RIGHT,
|
CameraInput.FLYCAM_RIGHT,
|
||||||
CameraInput.FLYCAM_UP,
|
CameraInput.FLYCAM_UP,
|
||||||
CameraInput.FLYCAM_DOWN,
|
CameraInput.FLYCAM_DOWN,
|
||||||
|
|
||||||
CameraInput.FLYCAM_STRAFELEFT,
|
CameraInput.FLYCAM_STRAFELEFT,
|
||||||
CameraInput.FLYCAM_STRAFERIGHT,
|
CameraInput.FLYCAM_STRAFERIGHT,
|
||||||
CameraInput.FLYCAM_FORWARD,
|
CameraInput.FLYCAM_FORWARD,
|
||||||
CameraInput.FLYCAM_BACKWARD,
|
CameraInput.FLYCAM_BACKWARD,
|
||||||
|
|
||||||
CameraInput.FLYCAM_ZOOMIN,
|
CameraInput.FLYCAM_ZOOMIN,
|
||||||
CameraInput.FLYCAM_ZOOMOUT,
|
CameraInput.FLYCAM_ZOOMOUT,
|
||||||
CameraInput.FLYCAM_ROTATEDRAG,
|
CameraInput.FLYCAM_ROTATEDRAG,
|
||||||
|
|
||||||
CameraInput.FLYCAM_RISE,
|
CameraInput.FLYCAM_RISE,
|
||||||
CameraInput.FLYCAM_LOWER,
|
CameraInput.FLYCAM_LOWER,
|
||||||
|
|
||||||
CameraInput.FLYCAM_INVERTY
|
|
||||||
};
|
|
||||||
|
|
||||||
|
CameraInput.FLYCAM_INVERTY
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* camera controlled by this controller (not null)
|
||||||
|
*/
|
||||||
protected Camera cam;
|
protected Camera cam;
|
||||||
|
/**
|
||||||
|
* normalized "up" direction (a unit vector)
|
||||||
|
*/
|
||||||
protected Vector3f initialUpVec;
|
protected Vector3f initialUpVec;
|
||||||
|
/**
|
||||||
|
* rotation-rate multiplier (1=default)
|
||||||
|
*/
|
||||||
protected float rotationSpeed = 1f;
|
protected float rotationSpeed = 1f;
|
||||||
|
/**
|
||||||
|
* translation speed (in world units per second)
|
||||||
|
*/
|
||||||
protected float moveSpeed = 3f;
|
protected float moveSpeed = 3f;
|
||||||
|
/**
|
||||||
|
* zoom-rate multiplier (1=default)
|
||||||
|
*/
|
||||||
protected float zoomSpeed = 1f;
|
protected float zoomSpeed = 1f;
|
||||||
protected MotionAllowedListener motionAllowed = null;
|
protected MotionAllowedListener motionAllowed = null;
|
||||||
|
/**
|
||||||
|
* enable flag for controller (false→ignoring input)
|
||||||
|
*/
|
||||||
protected boolean enabled = true;
|
protected boolean enabled = true;
|
||||||
|
/**
|
||||||
|
* drag-to-rotate mode flag
|
||||||
|
*/
|
||||||
protected boolean dragToRotate = false;
|
protected boolean dragToRotate = false;
|
||||||
protected boolean canRotate = false;
|
protected boolean canRotate = false;
|
||||||
protected boolean invertY = false;
|
protected boolean invertY = false;
|
||||||
protected InputManager inputManager;
|
protected InputManager inputManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new FlyByCamera to control the given Camera object.
|
* Creates a new FlyByCamera to control the specified camera.
|
||||||
* @param cam
|
*
|
||||||
|
* @param cam camera to be controlled (not null)
|
||||||
*/
|
*/
|
||||||
public FlyByCamera(Camera cam){
|
public FlyByCamera(Camera cam){
|
||||||
this.cam = cam;
|
this.cam = cam;
|
||||||
@ -96,10 +122,11 @@ public class FlyByCamera implements AnalogListener, ActionListener {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the up vector that should be used for the camera.
|
* Sets the up vector that should be used for the camera.
|
||||||
|
*
|
||||||
* @param upVec
|
* @param upVec
|
||||||
*/
|
*/
|
||||||
public void setUpVector(Vector3f upVec) {
|
public void setUpVector(Vector3f upVec) {
|
||||||
initialUpVec.set(upVec);
|
initialUpVec.set(upVec);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMotionAllowedListener(MotionAllowedListener listener){
|
public void setMotionAllowedListener(MotionAllowedListener listener){
|
||||||
@ -107,56 +134,68 @@ public class FlyByCamera implements AnalogListener, ActionListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the move speed. The speed is given in world units per second.
|
* Set the translation speed.
|
||||||
* @param moveSpeed
|
*
|
||||||
|
* @param moveSpeed new speed (in world units per second)
|
||||||
*/
|
*/
|
||||||
public void setMoveSpeed(float moveSpeed){
|
public void setMoveSpeed(float moveSpeed){
|
||||||
this.moveSpeed = moveSpeed;
|
this.moveSpeed = moveSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the move speed. The speed is given in world units per second.
|
* Read the translation speed.
|
||||||
* @return moveSpeed
|
*
|
||||||
|
* @return current speed (in world units per second)
|
||||||
*/
|
*/
|
||||||
public float getMoveSpeed(){
|
public float getMoveSpeed(){
|
||||||
return moveSpeed;
|
return moveSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the rotation speed.
|
* Set the rotation-rate multiplier. The bigger the multiplier, the more
|
||||||
* @param rotationSpeed
|
* rotation for a given movement of the mouse.
|
||||||
|
*
|
||||||
|
* @param rotationSpeed new rate multiplier (1=default)
|
||||||
*/
|
*/
|
||||||
public void setRotationSpeed(float rotationSpeed){
|
public void setRotationSpeed(float rotationSpeed){
|
||||||
this.rotationSpeed = rotationSpeed;
|
this.rotationSpeed = rotationSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the move speed. The speed is given in world units per second.
|
* Read the rotation-rate multiplier. The bigger the multiplier, the more
|
||||||
* @return rotationSpeed
|
* rotation for a given movement of the mouse.
|
||||||
|
*
|
||||||
|
* @return current rate multiplier (1=default)
|
||||||
*/
|
*/
|
||||||
public float getRotationSpeed(){
|
public float getRotationSpeed(){
|
||||||
return rotationSpeed;
|
return rotationSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the zoom speed.
|
* Set the zoom-rate multiplier. The bigger the multiplier, the more zoom
|
||||||
* @param zoomSpeed
|
* for a given movement of the mouse wheel.
|
||||||
|
*
|
||||||
|
* @param zoomSpeed new rate multiplier (1=default)
|
||||||
*/
|
*/
|
||||||
public void setZoomSpeed(float zoomSpeed) {
|
public void setZoomSpeed(float zoomSpeed) {
|
||||||
this.zoomSpeed = zoomSpeed;
|
this.zoomSpeed = zoomSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the zoom speed. The speed is a multiplier to increase/decrease
|
* Read the zoom-rate multiplier. The bigger the multiplier, the more zoom
|
||||||
* the zoom rate.
|
* for a given movement of the mouse wheel.
|
||||||
* @return zoomSpeed
|
*
|
||||||
|
* @return current rate multiplier (1=default)
|
||||||
*/
|
*/
|
||||||
public float getZoomSpeed() {
|
public float getZoomSpeed() {
|
||||||
return zoomSpeed;
|
return zoomSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param enable If false, the camera will ignore input.
|
* Enable or disable this controller. When disabled, the controller ignored
|
||||||
|
* input.
|
||||||
|
*
|
||||||
|
* @param enable true to enable, false to disable
|
||||||
*/
|
*/
|
||||||
public void setEnabled(boolean enable){
|
public void setEnabled(boolean enable){
|
||||||
if (enabled && !enable){
|
if (enabled && !enable){
|
||||||
@ -168,32 +207,36 @@ public class FlyByCamera implements AnalogListener, ActionListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return If enabled
|
* Test whether this controller is enabled.
|
||||||
* @see FlyByCamera#setEnabled(boolean)
|
*
|
||||||
|
* @return true if enabled, otherwise false
|
||||||
|
* @see #setEnabled(boolean)
|
||||||
*/
|
*/
|
||||||
public boolean isEnabled(){
|
public boolean isEnabled(){
|
||||||
return enabled;
|
return enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Test whether drag-to-rotate mode is enabled.
|
||||||
|
*
|
||||||
* @return If drag to rotate feature is enabled.
|
* @return If drag to rotate feature is enabled.
|
||||||
*
|
*
|
||||||
* @see FlyByCamera#setDragToRotate(boolean)
|
* @see #setDragToRotate(boolean)
|
||||||
*/
|
*/
|
||||||
public boolean isDragToRotate() {
|
public boolean isDragToRotate() {
|
||||||
return dragToRotate;
|
return dragToRotate;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set if drag to rotate mode is enabled.
|
* Enable or disable drag-to-rotate mode.
|
||||||
*
|
*
|
||||||
* When true, the user must hold the mouse button
|
* When drag-to-rotate mode is enabled, the user must hold the mouse button
|
||||||
* and drag over the screen to rotate the camera, and the cursor is
|
* and drag over the screen to rotate the camera, and the cursor is visible
|
||||||
* visible until dragged. Otherwise, the cursor is invisible at all times
|
* until dragged. When drag-to-rotate mode is disabled, the cursor is
|
||||||
* and holding the mouse button is not needed to rotate the camera.
|
* invisible at all times and holding the mouse button is not needed to
|
||||||
* This feature is disabled by default.
|
* rotate the camera. This mode is disabled by default.
|
||||||
*
|
*
|
||||||
* @param dragToRotate True if drag to rotate mode is enabled.
|
* @param dragToRotate true to enable, false to disable
|
||||||
*/
|
*/
|
||||||
public void setDragToRotate(boolean dragToRotate) {
|
public void setDragToRotate(boolean dragToRotate) {
|
||||||
this.dragToRotate = dragToRotate;
|
this.dragToRotate = dragToRotate;
|
||||||
@ -203,25 +246,26 @@ public class FlyByCamera implements AnalogListener, ActionListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers the FlyByCamera to receive input events from the provided
|
* Register this controller to receive input events from the specified input
|
||||||
* Dispatcher.
|
* manager.
|
||||||
|
*
|
||||||
* @param inputManager
|
* @param inputManager
|
||||||
*/
|
*/
|
||||||
public void registerWithInput(InputManager inputManager){
|
public void registerWithInput(InputManager inputManager){
|
||||||
this.inputManager = inputManager;
|
this.inputManager = inputManager;
|
||||||
|
|
||||||
// both mouse and button - rotation of cam
|
// both mouse and button - rotation of cam
|
||||||
inputManager.addMapping(CameraInput.FLYCAM_LEFT, new MouseAxisTrigger(MouseInput.AXIS_X, true),
|
inputManager.addMapping(CameraInput.FLYCAM_LEFT, new MouseAxisTrigger(MouseInput.AXIS_X, true),
|
||||||
new KeyTrigger(KeyInput.KEY_LEFT));
|
new KeyTrigger(KeyInput.KEY_LEFT));
|
||||||
|
|
||||||
inputManager.addMapping(CameraInput.FLYCAM_RIGHT, new MouseAxisTrigger(MouseInput.AXIS_X, false),
|
inputManager.addMapping(CameraInput.FLYCAM_RIGHT, new MouseAxisTrigger(MouseInput.AXIS_X, false),
|
||||||
new KeyTrigger(KeyInput.KEY_RIGHT));
|
new KeyTrigger(KeyInput.KEY_RIGHT));
|
||||||
|
|
||||||
inputManager.addMapping(CameraInput.FLYCAM_UP, new MouseAxisTrigger(MouseInput.AXIS_Y, false),
|
inputManager.addMapping(CameraInput.FLYCAM_UP, new MouseAxisTrigger(MouseInput.AXIS_Y, false),
|
||||||
new KeyTrigger(KeyInput.KEY_UP));
|
new KeyTrigger(KeyInput.KEY_UP));
|
||||||
|
|
||||||
inputManager.addMapping(CameraInput.FLYCAM_DOWN, new MouseAxisTrigger(MouseInput.AXIS_Y, true),
|
inputManager.addMapping(CameraInput.FLYCAM_DOWN, new MouseAxisTrigger(MouseInput.AXIS_Y, true),
|
||||||
new KeyTrigger(KeyInput.KEY_DOWN));
|
new KeyTrigger(KeyInput.KEY_DOWN));
|
||||||
|
|
||||||
// mouse only - zoom in/out with wheel, and rotate drag
|
// mouse only - zoom in/out with wheel, and rotate drag
|
||||||
inputManager.addMapping(CameraInput.FLYCAM_ZOOMIN, new MouseAxisTrigger(MouseInput.AXIS_WHEEL, false));
|
inputManager.addMapping(CameraInput.FLYCAM_ZOOMIN, new MouseAxisTrigger(MouseInput.AXIS_WHEEL, false));
|
||||||
@ -248,43 +292,42 @@ public class FlyByCamera implements AnalogListener, ActionListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void mapJoystick( Joystick joystick ) {
|
protected void mapJoystick( Joystick joystick ) {
|
||||||
|
|
||||||
// Map it differently if there are Z axis
|
// Map it differently if there are Z axis
|
||||||
if( joystick.getAxis( JoystickAxis.Z_ROTATION ) != null && joystick.getAxis( JoystickAxis.Z_AXIS ) != null ) {
|
if( joystick.getAxis( JoystickAxis.Z_ROTATION ) != null && joystick.getAxis( JoystickAxis.Z_AXIS ) != null ) {
|
||||||
|
|
||||||
// Make the left stick move
|
// Make the left stick move
|
||||||
joystick.getXAxis().assignAxis( CameraInput.FLYCAM_STRAFERIGHT, CameraInput.FLYCAM_STRAFELEFT );
|
joystick.getXAxis().assignAxis( CameraInput.FLYCAM_STRAFERIGHT, CameraInput.FLYCAM_STRAFELEFT );
|
||||||
joystick.getYAxis().assignAxis( CameraInput.FLYCAM_BACKWARD, CameraInput.FLYCAM_FORWARD );
|
joystick.getYAxis().assignAxis( CameraInput.FLYCAM_BACKWARD, CameraInput.FLYCAM_FORWARD );
|
||||||
|
|
||||||
// And the right stick control the camera
|
// And the right stick control the camera
|
||||||
joystick.getAxis( JoystickAxis.Z_ROTATION ).assignAxis( CameraInput.FLYCAM_DOWN, CameraInput.FLYCAM_UP );
|
joystick.getAxis( JoystickAxis.Z_ROTATION ).assignAxis( CameraInput.FLYCAM_DOWN, CameraInput.FLYCAM_UP );
|
||||||
joystick.getAxis( JoystickAxis.Z_AXIS ).assignAxis( CameraInput.FLYCAM_RIGHT, CameraInput.FLYCAM_LEFT );
|
joystick.getAxis( JoystickAxis.Z_AXIS ).assignAxis( CameraInput.FLYCAM_RIGHT, CameraInput.FLYCAM_LEFT );
|
||||||
|
|
||||||
// And let the dpad be up and down
|
// And let the dpad be up and down
|
||||||
joystick.getPovYAxis().assignAxis(CameraInput.FLYCAM_RISE, CameraInput.FLYCAM_LOWER);
|
joystick.getPovYAxis().assignAxis(CameraInput.FLYCAM_RISE, CameraInput.FLYCAM_LOWER);
|
||||||
|
|
||||||
if( joystick.getButton( "Button 8" ) != null ) {
|
if( joystick.getButton( "Button 8" ) != null ) {
|
||||||
// Let the stanard select button be the y invert toggle
|
// Let the stanard select button be the y invert toggle
|
||||||
joystick.getButton( "Button 8" ).assignButton( CameraInput.FLYCAM_INVERTY );
|
joystick.getButton( "Button 8" ).assignButton( CameraInput.FLYCAM_INVERTY );
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
joystick.getPovXAxis().assignAxis(CameraInput.FLYCAM_STRAFERIGHT, CameraInput.FLYCAM_STRAFELEFT);
|
joystick.getPovXAxis().assignAxis(CameraInput.FLYCAM_STRAFERIGHT, CameraInput.FLYCAM_STRAFELEFT);
|
||||||
joystick.getPovYAxis().assignAxis(CameraInput.FLYCAM_FORWARD, CameraInput.FLYCAM_BACKWARD);
|
joystick.getPovYAxis().assignAxis(CameraInput.FLYCAM_FORWARD, CameraInput.FLYCAM_BACKWARD);
|
||||||
joystick.getXAxis().assignAxis(CameraInput.FLYCAM_RIGHT, CameraInput.FLYCAM_LEFT);
|
joystick.getXAxis().assignAxis(CameraInput.FLYCAM_RIGHT, CameraInput.FLYCAM_LEFT);
|
||||||
joystick.getYAxis().assignAxis(CameraInput.FLYCAM_DOWN, CameraInput.FLYCAM_UP);
|
joystick.getYAxis().assignAxis(CameraInput.FLYCAM_DOWN, CameraInput.FLYCAM_UP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregisters the FlyByCamera from the event Dispatcher.
|
* Unregister this controller from its input manager.
|
||||||
*/
|
*/
|
||||||
public void unregisterInput(){
|
public void unregisterInput(){
|
||||||
|
|
||||||
if (inputManager == null) {
|
if (inputManager == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String s : mappings) {
|
for (String s : mappings) {
|
||||||
if (inputManager.hasMapping(s)) {
|
if (inputManager.hasMapping(s)) {
|
||||||
inputManager.deleteMapping( s );
|
inputManager.deleteMapping( s );
|
||||||
@ -296,12 +339,16 @@ public class FlyByCamera implements AnalogListener, ActionListener {
|
|||||||
|
|
||||||
Joystick[] joysticks = inputManager.getJoysticks();
|
Joystick[] joysticks = inputManager.getJoysticks();
|
||||||
if (joysticks != null && joysticks.length > 0){
|
if (joysticks != null && joysticks.length > 0){
|
||||||
Joystick joystick = joysticks[0];
|
// No way to unassign axis
|
||||||
|
|
||||||
// No way to unassing axis
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotate the camera by the specified amount around the specified axis.
|
||||||
|
*
|
||||||
|
* @param value rotation amount
|
||||||
|
* @param axis direction of rotation (a unit vector)
|
||||||
|
*/
|
||||||
protected void rotateCamera(float value, Vector3f axis){
|
protected void rotateCamera(float value, Vector3f axis){
|
||||||
if (dragToRotate){
|
if (dragToRotate){
|
||||||
if (canRotate){
|
if (canRotate){
|
||||||
@ -329,6 +376,11 @@ public class FlyByCamera implements AnalogListener, ActionListener {
|
|||||||
cam.setAxes(q);
|
cam.setAxes(q);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zoom the camera by the specified amount.
|
||||||
|
*
|
||||||
|
* @param value zoom amount
|
||||||
|
*/
|
||||||
protected void zoomCamera(float value){
|
protected void zoomCamera(float value){
|
||||||
// derive fovY value
|
// derive fovY value
|
||||||
float h = cam.getFrustumTop();
|
float h = cam.getFrustumTop();
|
||||||
@ -338,7 +390,7 @@ public class FlyByCamera implements AnalogListener, ActionListener {
|
|||||||
float near = cam.getFrustumNear();
|
float near = cam.getFrustumNear();
|
||||||
|
|
||||||
float fovY = FastMath.atan(h / near)
|
float fovY = FastMath.atan(h / near)
|
||||||
/ (FastMath.DEG_TO_RAD * .5f);
|
/ (FastMath.DEG_TO_RAD * .5f);
|
||||||
float newFovY = fovY + value * 0.1f * zoomSpeed;
|
float newFovY = fovY + value * 0.1f * zoomSpeed;
|
||||||
if (newFovY > 0f) {
|
if (newFovY > 0f) {
|
||||||
// Don't let the FOV go zero or negative.
|
// Don't let the FOV go zero or negative.
|
||||||
@ -354,6 +406,11 @@ public class FlyByCamera implements AnalogListener, ActionListener {
|
|||||||
cam.setFrustumRight(w);
|
cam.setFrustumRight(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate the camera upward by the specified amount.
|
||||||
|
*
|
||||||
|
* @param value translation amount
|
||||||
|
*/
|
||||||
protected void riseCamera(float value){
|
protected void riseCamera(float value){
|
||||||
Vector3f vel = new Vector3f(0, value * moveSpeed, 0);
|
Vector3f vel = new Vector3f(0, value * moveSpeed, 0);
|
||||||
Vector3f pos = cam.getLocation().clone();
|
Vector3f pos = cam.getLocation().clone();
|
||||||
@ -366,6 +423,12 @@ public class FlyByCamera implements AnalogListener, ActionListener {
|
|||||||
cam.setLocation(pos);
|
cam.setLocation(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate the camera left or forward by the specified amount.
|
||||||
|
*
|
||||||
|
* @param value translation amount
|
||||||
|
* @param sideways true→left, false→forward
|
||||||
|
*/
|
||||||
protected void moveCamera(float value, boolean sideways){
|
protected void moveCamera(float value, boolean sideways){
|
||||||
Vector3f vel = new Vector3f();
|
Vector3f vel = new Vector3f();
|
||||||
Vector3f pos = cam.getLocation().clone();
|
Vector3f pos = cam.getLocation().clone();
|
||||||
@ -385,6 +448,14 @@ public class FlyByCamera implements AnalogListener, ActionListener {
|
|||||||
cam.setLocation(pos);
|
cam.setLocation(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback to notify this controller of an analog input event.
|
||||||
|
*
|
||||||
|
* @param name name of the input event
|
||||||
|
* @param value value of the axis (from 0 to 1)
|
||||||
|
* @param tpf time per frame (in seconds)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
public void onAnalog(String name, float value, float tpf) {
|
public void onAnalog(String name, float value, float tpf) {
|
||||||
if (!enabled)
|
if (!enabled)
|
||||||
return;
|
return;
|
||||||
@ -416,6 +487,14 @@ public class FlyByCamera implements AnalogListener, ActionListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback to notify this controller of an action input event.
|
||||||
|
*
|
||||||
|
* @param name name of the input event
|
||||||
|
* @param value true if the action is "pressed", false otherwise
|
||||||
|
* @param tpf time per frame (in seconds)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
public void onAction(String name, boolean value, float tpf) {
|
public void onAction(String name, boolean value, float tpf) {
|
||||||
if (!enabled)
|
if (!enabled)
|
||||||
return;
|
return;
|
||||||
@ -424,11 +503,10 @@ public class FlyByCamera implements AnalogListener, ActionListener {
|
|||||||
canRotate = value;
|
canRotate = value;
|
||||||
inputManager.setCursorVisible(!value);
|
inputManager.setCursorVisible(!value);
|
||||||
} else if (name.equals(CameraInput.FLYCAM_INVERTY)) {
|
} else if (name.equals(CameraInput.FLYCAM_INVERTY)) {
|
||||||
// Toggle on the up.
|
// Invert the "up" direction.
|
||||||
if( !value ) {
|
if( !value ) {
|
||||||
invertY = !invertY;
|
invertY = !invertY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -31,16 +31,15 @@
|
|||||||
*/
|
*/
|
||||||
package com.jme3.scene;
|
package com.jme3.scene;
|
||||||
|
|
||||||
import com.jme3.asset.AssetInfo;
|
|
||||||
import com.jme3.asset.AssetManager;
|
import com.jme3.asset.AssetManager;
|
||||||
import com.jme3.asset.ModelKey;
|
import com.jme3.asset.ModelKey;
|
||||||
import com.jme3.export.InputCapsule;
|
import com.jme3.export.InputCapsule;
|
||||||
import com.jme3.export.JmeExporter;
|
import com.jme3.export.JmeExporter;
|
||||||
import com.jme3.export.JmeImporter;
|
import com.jme3.export.JmeImporter;
|
||||||
import com.jme3.export.OutputCapsule;
|
import com.jme3.export.OutputCapsule;
|
||||||
import com.jme3.export.binary.BinaryImporter;
|
|
||||||
import com.jme3.util.clone.Cloner;
|
|
||||||
import com.jme3.util.SafeArrayList;
|
import com.jme3.util.SafeArrayList;
|
||||||
|
import com.jme3.util.clone.Cloner;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
@ -164,25 +163,24 @@ public class AssetLinkNode extends Node {
|
|||||||
@Override
|
@Override
|
||||||
public void read(JmeImporter e) throws IOException {
|
public void read(JmeImporter e) throws IOException {
|
||||||
super.read(e);
|
super.read(e);
|
||||||
InputCapsule capsule = e.getCapsule(this);
|
|
||||||
BinaryImporter importer = BinaryImporter.getInstance();
|
final InputCapsule capsule = e.getCapsule(this);
|
||||||
AssetManager loaderManager = e.getAssetManager();
|
final AssetManager assetManager = e.getAssetManager();
|
||||||
|
|
||||||
assetLoaderKeys = (ArrayList<ModelKey>) capsule.readSavableArrayList("assetLoaderKeyList", new ArrayList<ModelKey>());
|
assetLoaderKeys = (ArrayList<ModelKey>) capsule.readSavableArrayList("assetLoaderKeyList", new ArrayList<ModelKey>());
|
||||||
for (Iterator<ModelKey> it = assetLoaderKeys.iterator(); it.hasNext();) {
|
|
||||||
ModelKey modelKey = it.next();
|
for (final Iterator<ModelKey> iterator = assetLoaderKeys.iterator(); iterator.hasNext(); ) {
|
||||||
AssetInfo info = loaderManager.locateAsset(modelKey);
|
|
||||||
Spatial child = null;
|
final ModelKey modelKey = iterator.next();
|
||||||
if (info != null) {
|
final Spatial child = assetManager.loadAsset(modelKey);
|
||||||
child = (Spatial) importer.load(info);
|
|
||||||
}
|
|
||||||
if (child != null) {
|
if (child != null) {
|
||||||
child.parent = this;
|
child.parent = this;
|
||||||
children.add(child);
|
children.add(child);
|
||||||
assetChildren.put(modelKey, child);
|
assetChildren.put(modelKey, child);
|
||||||
} else {
|
} else {
|
||||||
Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Cannot locate {0} for asset link node {1}",
|
Logger.getLogger(this.getClass().getName()).log(Level.WARNING,
|
||||||
new Object[]{ modelKey, key });
|
"Cannot locate {0} for asset link node {1}", new Object[]{modelKey, key});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -190,7 +188,7 @@ public class AssetLinkNode extends Node {
|
|||||||
@Override
|
@Override
|
||||||
public void write(JmeExporter e) throws IOException {
|
public void write(JmeExporter e) throws IOException {
|
||||||
SafeArrayList<Spatial> childs = children;
|
SafeArrayList<Spatial> childs = children;
|
||||||
children = new SafeArrayList<Spatial>(Spatial.class);
|
children = new SafeArrayList<>(Spatial.class);
|
||||||
super.write(e);
|
super.write(e);
|
||||||
OutputCapsule capsule = e.getCapsule(this);
|
OutputCapsule capsule = e.getCapsule(this);
|
||||||
capsule.writeSavableArrayList(assetLoaderKeys, "assetLoaderKeyList", null);
|
capsule.writeSavableArrayList(assetLoaderKeys, "assetLoaderKeyList", null);
|
||||||
|
@ -137,6 +137,20 @@ public class Geometry extends Spatial {
|
|||||||
return super.checkCulling(cam);
|
return super.checkCulling(cam);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the world transform of this Geometry and clear the
|
||||||
|
* TRANSFORM refresh flag.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
void checkDoTransformUpdate() {
|
||||||
|
if (ignoreTransform) {
|
||||||
|
worldTransform.loadIdentity();
|
||||||
|
refreshFlags &= ~RF_TRANSFORM;
|
||||||
|
} else {
|
||||||
|
super.checkDoTransformUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return If ignoreTransform mode is set.
|
* @return If ignoreTransform mode is set.
|
||||||
*
|
*
|
||||||
@ -151,6 +165,7 @@ public class Geometry extends Spatial {
|
|||||||
*/
|
*/
|
||||||
public void setIgnoreTransform(boolean ignoreTransform) {
|
public void setIgnoreTransform(boolean ignoreTransform) {
|
||||||
this.ignoreTransform = ignoreTransform;
|
this.ignoreTransform = ignoreTransform;
|
||||||
|
setTransformRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -398,9 +413,6 @@ public class Geometry extends Spatial {
|
|||||||
|
|
||||||
// Compute the cached world matrix
|
// Compute the cached world matrix
|
||||||
cachedWorldMat.loadIdentity();
|
cachedWorldMat.loadIdentity();
|
||||||
if (ignoreTransform) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cachedWorldMat.setRotationQuaternion(worldTransform.getRotation());
|
cachedWorldMat.setRotationQuaternion(worldTransform.getRotation());
|
||||||
cachedWorldMat.setTranslation(worldTransform.getTranslation());
|
cachedWorldMat.setTranslation(worldTransform.getTranslation());
|
||||||
|
|
||||||
|
@ -12,8 +12,8 @@ LOADER com.jme3.texture.plugins.DDSLoader : dds
|
|||||||
LOADER com.jme3.texture.plugins.PFMLoader : pfm
|
LOADER com.jme3.texture.plugins.PFMLoader : pfm
|
||||||
LOADER com.jme3.texture.plugins.HDRLoader : hdr
|
LOADER com.jme3.texture.plugins.HDRLoader : hdr
|
||||||
LOADER com.jme3.texture.plugins.TGALoader : tga
|
LOADER com.jme3.texture.plugins.TGALoader : tga
|
||||||
LOADER com.jme3.export.binary.BinaryImporter : j3o
|
LOADER com.jme3.export.binary.BinaryLoader : j3o
|
||||||
LOADER com.jme3.export.binary.BinaryImporter : j3f
|
LOADER com.jme3.export.binary.BinaryLoader : j3f
|
||||||
LOADER com.jme3.scene.plugins.OBJLoader : obj
|
LOADER com.jme3.scene.plugins.OBJLoader : obj
|
||||||
LOADER com.jme3.scene.plugins.MTLLoader : mtl
|
LOADER com.jme3.scene.plugins.MTLLoader : mtl
|
||||||
LOADER com.jme3.scene.plugins.ogre.MeshLoader : meshxml, mesh.xml
|
LOADER com.jme3.scene.plugins.ogre.MeshLoader : meshxml, mesh.xml
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
package com.jme3.export.binary;
|
||||||
|
|
||||||
|
import com.jme3.asset.AssetInfo;
|
||||||
|
import com.jme3.asset.AssetLoader;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.Deque;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default loader to load binaries files.
|
||||||
|
*
|
||||||
|
* @author JavaSaBr
|
||||||
|
*/
|
||||||
|
public class BinaryLoader implements AssetLoader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The importers queue.
|
||||||
|
*/
|
||||||
|
private final Deque<BinaryImporter> importers;
|
||||||
|
|
||||||
|
public BinaryLoader() {
|
||||||
|
importers = new ArrayDeque<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object load(final AssetInfo assetInfo) throws IOException {
|
||||||
|
|
||||||
|
BinaryImporter importer = importers.pollLast();
|
||||||
|
|
||||||
|
if (importer == null) {
|
||||||
|
importer = new BinaryImporter();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return importer.load(assetInfo);
|
||||||
|
} finally {
|
||||||
|
importers.addLast(importer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -42,6 +42,8 @@ import com.jme3.scene.Geometry;
|
|||||||
import com.jme3.scene.Mesh;
|
import com.jme3.scene.Mesh;
|
||||||
import com.jme3.scene.Node;
|
import com.jme3.scene.Node;
|
||||||
import com.jme3.scene.shape.Quad;
|
import com.jme3.scene.shape.Quad;
|
||||||
|
import com.jme3.system.JmeSystem;
|
||||||
|
import com.jme3.system.MockJmeSystemDelegate;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -87,6 +89,7 @@ public class CollideIgnoreTransformTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPhantomTriangles() {
|
public void testPhantomTriangles() {
|
||||||
|
JmeSystem.setSystemDelegate(new MockJmeSystemDelegate());
|
||||||
assetManager = new DesktopAssetManager();
|
assetManager = new DesktopAssetManager();
|
||||||
assetManager.registerLocator(null, ClasspathLocator.class);
|
assetManager.registerLocator(null, ClasspathLocator.class);
|
||||||
assetManager.registerLoader(J3MLoader.class, "j3m", "j3md");
|
assetManager.registerLoader(J3MLoader.class, "j3m", "j3md");
|
||||||
|
@ -0,0 +1,296 @@
|
|||||||
|
package com.jme3.scene.plugins.gltf;
|
||||||
|
|
||||||
|
import com.jme3.math.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class AnimData {
|
||||||
|
|
||||||
|
public enum Type {
|
||||||
|
Translation,
|
||||||
|
Rotation,
|
||||||
|
Scale
|
||||||
|
}
|
||||||
|
|
||||||
|
Float length;
|
||||||
|
float[] times;
|
||||||
|
List<TimeData> timeArrays = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
Vector3f[] translations;
|
||||||
|
Quaternion[] rotations;
|
||||||
|
Vector3f[] scales;
|
||||||
|
//not used for now
|
||||||
|
float[] weights;
|
||||||
|
|
||||||
|
public void update() {
|
||||||
|
|
||||||
|
if (equalTimes(timeArrays)) {
|
||||||
|
times = timeArrays.get(0).times;
|
||||||
|
ensureArraysInit();
|
||||||
|
} else {
|
||||||
|
//Times array are different and contains different sampling times.
|
||||||
|
//We have to merge them because JME needs the 3 types of transforms for each keyFrame.
|
||||||
|
|
||||||
|
//extracting keyframes information
|
||||||
|
List<KeyFrame> keyFrames = new ArrayList<>();
|
||||||
|
TimeData timeData = timeArrays.get(0);
|
||||||
|
Type type = timeData.type;
|
||||||
|
for (int i = 0; i < timeData.times.length; i++) {
|
||||||
|
float time = timeData.times[i];
|
||||||
|
KeyFrame keyFrame = new KeyFrame();
|
||||||
|
keyFrame.time = time;
|
||||||
|
setKeyFrameTransforms(type, keyFrame, timeData.times);
|
||||||
|
keyFrames.add(keyFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 1; i < timeArrays.size(); i++) {
|
||||||
|
timeData = timeArrays.get(i);
|
||||||
|
type = timeData.type;
|
||||||
|
for (float time : timeData.times) {
|
||||||
|
for (int j = 0; j < keyFrames.size(); j++) {
|
||||||
|
KeyFrame kf = keyFrames.get(j);
|
||||||
|
if (Float.floatToIntBits(kf.time) != Float.floatToIntBits(time)) {
|
||||||
|
if (time > kf.time) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
kf = new KeyFrame();
|
||||||
|
kf.time = time;
|
||||||
|
keyFrames.add(j, kf);
|
||||||
|
//we inserted a keyframe let's shift the counter.
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setKeyFrameTransforms(type, kf, timeData.times);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// populating transforms array from the keyframes, interpolating
|
||||||
|
times = new float[keyFrames.size()];
|
||||||
|
|
||||||
|
ensureArraysInit();
|
||||||
|
|
||||||
|
TransformIndices translationIndices = new TransformIndices();
|
||||||
|
TransformIndices rotationIndices = new TransformIndices();
|
||||||
|
TransformIndices scaleIndices = new TransformIndices();
|
||||||
|
|
||||||
|
for (int i = 0; i < keyFrames.size(); i++) {
|
||||||
|
KeyFrame kf = keyFrames.get(i);
|
||||||
|
//we need Interpolate between keyframes when transforms are sparse.
|
||||||
|
times[i] = kf.time;
|
||||||
|
populateTransform(Type.Translation, i, keyFrames, kf, translationIndices);
|
||||||
|
populateTransform(Type.Rotation, i, keyFrames, kf, rotationIndices);
|
||||||
|
populateTransform(Type.Scale, i, keyFrames, kf, scaleIndices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ensureArraysInit();
|
||||||
|
|
||||||
|
if (times[0] > 0) {
|
||||||
|
//Anim doesn't start at 0, JME can't handle that and will interpolate transforms linearly from 0 to the first frame of the anim.
|
||||||
|
//we need to add a frame at 0 that copies the first real frame
|
||||||
|
|
||||||
|
float[] newTimes = new float[times.length + 1];
|
||||||
|
newTimes[0] = 0f;
|
||||||
|
System.arraycopy(times, 0, newTimes, 1, times.length);
|
||||||
|
times = newTimes;
|
||||||
|
|
||||||
|
if (translations != null) {
|
||||||
|
Vector3f[] newTranslations = new Vector3f[translations.length + 1];
|
||||||
|
newTranslations[0] = translations[0];
|
||||||
|
System.arraycopy(translations, 0, newTranslations, 1, translations.length);
|
||||||
|
translations = newTranslations;
|
||||||
|
}
|
||||||
|
if (rotations != null) {
|
||||||
|
Quaternion[] newRotations = new Quaternion[rotations.length + 1];
|
||||||
|
newRotations[0] = rotations[0];
|
||||||
|
System.arraycopy(rotations, 0, newRotations, 1, rotations.length);
|
||||||
|
rotations = newRotations;
|
||||||
|
}
|
||||||
|
if (scales != null) {
|
||||||
|
Vector3f[] newScales = new Vector3f[scales.length + 1];
|
||||||
|
newScales[0] = scales[0];
|
||||||
|
System.arraycopy(scales, 0, newScales, 1, scales.length);
|
||||||
|
scales = newScales;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
length = times[times.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void populateTransform(Type type, int index, List<KeyFrame> keyFrames, KeyFrame currentKeyFrame, TransformIndices transformIndices) {
|
||||||
|
Object transform = getTransform(type, currentKeyFrame);
|
||||||
|
if (transform != null) {
|
||||||
|
getArray(type)[index] = transform;
|
||||||
|
transformIndices.last = index;
|
||||||
|
} else {
|
||||||
|
transformIndices.next = findNext(keyFrames, type, index);
|
||||||
|
if (transformIndices.next == -1) {
|
||||||
|
//no next let's use prev value.
|
||||||
|
if (transformIndices.last == -1) {
|
||||||
|
//last Transform Index = -1 it means there are no transforms. nothing more to do
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
KeyFrame lastKeyFrame = keyFrames.get(transformIndices.last);
|
||||||
|
getArray(type)[index] = getTransform(type, lastKeyFrame);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
KeyFrame nextKeyFrame = keyFrames.get(transformIndices.next);
|
||||||
|
if (transformIndices.last == -1) {
|
||||||
|
//no previous transforms let's use the new one.
|
||||||
|
translations[index] = nextKeyFrame.translation;
|
||||||
|
} else {
|
||||||
|
//interpolation between the previous transform and the next one.
|
||||||
|
KeyFrame lastKeyFrame = keyFrames.get(transformIndices.last);
|
||||||
|
float ratio = currentKeyFrame.time / (nextKeyFrame.time - lastKeyFrame.time);
|
||||||
|
interpolate(type, ratio, lastKeyFrame, nextKeyFrame, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int findNext(List<KeyFrame> keyFrames, Type type, int fromIndex) {
|
||||||
|
for (int i = fromIndex + 1; i < keyFrames.size(); i++) {
|
||||||
|
KeyFrame kf = keyFrames.get(i);
|
||||||
|
switch (type) {
|
||||||
|
case Translation:
|
||||||
|
if (kf.translation != null) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Rotation:
|
||||||
|
if (kf.rotation != null) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Scale:
|
||||||
|
if (kf.scale != null) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void interpolate(Type type, float ratio, KeyFrame lastKeyFrame, KeyFrame nextKeyFrame, int currentIndex) {
|
||||||
|
//TODO here we should interpolate differently according to the interpolation given in the gltf file.
|
||||||
|
switch (type) {
|
||||||
|
case Translation:
|
||||||
|
translations[currentIndex] = FastMath.interpolateLinear(ratio, lastKeyFrame.translation, nextKeyFrame.translation);
|
||||||
|
break;
|
||||||
|
case Rotation:
|
||||||
|
Quaternion rot = new Quaternion().set(lastKeyFrame.rotation);
|
||||||
|
rot.nlerp(nextKeyFrame.rotation, ratio);
|
||||||
|
rotations[currentIndex] = rot;
|
||||||
|
break;
|
||||||
|
case Scale:
|
||||||
|
scales[currentIndex] = FastMath.interpolateLinear(ratio, lastKeyFrame.scale, nextKeyFrame.scale);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object[] getArray(Type type) {
|
||||||
|
switch (type) {
|
||||||
|
case Translation:
|
||||||
|
return translations;
|
||||||
|
case Rotation:
|
||||||
|
return rotations;
|
||||||
|
case Scale:
|
||||||
|
return scales;
|
||||||
|
default:
|
||||||
|
return translations;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getTransform(Type type, KeyFrame kf) {
|
||||||
|
switch (type) {
|
||||||
|
case Translation:
|
||||||
|
return kf.translation;
|
||||||
|
case Rotation:
|
||||||
|
return kf.rotation;
|
||||||
|
case Scale:
|
||||||
|
return kf.scale;
|
||||||
|
default:
|
||||||
|
return kf.translation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureArraysInit() {
|
||||||
|
if (translations == null || translations.length < times.length) {
|
||||||
|
translations = new Vector3f[times.length];
|
||||||
|
for (int i = 0; i < translations.length; i++) {
|
||||||
|
translations[i] = new Vector3f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rotations == null || rotations.length < times.length) {
|
||||||
|
rotations = new Quaternion[times.length];
|
||||||
|
for (int i = 0; i < rotations.length; i++) {
|
||||||
|
rotations[i] = new Quaternion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (scales == null || scales.length < times.length) {
|
||||||
|
scales = new Vector3f[times.length];
|
||||||
|
for (int i = 0; i < scales.length; i++) {
|
||||||
|
scales[i] = new Vector3f().set(Vector3f.UNIT_XYZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setKeyFrameTransforms(Type type, KeyFrame keyFrame, float[] transformTimes) {
|
||||||
|
int index = 0;
|
||||||
|
while (Float.floatToIntBits(transformTimes[index]) != Float.floatToIntBits(keyFrame.time)) {
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
switch (type) {
|
||||||
|
case Translation:
|
||||||
|
keyFrame.translation = translations[index];
|
||||||
|
break;
|
||||||
|
case Rotation:
|
||||||
|
keyFrame.rotation = rotations[index];
|
||||||
|
break;
|
||||||
|
case Scale:
|
||||||
|
keyFrame.scale = scales[index];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean equalTimes(List<TimeData> timeData) {
|
||||||
|
if (timeData.size() == 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
float[] times0 = timeData.get(0).times;
|
||||||
|
for (int i = 1; i < timeData.size(); i++) {
|
||||||
|
float[] timesI = timeData.get(i).times;
|
||||||
|
if (!Arrays.equals(times0, timesI)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TimeData {
|
||||||
|
|
||||||
|
float[] times;
|
||||||
|
Type type;
|
||||||
|
|
||||||
|
public TimeData(float[] times, Type type) {
|
||||||
|
this.times = times;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TransformIndices {
|
||||||
|
int last = -1;
|
||||||
|
int next = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class KeyFrame {
|
||||||
|
float time;
|
||||||
|
Vector3f translation;
|
||||||
|
Quaternion rotation;
|
||||||
|
Vector3f scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package com.jme3.scene.plugins.gltf;
|
package com.jme3.scene.plugins.gltf;
|
||||||
|
|
||||||
import com.jme3.asset.AssetKey;
|
import com.jme3.asset.AssetKey;
|
||||||
|
import com.jme3.asset.cache.AssetCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Nehon on 09/09/2017.
|
* Created by Nehon on 09/09/2017.
|
||||||
@ -9,4 +10,9 @@ class BinDataKey extends AssetKey<Object> {
|
|||||||
public BinDataKey(String name) {
|
public BinDataKey(String name) {
|
||||||
super(name);
|
super(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends AssetCache> getCacheType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ import com.jme3.util.mikktspace.MikktspaceTangentGenerator;
|
|||||||
import javax.xml.bind.DatatypeConverter;
|
import javax.xml.bind.DatatypeConverter;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.Buffer;
|
import java.nio.Buffer;
|
||||||
|
import java.sql.Time;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@ -140,6 +141,8 @@ public class GltfLoader implements AssetLoader {
|
|||||||
return rootNode;
|
return rootNode;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new AssetLoadException("An error occurred loading " + assetInfo.getKey().getName(), e);
|
throw new AssetLoadException("An error occurred loading " + assetInfo.getKey().getName(), e);
|
||||||
|
} finally {
|
||||||
|
stream.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -527,7 +530,9 @@ public class GltfLoader implements AssetLoader {
|
|||||||
BinDataKey key = new BinDataKey(info.getKey().getFolder() + uri);
|
BinDataKey key = new BinDataKey(info.getKey().getFolder() + uri);
|
||||||
InputStream input = (InputStream) info.getManager().loadAsset(key);
|
InputStream input = (InputStream) info.getManager().loadAsset(key);
|
||||||
data = new byte[bufferLength];
|
data = new byte[bufferLength];
|
||||||
input.read(data);
|
DataInputStream dataStream = new DataInputStream(input);
|
||||||
|
dataStream.readFully(data);
|
||||||
|
dataStream.close();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//no URI this should not happen in a gltf file, only in glb files.
|
//no URI this should not happen in a gltf file, only in glb files.
|
||||||
@ -756,27 +761,27 @@ public class GltfLoader implements AssetLoader {
|
|||||||
times = readAccessorData(timeIndex, floatArrayPopulator);
|
times = readAccessorData(timeIndex, floatArrayPopulator);
|
||||||
addToCache("accessors", timeIndex, times, accessors.size());
|
addToCache("accessors", timeIndex, times, accessors.size());
|
||||||
}
|
}
|
||||||
if (animData.times == null) {
|
// if (animData.times == null) {
|
||||||
animData.times = times;
|
// animData.times = times;
|
||||||
} else {
|
// } else {
|
||||||
//check if we are loading the same time array
|
// //check if we are loading the same time array
|
||||||
if (animData.times != times) {
|
// if (animData.times != times) {
|
||||||
//TODO there might be work to do here... if the inputs are different we might want to merge the different times array...
|
// //TODO there might be work to do here... if the inputs are different we might want to merge the different times array...
|
||||||
//easier said than done.
|
// //easier said than done.
|
||||||
logger.log(Level.WARNING, "Channel has different input accessors for samplers");
|
// logger.log(Level.WARNING, "Channel has different input accessors for samplers");
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if (animData.length == null) {
|
|
||||||
//animation length is the last timestamp
|
|
||||||
animData.length = times[times.length - 1];
|
|
||||||
}
|
|
||||||
if (targetPath.equals("translation")) {
|
if (targetPath.equals("translation")) {
|
||||||
|
animData.timeArrays.add(new AnimData.TimeData(times, AnimData.Type.Translation));
|
||||||
Vector3f[] translations = readAccessorData(dataIndex, vector3fArrayPopulator);
|
Vector3f[] translations = readAccessorData(dataIndex, vector3fArrayPopulator);
|
||||||
animData.translations = translations;
|
animData.translations = translations;
|
||||||
} else if (targetPath.equals("scale")) {
|
} else if (targetPath.equals("scale")) {
|
||||||
|
animData.timeArrays.add(new AnimData.TimeData(times, AnimData.Type.Scale));
|
||||||
Vector3f[] scales = readAccessorData(dataIndex, vector3fArrayPopulator);
|
Vector3f[] scales = readAccessorData(dataIndex, vector3fArrayPopulator);
|
||||||
animData.scales = scales;
|
animData.scales = scales;
|
||||||
} else if (targetPath.equals("rotation")) {
|
} else if (targetPath.equals("rotation")) {
|
||||||
|
animData.timeArrays.add(new AnimData.TimeData(times, AnimData.Type.Rotation));
|
||||||
Quaternion[] rotations = readAccessorData(dataIndex, quaternionArrayPopulator);
|
Quaternion[] rotations = readAccessorData(dataIndex, quaternionArrayPopulator);
|
||||||
animData.rotations = rotations;
|
animData.rotations = rotations;
|
||||||
}
|
}
|
||||||
@ -797,10 +802,10 @@ public class GltfLoader implements AssetLoader {
|
|||||||
if (animData == null) {
|
if (animData == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
animData.update();
|
||||||
if (animData.length > anim.getLength()) {
|
if (animData.length > anim.getLength()) {
|
||||||
anim.setLength(animData.length);
|
anim.setLength(animData.length);
|
||||||
}
|
}
|
||||||
animData.update();
|
|
||||||
Object node = fetchFromCache("nodes", i, Object.class);
|
Object node = fetchFromCache("nodes", i, Object.class);
|
||||||
if (node instanceof Spatial) {
|
if (node instanceof Spatial) {
|
||||||
Spatial s = (Spatial) node;
|
Spatial s = (Spatial) node;
|
||||||
@ -1136,46 +1141,6 @@ public class GltfLoader implements AssetLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AnimData {
|
|
||||||
Float length;
|
|
||||||
float[] times;
|
|
||||||
Vector3f[] translations;
|
|
||||||
Quaternion[] rotations;
|
|
||||||
Vector3f[] scales;
|
|
||||||
//not used for now
|
|
||||||
float[] weights;
|
|
||||||
|
|
||||||
public void update() {
|
|
||||||
if (times[0] > 0) {
|
|
||||||
//Anim doesn't start at 0, JME can't handle that and will interpolate transforms linearly from 0 to the first frame of the anim.
|
|
||||||
//we need to add a frame at 0 that copies the first real frame
|
|
||||||
|
|
||||||
float[] newTimes = new float[times.length + 1];
|
|
||||||
newTimes[0] = 0f;
|
|
||||||
System.arraycopy(times, 0, newTimes, 1, times.length);
|
|
||||||
times = newTimes;
|
|
||||||
|
|
||||||
if (translations != null) {
|
|
||||||
Vector3f[] newTranslations = new Vector3f[translations.length + 1];
|
|
||||||
newTranslations[0] = translations[0];
|
|
||||||
System.arraycopy(translations, 0, newTranslations, 1, translations.length);
|
|
||||||
translations = newTranslations;
|
|
||||||
}
|
|
||||||
if (rotations != null) {
|
|
||||||
Quaternion[] newRotations = new Quaternion[rotations.length + 1];
|
|
||||||
newRotations[0] = rotations[0];
|
|
||||||
System.arraycopy(rotations, 0, newRotations, 1, rotations.length);
|
|
||||||
rotations = newRotations;
|
|
||||||
}
|
|
||||||
if (scales != null) {
|
|
||||||
Vector3f[] newScales = new Vector3f[scales.length + 1];
|
|
||||||
newScales[0] = scales[0];
|
|
||||||
System.arraycopy(scales, 0, newScales, 1, scales.length);
|
|
||||||
scales = newScales;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class BoneWrapper {
|
private class BoneWrapper {
|
||||||
Bone bone;
|
Bone bone;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user