From 651e77953a63e6ef3de91b6197da8ae8e6cbd8c2 Mon Sep 17 00:00:00 2001 From: seint Date: Thu, 13 Apr 2017 08:40:34 +0200 Subject: [PATCH 01/22] VR controller clean integration --- .../main/java/com/jme3/app/VRAppState.java | 12 +- .../main/java/com/jme3/app/VRApplication.java | 12 +- .../main/java/com/jme3/app/VREnvironment.java | 14 +++ .../main/java/com/jme3/input/vr/OpenVR.java | 37 +++--- .../java/com/jme3/input/vr/OpenVRInput.java | 115 +++++++++++++----- .../input/vr/OpenVRTrackedController.java | 93 ++++++++++++++ .../main/java/com/jme3/input/vr/VRBounds.java | 10 +- .../jme3/input/vr/VRTrackedController.java | 40 ++++++ .../com/jme3/util/AbstractVRViewManager.java | 41 ++++--- .../java/com/jme3/util/VRViewManager.java | 16 ++- .../java/com/jme3/util/VRViewManagerOSVR.java | 8 +- .../com/jme3/util/VRViewManagerOpenVR.java | 44 +++++-- 12 files changed, 348 insertions(+), 94 deletions(-) create mode 100644 jme3-vr/src/main/java/com/jme3/input/vr/OpenVRTrackedController.java diff --git a/jme3-vr/src/main/java/com/jme3/app/VRAppState.java b/jme3-vr/src/main/java/com/jme3/app/VRAppState.java index 5b850c931..a468e71b9 100644 --- a/jme3-vr/src/main/java/com/jme3/app/VRAppState.java +++ b/jme3-vr/src/main/java/com/jme3/app/VRAppState.java @@ -241,7 +241,7 @@ public class VRAppState extends AbstractAppState { return application.getViewPort(); } - return environment.getVRViewManager().getLeftViewport(); + return environment.getVRViewManager().getLeftViewPort(); } /** @@ -253,7 +253,7 @@ public class VRAppState extends AbstractAppState { if( environment.getVRViewManager() == null ){ return application.getViewPort(); } - return environment.getVRViewManager().getRightViewport(); + return environment.getVRViewManager().getRightViewPort(); } /** @@ -263,12 +263,12 @@ public class VRAppState extends AbstractAppState { public void setBackgroundColors(ColorRGBA clr) { if( environment.getVRViewManager() == null ) { application.getViewPort().setBackgroundColor(clr); - } else if( environment.getVRViewManager().getLeftViewport() != null ) { + } else if( environment.getVRViewManager().getLeftViewPort() != null ) { - environment.getVRViewManager().getLeftViewport().setBackgroundColor(clr); + environment.getVRViewManager().getLeftViewPort().setBackgroundColor(clr); - if( environment.getVRViewManager().getRightViewport() != null ){ - environment.getVRViewManager().getRightViewport().setBackgroundColor(clr); + if( environment.getVRViewManager().getRightViewPort() != null ){ + environment.getVRViewManager().getRightViewPort().setBackgroundColor(clr); } } } diff --git a/jme3-vr/src/main/java/com/jme3/app/VRApplication.java b/jme3-vr/src/main/java/com/jme3/app/VRApplication.java index 698451248..9d502431d 100644 --- a/jme3-vr/src/main/java/com/jme3/app/VRApplication.java +++ b/jme3-vr/src/main/java/com/jme3/app/VRApplication.java @@ -254,7 +254,7 @@ public abstract class VRApplication implements Application, SystemListener { dummyCam = new Camera(); initStateManager(); - + // Create the GUI manager. guiManager = new VRGuiManager(null); @@ -1085,7 +1085,7 @@ public abstract class VRApplication implements Application, SystemListener { */ public ViewPort getLeftViewPort() { if( viewmanager == null ) return getViewPort(); - return viewmanager.getLeftViewport(); + return viewmanager.getLeftViewPort(); } /** @@ -1095,7 +1095,7 @@ public abstract class VRApplication implements Application, SystemListener { */ public ViewPort getRightViewPort() { if( viewmanager == null ) return getViewPort(); - return viewmanager.getRightViewport(); + return viewmanager.getRightViewPort(); } @@ -1106,9 +1106,9 @@ public abstract class VRApplication implements Application, SystemListener { public void setBackgroundColors(ColorRGBA clr) { if( viewmanager == null ) { getViewPort().setBackgroundColor(clr); - } else if( viewmanager.getLeftViewport() != null ) { - viewmanager.getLeftViewport().setBackgroundColor(clr); - if( viewmanager.getRightViewport() != null ) viewmanager.getRightViewport().setBackgroundColor(clr); + } else if( viewmanager.getLeftViewPort() != null ) { + viewmanager.getLeftViewPort().setBackgroundColor(clr); + if( viewmanager.getRightViewPort() != null ) viewmanager.getRightViewPort().setBackgroundColor(clr); } } diff --git a/jme3-vr/src/main/java/com/jme3/app/VREnvironment.java b/jme3-vr/src/main/java/com/jme3/app/VREnvironment.java index e9e6d0aac..006400604 100644 --- a/jme3-vr/src/main/java/com/jme3/app/VREnvironment.java +++ b/jme3-vr/src/main/java/com/jme3/app/VREnvironment.java @@ -8,6 +8,7 @@ import com.jme3.app.state.AppState; import com.jme3.input.vr.OSVR; import com.jme3.input.vr.OpenVR; import com.jme3.input.vr.VRAPI; +import com.jme3.input.vr.VRBounds; import com.jme3.input.vr.VRInputAPI; import com.jme3.renderer.Camera; import com.jme3.scene.Spatial; @@ -27,6 +28,8 @@ public class VREnvironment { private VRGuiManager guiManager = null; private VRMouseManager mouseManager = null; private VRViewManager viewmanager = null; + private VRBounds bounds = null; + /** * The underlying system VR API. By default set to {@link VRConstants#SETTING_VRAPI_OPENVR_VALUE}. @@ -73,6 +76,9 @@ public class VREnvironment { guiManager = new VRGuiManager(this); mouseManager = new VRMouseManager(this); + + bounds = new VRBounds(); + dummyCam = new Camera(); processSettings(); @@ -86,6 +92,14 @@ public class VREnvironment { return hardware; } + /** + * Get the VR bounds. + * @return the VR bounds. + */ + public VRBounds getVRBounds(){ + return bounds; + } + /** * Get the VR dedicated input. * @return the VR dedicated input. diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OpenVR.java b/jme3-vr/src/main/java/com/jme3/input/vr/OpenVR.java index 8171bc48a..509e84987 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/OpenVR.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/OpenVR.java @@ -46,14 +46,14 @@ public class OpenVR implements VRAPI { private static boolean initSuccess = false; private static boolean flipEyes = false; - private static IntBuffer hmdDisplayFrequency; - private static TrackedDevicePose_t.ByReference hmdTrackedDevicePoseReference; - protected static TrackedDevicePose_t[] hmdTrackedDevicePoses; + private IntBuffer hmdDisplayFrequency; + private TrackedDevicePose_t.ByReference hmdTrackedDevicePoseReference; + protected TrackedDevicePose_t[] hmdTrackedDevicePoses; - protected static IntByReference hmdErrorStore; + protected IntByReference hmdErrorStore; - private static final Quaternion rotStore = new Quaternion(); - private static final Vector3f posStore = new Vector3f(); + private final Quaternion rotStore = new Quaternion(); + private final Vector3f posStore = new Vector3f(); private static FloatByReference tlastVsync; @@ -65,20 +65,21 @@ public class OpenVR implements VRAPI { // for debugging latency private int frames = 0; - protected static Matrix4f[] poseMatrices; + protected Matrix4f[] poseMatrices; - private static final Matrix4f hmdPose = Matrix4f.IDENTITY.clone(); - private static Matrix4f hmdProjectionLeftEye; - private static Matrix4f hmdProjectionRightEye; - private static Matrix4f hmdPoseLeftEye; - private static Matrix4f hmdPoseRightEye; + private final Matrix4f hmdPose = Matrix4f.IDENTITY.clone(); + private Matrix4f hmdProjectionLeftEye; + private Matrix4f hmdProjectionRightEye; + private Matrix4f hmdPoseLeftEye; + private Matrix4f hmdPoseRightEye; - private static Vector3f hmdPoseLeftEyeVec, hmdPoseRightEyeVec, hmdSeatToStand; + private Vector3f hmdPoseLeftEyeVec, hmdPoseRightEyeVec, hmdSeatToStand; + + private float vsyncToPhotons; + private double timePerFrame, frameCountRun; + private long frameCount; + private OpenVRInput VRinput; - private static float vsyncToPhotons; - private static double timePerFrame, frameCountRun; - private static long frameCount; - private static OpenVRInput VRinput; private VREnvironment environment = null; @@ -181,7 +182,7 @@ public class OpenVR implements VRAPI { VRinput.updateConnectedControllers(); // init bounds & chaperone info - VRBounds.init(); + environment.getVRBounds().init(this); logger.config("Initializing OpenVR system [SUCCESS]"); initSuccess = true; diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OpenVRInput.java b/jme3-vr/src/main/java/com/jme3/input/vr/OpenVRInput.java index 0c565d0a1..6336cb4d9 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/OpenVRInput.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/OpenVRInput.java @@ -5,6 +5,7 @@ */ package com.jme3.input.vr; +import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -183,28 +184,50 @@ public class OpenVRInput implements VRInputAPI { @Override public Vector3f getVelocity(int controllerIndex) { - int index = OpenVRInput.controllerIndex[controllerIndex]; - if( needsNewVelocity[index] ) { - OpenVR.hmdTrackedDevicePoses[index].readField("vVelocity"); - needsNewVelocity[index] = false; - } - tempVel.x = OpenVR.hmdTrackedDevicePoses[index].vVelocity.v[0]; - tempVel.y = OpenVR.hmdTrackedDevicePoses[index].vVelocity.v[1]; - tempVel.z = OpenVR.hmdTrackedDevicePoses[index].vVelocity.v[2]; - return tempVel; + + if (environment != null){ + + if (environment.getVRHardware() instanceof OpenVR){ + int index = OpenVRInput.controllerIndex[controllerIndex]; + if( needsNewVelocity[index] ) { + ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].readField("vVelocity"); + needsNewVelocity[index] = false; + } + tempVel.x = ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].vVelocity.v[0]; + tempVel.y = ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].vVelocity.v[1]; + tempVel.z = ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].vVelocity.v[2]; + return tempVel; + } else { + throw new IllegalStateException("VR hardware "+environment.getVRHardware().getClass().getSimpleName()+" is not a subclass of "+OpenVR.class.getSimpleName()); + } + } else { + throw new IllegalStateException("VR input is not attached to a VR environment."); + } } @Override public Vector3f getAngularVelocity(int controllerIndex) { - int index = OpenVRInput.controllerIndex[controllerIndex]; - if( needsNewAngVelocity[index] ) { - OpenVR.hmdTrackedDevicePoses[index].readField("vAngularVelocity"); - needsNewAngVelocity[index] = false; - } - tempVel.x = OpenVR.hmdTrackedDevicePoses[index].vAngularVelocity.v[0]; - tempVel.y = OpenVR.hmdTrackedDevicePoses[index].vAngularVelocity.v[1]; - tempVel.z = OpenVR.hmdTrackedDevicePoses[index].vAngularVelocity.v[2]; - return tempVel; + + if (environment != null){ + + if (environment.getVRHardware() instanceof OpenVR){ + + int index = OpenVRInput.controllerIndex[controllerIndex]; + if( needsNewAngVelocity[index] ) { + ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].readField("vAngularVelocity"); + needsNewAngVelocity[index] = false; + } + tempVel.x = ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].vAngularVelocity.v[0]; + tempVel.y = ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].vAngularVelocity.v[1]; + tempVel.z = ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].vAngularVelocity.v[2]; + return tempVel; + } else { + throw new IllegalStateException("VR hardware "+environment.getVRHardware().getClass().getSimpleName()+" is not a subclass of "+OpenVR.class.getSimpleName()); + } + } else { + throw new IllegalStateException("VR input is not attached to a VR environment."); + } + } @Override @@ -309,7 +332,16 @@ public class OpenVRInput implements VRInputAPI { return false; } - return OpenVR.hmdTrackedDevicePoses[controllerIndex[index]].bPoseIsValid != 0; + if (environment != null){ + + if (environment.getVRHardware() instanceof OpenVR){ + return ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[controllerIndex[index]].bPoseIsValid != 0; + } else { + throw new IllegalStateException("VR hardware "+environment.getVRHardware().getClass().getSimpleName()+" is not a subclass of "+OpenVR.class.getSimpleName()); + } + } else { + throw new IllegalStateException("VR input is not attached to a VR environment."); + } } @Override @@ -317,9 +349,19 @@ public class OpenVRInput implements VRInputAPI { if( isInputDeviceTracking(index) == false ){ return null; } - index = controllerIndex[index]; - VRUtil.convertMatrix4toQuat(OpenVR.poseMatrices[index], rotStore[index]); - return rotStore[index]; + + if (environment != null){ + + if (environment.getVRHardware() instanceof OpenVR){ + index = controllerIndex[index]; + VRUtil.convertMatrix4toQuat(((OpenVR)environment.getVRHardware()).poseMatrices[index], rotStore[index]); + return rotStore[index]; + } else { + throw new IllegalStateException("VR hardware "+environment.getVRHardware().getClass().getSimpleName()+" is not a subclass of "+OpenVR.class.getSimpleName()); + } + } else { + throw new IllegalStateException("VR input is not attached to a VR environment."); + } } @Override @@ -328,12 +370,23 @@ public class OpenVRInput implements VRInputAPI { return null; } - // the hmdPose comes in rotated funny, fix that here - index = controllerIndex[index]; - OpenVR.poseMatrices[index].toTranslationVector(posStore[index]); - posStore[index].x = -posStore[index].x; - posStore[index].z = -posStore[index].z; - return posStore[index]; + if (environment != null){ + + if (environment.getVRHardware() instanceof OpenVR){ + // the hmdPose comes in rotated funny, fix that here + index = controllerIndex[index]; + ((OpenVR)environment.getVRHardware()).poseMatrices[index].toTranslationVector(posStore[index]); + posStore[index].x = -posStore[index].x; + posStore[index].z = -posStore[index].z; + return posStore[index]; + } else { + throw new IllegalStateException("VR hardware "+environment.getVRHardware().getClass().getSimpleName()+" is not a subclass of "+OpenVR.class.getSimpleName()); + } + } else { + throw new IllegalStateException("VR input is not attached to a VR environment."); + } + + } @Override @@ -424,6 +477,12 @@ public class OpenVRInput implements VRInputAPI { controllerIndex[controllerCount] = i; + // Adding tracked controller to control. + if (trackedControllers == null){ + trackedControllers = new ArrayList(JOpenVRLibrary.k_unMaxTrackedDeviceCount); + } + trackedControllers.add(new OpenVRTrackedController(i, this, controllerName, manufacturerName, environment)); + // Send an Haptic pulse to the controller triggerHapticPulse(controllerCount, 1.0f); diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OpenVRTrackedController.java b/jme3-vr/src/main/java/com/jme3/input/vr/OpenVRTrackedController.java new file mode 100644 index 000000000..026830604 --- /dev/null +++ b/jme3-vr/src/main/java/com/jme3/input/vr/OpenVRTrackedController.java @@ -0,0 +1,93 @@ +package com.jme3.input.vr; + +import com.jme3.app.VREnvironment; +import com.jme3.math.Matrix4f; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector3f; + +public class OpenVRTrackedController implements VRTrackedController{ + + /** + * The index of the controller within the unserlying VR API. + */ + private int controllerIndex = -1; + + /** + * The underlying VRAPI. + */ + private OpenVRInput hardware = null; + + /** + * The name of the controller. + */ + private String name; + + private VREnvironment environment; + + /** + * Wrap a new VR tracked controller on an OpenVR system. + * @param controllerIndex the index of the controller within the underlying VR system. + * @param hardware the underlying VR system. + * @param name the name of the controller. + * @param manufacturer the manufacturer of the controller. + * @param environment the VR environment. + */ + public OpenVRTrackedController(int controllerIndex, OpenVRInput hardware, String name, String manufacturer, VREnvironment environment){ + this.controllerIndex = controllerIndex; + this.hardware = hardware; + + this.name = name; + this.manufacturer = manufacturer; + + this.environment = environment; + } + + /** + * The manufacturer of the controller. + */ + private String manufacturer; + + @Override + public Vector3f getPosition() { + if (hardware != null){ + return hardware.getPosition(controllerIndex); + } else { + throw new IllegalStateException("No underlying VR API."); + } + } + + @Override + public Quaternion getOrientation() { + if (hardware != null){ + return hardware.getOrientation(controllerIndex); + } else { + throw new IllegalStateException("No underlying VR API."); + } + } + + @Override + public Matrix4f getPose(){ + + if (environment != null){ + if (hardware != null){ + return ((OpenVR)environment.getVRHardware()).poseMatrices[controllerIndex]; + } else { + throw new IllegalStateException("No underlying VR API."); + } + } else { + throw new IllegalStateException("VR tracked device is not attached to any environment."); + } + + + } + + @Override + public String getControllerName() { + return name; + } + + @Override + public String getControllerManufacturer() { + return manufacturer; + } +} diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/VRBounds.java b/jme3-vr/src/main/java/com/jme3/input/vr/VRBounds.java index 15ce659ea..0f81b4105 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/VRBounds.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/VRBounds.java @@ -16,19 +16,19 @@ public class VRBounds { private static Logger logger = Logger.getLogger(VRBounds.class.getName()); - private static VR_IVRChaperone_FnTable vrChaperone; - private static Vector2f playSize; + private VR_IVRChaperone_FnTable vrChaperone; + private Vector2f playSize; /** * Initialize the VR bounds. * @return true if the initialization is a success and false otherwise. */ - public static boolean init() { + public boolean init(OpenVR api) { logger.config("Initialize VR bounds..."); if( vrChaperone == null ) { - vrChaperone = new VR_IVRChaperone_FnTable(JOpenVRLibrary.VR_GetGenericInterface(JOpenVRLibrary.IVRChaperone_Version, OpenVR.hmdErrorStore).getPointer()); + vrChaperone = new VR_IVRChaperone_FnTable(JOpenVRLibrary.VR_GetGenericInterface(JOpenVRLibrary.IVRChaperone_Version, api.hmdErrorStore).getPointer()); if( vrChaperone != null ) { vrChaperone.setAutoSynch(false); vrChaperone.read(); @@ -53,7 +53,7 @@ public class VRBounds { * Get the size of the VR world. * @return the size of the VR world. */ - public static Vector2f getPlaySize() { + public Vector2f getPlaySize() { return playSize; } diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/VRTrackedController.java b/jme3-vr/src/main/java/com/jme3/input/vr/VRTrackedController.java index 5fb2572a0..3ce20e5c0 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/VRTrackedController.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/VRTrackedController.java @@ -1,5 +1,9 @@ package com.jme3.input.vr; +import com.jme3.math.Matrix4f; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector3f; + /** * TODO * @author Julien Seinturier - (c) 2016 - JOrigin project - http:/www.jorigin.org @@ -7,4 +11,40 @@ package com.jme3.input.vr; */ public interface VRTrackedController { + /** + * Get the controller name. + * @return the controller name. + */ + public String getControllerName(); + + /** + * Get the controller manufacturer. + * @return the controller manufacturer. + */ + public String getControllerManufacturer(); + + /** + * Get the position of the tracked device. This value is the translation component of the device {@link #getPose() pose}. + * @return the position of the tracked device. + * @see #getOrientation() + * @see #getPose() + */ + public Vector3f getPosition(); + + /** + * Get the orientation of the tracked device. This value is the rotation component of the device {@link #getPose() pose}. + * @return the orientation of the tracked device. + * @see #getPosition() + * @see #getPose() + */ + public Quaternion getOrientation(); + + /** + * Get the pose of the tracked device. + * The pose is a 4x4 matrix than combine the {@link #getPosition() position} and the {@link #getOrientation() orientation} of the device. + * @return the pose of the tracked device. + * @see #getPosition() + * @see #getOrientation() + */ + public Matrix4f getPose(); } diff --git a/jme3-vr/src/main/java/com/jme3/util/AbstractVRViewManager.java b/jme3-vr/src/main/java/com/jme3/util/AbstractVRViewManager.java index 7a9b5ea56..f4f7e7f91 100644 --- a/jme3-vr/src/main/java/com/jme3/util/AbstractVRViewManager.java +++ b/jme3-vr/src/main/java/com/jme3/util/AbstractVRViewManager.java @@ -28,18 +28,20 @@ public abstract class AbstractVRViewManager implements VRViewManager { protected VREnvironment environment = null; protected Camera leftCamera; - protected ViewPort leftViewport; + protected ViewPort leftViewPort; protected FilterPostProcessor leftPostProcessor; protected Texture2D leftEyeTexture; protected Texture2D leftEyeDepth; protected Camera rightCamera; - protected ViewPort rightViewport; + protected ViewPort rightViewPort; protected FilterPostProcessor rightPostProcessor; protected Texture2D rightEyeTexture; protected Texture2D rightEyeDepth; - private float resMult = 1f; + protected ViewPort mirrorViewPort; + + private float resMult = 1f; private float heightAdjustment; @@ -54,15 +56,24 @@ public abstract class AbstractVRViewManager implements VRViewManager { } @Override - public ViewPort getLeftViewport() { - return leftViewport; + public ViewPort getLeftViewPort() { + return leftViewPort; } @Override - public ViewPort getRightViewport() { - return rightViewport; + public ViewPort getRightViewPort() { + return rightViewPort; } + /** + * Get the {@link ViewPort view port} attached to the mirror display. + * @return the view port attached to the mirror display. + */ + public ViewPort getMirrorViewPort() { + return mirrorViewPort; + } + + @Override public Texture2D getLeftTexture(){ return leftEyeTexture; @@ -124,7 +135,7 @@ public abstract class AbstractVRViewManager implements VRViewManager { public void moveScreenProcessingToEyes() { if (environment != null){ - if( getRightViewport() == null ){ + if( getRightViewPort() == null ){ return; } @@ -150,7 +161,7 @@ public abstract class AbstractVRViewManager implements VRViewManager { public void syncScreenProcessing(ViewPort sourceViewport) { if (environment != null){ - if( getRightViewport() == null ){ + if( getRightViewPort() == null ){ return; } @@ -163,13 +174,13 @@ public abstract class AbstractVRViewManager implements VRViewManager { // clear out all filters & processors, to start from scratch getRightPostProcessor().removeAllFilters(); getLeftPostProcessor().removeAllFilters(); - getLeftViewport().clearProcessors(); - getRightViewport().clearProcessors(); + getLeftViewPort().clearProcessors(); + getRightViewPort().clearProcessors(); // if we have no processors to sync, don't add the FilterPostProcessor if( sourceViewport.getProcessors().isEmpty() ) return; // add post processors we just made, which are empty - getLeftViewport().addProcessor(getLeftPostProcessor()); - getRightViewport().addProcessor(getRightPostProcessor()); + getLeftViewPort().addProcessor(getLeftPostProcessor()); + getRightViewPort().addProcessor(getRightPostProcessor()); // go through all of the filters in the processors list // add them to the left viewport processor & clone them to the right for(SceneProcessor sceneProcessor : sourceViewport.getProcessors()) { @@ -202,8 +213,8 @@ public abstract class AbstractVRViewManager implements VRViewManager { VRDirectionalLightShadowRenderer dlsr = (VRDirectionalLightShadowRenderer) sceneProcessor; VRDirectionalLightShadowRenderer dlsrRight = dlsr.clone(); dlsrRight.setLight(dlsr.getLight()); - getRightViewport().getProcessors().add(0, dlsrRight); - getLeftViewport().getProcessors().add(0, sceneProcessor); + getRightViewPort().getProcessors().add(0, dlsrRight); + getLeftViewPort().getProcessors().add(0, sceneProcessor); } } // make sure each has a translucent filter renderer diff --git a/jme3-vr/src/main/java/com/jme3/util/VRViewManager.java b/jme3-vr/src/main/java/com/jme3/util/VRViewManager.java index e2c503006..58a6c166e 100644 --- a/jme3-vr/src/main/java/com/jme3/util/VRViewManager.java +++ b/jme3-vr/src/main/java/com/jme3/util/VRViewManager.java @@ -42,17 +42,25 @@ public interface VRViewManager { /** * Get the {@link ViewPort viewport} attached to the left eye. * @return the {@link ViewPort viewport} attached to the left eye. - * @see #getRightViewport() + * @see #getRightViewPort() */ - public ViewPort getLeftViewport(); + public ViewPort getLeftViewPort(); /** * Get the {@link ViewPort viewport} attached to the right eye. * @return the {@link ViewPort viewport} attached to the right eye. - * @see #getLeftViewport() + * @see #getLeftViewPort() */ - public ViewPort getRightViewport(); + public ViewPort getRightViewPort(); + + /** + * Get the {@link ViewPort view port} attached to the mirror display. + * @return the view port attached to the mirror display. + * @see #getLeftViewPort() + * @see #getRightViewPort() + */ + public ViewPort getMirrorViewPort(); /** * Get the texture attached to the left eye. diff --git a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOSVR.java b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOSVR.java index 9b78396e5..e3d85406e 100644 --- a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOSVR.java +++ b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOSVR.java @@ -117,18 +117,18 @@ public class VRViewManagerOSVR extends AbstractVRViewManager{ /** * Get the {@link ViewPort viewport} attached to the left eye. * @return the {@link ViewPort viewport} attached to the left eye. - * @see #getRightViewport() + * @see #getRightViewPort() */ - public ViewPort getLeftViewport() { + public ViewPort getLeftViewPort() { return leftViewport; } /** * Get the {@link ViewPort viewport} attached to the right eye. * @return the {@link ViewPort viewport} attached to the right eye. - * @see #getLeftViewport() + * @see #getLeftViewPort() */ - public ViewPort getRightViewport() { + public ViewPort getRightViewPort() { return rightViewport; } diff --git a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOpenVR.java b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOpenVR.java index 49d08578b..545754a59 100644 --- a/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOpenVR.java +++ b/jme3-vr/src/main/java/com/jme3/util/VRViewManagerOpenVR.java @@ -7,6 +7,7 @@ package com.jme3.util; import com.jme3.app.VREnvironment; import com.jme3.input.vr.OpenVR; import com.jme3.input.vr.VRAPI; +import com.jme3.input.vr.VRTrackedController; import com.jme3.material.Material; import com.jme3.math.ColorRGBA; import com.jme3.math.Quaternion; @@ -346,10 +347,10 @@ public class VRViewManagerOpenVR extends AbstractVRViewManager { return; } - leftEyeTexture = (Texture2D) getLeftViewport().getOutputFrameBuffer().getColorBuffer().getTexture(); - rightEyeTexture = (Texture2D)getRightViewport().getOutputFrameBuffer().getColorBuffer().getTexture(); - leftEyeDepth = (Texture2D) getLeftViewport().getOutputFrameBuffer().getDepthBuffer().getTexture(); - rightEyeDepth = (Texture2D)getRightViewport().getOutputFrameBuffer().getDepthBuffer().getTexture(); + leftEyeTexture = (Texture2D) getLeftViewPort().getOutputFrameBuffer().getColorBuffer().getTexture(); + rightEyeTexture = (Texture2D)getRightViewPort().getOutputFrameBuffer().getColorBuffer().getTexture(); + leftEyeDepth = (Texture2D) getLeftViewPort().getOutputFrameBuffer().getDepthBuffer().getTexture(); + rightEyeDepth = (Texture2D)getRightViewPort().getOutputFrameBuffer().getDepthBuffer().getTexture(); // main viewport is either going to be a distortion scene or nothing // mirroring is handled by copying framebuffers @@ -387,6 +388,7 @@ public class VRViewManagerOpenVR extends AbstractVRViewManager { if( environment.getApplication().getContext().getSettings().isSwapBuffers() ) { setupMirrorBuffers(environment.getCamera(), leftEyeTexture, false); + } } else { throw new IllegalStateException("This VR environment is not attached to any application."); @@ -414,9 +416,35 @@ public class VRViewManagerOpenVR extends AbstractVRViewManager { // grab the hardware handle VRAPI dev = environment.getVRHardware(); if( dev != null ) { + + // update the HMD's position & orientation dev.updatePose(); dev.getPositionAndOrientation(hmdPos, hmdRot); +/* + // TOREMOVE + Vector3f v = dev.getVRinput().getTrackedController(0).getPosition(); + Quaternion q = dev.getVRinput().getTrackedController(0).getOrientation(); + if ((v != null)&&(q != null)){ + hmdPos.set(v); + hmdRot.set(q); + } + + logger.severe("HMD controller "); + logger.severe(" Position "+hmdPos); + logger.severe(" Orientation "+hmdRot); + + VRTrackedController tc = null; + for(int i = 0; i < dev.getVRinput().getTrackedControllerCount(); i++){ + tc = dev.getVRinput().getTrackedController(i); + logger.severe("Tracked controller "+i+": "+tc.getControllerName()); + logger.severe(" Position "+tc.getPosition()); + logger.severe(" Orientation "+tc.getOrientation()); + logger.severe(""); + } +*/ + // TOREMOVE + if( obs != null ) { // update hmdPos based on obs rotation finalRotation.set(objRot); @@ -490,18 +518,18 @@ public class VRViewManagerOpenVR extends AbstractVRViewManager { //org.lwjgl.opengl.GL11.glEnable(org.lwjgl.opengl.GL30.GL_FRAMEBUFFER_SRGB); if( !environment.isInstanceRendering()) { - leftViewport = setupViewBuffers(getLeftCamera(), LEFT_VIEW_NAME); + leftViewPort = setupViewBuffers(getLeftCamera(), LEFT_VIEW_NAME); rightCamera = getLeftCamera().clone(); if( environment.getVRHardware() != null ){ getRightCamera().setProjectionMatrix(environment.getVRHardware().getHMDMatrixProjectionRightEye(getRightCamera())); } - rightViewport = setupViewBuffers(getRightCamera(), RIGHT_VIEW_NAME); + rightViewPort = setupViewBuffers(getRightCamera(), RIGHT_VIEW_NAME); } else { if (environment.getApplication() != null){ logger.severe("THIS CODE NEED CHANGES !!!"); - leftViewport = environment.getApplication().getViewPort(); + leftViewPort = environment.getApplication().getViewPort(); //leftViewport.attachScene(app.getRootNode()); rightCamera = getLeftCamera().clone(); if( environment.getVRHardware() != null ){ @@ -520,7 +548,7 @@ public class VRViewManagerOpenVR extends AbstractVRViewManager { } // setup gui - environment.getVRGUIManager().setupGui(getLeftCamera(), getRightCamera(), getLeftViewport(), getRightViewport()); + environment.getVRGUIManager().setupGui(getLeftCamera(), getRightCamera(), getLeftViewPort(), getRightViewPort()); if( environment.getVRHardware() != null ) { // call these to cache the results internally From 888b66892de7b8af2db4511da8352dcfcf4f6d27 Mon Sep 17 00:00:00 2001 From: seint Date: Thu, 13 Apr 2017 08:43:34 +0200 Subject: [PATCH 02/22] Ignoring .gitignore files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index aa848af79..dc6d9aec8 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,4 @@ !/jme3-vr/src/main/resources/**/*.so.dbg !/jme3-vr/src/main/resources/**/*.dll !/jme3-vr/src/main/resources/**/*.pdb +/buildMaven.bat From 9e945462f74ffaff89c3416ae869d252043a7837 Mon Sep 17 00:00:00 2001 From: jseinturier Date: Thu, 13 Apr 2017 10:00:11 +0200 Subject: [PATCH 03/22] Conforming to last master branch --- .../main/java/com/jme3/app/VREnvironment.java | 21 +-- .../java/com/jme3/input/vr/OpenVRInput.java | 120 +++++------------- 2 files changed, 34 insertions(+), 107 deletions(-) diff --git a/jme3-vr/src/main/java/com/jme3/app/VREnvironment.java b/jme3-vr/src/main/java/com/jme3/app/VREnvironment.java index 006400604..5358ff42a 100644 --- a/jme3-vr/src/main/java/com/jme3/app/VREnvironment.java +++ b/jme3-vr/src/main/java/com/jme3/app/VREnvironment.java @@ -8,7 +8,6 @@ import com.jme3.app.state.AppState; import com.jme3.input.vr.OSVR; import com.jme3.input.vr.OpenVR; import com.jme3.input.vr.VRAPI; -import com.jme3.input.vr.VRBounds; import com.jme3.input.vr.VRInputAPI; import com.jme3.renderer.Camera; import com.jme3.scene.Spatial; @@ -28,8 +27,6 @@ public class VREnvironment { private VRGuiManager guiManager = null; private VRMouseManager mouseManager = null; private VRViewManager viewmanager = null; - private VRBounds bounds = null; - /** * The underlying system VR API. By default set to {@link VRConstants#SETTING_VRAPI_OPENVR_VALUE}. @@ -76,10 +73,7 @@ public class VREnvironment { guiManager = new VRGuiManager(this); mouseManager = new VRMouseManager(this); - - bounds = new VRBounds(); - - dummyCam = new Camera(); +// dummyCam = new Camera(settings.getWidth(), settings.getHeight()); processSettings(); } @@ -92,14 +86,6 @@ public class VREnvironment { return hardware; } - /** - * Get the VR bounds. - * @return the VR bounds. - */ - public VRBounds getVRBounds(){ - return bounds; - } - /** * Get the VR dedicated input. * @return the VR dedicated input. @@ -346,7 +332,7 @@ public class VREnvironment { */ public Camera getCamera() { if( isInVR() && getVRViewManager() != null && getVRViewManager().getLeftCamera() != null ) { - return dummyCam; + return getDummyCamera(); } return application.getCamera(); @@ -361,13 +347,12 @@ public class VREnvironment { if (application.getCamera() != null){ dummyCam = application.getCamera().clone(); } else { - return new Camera(); + return new Camera(settings.getWidth(), settings.getHeight()); } } else { throw new IllegalStateException("VR environment is not attached to any application."); } } - return dummyCam; } diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OpenVRInput.java b/jme3-vr/src/main/java/com/jme3/input/vr/OpenVRInput.java index 6336cb4d9..b5514afcc 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/OpenVRInput.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/OpenVRInput.java @@ -5,7 +5,6 @@ */ package com.jme3.input.vr; -import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -184,50 +183,28 @@ public class OpenVRInput implements VRInputAPI { @Override public Vector3f getVelocity(int controllerIndex) { - - if (environment != null){ - - if (environment.getVRHardware() instanceof OpenVR){ - int index = OpenVRInput.controllerIndex[controllerIndex]; - if( needsNewVelocity[index] ) { - ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].readField("vVelocity"); - needsNewVelocity[index] = false; - } - tempVel.x = ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].vVelocity.v[0]; - tempVel.y = ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].vVelocity.v[1]; - tempVel.z = ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].vVelocity.v[2]; - return tempVel; - } else { - throw new IllegalStateException("VR hardware "+environment.getVRHardware().getClass().getSimpleName()+" is not a subclass of "+OpenVR.class.getSimpleName()); - } - } else { - throw new IllegalStateException("VR input is not attached to a VR environment."); - } + int index = OpenVRInput.controllerIndex[controllerIndex]; + if( needsNewVelocity[index] ) { + OpenVR.hmdTrackedDevicePoses[index].readField("vVelocity"); + needsNewVelocity[index] = false; + } + tempVel.x = OpenVR.hmdTrackedDevicePoses[index].vVelocity.v[0]; + tempVel.y = OpenVR.hmdTrackedDevicePoses[index].vVelocity.v[1]; + tempVel.z = OpenVR.hmdTrackedDevicePoses[index].vVelocity.v[2]; + return tempVel; } @Override public Vector3f getAngularVelocity(int controllerIndex) { - - if (environment != null){ - - if (environment.getVRHardware() instanceof OpenVR){ - - int index = OpenVRInput.controllerIndex[controllerIndex]; - if( needsNewAngVelocity[index] ) { - ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].readField("vAngularVelocity"); - needsNewAngVelocity[index] = false; - } - tempVel.x = ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].vAngularVelocity.v[0]; - tempVel.y = ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].vAngularVelocity.v[1]; - tempVel.z = ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].vAngularVelocity.v[2]; - return tempVel; - } else { - throw new IllegalStateException("VR hardware "+environment.getVRHardware().getClass().getSimpleName()+" is not a subclass of "+OpenVR.class.getSimpleName()); - } - } else { - throw new IllegalStateException("VR input is not attached to a VR environment."); - } - + int index = OpenVRInput.controllerIndex[controllerIndex]; + if( needsNewAngVelocity[index] ) { + OpenVR.hmdTrackedDevicePoses[index].readField("vAngularVelocity"); + needsNewAngVelocity[index] = false; + } + tempVel.x = OpenVR.hmdTrackedDevicePoses[index].vAngularVelocity.v[0]; + tempVel.y = OpenVR.hmdTrackedDevicePoses[index].vAngularVelocity.v[1]; + tempVel.z = OpenVR.hmdTrackedDevicePoses[index].vAngularVelocity.v[2]; + return tempVel; } @Override @@ -332,16 +309,7 @@ public class OpenVRInput implements VRInputAPI { return false; } - if (environment != null){ - - if (environment.getVRHardware() instanceof OpenVR){ - return ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[controllerIndex[index]].bPoseIsValid != 0; - } else { - throw new IllegalStateException("VR hardware "+environment.getVRHardware().getClass().getSimpleName()+" is not a subclass of "+OpenVR.class.getSimpleName()); - } - } else { - throw new IllegalStateException("VR input is not attached to a VR environment."); - } + return OpenVR.hmdTrackedDevicePoses[controllerIndex[index]].bPoseIsValid != 0; } @Override @@ -349,19 +317,9 @@ public class OpenVRInput implements VRInputAPI { if( isInputDeviceTracking(index) == false ){ return null; } - - if (environment != null){ - - if (environment.getVRHardware() instanceof OpenVR){ - index = controllerIndex[index]; - VRUtil.convertMatrix4toQuat(((OpenVR)environment.getVRHardware()).poseMatrices[index], rotStore[index]); - return rotStore[index]; - } else { - throw new IllegalStateException("VR hardware "+environment.getVRHardware().getClass().getSimpleName()+" is not a subclass of "+OpenVR.class.getSimpleName()); - } - } else { - throw new IllegalStateException("VR input is not attached to a VR environment."); - } + index = controllerIndex[index]; + VRUtil.convertMatrix4toQuat(OpenVR.poseMatrices[index], rotStore[index]); + return rotStore[index]; } @Override @@ -370,23 +328,12 @@ public class OpenVRInput implements VRInputAPI { return null; } - if (environment != null){ - - if (environment.getVRHardware() instanceof OpenVR){ - // the hmdPose comes in rotated funny, fix that here - index = controllerIndex[index]; - ((OpenVR)environment.getVRHardware()).poseMatrices[index].toTranslationVector(posStore[index]); - posStore[index].x = -posStore[index].x; - posStore[index].z = -posStore[index].z; - return posStore[index]; - } else { - throw new IllegalStateException("VR hardware "+environment.getVRHardware().getClass().getSimpleName()+" is not a subclass of "+OpenVR.class.getSimpleName()); - } - } else { - throw new IllegalStateException("VR input is not attached to a VR environment."); - } - - + // the hmdPose comes in rotated funny, fix that here + index = controllerIndex[index]; + OpenVR.poseMatrices[index].toTranslationVector(posStore[index]); + posStore[index].x = -posStore[index].x; + posStore[index].z = -posStore[index].z; + return posStore[index]; } @Override @@ -464,7 +411,8 @@ public class OpenVRInput implements VRInputAPI { if (environment != null){ controllerCount = 0; for(int i=0;i(JOpenVRLibrary.k_unMaxTrackedDeviceCount); - } - trackedControllers.add(new OpenVRTrackedController(i, this, controllerName, manufacturerName, environment)); - // Send an Haptic pulse to the controller triggerHapticPulse(controllerCount, 1.0f); @@ -503,7 +445,7 @@ public class OpenVRInput implements VRInputAPI { if (environment != null){ for(int i=0;i Date: Thu, 13 Apr 2017 11:12:11 +0200 Subject: [PATCH 04/22] Conform to master... boring.... --- .../main/java/com/jme3/input/vr/OpenVR.java | 1049 ++++++++--------- 1 file changed, 524 insertions(+), 525 deletions(-) diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OpenVR.java b/jme3-vr/src/main/java/com/jme3/input/vr/OpenVR.java index 509e84987..cb2042885 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/OpenVR.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/OpenVR.java @@ -1,525 +1,524 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package com.jme3.input.vr; - -import com.jme3.app.VREnvironment; -import com.jme3.math.Matrix4f; -import com.jme3.math.Quaternion; -import com.jme3.math.Vector2f; -import com.jme3.math.Vector3f; -import com.jme3.renderer.Camera; -import com.jme3.system.jopenvr.HmdMatrix34_t; -import com.jme3.system.jopenvr.HmdMatrix44_t; -import com.jme3.system.jopenvr.JOpenVRLibrary; -import com.jme3.system.jopenvr.OpenVRUtil; -import com.jme3.system.jopenvr.TrackedDevicePose_t; -import com.jme3.system.jopenvr.VR_IVRCompositor_FnTable; -import com.jme3.system.jopenvr.VR_IVRSystem_FnTable; -import com.jme3.util.VRUtil; -import com.sun.jna.Memory; -import com.sun.jna.Pointer; -import com.sun.jna.ptr.FloatByReference; -import com.sun.jna.ptr.IntByReference; -import com.sun.jna.ptr.LongByReference; - -import java.nio.IntBuffer; -import java.util.Locale; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * A class that wraps an OpenVR system. - * @author reden - phr00t - https://github.com/phr00t - * @author Julien Seinturier - (c) 2016 - JOrigin project - http:/www.jorigin.org - */ -public class OpenVR implements VRAPI { - - private static final Logger logger = Logger.getLogger(OpenVR.class.getName()); - - private static VR_IVRCompositor_FnTable compositorFunctions; - private static VR_IVRSystem_FnTable vrsystemFunctions; - - private static boolean initSuccess = false; - private static boolean flipEyes = false; - - private IntBuffer hmdDisplayFrequency; - private TrackedDevicePose_t.ByReference hmdTrackedDevicePoseReference; - protected TrackedDevicePose_t[] hmdTrackedDevicePoses; - - protected IntByReference hmdErrorStore; - - private final Quaternion rotStore = new Quaternion(); - private final Vector3f posStore = new Vector3f(); - - private static FloatByReference tlastVsync; - - /** - * The actual frame count. - */ - public static LongByReference _tframeCount; - - // for debugging latency - private int frames = 0; - - protected Matrix4f[] poseMatrices; - - private final Matrix4f hmdPose = Matrix4f.IDENTITY.clone(); - private Matrix4f hmdProjectionLeftEye; - private Matrix4f hmdProjectionRightEye; - private Matrix4f hmdPoseLeftEye; - private Matrix4f hmdPoseRightEye; - - private Vector3f hmdPoseLeftEyeVec, hmdPoseRightEyeVec, hmdSeatToStand; - - private float vsyncToPhotons; - private double timePerFrame, frameCountRun; - private long frameCount; - private OpenVRInput VRinput; - - - private VREnvironment environment = null; - - /** - * Create a new OpenVR system - * attached to the given {@link VREnvironment VR environment}. - * @param environment the VR environment to which this API is attached. - */ - public OpenVR(VREnvironment environment){ - this.environment = environment; - } - - @Override - public OpenVRInput getVRinput() { - return VRinput; - } - - @Override - public VR_IVRSystem_FnTable getVRSystem() { - return vrsystemFunctions; - } - - @Override - public VR_IVRCompositor_FnTable getCompositor() { - return compositorFunctions; - } - - @Override - public String getName() { - return "OpenVR"; - } - - private static long latencyWaitTime = 0; - - @Override - public void setFlipEyes(boolean set) { - flipEyes = set; - } - - private boolean enableDebugLatency = false; - - @Override - public void printLatencyInfoToConsole(boolean set) { - enableDebugLatency = set; - } - - @Override - public int getDisplayFrequency() { - if( hmdDisplayFrequency == null ) return 0; - return hmdDisplayFrequency.get(0); - } - - @Override - public boolean initialize() { - - logger.config("Initializing OpenVR system..."); - - hmdErrorStore = new IntByReference(); - vrsystemFunctions = null; - JOpenVRLibrary.VR_InitInternal(hmdErrorStore, JOpenVRLibrary.EVRApplicationType.EVRApplicationType_VRApplication_Scene); - if( hmdErrorStore.getValue() == 0 ) { - vrsystemFunctions = new VR_IVRSystem_FnTable(JOpenVRLibrary.VR_GetGenericInterface(JOpenVRLibrary.IVRSystem_Version, hmdErrorStore).getPointer()); - } - - if( vrsystemFunctions == null || hmdErrorStore.getValue() != 0 ) { - logger.severe("OpenVR Initialize Result: " + JOpenVRLibrary.VR_GetVRInitErrorAsEnglishDescription(hmdErrorStore.getValue()).getString(0)); - logger.severe("Initializing OpenVR system [FAILED]"); - return false; - } else { - logger.config("OpenVR initialized & VR connected."); - - vrsystemFunctions.setAutoSynch(false); - vrsystemFunctions.read(); - - tlastVsync = new FloatByReference(); - _tframeCount = new LongByReference(); - - hmdDisplayFrequency = IntBuffer.allocate(1); - hmdDisplayFrequency.put( (int) JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_DisplayFrequency_Float); - hmdTrackedDevicePoseReference = new TrackedDevicePose_t.ByReference(); - hmdTrackedDevicePoses = (TrackedDevicePose_t[])hmdTrackedDevicePoseReference.toArray(JOpenVRLibrary.k_unMaxTrackedDeviceCount); - poseMatrices = new Matrix4f[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; - for(int i=0;i 0 ) VRUtil.sleepNanos(latencyWaitTime); - - vrsystemFunctions.GetTimeSinceLastVsync.apply(tlastVsync, _tframeCount); - float fSecondsUntilPhotons = (float)timePerFrame - tlastVsync.getValue() + vsyncToPhotons; - - if( enableDebugLatency ) { - if( frames == 10 ) { - System.out.println("Waited (nanos): " + Long.toString(latencyWaitTime)); - System.out.println("Predict ahead time: " + Float.toString(fSecondsUntilPhotons)); - } - frames = (frames + 1) % 60; - } - - // handle skipping frame stuff - long nowCount = _tframeCount.getValue(); - if( nowCount - frameCount > 1 ) { - // skipped a frame! - if( enableDebugLatency ) System.out.println("Frame skipped!"); - frameCountRun = 0; - if( latencyWaitTime > 0 ) { - latencyWaitTime -= TimeUnit.MILLISECONDS.toNanos(1); - if( latencyWaitTime < 0 ) latencyWaitTime = 0; - } - } else if( latencyWaitTime < timePerFrame * 1000000000.0 ) { - // didn't skip a frame, lets try waiting longer to improve latency - frameCountRun++; - latencyWaitTime += Math.round(Math.pow(frameCountRun / 10.0, 2.0)); - } - - frameCount = nowCount; - - vrsystemFunctions.GetDeviceToAbsoluteTrackingPose.apply( - environment.isSeatedExperience()?JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseSeated: - JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseStanding, - fSecondsUntilPhotons, hmdTrackedDevicePoseReference, JOpenVRLibrary.k_unMaxTrackedDeviceCount); - } - - // deal with controllers being plugged in and out - // causing an invalid memory crash... skipping for now - /*boolean hasEvent = false; - while( JOpenVRLibrary.VR_IVRSystem_PollNextEvent(OpenVR.getVRSystemInstance(), tempEvent) != 0 ) { - // wait until the events are clear.. - hasEvent = true; - } - if( hasEvent ) { - // an event probably changed controller state - VRInput._updateConnectedControllers(); - }*/ - //update controllers pose information - environment.getVRinput().updateControllerStates(); - - // read pose data from native - for (int nDevice = 0; nDevice < JOpenVRLibrary.k_unMaxTrackedDeviceCount; ++nDevice ){ - hmdTrackedDevicePoses[nDevice].readField("bPoseIsValid"); - if( hmdTrackedDevicePoses[nDevice].bPoseIsValid != 0 ){ - hmdTrackedDevicePoses[nDevice].readField("mDeviceToAbsoluteTracking"); - VRUtil.convertSteamVRMatrix3ToMatrix4f(hmdTrackedDevicePoses[nDevice].mDeviceToAbsoluteTracking, poseMatrices[nDevice]); - } - } - - if ( hmdTrackedDevicePoses[JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd].bPoseIsValid != 0 ){ - hmdPose.set(poseMatrices[JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd]); - } else { - hmdPose.set(Matrix4f.IDENTITY); - } - } - - @Override - public Matrix4f getHMDMatrixProjectionLeftEye(Camera cam){ - if( hmdProjectionLeftEye != null ) { - return hmdProjectionLeftEye; - } else if(vrsystemFunctions == null){ - return cam.getProjectionMatrix(); - } else { - HmdMatrix44_t mat = vrsystemFunctions.GetProjectionMatrix.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Left, cam.getFrustumNear(), cam.getFrustumFar()); - hmdProjectionLeftEye = new Matrix4f(); - VRUtil.convertSteamVRMatrix4ToMatrix4f(mat, hmdProjectionLeftEye); - return hmdProjectionLeftEye; - } - } - - @Override - public Matrix4f getHMDMatrixProjectionRightEye(Camera cam){ - if( hmdProjectionRightEye != null ) { - return hmdProjectionRightEye; - } else if(vrsystemFunctions == null){ - return cam.getProjectionMatrix(); - } else { - HmdMatrix44_t mat = vrsystemFunctions.GetProjectionMatrix.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Right, cam.getFrustumNear(), cam.getFrustumFar()); - hmdProjectionRightEye = new Matrix4f(); - VRUtil.convertSteamVRMatrix4ToMatrix4f(mat, hmdProjectionRightEye); - return hmdProjectionRightEye; - } - } - - @Override - public Vector3f getHMDVectorPoseLeftEye() { - if( hmdPoseLeftEyeVec == null ) { - hmdPoseLeftEyeVec = getHMDMatrixPoseLeftEye().toTranslationVector(); - // set default IPD if none or broken - if( hmdPoseLeftEyeVec.x <= 0.080f * -0.5f || hmdPoseLeftEyeVec.x >= 0.040f * -0.5f ) { - hmdPoseLeftEyeVec.x = 0.065f * -0.5f; - } - if( flipEyes == false ) hmdPoseLeftEyeVec.x *= -1f; // it seems these need flipping - } - return hmdPoseLeftEyeVec; - } - - @Override - public Vector3f getHMDVectorPoseRightEye() { - if( hmdPoseRightEyeVec == null ) { - hmdPoseRightEyeVec = getHMDMatrixPoseRightEye().toTranslationVector(); - // set default IPD if none or broken - if( hmdPoseRightEyeVec.x >= 0.080f * 0.5f || hmdPoseRightEyeVec.x <= 0.040f * 0.5f ) { - hmdPoseRightEyeVec.x = 0.065f * 0.5f; - } - if( flipEyes == false ) hmdPoseRightEyeVec.x *= -1f; // it seems these need flipping - } - return hmdPoseRightEyeVec; - } - - @Override - public Vector3f getSeatedToAbsolutePosition() { - if( environment.isSeatedExperience() == false ) return Vector3f.ZERO; - if( hmdSeatToStand == null ) { - hmdSeatToStand = new Vector3f(); - HmdMatrix34_t mat = vrsystemFunctions.GetSeatedZeroPoseToStandingAbsoluteTrackingPose.apply(); - Matrix4f tempmat = new Matrix4f(); - VRUtil.convertSteamVRMatrix3ToMatrix4f(mat, tempmat); - tempmat.toTranslationVector(hmdSeatToStand); - } - return hmdSeatToStand; - } - - @Override - public Matrix4f getHMDMatrixPoseLeftEye(){ - if( hmdPoseLeftEye != null ) { - return hmdPoseLeftEye; - } else if(vrsystemFunctions == null) { - return Matrix4f.IDENTITY; - } else { - HmdMatrix34_t mat = vrsystemFunctions.GetEyeToHeadTransform.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Left); - hmdPoseLeftEye = new Matrix4f(); - return VRUtil.convertSteamVRMatrix3ToMatrix4f(mat, hmdPoseLeftEye); - } - } - - @Override - public HmdType getType() { - if( vrsystemFunctions != null ) { - Pointer str1 = new Memory(128); - Pointer str2 = new Memory(128); - String completeName = ""; - vrsystemFunctions.GetStringTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd, - JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_ManufacturerName_String, - str1, 128, hmdErrorStore); - if( hmdErrorStore.getValue() == 0 ) completeName += str1.getString(0); - vrsystemFunctions.GetStringTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd, - JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_ModelNumber_String, - str2, 128, hmdErrorStore); - if( hmdErrorStore.getValue() == 0 ) completeName += " " + str2.getString(0); - if( completeName.length() > 0 ) { - completeName = completeName.toLowerCase(Locale.ENGLISH).trim(); - if( completeName.contains("htc") || completeName.contains("vive") ) { - return HmdType.HTC_VIVE; - } else if( completeName.contains("osvr") ) { - return HmdType.OSVR; - } else if( completeName.contains("oculus") || completeName.contains("rift") || - completeName.contains("dk1") || completeName.contains("dk2") || completeName.contains("cv1") ) { - return HmdType.OCULUS_RIFT; - } else if( completeName.contains("fove") ) { - return HmdType.FOVE; - } else if( completeName.contains("game") && completeName.contains("face") ) { - return HmdType.GAMEFACE; - } else if( completeName.contains("morpheus") ) { - return HmdType.MORPHEUS; - } else if( completeName.contains("gear") ) { - return HmdType.GEARVR; - } else if( completeName.contains("star") ) { - return HmdType.STARVR; - } else if( completeName.contains("null") ) { - return HmdType.NULL; - } - } - } else return HmdType.NONE; - return HmdType.OTHER; - } - - @Override - public Matrix4f getHMDMatrixPoseRightEye(){ - if( hmdPoseRightEye != null ) { - return hmdPoseRightEye; - } else if(vrsystemFunctions == null) { - return Matrix4f.IDENTITY; - } else { - HmdMatrix34_t mat = vrsystemFunctions.GetEyeToHeadTransform.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Right); - hmdPoseRightEye = new Matrix4f(); - return VRUtil.convertSteamVRMatrix3ToMatrix4f(mat, hmdPoseRightEye); - } - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.jme3.input.vr; + +import com.jme3.app.VREnvironment; +import com.jme3.math.Matrix4f; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector2f; +import com.jme3.math.Vector3f; +import com.jme3.renderer.Camera; +import com.jme3.system.jopenvr.HmdMatrix34_t; +import com.jme3.system.jopenvr.HmdMatrix44_t; +import com.jme3.system.jopenvr.JOpenVRLibrary; +import com.jme3.system.jopenvr.OpenVRUtil; +import com.jme3.system.jopenvr.TrackedDevicePose_t; +import com.jme3.system.jopenvr.VR_IVRCompositor_FnTable; +import com.jme3.system.jopenvr.VR_IVRSystem_FnTable; +import com.jme3.util.VRUtil; +import com.sun.jna.Memory; +import com.sun.jna.Pointer; +import com.sun.jna.ptr.FloatByReference; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.LongByReference; + +import java.nio.IntBuffer; +import java.util.Locale; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * A class that wraps an OpenVR system. + * @author reden - phr00t - https://github.com/phr00t + * @author Julien Seinturier - (c) 2016 - JOrigin project - http:/www.jorigin.org + */ +public class OpenVR implements VRAPI { + + private static final Logger logger = Logger.getLogger(OpenVR.class.getName()); + + private static VR_IVRCompositor_FnTable compositorFunctions; + private static VR_IVRSystem_FnTable vrsystemFunctions; + + private static boolean initSuccess = false; + private static boolean flipEyes = false; + + private static IntBuffer hmdDisplayFrequency; + private static TrackedDevicePose_t.ByReference hmdTrackedDevicePoseReference; + protected static TrackedDevicePose_t[] hmdTrackedDevicePoses; + + protected static IntByReference hmdErrorStore; + + private static final Quaternion rotStore = new Quaternion(); + private static final Vector3f posStore = new Vector3f(); + + private static FloatByReference tlastVsync; + + /** + * The actual frame count. + */ + public static LongByReference _tframeCount; + + // for debugging latency + private int frames = 0; + + protected static Matrix4f[] poseMatrices; + + private static final Matrix4f hmdPose = Matrix4f.IDENTITY.clone(); + private static Matrix4f hmdProjectionLeftEye; + private static Matrix4f hmdProjectionRightEye; + private static Matrix4f hmdPoseLeftEye; + private static Matrix4f hmdPoseRightEye; + + private static Vector3f hmdPoseLeftEyeVec, hmdPoseRightEyeVec, hmdSeatToStand; + + private static float vsyncToPhotons; + private static double timePerFrame, frameCountRun; + private static long frameCount; + private static OpenVRInput VRinput; + + private VREnvironment environment = null; + + /** + * Create a new OpenVR system + * attached to the given {@link VREnvironment VR environment}. + * @param environment the VR environment to which this API is attached. + */ + public OpenVR(VREnvironment environment){ + this.environment = environment; + } + + @Override + public OpenVRInput getVRinput() { + return VRinput; + } + + @Override + public VR_IVRSystem_FnTable getVRSystem() { + return vrsystemFunctions; + } + + @Override + public VR_IVRCompositor_FnTable getCompositor() { + return compositorFunctions; + } + + @Override + public String getName() { + return "OpenVR"; + } + + private static long latencyWaitTime = 0; + + @Override + public void setFlipEyes(boolean set) { + flipEyes = set; + } + + private boolean enableDebugLatency = false; + + @Override + public void printLatencyInfoToConsole(boolean set) { + enableDebugLatency = set; + } + + @Override + public int getDisplayFrequency() { + if( hmdDisplayFrequency == null ) return 0; + return hmdDisplayFrequency.get(0); + } + + @Override + public boolean initialize() { + + logger.config("Initializing OpenVR system..."); + + hmdErrorStore = new IntByReference(); + vrsystemFunctions = null; + JOpenVRLibrary.VR_InitInternal(hmdErrorStore, JOpenVRLibrary.EVRApplicationType.EVRApplicationType_VRApplication_Scene); + if( hmdErrorStore.getValue() == 0 ) { + vrsystemFunctions = new VR_IVRSystem_FnTable(JOpenVRLibrary.VR_GetGenericInterface(JOpenVRLibrary.IVRSystem_Version, hmdErrorStore).getPointer()); + } + + if( vrsystemFunctions == null || hmdErrorStore.getValue() != 0 ) { + logger.severe("OpenVR Initialize Result: " + JOpenVRLibrary.VR_GetVRInitErrorAsEnglishDescription(hmdErrorStore.getValue()).getString(0)); + logger.severe("Initializing OpenVR system [FAILED]"); + return false; + } else { + logger.config("OpenVR initialized & VR connected."); + + vrsystemFunctions.setAutoSynch(false); + vrsystemFunctions.read(); + + tlastVsync = new FloatByReference(); + _tframeCount = new LongByReference(); + + hmdDisplayFrequency = IntBuffer.allocate(1); + hmdDisplayFrequency.put( (int) JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_DisplayFrequency_Float); + hmdTrackedDevicePoseReference = new TrackedDevicePose_t.ByReference(); + hmdTrackedDevicePoses = (TrackedDevicePose_t[])hmdTrackedDevicePoseReference.toArray(JOpenVRLibrary.k_unMaxTrackedDeviceCount); + poseMatrices = new Matrix4f[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; + for(int i=0;i 0 ) VRUtil.sleepNanos(latencyWaitTime); + + vrsystemFunctions.GetTimeSinceLastVsync.apply(tlastVsync, _tframeCount); + float fSecondsUntilPhotons = (float)timePerFrame - tlastVsync.getValue() + vsyncToPhotons; + + if( enableDebugLatency ) { + if( frames == 10 ) { + System.out.println("Waited (nanos): " + Long.toString(latencyWaitTime)); + System.out.println("Predict ahead time: " + Float.toString(fSecondsUntilPhotons)); + } + frames = (frames + 1) % 60; + } + + // handle skipping frame stuff + long nowCount = _tframeCount.getValue(); + if( nowCount - frameCount > 1 ) { + // skipped a frame! + if( enableDebugLatency ) System.out.println("Frame skipped!"); + frameCountRun = 0; + if( latencyWaitTime > 0 ) { + latencyWaitTime -= TimeUnit.MILLISECONDS.toNanos(1); + if( latencyWaitTime < 0 ) latencyWaitTime = 0; + } + } else if( latencyWaitTime < timePerFrame * 1000000000.0 ) { + // didn't skip a frame, lets try waiting longer to improve latency + frameCountRun++; + latencyWaitTime += Math.round(Math.pow(frameCountRun / 10.0, 2.0)); + } + + frameCount = nowCount; + + vrsystemFunctions.GetDeviceToAbsoluteTrackingPose.apply( + environment.isSeatedExperience()?JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseSeated: + JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseStanding, + fSecondsUntilPhotons, hmdTrackedDevicePoseReference, JOpenVRLibrary.k_unMaxTrackedDeviceCount); + } + + // deal with controllers being plugged in and out + // causing an invalid memory crash... skipping for now + /*boolean hasEvent = false; + while( JOpenVRLibrary.VR_IVRSystem_PollNextEvent(OpenVR.getVRSystemInstance(), tempEvent) != 0 ) { + // wait until the events are clear.. + hasEvent = true; + } + if( hasEvent ) { + // an event probably changed controller state + VRInput._updateConnectedControllers(); + }*/ + //update controllers pose information + environment.getVRinput().updateControllerStates(); + + // read pose data from native + for (int nDevice = 0; nDevice < JOpenVRLibrary.k_unMaxTrackedDeviceCount; ++nDevice ){ + hmdTrackedDevicePoses[nDevice].readField("bPoseIsValid"); + if( hmdTrackedDevicePoses[nDevice].bPoseIsValid != 0 ){ + hmdTrackedDevicePoses[nDevice].readField("mDeviceToAbsoluteTracking"); + VRUtil.convertSteamVRMatrix3ToMatrix4f(hmdTrackedDevicePoses[nDevice].mDeviceToAbsoluteTracking, poseMatrices[nDevice]); + } + } + + if ( hmdTrackedDevicePoses[JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd].bPoseIsValid != 0 ){ + hmdPose.set(poseMatrices[JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd]); + } else { + hmdPose.set(Matrix4f.IDENTITY); + } + } + + @Override + public Matrix4f getHMDMatrixProjectionLeftEye(Camera cam){ + if( hmdProjectionLeftEye != null ) { + return hmdProjectionLeftEye; + } else if(vrsystemFunctions == null){ + return cam.getProjectionMatrix(); + } else { + HmdMatrix44_t mat = vrsystemFunctions.GetProjectionMatrix.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Left, cam.getFrustumNear(), cam.getFrustumFar()); + hmdProjectionLeftEye = new Matrix4f(); + VRUtil.convertSteamVRMatrix4ToMatrix4f(mat, hmdProjectionLeftEye); + return hmdProjectionLeftEye; + } + } + + @Override + public Matrix4f getHMDMatrixProjectionRightEye(Camera cam){ + if( hmdProjectionRightEye != null ) { + return hmdProjectionRightEye; + } else if(vrsystemFunctions == null){ + return cam.getProjectionMatrix(); + } else { + HmdMatrix44_t mat = vrsystemFunctions.GetProjectionMatrix.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Right, cam.getFrustumNear(), cam.getFrustumFar()); + hmdProjectionRightEye = new Matrix4f(); + VRUtil.convertSteamVRMatrix4ToMatrix4f(mat, hmdProjectionRightEye); + return hmdProjectionRightEye; + } + } + + @Override + public Vector3f getHMDVectorPoseLeftEye() { + if( hmdPoseLeftEyeVec == null ) { + hmdPoseLeftEyeVec = getHMDMatrixPoseLeftEye().toTranslationVector(); + // set default IPD if none or broken + if( hmdPoseLeftEyeVec.x <= 0.080f * -0.5f || hmdPoseLeftEyeVec.x >= 0.040f * -0.5f ) { + hmdPoseLeftEyeVec.x = 0.065f * -0.5f; + } + if( flipEyes == false ) hmdPoseLeftEyeVec.x *= -1f; // it seems these need flipping + } + return hmdPoseLeftEyeVec; + } + + @Override + public Vector3f getHMDVectorPoseRightEye() { + if( hmdPoseRightEyeVec == null ) { + hmdPoseRightEyeVec = getHMDMatrixPoseRightEye().toTranslationVector(); + // set default IPD if none or broken + if( hmdPoseRightEyeVec.x >= 0.080f * 0.5f || hmdPoseRightEyeVec.x <= 0.040f * 0.5f ) { + hmdPoseRightEyeVec.x = 0.065f * 0.5f; + } + if( flipEyes == false ) hmdPoseRightEyeVec.x *= -1f; // it seems these need flipping + } + return hmdPoseRightEyeVec; + } + + @Override + public Vector3f getSeatedToAbsolutePosition() { + if( environment.isSeatedExperience() == false ) return Vector3f.ZERO; + if( hmdSeatToStand == null ) { + hmdSeatToStand = new Vector3f(); + HmdMatrix34_t mat = vrsystemFunctions.GetSeatedZeroPoseToStandingAbsoluteTrackingPose.apply(); + Matrix4f tempmat = new Matrix4f(); + VRUtil.convertSteamVRMatrix3ToMatrix4f(mat, tempmat); + tempmat.toTranslationVector(hmdSeatToStand); + } + return hmdSeatToStand; + } + + @Override + public Matrix4f getHMDMatrixPoseLeftEye(){ + if( hmdPoseLeftEye != null ) { + return hmdPoseLeftEye; + } else if(vrsystemFunctions == null) { + return Matrix4f.IDENTITY; + } else { + HmdMatrix34_t mat = vrsystemFunctions.GetEyeToHeadTransform.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Left); + hmdPoseLeftEye = new Matrix4f(); + return VRUtil.convertSteamVRMatrix3ToMatrix4f(mat, hmdPoseLeftEye); + } + } + + @Override + public HmdType getType() { + if( vrsystemFunctions != null ) { + Pointer str1 = new Memory(128); + Pointer str2 = new Memory(128); + String completeName = ""; + vrsystemFunctions.GetStringTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd, + JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_ManufacturerName_String, + str1, 128, hmdErrorStore); + if( hmdErrorStore.getValue() == 0 ) completeName += str1.getString(0); + vrsystemFunctions.GetStringTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd, + JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_ModelNumber_String, + str2, 128, hmdErrorStore); + if( hmdErrorStore.getValue() == 0 ) completeName += " " + str2.getString(0); + if( completeName.length() > 0 ) { + completeName = completeName.toLowerCase(Locale.ENGLISH).trim(); + if( completeName.contains("htc") || completeName.contains("vive") ) { + return HmdType.HTC_VIVE; + } else if( completeName.contains("osvr") ) { + return HmdType.OSVR; + } else if( completeName.contains("oculus") || completeName.contains("rift") || + completeName.contains("dk1") || completeName.contains("dk2") || completeName.contains("cv1") ) { + return HmdType.OCULUS_RIFT; + } else if( completeName.contains("fove") ) { + return HmdType.FOVE; + } else if( completeName.contains("game") && completeName.contains("face") ) { + return HmdType.GAMEFACE; + } else if( completeName.contains("morpheus") ) { + return HmdType.MORPHEUS; + } else if( completeName.contains("gear") ) { + return HmdType.GEARVR; + } else if( completeName.contains("star") ) { + return HmdType.STARVR; + } else if( completeName.contains("null") ) { + return HmdType.NULL; + } + } + } else return HmdType.NONE; + return HmdType.OTHER; + } + + @Override + public Matrix4f getHMDMatrixPoseRightEye(){ + if( hmdPoseRightEye != null ) { + return hmdPoseRightEye; + } else if(vrsystemFunctions == null) { + return Matrix4f.IDENTITY; + } else { + HmdMatrix34_t mat = vrsystemFunctions.GetEyeToHeadTransform.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Right); + hmdPoseRightEye = new Matrix4f(); + return VRUtil.convertSteamVRMatrix3ToMatrix4f(mat, hmdPoseRightEye); + } + } + +} From 2cacf16f69c1a63616753fd2cf15115431b82192 Mon Sep 17 00:00:00 2001 From: jseinturier Date: Thu, 13 Apr 2017 11:13:52 +0200 Subject: [PATCH 05/22] Conform to... Oh you know what i mean... --- .../java/com/jme3/input/vr/OpenVRInput.java | 920 +++++++++--------- 1 file changed, 460 insertions(+), 460 deletions(-) diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OpenVRInput.java b/jme3-vr/src/main/java/com/jme3/input/vr/OpenVRInput.java index b5514afcc..b4b5cdce5 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/OpenVRInput.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/OpenVRInput.java @@ -1,460 +1,460 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package com.jme3.input.vr; - -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.jme3.app.VREnvironment; -import com.jme3.math.Quaternion; -import com.jme3.math.Vector2f; -import com.jme3.math.Vector3f; -import com.jme3.renderer.Camera; -import com.jme3.scene.Spatial; -import com.jme3.system.jopenvr.JOpenVRLibrary; -import com.jme3.system.jopenvr.OpenVRUtil; -import com.jme3.system.jopenvr.VRControllerState_t; -import com.jme3.system.jopenvr.VR_IVRSystem_FnTable; -import com.jme3.util.VRUtil; -import com.jme3.util.VRViewManagerOpenVR; - -/* -make helper functions to pull the following easily from raw data (DONE) - -trigger: -Controller#1, Axis#0 X: 0.0, Y: 0.0 -Controller#1, Axis#1 X: 1.0, Y: 0.0 -Controller#1, Axis#2 X: 0.0, Y: 0.0 -Controller#1, Axis#3 X: 0.0, Y: 0.0 -Controller#1, Axis#4 X: 0.0, Y: 0.0 -Button press: 8589934592 (when full), touch: 8589934592 - -touchpad (upper left): -Controller#1, Axis#0 X: -0.6059755, Y: 0.2301706 -Controller#1, Axis#1 X: 0.0, Y: 0.0 -Controller#1, Axis#2 X: 0.0, Y: 0.0 -Controller#1, Axis#3 X: 0.0, Y: 0.0 -Controller#1, Axis#4 X: 0.0, Y: 0.0 -Button press: 4294967296 (when pressed in), touch: 4294967296 - -grip: -Controller#1, Axis#0 X: 0.0, Y: 0.0 -Controller#1, Axis#1 X: 0.0, Y: 0.0 -Controller#1, Axis#2 X: 0.0, Y: 0.0 -Controller#1, Axis#3 X: 0.0, Y: 0.0 -Controller#1, Axis#4 X: 0.0, Y: 0.0 -Button press: 4, touch: 4 - -thumb: -Controller#1, Axis#0 X: 0.0, Y: 0.0 -Controller#1, Axis#1 X: 0.0, Y: 0.0 -Controller#1, Axis#2 X: 0.0, Y: 0.0 -Controller#1, Axis#3 X: 0.0, Y: 0.0 -Controller#1, Axis#4 X: 0.0, Y: 0.0 -Button press: 2, touch: 2 - -*/ - -/** - * A class that wraps an OpenVR input.
- * null values will be returned if no valid pose exists, or that input device isn't available - * user code should check for null values. - * @author reden - phr00t - https://github.com/phr00t - * @author Julien Seinturier - (c) 2016 - JOrigin project - http:/www.jorigin.org - */ -public class OpenVRInput implements VRInputAPI { - - private static final Logger logger = Logger.getLogger(OpenVRInput.class.getName()); - - private final VRControllerState_t[] cStates = new VRControllerState_t[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; - - private final Quaternion[] rotStore = new Quaternion[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; - - private final Vector3f[] posStore = new Vector3f[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; - - private static final int[] controllerIndex = new int[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; - - private int controllerCount = 0; - - private final Vector2f tempAxis = new Vector2f(), temp2Axis = new Vector2f(); - - private final Vector2f lastCallAxis[] = new Vector2f[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; - - private final boolean needsNewVelocity[] = new boolean[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; - - private final boolean needsNewAngVelocity[] = new boolean[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; - - private final boolean buttonDown[][] = new boolean[JOpenVRLibrary.k_unMaxTrackedDeviceCount][16]; - - private float axisMultiplier = 1f; - - private final Vector3f tempVel = new Vector3f(); - - private final Quaternion tempq = new Quaternion(); - - private VREnvironment environment; - - private List trackedControllers = null; - - /** - * Create a new OpenVR input attached to the given VR environment. - * @param environment the VR environment to which the input is attached. - */ - public OpenVRInput(VREnvironment environment){ - this.environment = environment; - } - - @Override - public float getAxisMultiplier() { - return axisMultiplier; - } - - @Override - public void setAxisMultiplier(float set) { - axisMultiplier = set; - } - - @Override - public void swapHands() { - if( controllerCount != 2 ) return; - int temp = controllerIndex[0]; - controllerIndex[0] = controllerIndex[1]; - controllerIndex[1] = temp; - } - - @Override - public boolean isButtonDown(int controllerIndex, VRInputType checkButton) { - VRControllerState_t cs = cStates[OpenVRInput.controllerIndex[controllerIndex]]; - switch( checkButton ) { - default: - return false; - case ViveGripButton: - return (cs.ulButtonPressed & 4) != 0; - case ViveMenuButton: - return (cs.ulButtonPressed & 2) != 0; - case ViveTrackpadAxis: - return (cs.ulButtonPressed & 4294967296l) != 0; - case ViveTriggerAxis: - return (cs.ulButtonPressed & 8589934592l) != 0; - } - } - - @Override - public boolean wasButtonPressedSinceLastCall(int controllerIndex, VRInputType checkButton) { - boolean buttonDownNow = isButtonDown(controllerIndex, checkButton); - int checkButtonValue = checkButton.getValue(); - int cIndex = OpenVRInput.controllerIndex[controllerIndex]; - boolean retval = buttonDownNow == true && buttonDown[cIndex][checkButtonValue] == false; - buttonDown[cIndex][checkButtonValue] = buttonDownNow; - return retval; - } - - @Override - public void resetInputSinceLastCall() { - for(int i=0;i 0) && (index < trackedControllers.size())){ - return trackedControllers.get(index); - } - } - - return null; - } - - @Override - public int getTrackedControllerCount() { - return controllerCount; - } - - @Override - public VRControllerState_t getRawControllerState(int index) { - if( isInputDeviceTracking(index) == false ) return null; - return cStates[controllerIndex[index]]; - } - - //public Matrix4f getPoseForInputDevice(int index) { - // if( isInputDeviceTracking(index) == false ) return null; - // return OpenVR.poseMatrices[controllerIndex[index]]; - //} - - @Override - public boolean isInputFocused() { - - if (environment != null){ - return ((VR_IVRSystem_FnTable)environment.getVRHardware().getVRSystem()).IsInputFocusCapturedByAnotherProcess.apply() == 0; - } else { - throw new IllegalStateException("VR input is not attached to a VR environment."); - } - } - - @Override - public boolean isInputDeviceTracking(int index) { - if( index < 0 || index >= controllerCount ){ - return false; - } - - return OpenVR.hmdTrackedDevicePoses[controllerIndex[index]].bPoseIsValid != 0; - } - - @Override - public Quaternion getOrientation(int index) { - if( isInputDeviceTracking(index) == false ){ - return null; - } - index = controllerIndex[index]; - VRUtil.convertMatrix4toQuat(OpenVR.poseMatrices[index], rotStore[index]); - return rotStore[index]; - } - - @Override - public Vector3f getPosition(int index) { - if( isInputDeviceTracking(index) == false ){ - return null; - } - - // the hmdPose comes in rotated funny, fix that here - index = controllerIndex[index]; - OpenVR.poseMatrices[index].toTranslationVector(posStore[index]); - posStore[index].x = -posStore[index].x; - posStore[index].z = -posStore[index].z; - return posStore[index]; - } - - @Override - public Quaternion getFinalObserverRotation(int index) { - - if (environment != null){ - VRViewManagerOpenVR vrvm = (VRViewManagerOpenVR)environment.getVRViewManager(); - - if (vrvm != null){ - if(isInputDeviceTracking(index) == false ){ - return null; - } - - Object obs = environment.getObserver(); - if( obs instanceof Camera ) { - tempq.set(((Camera)obs).getRotation()); - } else { - tempq.set(((Spatial)obs).getWorldRotation()); - } - - return tempq.multLocal(getOrientation(index)); - } else { - throw new IllegalStateException("VR environment has no valid view manager."); - } - - - } else { - throw new IllegalStateException("VR input is not attached to a VR environment."); - } - } - - @Override - public Vector3f getFinalObserverPosition(int index) { - - if (environment != null){ - VRViewManagerOpenVR vrvm = (VRViewManagerOpenVR)environment.getVRViewManager(); - - if (vrvm != null){ - if(isInputDeviceTracking(index) == false ){ - return null; - } - Object obs = environment.getObserver(); - Vector3f pos = getPosition(index); - if( obs instanceof Camera ) { - ((Camera)obs).getRotation().mult(pos, pos); - return pos.addLocal(((Camera)obs).getLocation()); - } else { - ((Spatial)obs).getWorldRotation().mult(pos, pos); - return pos.addLocal(((Spatial)obs).getWorldTranslation()); - } - } else { - throw new IllegalStateException("VR environment has no valid view manager."); - } - - } else { - throw new IllegalStateException("VR input is not attached to a VR environment."); - } - } - - @Override - public void triggerHapticPulse(int controllerIndex, float seconds) { - if( environment.isInVR() == false || isInputDeviceTracking(controllerIndex) == false ){ - return; - } - - // apparently only axis ID of 0 works - ((VR_IVRSystem_FnTable)environment.getVRHardware().getVRSystem()).TriggerHapticPulse.apply(OpenVRInput.controllerIndex[controllerIndex], - 0, (short)Math.round(3f * seconds / 1e-3f)); - } - - @Override - public void updateConnectedControllers() { - logger.config("Updating connected controllers."); - - if (environment != null){ - controllerCount = 0; - for(int i=0;iOpenVR input.
+ * null values will be returned if no valid pose exists, or that input device isn't available + * user code should check for null values. + * @author reden - phr00t - https://github.com/phr00t + * @author Julien Seinturier - (c) 2016 - JOrigin project - http:/www.jorigin.org + */ +public class OpenVRInput implements VRInputAPI { + + private static final Logger logger = Logger.getLogger(OpenVRInput.class.getName()); + + private final VRControllerState_t[] cStates = new VRControllerState_t[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; + + private final Quaternion[] rotStore = new Quaternion[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; + + private final Vector3f[] posStore = new Vector3f[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; + + private static final int[] controllerIndex = new int[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; + + private int controllerCount = 0; + + private final Vector2f tempAxis = new Vector2f(), temp2Axis = new Vector2f(); + + private final Vector2f lastCallAxis[] = new Vector2f[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; + + private final boolean needsNewVelocity[] = new boolean[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; + + private final boolean needsNewAngVelocity[] = new boolean[JOpenVRLibrary.k_unMaxTrackedDeviceCount]; + + private final boolean buttonDown[][] = new boolean[JOpenVRLibrary.k_unMaxTrackedDeviceCount][16]; + + private float axisMultiplier = 1f; + + private final Vector3f tempVel = new Vector3f(); + + private final Quaternion tempq = new Quaternion(); + + private VREnvironment environment; + + private List trackedControllers = null; + + /** + * Create a new OpenVR input attached to the given VR environment. + * @param environment the VR environment to which the input is attached. + */ + public OpenVRInput(VREnvironment environment){ + this.environment = environment; + } + + @Override + public float getAxisMultiplier() { + return axisMultiplier; + } + + @Override + public void setAxisMultiplier(float set) { + axisMultiplier = set; + } + + @Override + public void swapHands() { + if( controllerCount != 2 ) return; + int temp = controllerIndex[0]; + controllerIndex[0] = controllerIndex[1]; + controllerIndex[1] = temp; + } + + @Override + public boolean isButtonDown(int controllerIndex, VRInputType checkButton) { + VRControllerState_t cs = cStates[OpenVRInput.controllerIndex[controllerIndex]]; + switch( checkButton ) { + default: + return false; + case ViveGripButton: + return (cs.ulButtonPressed & 4) != 0; + case ViveMenuButton: + return (cs.ulButtonPressed & 2) != 0; + case ViveTrackpadAxis: + return (cs.ulButtonPressed & 4294967296l) != 0; + case ViveTriggerAxis: + return (cs.ulButtonPressed & 8589934592l) != 0; + } + } + + @Override + public boolean wasButtonPressedSinceLastCall(int controllerIndex, VRInputType checkButton) { + boolean buttonDownNow = isButtonDown(controllerIndex, checkButton); + int checkButtonValue = checkButton.getValue(); + int cIndex = OpenVRInput.controllerIndex[controllerIndex]; + boolean retval = buttonDownNow == true && buttonDown[cIndex][checkButtonValue] == false; + buttonDown[cIndex][checkButtonValue] = buttonDownNow; + return retval; + } + + @Override + public void resetInputSinceLastCall() { + for(int i=0;i 0) && (index < trackedControllers.size())){ + return trackedControllers.get(index); + } + } + + return null; + } + + @Override + public int getTrackedControllerCount() { + return controllerCount; + } + + @Override + public VRControllerState_t getRawControllerState(int index) { + if( isInputDeviceTracking(index) == false ) return null; + return cStates[controllerIndex[index]]; + } + + //public Matrix4f getPoseForInputDevice(int index) { + // if( isInputDeviceTracking(index) == false ) return null; + // return OpenVR.poseMatrices[controllerIndex[index]]; + //} + + @Override + public boolean isInputFocused() { + + if (environment != null){ + return ((VR_IVRSystem_FnTable)environment.getVRHardware().getVRSystem()).IsInputFocusCapturedByAnotherProcess.apply() == 0; + } else { + throw new IllegalStateException("VR input is not attached to a VR environment."); + } + } + + @Override + public boolean isInputDeviceTracking(int index) { + if( index < 0 || index >= controllerCount ){ + return false; + } + + return OpenVR.hmdTrackedDevicePoses[controllerIndex[index]].bPoseIsValid != 0; + } + + @Override + public Quaternion getOrientation(int index) { + if( isInputDeviceTracking(index) == false ){ + return null; + } + index = controllerIndex[index]; + VRUtil.convertMatrix4toQuat(OpenVR.poseMatrices[index], rotStore[index]); + return rotStore[index]; + } + + @Override + public Vector3f getPosition(int index) { + if( isInputDeviceTracking(index) == false ){ + return null; + } + + // the hmdPose comes in rotated funny, fix that here + index = controllerIndex[index]; + OpenVR.poseMatrices[index].toTranslationVector(posStore[index]); + posStore[index].x = -posStore[index].x; + posStore[index].z = -posStore[index].z; + return posStore[index]; + } + + @Override + public Quaternion getFinalObserverRotation(int index) { + + if (environment != null){ + VRViewManagerOpenVR vrvm = (VRViewManagerOpenVR)environment.getVRViewManager(); + + if (vrvm != null){ + if(isInputDeviceTracking(index) == false ){ + return null; + } + + Object obs = environment.getObserver(); + if( obs instanceof Camera ) { + tempq.set(((Camera)obs).getRotation()); + } else { + tempq.set(((Spatial)obs).getWorldRotation()); + } + + return tempq.multLocal(getOrientation(index)); + } else { + throw new IllegalStateException("VR environment has no valid view manager."); + } + + + } else { + throw new IllegalStateException("VR input is not attached to a VR environment."); + } + } + + @Override + public Vector3f getFinalObserverPosition(int index) { + + if (environment != null){ + VRViewManagerOpenVR vrvm = (VRViewManagerOpenVR)environment.getVRViewManager(); + + if (vrvm != null){ + if(isInputDeviceTracking(index) == false ){ + return null; + } + Object obs = environment.getObserver(); + Vector3f pos = getPosition(index); + if( obs instanceof Camera ) { + ((Camera)obs).getRotation().mult(pos, pos); + return pos.addLocal(((Camera)obs).getLocation()); + } else { + ((Spatial)obs).getWorldRotation().mult(pos, pos); + return pos.addLocal(((Spatial)obs).getWorldTranslation()); + } + } else { + throw new IllegalStateException("VR environment has no valid view manager."); + } + + } else { + throw new IllegalStateException("VR input is not attached to a VR environment."); + } + } + + @Override + public void triggerHapticPulse(int controllerIndex, float seconds) { + if( environment.isInVR() == false || isInputDeviceTracking(controllerIndex) == false ){ + return; + } + + // apparently only axis ID of 0 works + ((VR_IVRSystem_FnTable)environment.getVRHardware().getVRSystem()).TriggerHapticPulse.apply(OpenVRInput.controllerIndex[controllerIndex], + 0, (short)Math.round(3f * seconds / 1e-3f)); + } + + @Override + public void updateConnectedControllers() { + logger.config("Updating connected controllers."); + + if (environment != null){ + controllerCount = 0; + for(int i=0;i Date: Thu, 13 Apr 2017 11:37:36 +0200 Subject: [PATCH 06/22] Removing more static references. --- .../main/java/com/jme3/app/VREnvironment.java | 22 +++- .../main/java/com/jme3/input/vr/OpenVR.java | 37 +++--- .../java/com/jme3/input/vr/OpenVRInput.java | 119 +++++++++++++----- 3 files changed, 127 insertions(+), 51 deletions(-) diff --git a/jme3-vr/src/main/java/com/jme3/app/VREnvironment.java b/jme3-vr/src/main/java/com/jme3/app/VREnvironment.java index 5358ff42a..e0e9048dc 100644 --- a/jme3-vr/src/main/java/com/jme3/app/VREnvironment.java +++ b/jme3-vr/src/main/java/com/jme3/app/VREnvironment.java @@ -8,6 +8,7 @@ import com.jme3.app.state.AppState; import com.jme3.input.vr.OSVR; import com.jme3.input.vr.OpenVR; import com.jme3.input.vr.VRAPI; +import com.jme3.input.vr.VRBounds; import com.jme3.input.vr.VRInputAPI; import com.jme3.renderer.Camera; import com.jme3.scene.Spatial; @@ -28,6 +29,8 @@ public class VREnvironment { private VRMouseManager mouseManager = null; private VRViewManager viewmanager = null; + private VRBounds bounds = null; + /** * The underlying system VR API. By default set to {@link VRConstants#SETTING_VRAPI_OPENVR_VALUE}. */ @@ -65,7 +68,6 @@ public class VREnvironment { private boolean initialized = false; - private boolean attached = false; public VREnvironment(AppSettings settings){ @@ -73,7 +75,8 @@ public class VREnvironment { guiManager = new VRGuiManager(this); mouseManager = new VRMouseManager(this); -// dummyCam = new Camera(settings.getWidth(), settings.getHeight()); + + bounds = new VRBounds(); processSettings(); } @@ -86,6 +89,14 @@ public class VREnvironment { return hardware; } + /** + * Get the VR bounds. + * @return the VR bounds. + */ + public VRBounds getVRBounds(){ + return bounds; + } + /** * Get the VR dedicated input. * @return the VR dedicated input. @@ -347,7 +358,12 @@ public class VREnvironment { if (application.getCamera() != null){ dummyCam = application.getCamera().clone(); } else { - return new Camera(settings.getWidth(), settings.getHeight()); + + if ((settings != null) && (settings.getWidth() != 0) && (settings.getHeight() != 0)){ + dummyCam = new Camera(settings.getWidth(), settings.getHeight()); + } else { + dummyCam = new Camera(); + } } } else { throw new IllegalStateException("VR environment is not attached to any application."); diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OpenVR.java b/jme3-vr/src/main/java/com/jme3/input/vr/OpenVR.java index cb2042885..a9f3fdcfb 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/OpenVR.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/OpenVR.java @@ -46,14 +46,14 @@ public class OpenVR implements VRAPI { private static boolean initSuccess = false; private static boolean flipEyes = false; - private static IntBuffer hmdDisplayFrequency; - private static TrackedDevicePose_t.ByReference hmdTrackedDevicePoseReference; - protected static TrackedDevicePose_t[] hmdTrackedDevicePoses; + private IntBuffer hmdDisplayFrequency; + private TrackedDevicePose_t.ByReference hmdTrackedDevicePoseReference; + protected TrackedDevicePose_t[] hmdTrackedDevicePoses; - protected static IntByReference hmdErrorStore; + protected IntByReference hmdErrorStore; - private static final Quaternion rotStore = new Quaternion(); - private static final Vector3f posStore = new Vector3f(); + private final Quaternion rotStore = new Quaternion(); + private final Vector3f posStore = new Vector3f(); private static FloatByReference tlastVsync; @@ -65,20 +65,21 @@ public class OpenVR implements VRAPI { // for debugging latency private int frames = 0; - protected static Matrix4f[] poseMatrices; + protected Matrix4f[] poseMatrices; - private static final Matrix4f hmdPose = Matrix4f.IDENTITY.clone(); - private static Matrix4f hmdProjectionLeftEye; - private static Matrix4f hmdProjectionRightEye; - private static Matrix4f hmdPoseLeftEye; - private static Matrix4f hmdPoseRightEye; + private final Matrix4f hmdPose = Matrix4f.IDENTITY.clone(); + private Matrix4f hmdProjectionLeftEye; + private Matrix4f hmdProjectionRightEye; + private Matrix4f hmdPoseLeftEye; + private Matrix4f hmdPoseRightEye; - private static Vector3f hmdPoseLeftEyeVec, hmdPoseRightEyeVec, hmdSeatToStand; + private Vector3f hmdPoseLeftEyeVec, hmdPoseRightEyeVec, hmdSeatToStand; + + private float vsyncToPhotons; + private double timePerFrame, frameCountRun; + private long frameCount; + private OpenVRInput VRinput; - private static float vsyncToPhotons; - private static double timePerFrame, frameCountRun; - private static long frameCount; - private static OpenVRInput VRinput; private VREnvironment environment = null; @@ -181,7 +182,7 @@ public class OpenVR implements VRAPI { VRinput.updateConnectedControllers(); // init bounds & chaperone info - VRBounds.init(); + environment.getVRBounds().init(this); logger.config("Initializing OpenVR system [SUCCESS]"); initSuccess = true; diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OpenVRInput.java b/jme3-vr/src/main/java/com/jme3/input/vr/OpenVRInput.java index b4b5cdce5..2f9b81e95 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/OpenVRInput.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/OpenVRInput.java @@ -5,6 +5,7 @@ */ package com.jme3.input.vr; +import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -183,28 +184,50 @@ public class OpenVRInput implements VRInputAPI { @Override public Vector3f getVelocity(int controllerIndex) { - int index = OpenVRInput.controllerIndex[controllerIndex]; - if( needsNewVelocity[index] ) { - OpenVR.hmdTrackedDevicePoses[index].readField("vVelocity"); - needsNewVelocity[index] = false; - } - tempVel.x = OpenVR.hmdTrackedDevicePoses[index].vVelocity.v[0]; - tempVel.y = OpenVR.hmdTrackedDevicePoses[index].vVelocity.v[1]; - tempVel.z = OpenVR.hmdTrackedDevicePoses[index].vVelocity.v[2]; - return tempVel; + + if (environment != null){ + + if (environment.getVRHardware() instanceof OpenVR){ + int index = OpenVRInput.controllerIndex[controllerIndex]; + if( needsNewVelocity[index] ) { + ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].readField("vVelocity"); + needsNewVelocity[index] = false; + } + tempVel.x = ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].vVelocity.v[0]; + tempVel.y = ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].vVelocity.v[1]; + tempVel.z = ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].vVelocity.v[2]; + return tempVel; + } else { + throw new IllegalStateException("VR hardware "+environment.getVRHardware().getClass().getSimpleName()+" is not a subclass of "+OpenVR.class.getSimpleName()); + } + } else { + throw new IllegalStateException("VR input is not attached to a VR environment."); + } } @Override public Vector3f getAngularVelocity(int controllerIndex) { - int index = OpenVRInput.controllerIndex[controllerIndex]; - if( needsNewAngVelocity[index] ) { - OpenVR.hmdTrackedDevicePoses[index].readField("vAngularVelocity"); - needsNewAngVelocity[index] = false; - } - tempVel.x = OpenVR.hmdTrackedDevicePoses[index].vAngularVelocity.v[0]; - tempVel.y = OpenVR.hmdTrackedDevicePoses[index].vAngularVelocity.v[1]; - tempVel.z = OpenVR.hmdTrackedDevicePoses[index].vAngularVelocity.v[2]; - return tempVel; + + if (environment != null){ + + if (environment.getVRHardware() instanceof OpenVR){ + + int index = OpenVRInput.controllerIndex[controllerIndex]; + if( needsNewAngVelocity[index] ) { + ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].readField("vAngularVelocity"); + needsNewAngVelocity[index] = false; + } + tempVel.x = ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].vAngularVelocity.v[0]; + tempVel.y = ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].vAngularVelocity.v[1]; + tempVel.z = ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[index].vAngularVelocity.v[2]; + return tempVel; + } else { + throw new IllegalStateException("VR hardware "+environment.getVRHardware().getClass().getSimpleName()+" is not a subclass of "+OpenVR.class.getSimpleName()); + } + } else { + throw new IllegalStateException("VR input is not attached to a VR environment."); + } + } @Override @@ -309,7 +332,16 @@ public class OpenVRInput implements VRInputAPI { return false; } - return OpenVR.hmdTrackedDevicePoses[controllerIndex[index]].bPoseIsValid != 0; + if (environment != null){ + + if (environment.getVRHardware() instanceof OpenVR){ + return ((OpenVR)environment.getVRHardware()).hmdTrackedDevicePoses[controllerIndex[index]].bPoseIsValid != 0; + } else { + throw new IllegalStateException("VR hardware "+environment.getVRHardware().getClass().getSimpleName()+" is not a subclass of "+OpenVR.class.getSimpleName()); + } + } else { + throw new IllegalStateException("VR input is not attached to a VR environment."); + } } @Override @@ -317,9 +349,19 @@ public class OpenVRInput implements VRInputAPI { if( isInputDeviceTracking(index) == false ){ return null; } - index = controllerIndex[index]; - VRUtil.convertMatrix4toQuat(OpenVR.poseMatrices[index], rotStore[index]); - return rotStore[index]; + + if (environment != null){ + + if (environment.getVRHardware() instanceof OpenVR){ + index = controllerIndex[index]; + VRUtil.convertMatrix4toQuat(((OpenVR)environment.getVRHardware()).poseMatrices[index], rotStore[index]); + return rotStore[index]; + } else { + throw new IllegalStateException("VR hardware "+environment.getVRHardware().getClass().getSimpleName()+" is not a subclass of "+OpenVR.class.getSimpleName()); + } + } else { + throw new IllegalStateException("VR input is not attached to a VR environment."); + } } @Override @@ -328,12 +370,23 @@ public class OpenVRInput implements VRInputAPI { return null; } - // the hmdPose comes in rotated funny, fix that here - index = controllerIndex[index]; - OpenVR.poseMatrices[index].toTranslationVector(posStore[index]); - posStore[index].x = -posStore[index].x; - posStore[index].z = -posStore[index].z; - return posStore[index]; + if (environment != null){ + + if (environment.getVRHardware() instanceof OpenVR){ + // the hmdPose comes in rotated funny, fix that here + index = controllerIndex[index]; + ((OpenVR)environment.getVRHardware()).poseMatrices[index].toTranslationVector(posStore[index]); + posStore[index].x = -posStore[index].x; + posStore[index].z = -posStore[index].z; + return posStore[index]; + } else { + throw new IllegalStateException("VR hardware "+environment.getVRHardware().getClass().getSimpleName()+" is not a subclass of "+OpenVR.class.getSimpleName()); + } + } else { + throw new IllegalStateException("VR input is not attached to a VR environment."); + } + + } @Override @@ -411,9 +464,9 @@ public class OpenVRInput implements VRInputAPI { if (environment != null){ controllerCount = 0; for(int i=0;i(JOpenVRLibrary.k_unMaxTrackedDeviceCount); + } + trackedControllers.add(new OpenVRTrackedController(i, this, controllerName, manufacturerName, environment)); + // Send an Haptic pulse to the controller triggerHapticPulse(controllerCount, 1.0f); From 88125da16d48cdbf1eb7cc90ff112d7afe5f604b Mon Sep 17 00:00:00 2001 From: NemesisMate Date: Wed, 24 May 2017 13:51:28 +0100 Subject: [PATCH 07/22] Fixed IllegalArgumentException when removing a LodControl from a spatial LodControl throws an exception when the spatial being added to isn't a geometry, however, when the controls is removed it calls setSpatial with a null value triggering the exception throw. Now checking for this null value case. --- .../com/jme3/scene/control/LodControl.java | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/scene/control/LodControl.java b/jme3-core/src/main/java/com/jme3/scene/control/LodControl.java index f6b657842..eac48db4e 100644 --- a/jme3-core/src/main/java/com/jme3/scene/control/LodControl.java +++ b/jme3-core/src/main/java/com/jme3/scene/control/LodControl.java @@ -121,17 +121,23 @@ public class LodControl extends AbstractControl implements Cloneable, JmeCloneab @Override public void setSpatial(Spatial spatial) { - if (!(spatial instanceof Geometry)) { + if (spatial != null && !(spatial instanceof Geometry)) { throw new IllegalArgumentException("LodControl can only be attached to Geometry!"); } super.setSpatial(spatial); - Geometry geom = (Geometry) spatial; - Mesh mesh = geom.getMesh(); - numLevels = mesh.getNumLodLevels(); - numTris = new int[numLevels]; - for (int i = numLevels - 1; i >= 0; i--) { - numTris[i] = mesh.getTriangleCount(i); + + if(spatial != null) { + Geometry geom = (Geometry) spatial; + Mesh mesh = geom.getMesh(); + numLevels = mesh.getNumLodLevels(); + numTris = new int[numLevels]; + for (int i = numLevels - 1; i >= 0; i--) { + numTris[i] = mesh.getTriangleCount(i); + } + } else { + numLevels = 0; + numTris = null; } } From 1bfabd8d8f46a3cc4bbba3ec056b1197bf57fcd3 Mon Sep 17 00:00:00 2001 From: Quazi Irfan GD Date: Sat, 27 May 2017 00:30:12 -0500 Subject: [PATCH 08/22] Update Mesh(Point) documentation --- jme3-core/src/main/java/com/jme3/scene/Mesh.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jme3-core/src/main/java/com/jme3/scene/Mesh.java b/jme3-core/src/main/java/com/jme3/scene/Mesh.java index 68b153633..aca84e6d2 100644 --- a/jme3-core/src/main/java/com/jme3/scene/Mesh.java +++ b/jme3-core/src/main/java/com/jme3/scene/Mesh.java @@ -64,7 +64,7 @@ import java.util.ArrayList; * Meshes may contain three types of geometric primitives: *
    *
  • Points - Every vertex represents a single point in space, - * the size of each point is specified via {@link Mesh#setPointSize(float) }. + * the size of {@link Mode#Points points} is determined via the vertex shader's gl_PointSize output. * Points can also be used for {@link RenderState#setPointSprite(boolean) point * sprite} mode.
  • *
  • Lines - 2 vertices represent a line segment, with the width specified From cf07e094af3f737de15cb89466510d422b4ff91f Mon Sep 17 00:00:00 2001 From: Quazi Irfan GD Date: Sat, 27 May 2017 00:40:04 -0500 Subject: [PATCH 09/22] Cleanup Mesh(Point) documentation --- jme3-core/src/main/java/com/jme3/scene/Mesh.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/scene/Mesh.java b/jme3-core/src/main/java/com/jme3/scene/Mesh.java index aca84e6d2..f115f8121 100644 --- a/jme3-core/src/main/java/com/jme3/scene/Mesh.java +++ b/jme3-core/src/main/java/com/jme3/scene/Mesh.java @@ -63,8 +63,7 @@ import java.util.ArrayList; * All visible elements in a scene are represented by meshes. * Meshes may contain three types of geometric primitives: *
      - *
    • Points - Every vertex represents a single point in space, - * the size of {@link Mode#Points points} is determined via the vertex shader's gl_PointSize output. + *
    • Points - Every vertex represents a single point in space. * Points can also be used for {@link RenderState#setPointSprite(boolean) point * sprite} mode.
    • *
    • Lines - 2 vertices represent a line segment, with the width specified @@ -82,8 +81,8 @@ public class Mesh implements Savable, Cloneable, JmeCloneable { */ public enum Mode { /** - * A primitive is a single point in space. The size of the points - * can be specified with {@link Mesh#setPointSize(float) }. + * A primitive is a single point in space. The size of {@link Mode#Points points} are + * determined via the vertex shader's gl_PointSize output. */ Points(true), From 97135fa9bb872b3f442723eab52e9d011807d933 Mon Sep 17 00:00:00 2001 From: Quazi Irfan GD Date: Sat, 27 May 2017 14:13:57 -0500 Subject: [PATCH 10/22] Add missing override in Geometry --- jme3-core/src/main/java/com/jme3/scene/Geometry.java | 1 + 1 file changed, 1 insertion(+) diff --git a/jme3-core/src/main/java/com/jme3/scene/Geometry.java b/jme3-core/src/main/java/com/jme3/scene/Geometry.java index fe65adf74..1e1bbcf50 100644 --- a/jme3-core/src/main/java/com/jme3/scene/Geometry.java +++ b/jme3-core/src/main/java/com/jme3/scene/Geometry.java @@ -506,6 +506,7 @@ public class Geometry extends Spatial { /** * The old clone() method that did not use the new Cloner utility. */ + @Override public Geometry oldClone(boolean cloneMaterial) { Geometry geomClone = (Geometry) super.clone(cloneMaterial); From 26ccaaea8800701213fb01e72d5ff7058516b103 Mon Sep 17 00:00:00 2001 From: Nehon Date: Sat, 20 May 2017 11:09:22 +0200 Subject: [PATCH 11/22] DDSLoader now supports Luminance16f format (r16f) --- .../plugins/java/com/jme3/texture/plugins/DDSLoader.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/jme3-core/src/plugins/java/com/jme3/texture/plugins/DDSLoader.java b/jme3-core/src/plugins/java/com/jme3/texture/plugins/DDSLoader.java index 948abe87e..5132fbd2f 100644 --- a/jme3-core/src/plugins/java/com/jme3/texture/plugins/DDSLoader.java +++ b/jme3-core/src/plugins/java/com/jme3/texture/plugins/DDSLoader.java @@ -302,6 +302,12 @@ public class DDSLoader implements AssetLoader { bpp = 64; pixelFormat = Image.Format.RGBA16F; break; + case 111: + compressed = false; + bpp = 16; + pixelFormat = Format.Luminance16F; + grayscaleOrAlpha = true; + break; default: throw new IOException("Unknown fourcc: " + string(fourcc) + ", " + Integer.toHexString(fourcc)); } From 2f06c9b37b37a262d094c01adee1f3f3d6cf3173 Mon Sep 17 00:00:00 2001 From: Nehon Date: Thu, 1 Jun 2017 21:02:59 +0200 Subject: [PATCH 12/22] ShaderNodeDefinition path is now set while loading the definition. --- .../com/jme3/material/plugins/ShaderNodeLoaderDelegate.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java b/jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java index 9c6fb7c31..f9efd8fdc 100644 --- a/jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java +++ b/jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java @@ -193,6 +193,7 @@ public class ShaderNodeLoaderDelegate { shaderNodeDefinition = new ShaderNodeDefinition(); getNodeDefinitions().put(name, shaderNodeDefinition); shaderNodeDefinition.setName(name); + shaderNodeDefinition.setPath(key.getName()); readShaderNodeDefinition(statement.getContents(), key); } @@ -942,7 +943,6 @@ public class ShaderNodeLoaderDelegate { } for (ShaderNodeDefinition definition : defs) { - definition.setPath(defLine[2].trim()); if (defName.equals(definition.getName())) { def = definition; } From ee9c6d366aec5feaaec46f8bee40c9fd90b98c8b Mon Sep 17 00:00:00 2001 From: Nehon Date: Thu, 1 Jun 2017 21:06:10 +0200 Subject: [PATCH 13/22] ShaderNodeVariable now has a prefix attribute that is concatenated to the name when generating the shader (basically g_ and m_ when the variable is an uniform) It avoid to change the name of the variable when loading the definition and always have weird inconsistencies with mat params because the name has a m_ or not. --- .../jme3/shader/Glsl100ShaderGenerator.java | 3 +- .../com/jme3/shader/ShaderNodeVariable.java | 42 ++++++++++++++++++- .../plugins/ShaderNodeLoaderDelegate.java | 9 ++-- 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/shader/Glsl100ShaderGenerator.java b/jme3-core/src/main/java/com/jme3/shader/Glsl100ShaderGenerator.java index 9edcd505a..4a85a9197 100644 --- a/jme3-core/src/main/java/com/jme3/shader/Glsl100ShaderGenerator.java +++ b/jme3-core/src/main/java/com/jme3/shader/Glsl100ShaderGenerator.java @@ -243,7 +243,7 @@ public class Glsl100ShaderGenerator extends ShaderGenerator { //all variables fed with a matparam or world param are replaced but the matparam itself //it avoids issue with samplers that have to be uniforms, and it optimize a but the shader code. if (isWorldOrMaterialParam(mapping.getRightVariable())) { - nodeSource = replace(nodeSource, mapping.getLeftVariable(), mapping.getRightVariable().getName()); + nodeSource = replace(nodeSource, mapping.getLeftVariable(), mapping.getRightVariable().getPrefix() + mapping.getRightVariable().getName()); } else { if (mapping.getLeftVariable().getType().startsWith("sampler")) { throw new IllegalArgumentException("a Sampler must be a uniform"); @@ -338,6 +338,7 @@ public class Glsl100ShaderGenerator extends ShaderGenerator { source.append(var.getNameSpace()); source.append("_"); } + source.append(var.getPrefix()); source.append(var.getName()); if (var.getMultiplicity() != null) { source.append("["); diff --git a/jme3-core/src/main/java/com/jme3/shader/ShaderNodeVariable.java b/jme3-core/src/main/java/com/jme3/shader/ShaderNodeVariable.java index 22811de6f..a1363ead1 100644 --- a/jme3-core/src/main/java/com/jme3/shader/ShaderNodeVariable.java +++ b/jme3-core/src/main/java/com/jme3/shader/ShaderNodeVariable.java @@ -45,6 +45,7 @@ import java.io.IOException; */ public class ShaderNodeVariable implements Savable, Cloneable { + private String prefix = ""; private String name; private String type; private String nameSpace; @@ -62,8 +63,7 @@ public class ShaderNodeVariable implements Savable, Cloneable { this.name = name; this.type = type; } - - + /** * creates a ShaderNodeVariable * @@ -80,6 +80,22 @@ public class ShaderNodeVariable implements Savable, Cloneable { this.multiplicity = multiplicity; } + + /** + * creates a ShaderNodeVariable + * + * @param type the glsl type of the variable + * @param nameSpace the nameSpace (can be the name of the shaderNode or + * Global,Attr,MatParam,WorldParam) + * @param name the name of the variable + * @param multiplicity the number of element if this variable is an array. Can be an Int of a declared material parameter + * @param prefix the variable prefix to append at generation times. This is mostly to add the g_ and m_ for uniforms + */ + public ShaderNodeVariable(String type, String nameSpace, String name, String multiplicity, String prefix) { + this(type, nameSpace, name, multiplicity); + this.prefix = prefix; + } + /** * creates a ShaderNodeVariable * @@ -138,6 +154,22 @@ public class ShaderNodeVariable implements Savable, Cloneable { return nameSpace; } + /** + * @return the variable prefix + */ + public String getPrefix() { + return prefix; + } + + /** + * Sets the variable prefix (m_ or g_) + * + * @param prefix + */ + public void setPrefix(String prefix) { + this.prefix = prefix; + } + /** * sets the nameSpace (can be the name of the shaderNode or * Global,Attr,MatParam,WorldParam) @@ -153,6 +185,7 @@ public class ShaderNodeVariable implements Savable, Cloneable { int hash = 7; hash = 29 * hash + (name != null?name.hashCode():0); hash = 29 * hash + (type != null?type.hashCode():0); + hash = 29 * hash + (prefix != null ? prefix.hashCode() : 0); hash = 29 * hash + (nameSpace != null?nameSpace.hashCode():0); hash = 29 * hash + (condition != null?condition.hashCode():0); hash = 29 * hash + (multiplicity != null?multiplicity.hashCode():0); @@ -174,6 +207,9 @@ public class ShaderNodeVariable implements Savable, Cloneable { if ((this.type == null) ? (other.type != null) : !this.type.equals(other.type)) { return false; } + if ((this.prefix == null) ? (other.prefix != null) : !this.prefix.equals(other.prefix)) { + return false; + } if ((this.nameSpace == null) ? (other.nameSpace != null) : !this.nameSpace.equals(other.nameSpace)) { return false; } @@ -197,6 +233,7 @@ public class ShaderNodeVariable implements Savable, Cloneable { OutputCapsule oc = (OutputCapsule) ex.getCapsule(this); oc.write(name, "name", ""); oc.write(type, "type", ""); + oc.write(prefix, "prefix", ""); oc.write(nameSpace, "nameSpace", ""); oc.write(condition, "condition", null); oc.write(shaderOutput, "shaderOutput", false); @@ -215,6 +252,7 @@ public class ShaderNodeVariable implements Savable, Cloneable { InputCapsule ic = (InputCapsule) im.getCapsule(this); name = ic.readString("name", ""); type = ic.readString("type", ""); + prefix = ic.readString("pefix", ""); nameSpace = ic.readString("nameSpace", ""); condition = ic.readString("condition", null); shaderOutput = ic.readBoolean("shaderOutput", false); diff --git a/jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java b/jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java index f9efd8fdc..dba4f6389 100644 --- a/jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java +++ b/jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java @@ -542,11 +542,13 @@ public class ShaderNodeLoaderDelegate { */ protected boolean updateRightFromUniforms(UniformBinding param, VariableMapping mapping, Map map) { ShaderNodeVariable right = mapping.getRightVariable(); - String name = "g_" + param.toString(); + String name = param.toString(); + DeclaredVariable dv = map.get(name); if (dv == null) { right.setType(param.getGlslType()); right.setName(name); + right.setPrefix("g_"); dv = new DeclaredVariable(right); map.put(right.getName(), dv); dv.addNode(shaderNode); @@ -570,10 +572,11 @@ public class ShaderNodeLoaderDelegate { */ public boolean updateRightFromUniforms(MatParam param, VariableMapping mapping, Map map, Statement statement) throws MatParseException { ShaderNodeVariable right = mapping.getRightVariable(); - DeclaredVariable dv = map.get(param.getPrefixedName()); + DeclaredVariable dv = map.get(param.getName()); if (dv == null) { right.setType(param.getVarType().getGlslType()); - right.setName(param.getPrefixedName()); + right.setName(param.getName()); + right.setPrefix("m_"); if(mapping.getLeftVariable().getMultiplicity() != null){ if(!param.getVarType().name().endsWith("Array")){ throw new MatParseException(param.getName() + " is not of Array type", statement); From edf279a06d18e755279d20b8ae7af089bab2c2a2 Mon Sep 17 00:00:00 2001 From: stophe Date: Sun, 18 Jun 2017 16:35:35 +0200 Subject: [PATCH 14/22] Refactored Cylinder generation to be more maintainable, readable, and fix #640. --- .../java/com/jme3/scene/shape/Cylinder.java | 409 ++++++++++-------- 1 file changed, 225 insertions(+), 184 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/scene/shape/Cylinder.java b/jme3-core/src/main/java/com/jme3/scene/shape/Cylinder.java index 1e0b1cae2..bc8bf92b1 100644 --- a/jme3-core/src/main/java/com/jme3/scene/shape/Cylinder.java +++ b/jme3-core/src/main/java/com/jme3/scene/shape/Cylinder.java @@ -40,14 +40,11 @@ import com.jme3.math.FastMath; import com.jme3.math.Vector3f; import com.jme3.scene.Mesh; import com.jme3.scene.VertexBuffer.Type; -import com.jme3.scene.mesh.IndexBuffer; import com.jme3.util.BufferUtils; -import static com.jme3.util.BufferUtils.*; import java.io.IOException; -import java.nio.FloatBuffer; /** - * A simple cylinder, defined by it's height and radius. + * A simple cylinder, defined by its height and radius. * (Ported to jME3) * * @author Mark Powell @@ -127,10 +124,10 @@ public class Cylinder extends Mesh { * mapped to texture coordinates (0.5, 1), bottom to (0.5, 0). Thus you need * a suited distorted texture. * - * @param axisSamples - * Number of triangle samples along the axis. - * @param radialSamples - * Number of triangle samples along the radial. + * @param axisSamples The number of vertices samples along the axis. It is equal to the number of segments + 1; so + * that, for instance, 4 samples mean the cylinder will be made of 3 segments. + * @param radialSamples The number of triangle samples along the radius. For instance, 4 means that the sides of the + * cylinder are made of 4 rectangles, and the top and bottom are made of 4 triangles. * @param radius * The radius of the cylinder. * @param height @@ -201,194 +198,240 @@ public class Cylinder extends Mesh { /** * Rebuilds the cylinder based on a new set of parameters. * - * @param axisSamples the number of samples along the axis. - * @param radialSamples the number of samples around the radial. - * @param radius the radius of the bottom of the cylinder. - * @param radius2 the radius of the top of the cylinder. + * @param axisSamples The number of vertices samples along the axis. It is equal to the number of segments + 1; so + * that, for instance, 4 samples mean the cylinder will be made of 3 segments. + * @param radialSamples The number of triangle samples along the radius. For instance, 4 means that the sides of the + * cylinder are made of 4 rectangles, and the top and bottom are made of 4 triangles. + * @param topRadius the radius of the top of the cylinder. + * @param bottomRadius the radius of the bottom of the cylinder. * @param height the cylinder's height. * @param closed should the cylinder have top and bottom surfaces. * @param inverted is the cylinder is meant to be viewed from the inside. */ public void updateGeometry(int axisSamples, int radialSamples, - float radius, float radius2, float height, boolean closed, boolean inverted) { - this.axisSamples = axisSamples; + float topRadius, float bottomRadius, float height, boolean closed, boolean inverted) + { + // Ensure there's at least two axis samples and 3 radial samples, and positive geometries. + if( axisSamples < 2 + || radialSamples < 3 + || topRadius <= 0 + || bottomRadius <= 0 + || height <= 0 ) + return; + + this.axisSamples = axisSamples; this.radialSamples = radialSamples; - this.radius = radius; - this.radius2 = radius2; + this.radius = bottomRadius; + this.radius2 = topRadius; this.height = height; this.closed = closed; this.inverted = inverted; -// VertexBuffer pvb = getBuffer(Type.Position); -// VertexBuffer nvb = getBuffer(Type.Normal); -// VertexBuffer tvb = getBuffer(Type.TexCoord); - axisSamples += (closed ? 2 : 0); - - // Vertices - int vertCount = axisSamples * (radialSamples + 1) + (closed ? 2 : 0); - - setBuffer(Type.Position, 3, createVector3Buffer(getFloatBuffer(Type.Position), vertCount)); - - // Normals - setBuffer(Type.Normal, 3, createVector3Buffer(getFloatBuffer(Type.Normal), vertCount)); - - // Texture co-ordinates - setBuffer(Type.TexCoord, 2, createVector2Buffer(vertCount)); - - int triCount = ((closed ? 2 : 0) + 2 * (axisSamples - 1)) * radialSamples; - - setBuffer(Type.Index, 3, createShortBuffer(getShortBuffer(Type.Index), 3 * triCount)); - - // generate geometry - float inverseRadial = 1.0f / radialSamples; - float inverseAxisLess = 1.0f / (closed ? axisSamples - 3 : axisSamples - 1); - float inverseAxisLessTexture = 1.0f / (axisSamples - 1); - float halfHeight = 0.5f * height; - - // Generate points on the unit circle to be used in computing the mesh - // points on a cylinder slice. - float[] sin = new float[radialSamples + 1]; - float[] cos = new float[radialSamples + 1]; - - for (int radialCount = 0; radialCount < radialSamples; radialCount++) { - float angle = FastMath.TWO_PI * inverseRadial * radialCount; - cos[radialCount] = FastMath.cos(angle); - sin[radialCount] = FastMath.sin(angle); + // Vertices : One per radial sample plus one duplicate for texture closing around the sides. + int verticesCount = axisSamples * (radialSamples +1); + // Triangles: Two per side rectangle, which is the product of numbers of samples. + int trianglesCount = axisSamples * radialSamples * 2 ; + if( closed ) + { + // If there are caps, add two additional rims and two summits. + verticesCount += 2 + 2 * (radialSamples +1); + // Add one triangle per radial sample, twice, to form the caps. + trianglesCount += 2 * radialSamples ; + } + + // Compute the points along a unit circle: + float[][] circlePoints = new float[radialSamples+1][2]; + for (int circlePoint = 0; circlePoint < radialSamples; circlePoint++) + { + float angle = FastMath.TWO_PI / radialSamples * circlePoint; + circlePoints[circlePoint][0] = FastMath.cos(angle); + circlePoints[circlePoint][1] = FastMath.sin(angle); } - sin[radialSamples] = sin[0]; - cos[radialSamples] = cos[0]; - - // calculate normals - Vector3f[] vNormals = null; - Vector3f vNormal = Vector3f.UNIT_Z; - - if ((height != 0.0f) && (radius != radius2)) { - vNormals = new Vector3f[radialSamples]; - Vector3f vHeight = Vector3f.UNIT_Z.mult(height); - Vector3f vRadial = new Vector3f(); - - for (int radialCount = 0; radialCount < radialSamples; radialCount++) { - vRadial.set(cos[radialCount], sin[radialCount], 0.0f); - Vector3f vRadius = vRadial.mult(radius); - Vector3f vRadius2 = vRadial.mult(radius2); - Vector3f vMantle = vHeight.subtract(vRadius2.subtract(vRadius)); - Vector3f vTangent = vRadial.cross(Vector3f.UNIT_Z); - vNormals[radialCount] = vMantle.cross(vTangent).normalize(); - } - } - - FloatBuffer nb = getFloatBuffer(Type.Normal); - FloatBuffer pb = getFloatBuffer(Type.Position); - FloatBuffer tb = getFloatBuffer(Type.TexCoord); - - // generate the cylinder itself - Vector3f tempNormal = new Vector3f(); - for (int axisCount = 0, i = 0; axisCount < axisSamples; axisCount++, i++) { - float axisFraction; - float axisFractionTexture; - int topBottom = 0; - if (!closed) { - axisFraction = axisCount * inverseAxisLess; // in [0,1] - axisFractionTexture = axisFraction; - } else { - if (axisCount == 0) { - topBottom = -1; // bottom - axisFraction = 0; - axisFractionTexture = inverseAxisLessTexture; - } else if (axisCount == axisSamples - 1) { - topBottom = 1; // top - axisFraction = 1; - axisFractionTexture = 1 - inverseAxisLessTexture; - } else { - axisFraction = (axisCount - 1) * inverseAxisLess; - axisFractionTexture = axisCount * inverseAxisLessTexture; - } - } - - // compute center of slice - float z = -halfHeight + height * axisFraction; - Vector3f sliceCenter = new Vector3f(0, 0, z); - - // compute slice vertices with duplication at end point - int save = i; - for (int radialCount = 0; radialCount < radialSamples; radialCount++, i++) { - float radialFraction = radialCount * inverseRadial; // in [0,1) - tempNormal.set(cos[radialCount], sin[radialCount], 0.0f); - - if (vNormals != null) { - vNormal = vNormals[radialCount]; - } else if (radius == radius2) { - vNormal = tempNormal; - } - - if (topBottom == 0) { - if (!inverted) - nb.put(vNormal.x).put(vNormal.y).put(vNormal.z); - else - nb.put(-vNormal.x).put(-vNormal.y).put(-vNormal.z); - } else { - nb.put(0).put(0).put(topBottom * (inverted ? -1 : 1)); - } - - tempNormal.multLocal((radius - radius2) * axisFraction + radius2) - .addLocal(sliceCenter); - pb.put(tempNormal.x).put(tempNormal.y).put(tempNormal.z); - - tb.put((inverted ? 1 - radialFraction : radialFraction)) - .put(axisFractionTexture); - } - - BufferUtils.copyInternalVector3(pb, save, i); - BufferUtils.copyInternalVector3(nb, save, i); - - tb.put((inverted ? 0.0f : 1.0f)) - .put(axisFractionTexture); + // Add an additional point for closing the texture around the side of the cylinder. + circlePoints[radialSamples][0] = circlePoints[0][0]; + circlePoints[radialSamples][1] = circlePoints[0][1]; + + // Calculate normals. + // + // A---------B + // \ | + // \ | + // \ | + // D-----C + // + // Let be B and C the top and bottom points of the axis, and A and D the top and bottom edges. + // The normal in A and D is simply orthogonal to AD, which means we can get it once per sample. + // + Vector3f[] circleNormals = new Vector3f[radialSamples+1]; + for (int circlePoint = 0; circlePoint < radialSamples+1; circlePoint++) + { + // The normal is the orthogonal to the side, which can be got without trigonometry. + // The edge direction is oriented so that it goes up by Height, and out by the radius difference; let's use + // those values in reverse order. + Vector3f normal = new Vector3f(height * circlePoints[circlePoint][0], height * circlePoints[circlePoint][1], bottomRadius - topRadius ); + circleNormals[circlePoint] = normal.normalizeLocal(); } - if (closed) { - pb.put(0).put(0).put(-halfHeight); // bottom center - nb.put(0).put(0).put(-1 * (inverted ? -1 : 1)); - tb.put(0.5f).put(0); - pb.put(0).put(0).put(halfHeight); // top center - nb.put(0).put(0).put(1 * (inverted ? -1 : 1)); - tb.put(0.5f).put(1); - } - - IndexBuffer ib = getIndexBuffer(); - int index = 0; - // Connectivity - for (int axisCount = 0, axisStart = 0; axisCount < axisSamples - 1; axisCount++) { - int i0 = axisStart; - int i1 = i0 + 1; - axisStart += radialSamples + 1; - int i2 = axisStart; - int i3 = i2 + 1; - for (int i = 0; i < radialSamples; i++) { - if (closed && axisCount == 0) { - if (!inverted) { - ib.put(index++, i0++); - ib.put(index++, vertCount - 2); - ib.put(index++, i1++); - } else { - ib.put(index++, i0++); - ib.put(index++, i1++); - ib.put(index++, vertCount - 2); - } - } else if (closed && axisCount == axisSamples - 2) { - ib.put(index++, i2++); - ib.put(index++, inverted ? vertCount - 1 : i3++); - ib.put(index++, inverted ? i3++ : vertCount - 1); - } else { - ib.put(index++, i0++); - ib.put(index++, inverted ? i2 : i1); - ib.put(index++, inverted ? i1 : i2); - ib.put(index++, i1++); - ib.put(index++, inverted ? i2++ : i3++); - ib.put(index++, inverted ? i3++ : i2++); - } - } + float[] vertices = new float[verticesCount * 3]; + float[] normals = new float[verticesCount * 3]; + float[] textureCoords = new float[verticesCount * 2]; + int currentIndex = 0; + + // Add a circle of points for each axis sample. + for(int axisSample = 0; axisSample < axisSamples; axisSample++ ) + { + float currentHeight = -height / 2 + height * axisSample / (axisSamples-1); + float currentRadius = bottomRadius + (topRadius - bottomRadius) * axisSample / (axisSamples-1); + + for (int circlePoint = 0; circlePoint < radialSamples + 1; circlePoint++) + { + // Position, by multipliying the position on a unit circle with the current radius. + vertices[currentIndex*3] = circlePoints[circlePoint][0] * currentRadius; + vertices[currentIndex*3 +1] = circlePoints[circlePoint][1] * currentRadius; + vertices[currentIndex*3 +2] = currentHeight; + + // Normal + Vector3f currentNormal = circleNormals[circlePoint]; + normals[currentIndex*3] = currentNormal.x; + normals[currentIndex*3+1] = currentNormal.y; + normals[currentIndex*3+2] = currentNormal.z; + + // Texture + // The X is the angular position of the point. + textureCoords[currentIndex *2] = (float) circlePoint / radialSamples; + // Depending on whether there is a cap, the Y is either the height scaled to [0,1], or the radii of + // the cap count as well. + if (closed) + textureCoords[currentIndex *2 +1] = (bottomRadius + height / 2 + currentHeight) / (bottomRadius + height + topRadius); + else + textureCoords[currentIndex *2 +1] = height / 2 + currentHeight; + + currentIndex++; + } + } + + // If closed, add duplicate rims on top and bottom, with normals facing up and down. + if (closed) + { + // Bottom + for (int circlePoint = 0; circlePoint < radialSamples + 1; circlePoint++) + { + vertices[currentIndex*3] = circlePoints[circlePoint][0] * bottomRadius; + vertices[currentIndex*3 +1] = circlePoints[circlePoint][1] * bottomRadius; + vertices[currentIndex*3 +2] = -height/2; + + normals[currentIndex*3] = 0; + normals[currentIndex*3+1] = 0; + normals[currentIndex*3+2] = -1; + + textureCoords[currentIndex *2] = (float) circlePoint / radialSamples; + textureCoords[currentIndex *2 +1] = bottomRadius / (bottomRadius + height + topRadius); + + currentIndex++; + } + // Top + for (int circlePoint = 0; circlePoint < radialSamples + 1; circlePoint++) + { + vertices[currentIndex*3] = circlePoints[circlePoint][0] * topRadius; + vertices[currentIndex*3 +1] = circlePoints[circlePoint][1] * topRadius; + vertices[currentIndex*3 +2] = height/2; + + normals[currentIndex*3] = 0; + normals[currentIndex*3+1] = 0; + normals[currentIndex*3+2] = 1; + + textureCoords[currentIndex *2] = (float) circlePoint / radialSamples; + textureCoords[currentIndex *2 +1] = (bottomRadius + height) / (bottomRadius + height + topRadius); + + currentIndex++; + } + + // Add the centers of the caps. + vertices[currentIndex*3] = 0; + vertices[currentIndex*3 +1] = 0; + vertices[currentIndex*3 +2] = -height/2; + + normals[currentIndex*3] = 0; + normals[currentIndex*3+1] = 0; + normals[currentIndex*3+2] = -1; + + textureCoords[currentIndex *2] = 0.5f; + textureCoords[currentIndex *2+1] = 0f; + + currentIndex++; + + vertices[currentIndex*3] = 0; + vertices[currentIndex*3 +1] = 0; + vertices[currentIndex*3 +2] = height/2; + + normals[currentIndex*3] = 0; + normals[currentIndex*3+1] = 0; + normals[currentIndex*3+2] = 1; + + textureCoords[currentIndex *2] = 0.5f; + textureCoords[currentIndex *2+1] = 1f; } + // Add the triangles indexes. + short[] indices = new short[trianglesCount * 3]; + currentIndex = 0; + for (short axisSample = 0; axisSample < axisSamples - 1; axisSample++) + { + for (int circlePoint = 0; circlePoint < radialSamples; circlePoint++) + { + indices[currentIndex++] = (short) (axisSample * (radialSamples + 1) + circlePoint); + indices[currentIndex++] = (short) (axisSample * (radialSamples + 1) + circlePoint + 1); + indices[currentIndex++] = (short) ((axisSample + 1) * (radialSamples + 1) + circlePoint); + + indices[currentIndex++] = (short) ((axisSample + 1) * (radialSamples + 1) + circlePoint); + indices[currentIndex++] = (short) (axisSample * (radialSamples + 1) + circlePoint + 1); + indices[currentIndex++] = (short) ((axisSample + 1) * (radialSamples + 1) + circlePoint + 1); + } + } + // Add caps if needed. + if(closed) + { + short bottomCapIndex = (short) (verticesCount - 2); + short topCapIndex = (short) (verticesCount - 1); + + int bottomRowOffset = (axisSamples) * (radialSamples +1 ); + int topRowOffset = (axisSamples+1) * (radialSamples +1 ); + + for (int circlePoint = 0; circlePoint < radialSamples; circlePoint++) + { + indices[currentIndex++] = (short) (bottomRowOffset + circlePoint +1); + indices[currentIndex++] = (short) (bottomRowOffset + circlePoint); + indices[currentIndex++] = bottomCapIndex; + + + indices[currentIndex++] = (short) (topRowOffset + circlePoint); + indices[currentIndex++] = (short) (topRowOffset + circlePoint +1); + indices[currentIndex++] = topCapIndex; + } + } + + // If inverted, the triangles and normals are all reverted. + if (inverted) + { + for (int i = 0; i < indices.length / 2; i++) + { + short temp = indices[i]; + indices[i] = indices[indices.length - 1 - i]; + indices[indices.length - 1 - i] = temp; + } + + for(int i = 0; i< normals.length; i++) + { + normals[i] = -normals[i]; + } + } + + // Fill in the buffers. + setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(vertices)); + setBuffer(Type.Normal, 3, BufferUtils.createFloatBuffer(normals)); + setBuffer(Type.TexCoord, 2, BufferUtils.createFloatBuffer(textureCoords)); + setBuffer(Type.Index, 3, BufferUtils.createShortBuffer(indices)); + updateBound(); setStatic(); } @@ -418,6 +461,4 @@ public class Cylinder extends Mesh { capsule.write(closed, "closed", false); capsule.write(inverted, "inverted", false); } - - -} +} \ No newline at end of file From 61c22d57095449d0d5e6904158873e2fe85a1d22 Mon Sep 17 00:00:00 2001 From: stophe Date: Sun, 18 Jun 2017 21:27:57 +0200 Subject: [PATCH 15/22] Providing invalid parameters in cylinders generation throws an exception. --- .../src/main/java/com/jme3/scene/shape/Cylinder.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/scene/shape/Cylinder.java b/jme3-core/src/main/java/com/jme3/scene/shape/Cylinder.java index bc8bf92b1..c46a81b4f 100644 --- a/jme3-core/src/main/java/com/jme3/scene/shape/Cylinder.java +++ b/jme3-core/src/main/java/com/jme3/scene/shape/Cylinder.java @@ -44,7 +44,7 @@ import com.jme3.util.BufferUtils; import java.io.IOException; /** - * A simple cylinder, defined by its height and radius. + * A simple cylinder, defined by it's height and radius. * (Ported to jME3) * * @author Mark Powell @@ -211,13 +211,13 @@ public class Cylinder extends Mesh { public void updateGeometry(int axisSamples, int radialSamples, float topRadius, float bottomRadius, float height, boolean closed, boolean inverted) { - // Ensure there's at least two axis samples and 3 radial samples, and positive geometries. + // Ensure there's at least two axis samples and 3 radial samples, and positive dimensions. if( axisSamples < 2 || radialSamples < 3 || topRadius <= 0 || bottomRadius <= 0 || height <= 0 ) - return; + throw new IllegalArgumentException("Cylinders must have at least 2 axis samples and 3 radial samples, and positive dimensions."); this.axisSamples = axisSamples; this.radialSamples = radialSamples; @@ -461,4 +461,4 @@ public class Cylinder extends Mesh { capsule.write(closed, "closed", false); capsule.write(inverted, "inverted", false); } -} \ No newline at end of file +} From ebaad20f2f03fa3c96fc45e39dc5c8d53df6c6f5 Mon Sep 17 00:00:00 2001 From: stophe Date: Sun, 18 Jun 2017 23:56:14 +0200 Subject: [PATCH 16/22] Added a unit test and fixed indentation. --- .../java/com/jme3/scene/shape/Cylinder.java | 397 +++++++++--------- .../com/jme3/scene/ShapeGeometryTest.java | 78 ++++ 2 files changed, 269 insertions(+), 206 deletions(-) create mode 100644 jme3-core/src/test/java/com/jme3/scene/ShapeGeometryTest.java diff --git a/jme3-core/src/main/java/com/jme3/scene/shape/Cylinder.java b/jme3-core/src/main/java/com/jme3/scene/shape/Cylinder.java index c46a81b4f..97dd70f30 100644 --- a/jme3-core/src/main/java/com/jme3/scene/shape/Cylinder.java +++ b/jme3-core/src/main/java/com/jme3/scene/shape/Cylinder.java @@ -125,9 +125,9 @@ public class Cylinder extends Mesh { * a suited distorted texture. * * @param axisSamples The number of vertices samples along the axis. It is equal to the number of segments + 1; so - * that, for instance, 4 samples mean the cylinder will be made of 3 segments. + * that, for instance, 4 samples mean the cylinder will be made of 3 segments. * @param radialSamples The number of triangle samples along the radius. For instance, 4 means that the sides of the - * cylinder are made of 4 rectangles, and the top and bottom are made of 4 triangles. + * cylinder are made of 4 rectangles, and the top and bottom are made of 4 triangles. * @param radius * The radius of the cylinder. * @param height @@ -199,27 +199,27 @@ public class Cylinder extends Mesh { * Rebuilds the cylinder based on a new set of parameters. * * @param axisSamples The number of vertices samples along the axis. It is equal to the number of segments + 1; so - * that, for instance, 4 samples mean the cylinder will be made of 3 segments. + * that, for instance, 4 samples mean the cylinder will be made of 3 segments. * @param radialSamples The number of triangle samples along the radius. For instance, 4 means that the sides of the - * cylinder are made of 4 rectangles, and the top and bottom are made of 4 triangles. + * cylinder are made of 4 rectangles, and the top and bottom are made of 4 triangles. * @param topRadius the radius of the top of the cylinder. - * @param bottomRadius the radius of the bottom of the cylinder. + * @param bottomRadius the radius of the bottom of the cylinder. * @param height the cylinder's height. * @param closed should the cylinder have top and bottom surfaces. * @param inverted is the cylinder is meant to be viewed from the inside. */ public void updateGeometry(int axisSamples, int radialSamples, - float topRadius, float bottomRadius, float height, boolean closed, boolean inverted) - { - // Ensure there's at least two axis samples and 3 radial samples, and positive dimensions. - if( axisSamples < 2 - || radialSamples < 3 - || topRadius <= 0 - || bottomRadius <= 0 - || height <= 0 ) - throw new IllegalArgumentException("Cylinders must have at least 2 axis samples and 3 radial samples, and positive dimensions."); - - this.axisSamples = axisSamples; + float topRadius, float bottomRadius, float height, boolean closed, boolean inverted) { + // Ensure there's at least two axis samples and 3 radial samples, and positive dimensions. + if( axisSamples < 2 + || radialSamples < 3 + || topRadius <= 0 + || bottomRadius <= 0 + || height <= 0 ) { + throw new IllegalArgumentException("Cylinders must have at least 2 axis samples and 3 radial samples, and positive dimensions."); + } + + this.axisSamples = axisSamples; this.radialSamples = radialSamples; this.radius = bottomRadius; this.radius2 = topRadius; @@ -229,209 +229,194 @@ public class Cylinder extends Mesh { // Vertices : One per radial sample plus one duplicate for texture closing around the sides. int verticesCount = axisSamples * (radialSamples +1); - // Triangles: Two per side rectangle, which is the product of numbers of samples. - int trianglesCount = axisSamples * radialSamples * 2 ; - if( closed ) - { - // If there are caps, add two additional rims and two summits. - verticesCount += 2 + 2 * (radialSamples +1); - // Add one triangle per radial sample, twice, to form the caps. - trianglesCount += 2 * radialSamples ; - } - - // Compute the points along a unit circle: - float[][] circlePoints = new float[radialSamples+1][2]; - for (int circlePoint = 0; circlePoint < radialSamples; circlePoint++) - { + // Triangles: Two per side rectangle, which is the product of numbers of samples. + int trianglesCount = axisSamples * radialSamples * 2 ; + if( closed ) { + // If there are caps, add two additional rims and two summits. + verticesCount += 2 + 2 * (radialSamples +1); + // Add one triangle per radial sample, twice, to form the caps. + trianglesCount += 2 * radialSamples ; + } + + // Compute the points along a unit circle: + float[][] circlePoints = new float[radialSamples+1][2]; + for (int circlePoint = 0; circlePoint < radialSamples; circlePoint++) { float angle = FastMath.TWO_PI / radialSamples * circlePoint; circlePoints[circlePoint][0] = FastMath.cos(angle); circlePoints[circlePoint][1] = FastMath.sin(angle); } - // Add an additional point for closing the texture around the side of the cylinder. - circlePoints[radialSamples][0] = circlePoints[0][0]; + // Add an additional point for closing the texture around the side of the cylinder. + circlePoints[radialSamples][0] = circlePoints[0][0]; circlePoints[radialSamples][1] = circlePoints[0][1]; - + // Calculate normals. - // - // A---------B - // \ | - // \ | - // \ | - // D-----C - // - // Let be B and C the top and bottom points of the axis, and A and D the top and bottom edges. - // The normal in A and D is simply orthogonal to AD, which means we can get it once per sample. - // - Vector3f[] circleNormals = new Vector3f[radialSamples+1]; - for (int circlePoint = 0; circlePoint < radialSamples+1; circlePoint++) - { + // + // A---------B + // \ | + // \ | + // \ | + // D-----C + // + // Let be B and C the top and bottom points of the axis, and A and D the top and bottom edges. + // The normal in A and D is simply orthogonal to AD, which means we can get it once per sample. + // + Vector3f[] circleNormals = new Vector3f[radialSamples+1]; + for (int circlePoint = 0; circlePoint < radialSamples+1; circlePoint++) { // The normal is the orthogonal to the side, which can be got without trigonometry. - // The edge direction is oriented so that it goes up by Height, and out by the radius difference; let's use - // those values in reverse order. - Vector3f normal = new Vector3f(height * circlePoints[circlePoint][0], height * circlePoints[circlePoint][1], bottomRadius - topRadius ); - circleNormals[circlePoint] = normal.normalizeLocal(); + // The edge direction is oriented so that it goes up by Height, and out by the radius difference; let's use + // those values in reverse order. + Vector3f normal = new Vector3f(height * circlePoints[circlePoint][0], height * circlePoints[circlePoint][1], bottomRadius - topRadius ); + circleNormals[circlePoint] = normal.normalizeLocal(); } - float[] vertices = new float[verticesCount * 3]; - float[] normals = new float[verticesCount * 3]; - float[] textureCoords = new float[verticesCount * 2]; - int currentIndex = 0; - - // Add a circle of points for each axis sample. - for(int axisSample = 0; axisSample < axisSamples; axisSample++ ) - { - float currentHeight = -height / 2 + height * axisSample / (axisSamples-1); - float currentRadius = bottomRadius + (topRadius - bottomRadius) * axisSample / (axisSamples-1); - - for (int circlePoint = 0; circlePoint < radialSamples + 1; circlePoint++) - { - // Position, by multipliying the position on a unit circle with the current radius. - vertices[currentIndex*3] = circlePoints[circlePoint][0] * currentRadius; - vertices[currentIndex*3 +1] = circlePoints[circlePoint][1] * currentRadius; - vertices[currentIndex*3 +2] = currentHeight; - - // Normal - Vector3f currentNormal = circleNormals[circlePoint]; - normals[currentIndex*3] = currentNormal.x; - normals[currentIndex*3+1] = currentNormal.y; - normals[currentIndex*3+2] = currentNormal.z; - - // Texture - // The X is the angular position of the point. - textureCoords[currentIndex *2] = (float) circlePoint / radialSamples; - // Depending on whether there is a cap, the Y is either the height scaled to [0,1], or the radii of - // the cap count as well. - if (closed) - textureCoords[currentIndex *2 +1] = (bottomRadius + height / 2 + currentHeight) / (bottomRadius + height + topRadius); - else - textureCoords[currentIndex *2 +1] = height / 2 + currentHeight; - - currentIndex++; - } - } - - // If closed, add duplicate rims on top and bottom, with normals facing up and down. - if (closed) - { - // Bottom - for (int circlePoint = 0; circlePoint < radialSamples + 1; circlePoint++) - { - vertices[currentIndex*3] = circlePoints[circlePoint][0] * bottomRadius; - vertices[currentIndex*3 +1] = circlePoints[circlePoint][1] * bottomRadius; - vertices[currentIndex*3 +2] = -height/2; - - normals[currentIndex*3] = 0; - normals[currentIndex*3+1] = 0; - normals[currentIndex*3+2] = -1; - - textureCoords[currentIndex *2] = (float) circlePoint / radialSamples; - textureCoords[currentIndex *2 +1] = bottomRadius / (bottomRadius + height + topRadius); - - currentIndex++; - } - // Top - for (int circlePoint = 0; circlePoint < radialSamples + 1; circlePoint++) - { - vertices[currentIndex*3] = circlePoints[circlePoint][0] * topRadius; - vertices[currentIndex*3 +1] = circlePoints[circlePoint][1] * topRadius; - vertices[currentIndex*3 +2] = height/2; - - normals[currentIndex*3] = 0; - normals[currentIndex*3+1] = 0; - normals[currentIndex*3+2] = 1; - - textureCoords[currentIndex *2] = (float) circlePoint / radialSamples; - textureCoords[currentIndex *2 +1] = (bottomRadius + height) / (bottomRadius + height + topRadius); - - currentIndex++; - } - - // Add the centers of the caps. - vertices[currentIndex*3] = 0; - vertices[currentIndex*3 +1] = 0; - vertices[currentIndex*3 +2] = -height/2; - - normals[currentIndex*3] = 0; - normals[currentIndex*3+1] = 0; - normals[currentIndex*3+2] = -1; - - textureCoords[currentIndex *2] = 0.5f; - textureCoords[currentIndex *2+1] = 0f; - - currentIndex++; - - vertices[currentIndex*3] = 0; - vertices[currentIndex*3 +1] = 0; - vertices[currentIndex*3 +2] = height/2; - - normals[currentIndex*3] = 0; - normals[currentIndex*3+1] = 0; - normals[currentIndex*3+2] = 1; - - textureCoords[currentIndex *2] = 0.5f; - textureCoords[currentIndex *2+1] = 1f; + float[] vertices = new float[verticesCount * 3]; + float[] normals = new float[verticesCount * 3]; + float[] textureCoords = new float[verticesCount * 2]; + int currentIndex = 0; + + // Add a circle of points for each axis sample. + for(int axisSample = 0; axisSample < axisSamples; axisSample++ ) { + float currentHeight = -height / 2 + height * axisSample / (axisSamples-1); + float currentRadius = bottomRadius + (topRadius - bottomRadius) * axisSample / (axisSamples-1); + + for (int circlePoint = 0; circlePoint < radialSamples + 1; circlePoint++) { + // Position, by multipliying the position on a unit circle with the current radius. + vertices[currentIndex*3] = circlePoints[circlePoint][0] * currentRadius; + vertices[currentIndex*3 +1] = circlePoints[circlePoint][1] * currentRadius; + vertices[currentIndex*3 +2] = currentHeight; + + // Normal + Vector3f currentNormal = circleNormals[circlePoint]; + normals[currentIndex*3] = currentNormal.x; + normals[currentIndex*3+1] = currentNormal.y; + normals[currentIndex*3+2] = currentNormal.z; + + // Texture + // The X is the angular position of the point. + textureCoords[currentIndex *2] = (float) circlePoint / radialSamples; + // Depending on whether there is a cap, the Y is either the height scaled to [0,1], or the radii of + // the cap count as well. + if (closed) + textureCoords[currentIndex *2 +1] = (bottomRadius + height / 2 + currentHeight) / (bottomRadius + height + topRadius); + else + textureCoords[currentIndex *2 +1] = height / 2 + currentHeight; + + currentIndex++; + } + } + + // If closed, add duplicate rims on top and bottom, with normals facing up and down. + if (closed) { + // Bottom + for (int circlePoint = 0; circlePoint < radialSamples + 1; circlePoint++) { + vertices[currentIndex*3] = circlePoints[circlePoint][0] * bottomRadius; + vertices[currentIndex*3 +1] = circlePoints[circlePoint][1] * bottomRadius; + vertices[currentIndex*3 +2] = -height/2; + + normals[currentIndex*3] = 0; + normals[currentIndex*3+1] = 0; + normals[currentIndex*3+2] = -1; + + textureCoords[currentIndex *2] = (float) circlePoint / radialSamples; + textureCoords[currentIndex *2 +1] = bottomRadius / (bottomRadius + height + topRadius); + + currentIndex++; + } + // Top + for (int circlePoint = 0; circlePoint < radialSamples + 1; circlePoint++) { + vertices[currentIndex*3] = circlePoints[circlePoint][0] * topRadius; + vertices[currentIndex*3 +1] = circlePoints[circlePoint][1] * topRadius; + vertices[currentIndex*3 +2] = height/2; + + normals[currentIndex*3] = 0; + normals[currentIndex*3+1] = 0; + normals[currentIndex*3+2] = 1; + + textureCoords[currentIndex *2] = (float) circlePoint / radialSamples; + textureCoords[currentIndex *2 +1] = (bottomRadius + height) / (bottomRadius + height + topRadius); + + currentIndex++; + } + + // Add the centers of the caps. + vertices[currentIndex*3] = 0; + vertices[currentIndex*3 +1] = 0; + vertices[currentIndex*3 +2] = -height/2; + + normals[currentIndex*3] = 0; + normals[currentIndex*3+1] = 0; + normals[currentIndex*3+2] = -1; + + textureCoords[currentIndex *2] = 0.5f; + textureCoords[currentIndex *2+1] = 0f; + + currentIndex++; + + vertices[currentIndex*3] = 0; + vertices[currentIndex*3 +1] = 0; + vertices[currentIndex*3 +2] = height/2; + + normals[currentIndex*3] = 0; + normals[currentIndex*3+1] = 0; + normals[currentIndex*3+2] = 1; + + textureCoords[currentIndex *2] = 0.5f; + textureCoords[currentIndex *2+1] = 1f; } - // Add the triangles indexes. + // Add the triangles indexes. short[] indices = new short[trianglesCount * 3]; - currentIndex = 0; - for (short axisSample = 0; axisSample < axisSamples - 1; axisSample++) - { - for (int circlePoint = 0; circlePoint < radialSamples; circlePoint++) - { - indices[currentIndex++] = (short) (axisSample * (radialSamples + 1) + circlePoint); - indices[currentIndex++] = (short) (axisSample * (radialSamples + 1) + circlePoint + 1); - indices[currentIndex++] = (short) ((axisSample + 1) * (radialSamples + 1) + circlePoint); - - indices[currentIndex++] = (short) ((axisSample + 1) * (radialSamples + 1) + circlePoint); - indices[currentIndex++] = (short) (axisSample * (radialSamples + 1) + circlePoint + 1); - indices[currentIndex++] = (short) ((axisSample + 1) * (radialSamples + 1) + circlePoint + 1); - } - } - // Add caps if needed. - if(closed) - { - short bottomCapIndex = (short) (verticesCount - 2); - short topCapIndex = (short) (verticesCount - 1); - - int bottomRowOffset = (axisSamples) * (radialSamples +1 ); - int topRowOffset = (axisSamples+1) * (radialSamples +1 ); - - for (int circlePoint = 0; circlePoint < radialSamples; circlePoint++) - { - indices[currentIndex++] = (short) (bottomRowOffset + circlePoint +1); - indices[currentIndex++] = (short) (bottomRowOffset + circlePoint); - indices[currentIndex++] = bottomCapIndex; - - - indices[currentIndex++] = (short) (topRowOffset + circlePoint); - indices[currentIndex++] = (short) (topRowOffset + circlePoint +1); - indices[currentIndex++] = topCapIndex; - } - } - - // If inverted, the triangles and normals are all reverted. - if (inverted) - { - for (int i = 0; i < indices.length / 2; i++) - { - short temp = indices[i]; - indices[i] = indices[indices.length - 1 - i]; - indices[indices.length - 1 - i] = temp; - } - - for(int i = 0; i< normals.length; i++) - { - normals[i] = -normals[i]; - } - } - - // Fill in the buffers. - setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(vertices)); - setBuffer(Type.Normal, 3, BufferUtils.createFloatBuffer(normals)); - setBuffer(Type.TexCoord, 2, BufferUtils.createFloatBuffer(textureCoords)); + currentIndex = 0; + for (short axisSample = 0; axisSample < axisSamples - 1; axisSample++) { + for (int circlePoint = 0; circlePoint < radialSamples; circlePoint++) { + indices[currentIndex++] = (short) (axisSample * (radialSamples + 1) + circlePoint); + indices[currentIndex++] = (short) (axisSample * (radialSamples + 1) + circlePoint + 1); + indices[currentIndex++] = (short) ((axisSample + 1) * (radialSamples + 1) + circlePoint); + + indices[currentIndex++] = (short) ((axisSample + 1) * (radialSamples + 1) + circlePoint); + indices[currentIndex++] = (short) (axisSample * (radialSamples + 1) + circlePoint + 1); + indices[currentIndex++] = (short) ((axisSample + 1) * (radialSamples + 1) + circlePoint + 1); + } + } + // Add caps if needed. + if(closed) { + short bottomCapIndex = (short) (verticesCount - 2); + short topCapIndex = (short) (verticesCount - 1); + + int bottomRowOffset = (axisSamples) * (radialSamples +1 ); + int topRowOffset = (axisSamples+1) * (radialSamples +1 ); + + for (int circlePoint = 0; circlePoint < radialSamples; circlePoint++) { + indices[currentIndex++] = (short) (bottomRowOffset + circlePoint +1); + indices[currentIndex++] = (short) (bottomRowOffset + circlePoint); + indices[currentIndex++] = bottomCapIndex; + + + indices[currentIndex++] = (short) (topRowOffset + circlePoint); + indices[currentIndex++] = (short) (topRowOffset + circlePoint +1); + indices[currentIndex++] = topCapIndex; + } + } + + // If inverted, the triangles and normals are all reverted. + if (inverted) { + for (int i = 0; i < indices.length / 2; i++) { + short temp = indices[i]; + indices[i] = indices[indices.length - 1 - i]; + indices[indices.length - 1 - i] = temp; + } + + for(int i = 0; i< normals.length; i++) { + normals[i] = -normals[i]; + } + } + + // Fill in the buffers. + setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(vertices)); + setBuffer(Type.Normal, 3, BufferUtils.createFloatBuffer(normals)); + setBuffer(Type.TexCoord, 2, BufferUtils.createFloatBuffer(textureCoords)); setBuffer(Type.Index, 3, BufferUtils.createShortBuffer(indices)); - + updateBound(); setStatic(); } diff --git a/jme3-core/src/test/java/com/jme3/scene/ShapeGeometryTest.java b/jme3-core/src/test/java/com/jme3/scene/ShapeGeometryTest.java new file mode 100644 index 000000000..af20a134d --- /dev/null +++ b/jme3-core/src/test/java/com/jme3/scene/ShapeGeometryTest.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2009-2017 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.scene; + +import com.jme3.collision.CollisionResults; +import com.jme3.math.FastMath; +import com.jme3.math.Ray; +import com.jme3.math.Vector3f; +import com.jme3.scene.shape.Cylinder; +import java.util.Random; +import org.junit.Test; + +/** + * Ensures that geometries behave correctly, by casting rays and ensure they don't break. + * + * @author Christophe Carpentier + */ +public class ShapeGeometryTest { + + protected static final int NUMBER_OF_TRIES = 1000; + + @Test + public void testCylinders() { + Random random = new Random(); + + // Create a cylinder, cast a random ray, and ensure everything goes well. + Node scene = new Node("Scene Node"); + + for (int i = 0; i < NUMBER_OF_TRIES; i++) { + scene.detachAllChildren(); + + Cylinder cylinder = new Cylinder(2, 8, 1, 1, true); + Geometry geometry = new Geometry("cylinder", cylinder); + geometry.rotate(FastMath.HALF_PI, 0, 0); + scene.attachChild(geometry); + + // Cast a random ray, and count successes and IndexOutOfBoundsExceptions. + Vector3f randomPoint = new Vector3f(random.nextFloat(), random.nextFloat(), random.nextFloat()); + Vector3f randomDirection = new Vector3f(random.nextFloat(), random.nextFloat(), random.nextFloat()); + randomDirection.normalizeLocal(); + + Ray ray = new Ray(randomPoint, randomDirection); + CollisionResults collisionResults = new CollisionResults(); + + // If the geometry is invalid, this should throw various exceptions. + scene.collideWith(ray, collisionResults); + } + } +} From 1dd1888b55fc3b3af8742ce7d5844feb6b6957b6 Mon Sep 17 00:00:00 2001 From: Thomas JOUANNOT Date: Thu, 22 Jun 2017 13:26:47 +0200 Subject: [PATCH 17/22] LodGenerator should accept a constructor by mesh --- .../tools/java/jme3tools/optimize/LodGenerator.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/jme3-core/src/tools/java/jme3tools/optimize/LodGenerator.java b/jme3-core/src/tools/java/jme3tools/optimize/LodGenerator.java index 56bc02bd1..5b2ee6fd8 100644 --- a/jme3-core/src/tools/java/jme3tools/optimize/LodGenerator.java +++ b/jme3-core/src/tools/java/jme3tools/optimize/LodGenerator.java @@ -254,6 +254,16 @@ public class LodGenerator { } }; + /** + * Construct a LodGenerator for the given mesh + * + * @param mesh the mesh to consider to generate de Lods. + */ + public LodGenerator(Mesh mesh) { + this.mesh = mesh; + build(); + } + /** * Construct a LodGenerator for the given geometry * From 2ff0bf5f759b49c2e11a7bf4673dce6cb0016696 Mon Sep 17 00:00:00 2001 From: RemboL Date: Sun, 25 Jun 2017 08:54:04 +0200 Subject: [PATCH 18/22] fixing documentation link --- .../src/main/java/com/jme3/post/filters/BloomFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jme3-effects/src/main/java/com/jme3/post/filters/BloomFilter.java b/jme3-effects/src/main/java/com/jme3/post/filters/BloomFilter.java index 778111a3c..7a83809cd 100644 --- a/jme3-effects/src/main/java/com/jme3/post/filters/BloomFilter.java +++ b/jme3-effects/src/main/java/com/jme3/post/filters/BloomFilter.java @@ -52,7 +52,7 @@ import java.util.ArrayList; * There are 2 mode : Scene and Objects.
      * Scene mode extracts the bright parts of the scene to make them glow
      * Object mode make objects glow according to their material's glowMap or their GlowColor
      - * @see advanced:bloom_and_glow for more details + * @see advanced:bloom_and_glow for more details * * @author Rémy Bouquet aka Nehon */ From d3733fba152c39e74290680a53f109d82efeb636 Mon Sep 17 00:00:00 2001 From: Christopher Hughes Date: Thu, 6 Jul 2017 16:52:39 -0400 Subject: [PATCH 19/22] Overrode SkeletonDebugger clone method. Ticket #660 --- .../com/jme3/scene/debug/SkeletonDebugger.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/scene/debug/SkeletonDebugger.java b/jme3-core/src/main/java/com/jme3/scene/debug/SkeletonDebugger.java index b633ab5b8..f764304c7 100644 --- a/jme3-core/src/main/java/com/jme3/scene/debug/SkeletonDebugger.java +++ b/jme3-core/src/main/java/com/jme3/scene/debug/SkeletonDebugger.java @@ -31,12 +31,13 @@ */ package com.jme3.scene.debug; -import java.util.Map; - import com.jme3.animation.Skeleton; import com.jme3.renderer.queue.RenderQueue.Bucket; import com.jme3.scene.Geometry; import com.jme3.scene.Node; +import com.jme3.util.clone.Cloner; + +import java.util.Map; /** * The class that creates a mesh to display how bones behave. @@ -122,4 +123,13 @@ public class SkeletonDebugger extends Node { public SkeletonInterBoneWire getInterBoneWires() { return interBoneWires; } + + @Override + public void cloneFields(Cloner cloner, Object original) { + super.cloneFields(cloner, original); + + this.wires = cloner.clone(wires); + this.points = cloner.clone(points); + this.interBoneWires = cloner.clone(interBoneWires); + } } \ No newline at end of file From 5ac5731ebfacd8eda64c26631c0312fdc1a02b18 Mon Sep 17 00:00:00 2001 From: Nehon Date: Sat, 8 Jul 2017 14:04:57 +0200 Subject: [PATCH 20/22] Better error reporting when loading shaderNodeDefinition --- .../plugins/ShaderNodeLoaderDelegate.java | 75 +++++++++++-------- 1 file changed, 45 insertions(+), 30 deletions(-) diff --git a/jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java b/jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java index dba4f6389..6fcdf2eda 100644 --- a/jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java +++ b/jme3-core/src/plugins/java/com/jme3/material/plugins/ShaderNodeLoaderDelegate.java @@ -232,40 +232,52 @@ public class ShaderNodeLoaderDelegate { protected void readShaderNodeDefinition(List statements, ShaderNodeDefinitionKey key) throws IOException { boolean isLoadDoc = key instanceof ShaderNodeDefinitionKey && ((ShaderNodeDefinitionKey) key).isLoadDocumentation(); for (Statement statement : statements) { - String[] split = statement.getLine().split("[ \\{]"); - String line = statement.getLine(); - - if (line.startsWith("Type")) { - String type = line.substring(line.lastIndexOf(':') + 1).trim(); - shaderNodeDefinition.setType(Shader.ShaderType.valueOf(type)); - } else if (line.startsWith("Shader ")) { - readShaderStatement(statement); - shaderNodeDefinition.getShadersLanguage().add(shaderLanguage); - shaderNodeDefinition.getShadersPath().add(shaderName); - } else if (line.startsWith("Documentation")) { - if (isLoadDoc) { - String doc = ""; + try { + String[] split = statement.getLine().split("[ \\{]"); + String line = statement.getLine(); + + if (line.startsWith("Type")) { + String type = line.substring(line.lastIndexOf(':') + 1).trim(); + shaderNodeDefinition.setType(Shader.ShaderType.valueOf(type)); + } else if (line.startsWith("Shader ")) { + readShaderStatement(statement); + shaderNodeDefinition.getShadersLanguage().add(shaderLanguage); + shaderNodeDefinition.getShadersPath().add(shaderName); + } else if (line.startsWith("Documentation")) { + if (isLoadDoc) { + String doc = ""; + for (Statement statement1 : statement.getContents()) { + doc += "\n" + statement1.getLine(); + } + shaderNodeDefinition.setDocumentation(doc); + } + } else if (line.startsWith("Input")) { + varNames = ""; for (Statement statement1 : statement.getContents()) { - doc += "\n" + statement1.getLine(); + try { + shaderNodeDefinition.getInputs().add(readVariable(statement1)); + } catch (RuntimeException e) { + throw new MatParseException(e.getMessage(), statement1, e); + } } - shaderNodeDefinition.setDocumentation(doc); - } - } else if (line.startsWith("Input")) { - varNames = ""; - for (Statement statement1 : statement.getContents()) { - shaderNodeDefinition.getInputs().add(readVariable(statement1)); - } - } else if (line.startsWith("Output")) { - varNames = ""; - for (Statement statement1 : statement.getContents()) { - if(statement1.getLine().trim().equals("None")){ - shaderNodeDefinition.setNoOutput(true); - }else{ - shaderNodeDefinition.getOutputs().add(readVariable(statement1)); + } else if (line.startsWith("Output")) { + varNames = ""; + for (Statement statement1 : statement.getContents()) { + try { + if (statement1.getLine().trim().equals("None")) { + shaderNodeDefinition.setNoOutput(true); + } else { + shaderNodeDefinition.getOutputs().add(readVariable(statement1)); + } + } catch (RuntimeException e) { + throw new MatParseException(e.getMessage(), statement1, e); + } } + } else { + throw new MatParseException("one of Type, Shader, Documentation, Input, Output", split[0], statement); } - } else { - throw new MatParseException("one of Type, Shader, Documentation, Input, Output", split[0], statement); + } catch (RuntimeException e) { + throw new MatParseException(e.getMessage(), statement, e); } } } @@ -280,6 +292,9 @@ public class ShaderNodeLoaderDelegate { protected ShaderNodeVariable readVariable(Statement statement) throws IOException { String line = statement.getLine().trim().replaceAll("\\s*\\[", "["); String[] splitVar = line.split("\\s"); + if (splitVar.length != 2) { + throw new MatParseException("2 arguments", splitVar.length + "", statement); + } String varName = splitVar[1]; String varType = splitVar[0]; String multiplicity = null; From debd62ee87864eb6aba260606f1aafcc0797886c Mon Sep 17 00:00:00 2001 From: Nehon Date: Sat, 8 Jul 2017 14:06:42 +0200 Subject: [PATCH 21/22] Display generated source in the ShaderNode test --- .../src/main/java/jme3test/material/TestShaderNodes.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/jme3-examples/src/main/java/jme3test/material/TestShaderNodes.java b/jme3-examples/src/main/java/jme3test/material/TestShaderNodes.java index a580f54ed..b1683fcff 100644 --- a/jme3-examples/src/main/java/jme3test/material/TestShaderNodes.java +++ b/jme3-examples/src/main/java/jme3test/material/TestShaderNodes.java @@ -31,10 +31,9 @@ public class TestShaderNodes extends SimpleApplication { mat.selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager); Technique t = mat.getActiveTechnique(); -// for (Shader.ShaderSource shaderSource : t.getShader().getSources()) { -// System.out.println(shaderSource.getSource()); -// } - + for (Shader.ShaderSource shaderSource : t.getDef().getShader(assetManager, renderer.getCaps(), t.getDynamicDefines()).getSources()) { + System.out.println(shaderSource.getSource()); + } mat.setColor("Color", ColorRGBA.Yellow); mat.setTexture("ColorMap", tex); From 54f283e8d2c5dc8b0dfd913e5cc0dc3f981e2d14 Mon Sep 17 00:00:00 2001 From: Nehon Date: Sat, 8 Jul 2017 23:39:05 +0200 Subject: [PATCH 22/22] Fixed an issue where the color space of a texture mat param was disregarded when loaded from a j3o. --- .../main/java/com/jme3/material/Material.java | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/material/Material.java b/jme3-core/src/main/java/com/jme3/material/Material.java index 72a85f760..a14bc357d 100644 --- a/jme3-core/src/main/java/com/jme3/material/Material.java +++ b/jme3-core/src/main/java/com/jme3/material/Material.java @@ -529,24 +529,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { checkSetParam(type, name); MatParamTexture val = getTextureParam(name); if (val == null) { - MatParamTexture paramDef = (MatParamTexture) def.getMaterialParam(name); - if (paramDef.getColorSpace() != null && paramDef.getColorSpace() != value.getImage().getColorSpace()) { - value.getImage().setColorSpace(paramDef.getColorSpace()); - logger.log(Level.FINE, "Material parameter {0} needs a {1} texture, " - + "texture {2} was switched to {3} color space.", - new Object[]{name, paramDef.getColorSpace().toString(), - value.getName(), - value.getImage().getColorSpace().name()}); - } else if (paramDef.getColorSpace() == null && value.getName() != null && value.getImage().getColorSpace() == ColorSpace.Linear) { - logger.log(Level.WARNING, - "The texture {0} has linear color space, but the material " - + "parameter {2} specifies no color space requirement, this may " - + "lead to unexpected behavior.\nCheck if the image " - + "was not set to another material parameter with a linear " - + "color space, or that you did not set the ColorSpace to " - + "Linear using texture.getImage.setColorSpace().", - new Object[]{value.getName(), value.getImage().getColorSpace().name(), name}); - } + checkTextureParamColorSpace(name, value); paramValues.put(name, new MatParamTexture(type, name, value, null)); } else { val.setTextureValue(value); @@ -560,6 +543,27 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { sortingId = -1; } + private void checkTextureParamColorSpace(String name, Texture value) { + MatParamTexture paramDef = (MatParamTexture) def.getMaterialParam(name); + if (paramDef.getColorSpace() != null && paramDef.getColorSpace() != value.getImage().getColorSpace()) { + value.getImage().setColorSpace(paramDef.getColorSpace()); + logger.log(Level.FINE, "Material parameter {0} needs a {1} texture, " + + "texture {2} was switched to {3} color space.", + new Object[]{name, paramDef.getColorSpace().toString(), + value.getName(), + value.getImage().getColorSpace().name()}); + } else if (paramDef.getColorSpace() == null && value.getName() != null && value.getImage().getColorSpace() == ColorSpace.Linear) { + logger.log(Level.WARNING, + "The texture {0} has linear color space, but the material " + + "parameter {2} specifies no color space requirement, this may " + + "lead to unexpected behavior.\nCheck if the image " + + "was not set to another material parameter with a linear " + + "color space, or that you did not set the ColorSpace to " + + "Linear using texture.getImage.setColorSpace().", + new Object[]{value.getName(), value.getImage().getColorSpace().name(), name}); + } + } + /** * Pass a texture to the material shader. * @@ -1062,6 +1066,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { if (texVal.getTextureValue() == null || texVal.getTextureValue().getImage() == null) { continue; } + checkTextureParamColorSpace(texVal.getName(), texVal.getTextureValue()); } if (im.getFormatVersion() == 0 && param.getName().startsWith("m_")) {