Merge remote-tracking branch 'origin/master' into in-pass-shadows

in-pass-shadows
Kirill Vainer 7 years ago
commit 5108f52ebf
  1. 256
      jme3-core/src/main/java/com/jme3/input/FlyByCamera.java
  2. 30
      jme3-core/src/main/java/com/jme3/scene/AssetLinkNode.java
  3. 18
      jme3-core/src/main/java/com/jme3/scene/Geometry.java
  4. 4
      jme3-core/src/main/resources/com/jme3/asset/General.cfg
  5. 41
      jme3-core/src/plugins/java/com/jme3/export/binary/BinaryLoader.java
  6. 3
      jme3-core/src/test/java/com/jme3/collision/CollideIgnoreTransformTest.java
  7. 296
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/AnimData.java
  8. 6
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/BinDataKey.java
  9. 77
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java

@ -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_LOWER,
CameraInput.FLYCAM_INVERTY
};
CameraInput.FLYCAM_RISE,
CameraInput.FLYCAM_LOWER,
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…
Cancel
Save