Merge pull request #618 from jseinturier/jme3-vr-dev
Deprecated VRApplication. Now VRAppState should be used.fix-456
commit
ed4e614722
@ -0,0 +1,485 @@ |
||||
package com.jme3.app; |
||||
|
||||
import java.util.Locale; |
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
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.VRInputAPI; |
||||
import com.jme3.renderer.Camera; |
||||
import com.jme3.scene.Spatial; |
||||
import com.jme3.system.AppSettings; |
||||
import com.jme3.system.jopenvr.JOpenVRLibrary; |
||||
import com.jme3.util.VRGuiManager; |
||||
import com.jme3.util.VRMouseManager; |
||||
import com.jme3.util.VRViewManager; |
||||
import com.jme3.util.VRViewManagerOSVR; |
||||
import com.jme3.util.VRViewManagerOpenVR; |
||||
|
||||
public class VREnvironment { |
||||
|
||||
private static final Logger logger = Logger.getLogger(VREnvironment.class.getName()); |
||||
|
||||
private VRAPI hardware = null; |
||||
private VRGuiManager guiManager = null; |
||||
private VRMouseManager mouseManager = null; |
||||
private VRViewManager viewmanager = null; |
||||
|
||||
/** |
||||
* The underlying system VR API. By default set to {@link VRConstants#SETTING_VRAPI_OPENVR_VALUE}. |
||||
*/ |
||||
public int vrBinding = VRConstants.SETTING_VRAPI_OPENVR_VALUE; |
||||
|
||||
private boolean seated = false; |
||||
|
||||
private Spatial observer = null; |
||||
|
||||
private boolean forceVR = false; |
||||
|
||||
private boolean vrSupportedOS = false; |
||||
|
||||
private boolean nogui = false; |
||||
|
||||
private boolean compositorOS; |
||||
|
||||
private boolean useCompositor = true; |
||||
|
||||
private boolean instanceRendering = false; |
||||
|
||||
private boolean disableSwapBuffers = true; |
||||
|
||||
private float defaultFOV = 108f; |
||||
|
||||
private float defaultAspect = 1f; |
||||
|
||||
private AppSettings settings = null; |
||||
|
||||
private Application application = null; |
||||
|
||||
private Camera dummyCam = null; |
||||
|
||||
private AppState app = null; |
||||
|
||||
private boolean initialized = false; |
||||
|
||||
private boolean attached = false; |
||||
|
||||
public VREnvironment(AppSettings settings){ |
||||
|
||||
this.settings = settings; |
||||
|
||||
guiManager = new VRGuiManager(this); |
||||
mouseManager = new VRMouseManager(this); |
||||
dummyCam = new Camera(); |
||||
|
||||
processSettings(); |
||||
} |
||||
|
||||
/** |
||||
* Get the VR underlying hardware. |
||||
* @return the VR underlying hardware. |
||||
*/ |
||||
public VRAPI getVRHardware() { |
||||
return hardware; |
||||
} |
||||
|
||||
/** |
||||
* Get the VR dedicated input. |
||||
* @return the VR dedicated input. |
||||
*/ |
||||
public VRInputAPI getVRinput() { |
||||
if( hardware == null ){ |
||||
return null; |
||||
} |
||||
|
||||
return hardware.getVRinput(); |
||||
} |
||||
|
||||
/** |
||||
* Get the VR view manager. |
||||
* @return the VR view manager. |
||||
*/ |
||||
public VRViewManager getVRViewManager() { |
||||
return viewmanager; |
||||
} |
||||
|
||||
/** |
||||
* Get the GUI manager attached to this environment. |
||||
* @return the GUI manager attached to this environment. |
||||
*/ |
||||
public VRGuiManager getVRGUIManager(){ |
||||
return guiManager; |
||||
} |
||||
|
||||
/** |
||||
* Get the VR mouse manager attached to this environment. |
||||
* @return the VR mouse manager attached to this environment. |
||||
*/ |
||||
public VRMouseManager getVRMouseManager(){ |
||||
return mouseManager; |
||||
} |
||||
|
||||
/** |
||||
* Can be used to change seated experience during runtime. |
||||
* @param isSeated <code>true</code> if designed for sitting, <code>false</code> for standing/roomscale |
||||
* @see #isSeatedExperience() |
||||
*/ |
||||
public void setSeatedExperience(boolean isSeated) { |
||||
seated = isSeated; |
||||
if( hardware instanceof OpenVR ) { |
||||
if( hardware.getCompositor() == null ) { |
||||
return; |
||||
} |
||||
|
||||
if( seated ) { |
||||
((OpenVR)hardware).getCompositor().SetTrackingSpace.apply(JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseSeated); |
||||
} else { |
||||
((OpenVR)hardware).getCompositor().SetTrackingSpace.apply(JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseStanding); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Check if the application is configured as a seated experience. |
||||
* @return <code>true</code> if the application is configured as a seated experience and <code>false</code> otherwise. |
||||
* @see #setSeatedExperience(boolean) |
||||
*/ |
||||
public boolean isSeatedExperience() { |
||||
return seated; |
||||
} |
||||
|
||||
/** |
||||
* Set the VR headset height from the ground. |
||||
* @param amount the VR headset height from the ground. |
||||
* @see #getVRHeightAdjustment() |
||||
*/ |
||||
public void setVRHeightAdjustment(float amount) { |
||||
if( viewmanager != null ){ |
||||
viewmanager.setHeightAdjustment(amount); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Get the VR headset height from the ground. |
||||
* @return the VR headset height from the ground. |
||||
* @see #setVRHeightAdjustment(float) |
||||
*/ |
||||
public float getVRHeightAdjustment() { |
||||
if( viewmanager != null ){ |
||||
return viewmanager.getHeightAdjustment(); |
||||
} |
||||
return 0f; |
||||
} |
||||
|
||||
/** |
||||
* Get the scene observer. If no observer has been set, this method return the application {@link #getCamera() camera}. |
||||
* @return the scene observer. |
||||
* @see #setObserver(Spatial) |
||||
*/ |
||||
public Object getObserver() { |
||||
if( observer == null ) { |
||||
|
||||
if (application != null){ |
||||
return application.getCamera(); |
||||
} else { |
||||
throw new IllegalStateException("VR environment is not attached to any application."); |
||||
} |
||||
} |
||||
return observer; |
||||
} |
||||
|
||||
/** |
||||
* Set the scene observer. The VR headset will be linked to it. If no observer is set, the VR headset is linked to the the application {@link #getCamera() camera}. |
||||
* @param observer the scene observer. |
||||
*/ |
||||
public void setObserver(Spatial observer) { |
||||
this.observer = observer; |
||||
} |
||||
|
||||
/** |
||||
* Get the default Field Of View (FOV) value. |
||||
* @return the default Field Of View (FOV) value. |
||||
* @see #setDefaultFOV(float) |
||||
*/ |
||||
public float getDefaultFOV() { |
||||
return defaultFOV; |
||||
} |
||||
|
||||
/** |
||||
* Set the default Field Of View (FOV) value. |
||||
* @param defaultFOV the default Field Of View (FOV) value. |
||||
* @see #getDefaultFOV() |
||||
*/ |
||||
public void setDefaultFOV(float defaultFOV) { |
||||
this.defaultFOV = defaultFOV; |
||||
} |
||||
|
||||
/** |
||||
* Get the default aspect ratio. |
||||
* @return the default aspect ratio. |
||||
* @see #setDefaultAspect(float) |
||||
*/ |
||||
public float getDefaultAspect() { |
||||
return defaultAspect; |
||||
} |
||||
|
||||
/** |
||||
* Set the default aspect ratio. |
||||
* @param defaultAspect the default aspect ratio. |
||||
* @see #getDefaultAspect() |
||||
*/ |
||||
public void setDefaultAspect(float defaultAspect) { |
||||
this.defaultAspect = defaultAspect; |
||||
} |
||||
|
||||
/** |
||||
* Get the {@link AppSettings settings} attached to this environment. |
||||
* @return the {@link AppSettings settings} attached to this environment. |
||||
* @see #setSettings(AppSettings) |
||||
*/ |
||||
public AppSettings getSettings(){ |
||||
return settings; |
||||
} |
||||
|
||||
/** |
||||
* Set the {@link AppSettings settings} attached to this environment. |
||||
* @param settings the {@link AppSettings settings} attached to this environment. |
||||
* @see #getSettings() |
||||
*/ |
||||
public void setSettings(AppSettings settings){ |
||||
this.settings = settings; |
||||
processSettings(); |
||||
} |
||||
|
||||
/** |
||||
* Get if the system currently support VR. |
||||
* @return <code>true</code> if the system currently support VR and <code>false</Code> otherwise. |
||||
*/ |
||||
public boolean isVRSupported() { |
||||
return vrSupportedOS; |
||||
} |
||||
|
||||
/** |
||||
* Check if the VR mode is enabled. |
||||
* @return <code>true</code> if the VR mode is enabled and <code>false</code> otherwise. |
||||
*/ |
||||
public boolean isInVR() { |
||||
return (forceVR || vrSupportedOS && hardware != null && hardware.isInitialized() && isInitialized()); |
||||
} |
||||
|
||||
/** |
||||
* Check if the rendering is instanced (see <a href="https://en.wikipedia.org/wiki/Geometry_instancing">Geometry instancing</a>). |
||||
* @return <code>true</code> if the rendering is instanced and <code>false</code> otherwise. |
||||
*/ |
||||
public boolean isInstanceRendering() { |
||||
return instanceRendering; |
||||
} |
||||
|
||||
public boolean isSwapBuffers(){ |
||||
return disableSwapBuffers; |
||||
} |
||||
|
||||
/** |
||||
* Check if the application has a GUI overlay attached. |
||||
* @return <code>true</code> if the application has a GUI overlay attached and <code>false</code> otherwise. |
||||
*/ |
||||
public boolean hasTraditionalGUIOverlay() { |
||||
return !nogui; |
||||
} |
||||
|
||||
/** |
||||
* Check if the VR environment is initialized. A call to the {@link #initialize() initialize()} method should set this value to <code>true</code> |
||||
* @return <code>true</code> if the VR environment is initialized and <code>false</code> otherwise. |
||||
*/ |
||||
public boolean isInitialized(){ |
||||
return initialized; |
||||
} |
||||
|
||||
/** |
||||
* Is the VR compositor is active. |
||||
* @return <code>true</code> if the VR compositor is active and <code>false</code> otherwise. |
||||
*/ |
||||
public boolean compositorAllowed() { |
||||
return useCompositor && compositorOS; |
||||
} |
||||
|
||||
/** |
||||
* Reset headset pose if seating experience. |
||||
*/ |
||||
public void resetSeatedPose(){ |
||||
if( vrSupportedOS == false || isSeatedExperience() == false ){ |
||||
return; |
||||
} |
||||
getVRHardware().reset(); |
||||
} |
||||
|
||||
public AppState getAppState(){ |
||||
return app; |
||||
} |
||||
|
||||
public Application getApplication(){ |
||||
return application; |
||||
} |
||||
|
||||
/** |
||||
* Get the {@link Camera camera} used for rendering. |
||||
* If the VR mode is {@link #isInVR() active}, this method return a dummy camera, otherwise, |
||||
* this method return the camera of the attached application. |
||||
* @return the camera attached used for rendering. |
||||
*/ |
||||
public Camera getCamera() { |
||||
if( isInVR() && getVRViewManager() != null && getVRViewManager().getLeftCamera() != null ) { |
||||
return dummyCam; |
||||
} |
||||
|
||||
return application.getCamera(); |
||||
} |
||||
|
||||
public Camera getDummyCamera(){ |
||||
|
||||
if (dummyCam == null){ |
||||
|
||||
if (application != null){ |
||||
|
||||
if (application.getCamera() != null){ |
||||
dummyCam = application.getCamera().clone(); |
||||
} else { |
||||
return new Camera(); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("VR environment is not attached to any application."); |
||||
} |
||||
} |
||||
|
||||
return dummyCam; |
||||
} |
||||
|
||||
/** |
||||
* Attach the VR environment to the given app state and application. |
||||
* This method should be called within the {@link AppState#stateAttached(com.jme3.app.state.AppStateManager) stateAttached(com.jme3.app.state.AppStateManager)} method |
||||
* from the app state. |
||||
* @param appState the app state to attach. |
||||
* @param application the application to attach. |
||||
*/ |
||||
public void atttach(AppState appState, Application application){ |
||||
this.application = application; |
||||
this.app = appState; |
||||
|
||||
// Instanciate view manager
|
||||
if (vrBinding == VRConstants.SETTING_VRAPI_OPENVR_VALUE){ |
||||
viewmanager = new VRViewManagerOpenVR(this); |
||||
} else if (vrBinding == VRConstants.SETTING_VRAPI_OSVR_VALUE){ |
||||
viewmanager = new VRViewManagerOSVR(this); |
||||
} else { |
||||
logger.severe("Cannot instanciate view manager, unknown VRAPI type: "+vrBinding); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Initialize this VR environment. This method enable the system bindings and configure all the VR system modules. |
||||
* A call to this method has to be made before any use of VR capabilities. |
||||
* @return <code>true</code> if the VR environment is successfully initialized and <code>false</code> otherwise. |
||||
*/ |
||||
public boolean initialize(){ |
||||
|
||||
logger.config("Initializing VR environment."); |
||||
|
||||
initialized = false; |
||||
|
||||
// we are going to use OpenVR now, not the Oculus Rift
|
||||
// OpenVR does support the Rift
|
||||
String OS = System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH); |
||||
vrSupportedOS = !OS.contains("nux") && System.getProperty("sun.arch.data.model").equalsIgnoreCase("64"); //for the moment, linux/unix causes crashes, 64-bit only
|
||||
compositorOS = OS.contains("indows"); |
||||
|
||||
if( vrSupportedOS) { |
||||
if( vrBinding == VRConstants.SETTING_VRAPI_OSVR_VALUE ) { |
||||
hardware = new OSVR(this); |
||||
initialized = true; |
||||
logger.config("Creating OSVR wrapper [SUCCESS]"); |
||||
} else if( vrBinding == VRConstants.SETTING_VRAPI_OPENVR_VALUE ) { |
||||
hardware = new OpenVR(this); |
||||
initialized = true; |
||||
logger.config("Creating OpenVR wrapper [SUCCESS]"); |
||||
} else { |
||||
logger.config("Cannot create VR binding: "+vrBinding+" [FAILED]"); |
||||
logger.log(Level.SEVERE, "Cannot initialize VR environment [FAILED]"); |
||||
} |
||||
|
||||
if( hardware.initialize() ) { |
||||
initialized &= true; |
||||
logger.config("VR native wrapper initialized [SUCCESS]"); |
||||
} else { |
||||
initialized &= false; |
||||
logger.warning("VR native wrapper initialized [FAILED]"); |
||||
logger.log(Level.SEVERE, "Cannot initialize VR environment [FAILED]"); |
||||
} |
||||
} else { |
||||
logger.log(Level.SEVERE, "System does not support VR capabilities."); |
||||
logger.log(Level.SEVERE, "Cannot initialize VR environment [FAILED]"); |
||||
} |
||||
|
||||
return initialized; |
||||
} |
||||
|
||||
private void processSettings(){ |
||||
if (settings != null){ |
||||
|
||||
if (settings.get(VRConstants.SETTING_USE_COMPOSITOR) != null){ |
||||
useCompositor = settings.getBoolean(VRConstants.SETTING_USE_COMPOSITOR); |
||||
if( useCompositor == false ){ |
||||
disableSwapBuffers = false; |
||||
} |
||||
} |
||||
|
||||
if (settings.get(VRConstants.SETTING_ENABLE_MIRROR_WINDOW) != null){ |
||||
if( useCompositor == false ) { |
||||
disableSwapBuffers = false; |
||||
} else { |
||||
disableSwapBuffers = !settings.getBoolean(VRConstants.SETTING_ENABLE_MIRROR_WINDOW); |
||||
} |
||||
} |
||||
|
||||
if (settings.get(VRConstants.SETTING_GUI_OVERDRAW) != null){ |
||||
getVRGUIManager().setGuiOverdraw(settings.getBoolean(VRConstants.SETTING_GUI_OVERDRAW)); |
||||
} |
||||
|
||||
if (settings.get(VRConstants.SETTING_GUI_CURVED_SURFACE) != null){ |
||||
getVRGUIManager().setCurvedSurface(settings.getBoolean(VRConstants.SETTING_GUI_CURVED_SURFACE)); |
||||
} |
||||
|
||||
if (settings.get(VRConstants.SETTING_NO_GUI) != null){ |
||||
nogui = settings.getBoolean(VRConstants.SETTING_NO_GUI); |
||||
} |
||||
|
||||
if (settings.get(VRConstants.SETTING_VRAPI) != null){ |
||||
vrBinding = settings.getInteger(VRConstants.SETTING_VRAPI); |
||||
} |
||||
|
||||
if (settings.get(VRConstants.SETTING_SEATED_EXPERIENCE) != null){ |
||||
seated = settings.getBoolean(VRConstants.SETTING_SEATED_EXPERIENCE); |
||||
} |
||||
|
||||
if (settings.get(VRConstants.SETTING_INSTANCE_RENDERING) != null){ |
||||
instanceRendering = settings.getBoolean(VRConstants.SETTING_INSTANCE_RENDERING); |
||||
} |
||||
|
||||
if (settings.get(VRConstants.SETTING_DEFAULT_FOV) != null){ |
||||
defaultFOV = settings.getFloat(VRConstants.SETTING_DEFAULT_FOV); |
||||
} |
||||
|
||||
if (settings.get(VRConstants.SETTING_DEFAULT_ASPECT_RATIO) != null){ |
||||
defaultAspect = settings.getFloat(VRConstants.SETTING_DEFAULT_ASPECT_RATIO); |
||||
} |
||||
|
||||
if (settings.get(VRConstants.SETTING_FLIP_EYES) != null){ |
||||
if( getVRHardware() != null ){ |
||||
getVRHardware().setFlipEyes(settings.getBoolean(VRConstants.SETTING_FLIP_EYES)); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,4 +1,4 @@ |
||||
package osvrclientkit; |
||||
package com.jme3.system.osvr.osvrclientkit; |
||||
import com.sun.jna.Callback; |
||||
import com.sun.jna.Library; |
||||
import com.sun.jna.Native; |
@ -1,4 +1,4 @@ |
||||
package osvrclientreporttypes; |
||||
package com.jme3.system.osvr.osvrclientreporttypes; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrclientreporttypes; |
||||
package com.jme3.system.osvr.osvrclientreporttypes; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrclientreporttypes; |
||||
package com.jme3.system.osvr.osvrclientreporttypes; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrclientreporttypes; |
||||
package com.jme3.system.osvr.osvrclientreporttypes; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrclientreporttypes; |
||||
package com.jme3.system.osvr.osvrclientreporttypes; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrclientreporttypes; |
||||
package com.jme3.system.osvr.osvrclientreporttypes; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrclientreporttypes; |
||||
package com.jme3.system.osvr.osvrclientreporttypes; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrclientreporttypes; |
||||
package com.jme3.system.osvr.osvrclientreporttypes; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrclientreporttypes; |
||||
package com.jme3.system.osvr.osvrclientreporttypes; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrclientreporttypes; |
||||
package com.jme3.system.osvr.osvrclientreporttypes; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrclientreporttypes; |
||||
package com.jme3.system.osvr.osvrclientreporttypes; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrclientreporttypes; |
||||
package com.jme3.system.osvr.osvrclientreporttypes; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrclientreporttypes; |
||||
package com.jme3.system.osvr.osvrclientreporttypes; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrclientreporttypes; |
||||
package com.jme3.system.osvr.osvrclientreporttypes; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrclientreporttypes; |
||||
package com.jme3.system.osvr.osvrclientreporttypes; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrclientreporttypes; |
||||
package com.jme3.system.osvr.osvrclientreporttypes; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrclientreporttypes; |
||||
package com.jme3.system.osvr.osvrclientreporttypes; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrclientreporttypes; |
||||
package com.jme3.system.osvr.osvrclientreporttypes; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrclientreporttypes; |
||||
package com.jme3.system.osvr.osvrclientreporttypes; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrclientreporttypes; |
||||
package com.jme3.system.osvr.osvrclientreporttypes; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrclientreporttypes; |
||||
package com.jme3.system.osvr.osvrclientreporttypes; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrmatrixconventions; |
||||
package com.jme3.system.osvr.osvrclientreporttypes; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrclientreporttypes; |
||||
package com.jme3.system.osvr.osvrclientreporttypes; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrclientreporttypes; |
||||
package com.jme3.system.osvr.osvrclientreporttypes; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrclientreporttypes; |
||||
package com.jme3.system.osvr.osvrclientreporttypes; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrclientreporttypes; |
||||
package com.jme3.system.osvr.osvrclientreporttypes; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrclientreporttypes; |
||||
package com.jme3.system.osvr.osvrclientreporttypes; |
||||
import com.sun.jna.Library; |
||||
import com.sun.jna.Native; |
||||
import com.sun.jna.NativeLibrary; |
@ -1,10 +1,10 @@ |
||||
package osvrinterface; |
||||
package com.jme3.system.osvr.osvrinterface; |
||||
import com.jme3.system.osvr.osvrclientkit.OsvrClientKitLibrary.OSVR_ClientInterface; |
||||
import com.jme3.system.osvr.osvrclientreporttypes.OSVR_Pose3; |
||||
import com.jme3.system.osvr.osvrtimevalue.OSVR_TimeValue; |
||||
import com.sun.jna.Library; |
||||
import com.sun.jna.Native; |
||||
import com.sun.jna.NativeLibrary; |
||||
import osvrclientkit.OsvrClientKitLibrary.OSVR_ClientInterface; |
||||
import osvrclientreporttypes.OSVR_Pose3; |
||||
import osvrtimevalue.OSVR_TimeValue; |
||||
/** |
||||
* JNA Wrapper for library <b>osvrInterface</b><br> |
||||
* This file was autogenerated by <a href="http://jnaerator.googlecode.com/">JNAerator</a>,<br> |
@ -1,4 +1,4 @@ |
||||
package osvrmatrixconventions; |
||||
package com.jme3.system.osvr.osvrmatrixconventions; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrclientreporttypes; |
||||
package com.jme3.system.osvr.osvrmatrixconventions; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrmatrixconventions; |
||||
package com.jme3.system.osvr.osvrmatrixconventions; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrmatrixconventions; |
||||
package com.jme3.system.osvr.osvrmatrixconventions; |
||||
import com.sun.jna.Library; |
||||
import com.sun.jna.Native; |
||||
import com.sun.jna.NativeLibrary; |
@ -1,4 +1,4 @@ |
||||
package osvrrendermanager; |
||||
package com.jme3.system.osvr.osvrrendermanager; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrrendermanager; |
||||
package com.jme3.system.osvr.osvrrendermanager; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrrendermanageropengl; |
||||
package com.jme3.system.osvr.osvrrendermanager; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrrendermanager; |
||||
package com.jme3.system.osvr.osvrrendermanager; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrrendermanageropengl; |
||||
package com.jme3.system.osvr.osvrrendermanageropengl; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrrendermanageropengl; |
||||
package com.jme3.system.osvr.osvrrendermanageropengl; |
||||
import com.ochafik.lang.jnaerator.runtime.NativeSize; |
||||
import com.sun.jna.Callback; |
||||
import com.sun.jna.Pointer; |
@ -1,4 +1,4 @@ |
||||
package osvrrendermanageropengl; |
||||
package com.jme3.system.osvr.osvrrendermanageropengl; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrrendermanageropengl; |
||||
package com.jme3.system.osvr.osvrrendermanageropengl; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrrendermanageropengl; |
||||
package com.jme3.system.osvr.osvrrendermanageropengl; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrrendermanageropengl; |
||||
package com.jme3.system.osvr.osvrrendermanageropengl; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,9 +1,9 @@ |
||||
package osvrrendermanageropengl; |
||||
package com.jme3.system.osvr.osvrrendermanageropengl; |
||||
import com.jme3.system.osvr.osvrmatrixconventions.OSVR_Pose3; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
import osvrmatrixconventions.OSVR_Pose3; |
||||
/** |
||||
* This file was autogenerated by <a href="http://jnaerator.googlecode.com/">JNAerator</a>,<br> |
||||
* a tool written by <a href="http://ochafik.com/">Olivier Chafik</a> that <a href="http://code.google.com/p/jnaerator/wiki/CreditsAndLicense">uses a few opensource projects.</a>.<br> |
@ -1,4 +1,4 @@ |
||||
package osvrrendermanager; |
||||
package com.jme3.system.osvr.osvrrendermanageropengl; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrrendermanageropengl; |
||||
package com.jme3.system.osvr.osvrrendermanageropengl; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrtimevalue; |
||||
package com.jme3.system.osvr.osvrtimevalue; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.Structure; |
||||
import java.util.Arrays; |
@ -1,4 +1,4 @@ |
||||
package osvrtimevalue; |
||||
package com.jme3.system.osvr.osvrtimevalue; |
||||
import com.sun.jna.Library; |
||||
import com.sun.jna.Native; |
||||
import com.sun.jna.NativeLibrary; |
@ -0,0 +1,223 @@ |
||||
package com.jme3.util; |
||||
|
||||
import com.jme3.app.VREnvironment; |
||||
import com.jme3.post.CartoonSSAO; |
||||
import com.jme3.post.Filter; |
||||
import com.jme3.post.FilterPostProcessor; |
||||
import com.jme3.post.FilterUtil; |
||||
import com.jme3.post.SceneProcessor; |
||||
import com.jme3.post.filters.FogFilter; |
||||
import com.jme3.post.filters.TranslucentBucketFilter; |
||||
import com.jme3.post.ssao.SSAOFilter; |
||||
import com.jme3.renderer.Camera; |
||||
import com.jme3.renderer.ViewPort; |
||||
import com.jme3.shadow.DirectionalLightShadowFilter; |
||||
import com.jme3.shadow.VRDirectionalLightShadowRenderer; |
||||
import com.jme3.texture.Texture2D; |
||||
|
||||
/** |
||||
* A VR view manager. This class holds methods that enable to submit 3D views to the VR compositor. |
||||
* System dependent classes should extends from this one. |
||||
* @author Julien Seinturier - (c) 2016 - JOrigin project - <a href="http://www.jorigin.org">http:/www.jorigin.org</a> |
||||
*/ |
||||
public abstract class AbstractVRViewManager implements VRViewManager { |
||||
|
||||
|
||||
//private static final Logger logger = Logger.getLogger(AbstractVRViewManager.class.getName());
|
||||
|
||||
protected VREnvironment environment = null; |
||||
|
||||
protected Camera leftCamera; |
||||
protected ViewPort leftViewport; |
||||
protected FilterPostProcessor leftPostProcessor; |
||||
protected Texture2D leftEyeTexture; |
||||
protected Texture2D leftEyeDepth; |
||||
|
||||
protected Camera rightCamera; |
||||
protected ViewPort rightViewport; |
||||
protected FilterPostProcessor rightPostProcessor; |
||||
protected Texture2D rightEyeTexture; |
||||
protected Texture2D rightEyeDepth; |
||||
|
||||
private float resMult = 1f; |
||||
|
||||
private float heightAdjustment; |
||||
|
||||
@Override |
||||
public Camera getLeftCamera() { |
||||
return leftCamera; |
||||
} |
||||
|
||||
@Override |
||||
public Camera getRightCamera() { |
||||
return rightCamera; |
||||
} |
||||
|
||||
@Override |
||||
public ViewPort getLeftViewport() { |
||||
return leftViewport; |
||||
} |
||||
|
||||
@Override |
||||
public ViewPort getRightViewport() { |
||||
return rightViewport; |
||||
} |
||||
|
||||
@Override |
||||
public Texture2D getLeftTexture(){ |
||||
return leftEyeTexture; |
||||
} |
||||
|
||||
@Override |
||||
public Texture2D getRightTexture(){ |
||||
return rightEyeTexture; |
||||
} |
||||
|
||||
@Override |
||||
public Texture2D getLeftDepth(){ |
||||
return leftEyeDepth; |
||||
} |
||||
|
||||
@Override |
||||
public Texture2D getRightDepth(){ |
||||
return rightEyeDepth; |
||||
} |
||||
|
||||
@Override |
||||
public FilterPostProcessor getLeftPostProcessor(){ |
||||
return leftPostProcessor; |
||||
} |
||||
|
||||
@Override |
||||
public FilterPostProcessor getRightPostProcessor(){ |
||||
return rightPostProcessor; |
||||
} |
||||
|
||||
@Override |
||||
public float getResolutionMuliplier() { |
||||
return resMult; |
||||
} |
||||
|
||||
@Override |
||||
public void setResolutionMultiplier(float resMult) { |
||||
this.resMult = resMult; |
||||
} |
||||
|
||||
@Override |
||||
public float getHeightAdjustment() { |
||||
return heightAdjustment; |
||||
} |
||||
|
||||
@Override |
||||
public void setHeightAdjustment(float amount) { |
||||
heightAdjustment = amount; |
||||
} |
||||
|
||||
@Override |
||||
public VREnvironment getVREnvironment(){ |
||||
return environment; |
||||
} |
||||
|
||||
/** |
||||
* Handles moving filters from the main view to each eye |
||||
*/ |
||||
public void moveScreenProcessingToEyes() { |
||||
|
||||
if (environment != null){ |
||||
if( getRightViewport() == null ){ |
||||
return; |
||||
} |
||||
|
||||
if (environment.getApplication() != null){ |
||||
syncScreenProcessing(environment.getApplication().getViewPort()); |
||||
environment.getApplication().getViewPort().clearProcessors(); |
||||
} else { |
||||
throw new IllegalStateException("The VR environment is not attached to any application."); |
||||
} |
||||
|
||||
|
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
|
||||
|
||||
} |
||||
|
||||
/** |
||||
* Sets the two views to use the list of {@link SceneProcessor processors}. |
||||
* @param sourceViewport the {@link ViewPort viewport} that contains the processors to use. |
||||
*/ |
||||
public void syncScreenProcessing(ViewPort sourceViewport) { |
||||
|
||||
if (environment != null){ |
||||
if( getRightViewport() == null ){ |
||||
return; |
||||
} |
||||
|
||||
if (environment.getApplication() != null){ |
||||
// setup post processing filters
|
||||
if( getRightPostProcessor() == null ) { |
||||
rightPostProcessor = new FilterPostProcessor(environment.getApplication().getAssetManager()); |
||||
leftPostProcessor = new FilterPostProcessor(environment.getApplication().getAssetManager()); |
||||
} |
||||
// clear out all filters & processors, to start from scratch
|
||||
getRightPostProcessor().removeAllFilters(); |
||||
getLeftPostProcessor().removeAllFilters(); |
||||
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()); |
||||
// 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()) { |
||||
if (sceneProcessor instanceof FilterPostProcessor) { |
||||
for(Filter f : ((FilterPostProcessor)sceneProcessor).getFilterList() ) { |
||||
if( f instanceof TranslucentBucketFilter ) { |
||||
// just remove this filter, we will add it at the end manually
|
||||
((FilterPostProcessor)sceneProcessor).removeFilter(f); |
||||
} else { |
||||
getLeftPostProcessor().addFilter(f); |
||||
// clone to the right
|
||||
Filter f2; |
||||
if(f instanceof FogFilter){ |
||||
f2 = FilterUtil.cloneFogFilter((FogFilter)f); |
||||
} else if (f instanceof CartoonSSAO ) { |
||||
f2 = new CartoonSSAO((CartoonSSAO)f); |
||||
} else if (f instanceof SSAOFilter){ |
||||
f2 = FilterUtil.cloneSSAOFilter((SSAOFilter)f); |
||||
} else if (f instanceof DirectionalLightShadowFilter){ |
||||
f2 = FilterUtil.cloneDirectionalLightShadowFilter(environment.getApplication().getAssetManager(), (DirectionalLightShadowFilter)f); |
||||
} else { |
||||
f2 = f; // dof, bloom, lightscattering etc.
|
||||
} |
||||
getRightPostProcessor().addFilter(f2); |
||||
} |
||||
} |
||||
} else if (sceneProcessor instanceof VRDirectionalLightShadowRenderer) { |
||||
// shadow processing
|
||||
// TODO: make right shadow processor use same left shadow maps for performance
|
||||
VRDirectionalLightShadowRenderer dlsr = (VRDirectionalLightShadowRenderer) sceneProcessor; |
||||
VRDirectionalLightShadowRenderer dlsrRight = dlsr.clone(); |
||||
dlsrRight.setLight(dlsr.getLight()); |
||||
getRightViewport().getProcessors().add(0, dlsrRight); |
||||
getLeftViewport().getProcessors().add(0, sceneProcessor); |
||||
} |
||||
} |
||||
// make sure each has a translucent filter renderer
|
||||
getLeftPostProcessor().addFilter(new TranslucentBucketFilter()); |
||||
getRightPostProcessor().addFilter(new TranslucentBucketFilter()); |
||||
} else { |
||||
throw new IllegalStateException("The VR environment is not attached to any application."); |
||||
} |
||||
|
||||
|
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
|
||||
|
||||
} |
||||
} |
@ -0,0 +1,15 @@ |
||||
package com.jme3.util; |
||||
|
||||
/** |
||||
* A enumeration that describes the GUI display positioning modes. |
||||
* @author Julien Seinturier - (c) 2016 - JOrigin project - <a href="http://www.jorigin.org">http:/www.jorigin.org</a> |
||||
* |
||||
*/ |
||||
public enum VRGUIPositioningMode { |
||||
MANUAL, |
||||
AUTO_CAM_ALL, |
||||
AUTO_CAM_ALL_SKIP_PITCH, |
||||
AUTO_OBSERVER_POS_CAM_ROTATION, |
||||
AUTO_OBSERVER_ALL, |
||||
AUTO_OBSERVER_ALL_CAMHEIGHT |
||||
} |
@ -0,0 +1,474 @@ |
||||
/* |
||||
* To change this template, choose Tools | Templates |
||||
* and open the template in the editor. |
||||
*/ |
||||
package com.jme3.util; |
||||
|
||||
import com.jme3.app.VREnvironment; |
||||
import com.jme3.material.Material; |
||||
import com.jme3.material.RenderState.BlendMode; |
||||
import com.jme3.math.ColorRGBA; |
||||
import com.jme3.math.Matrix3f; |
||||
import com.jme3.math.Quaternion; |
||||
import com.jme3.math.Vector2f; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.renderer.Camera; |
||||
import com.jme3.renderer.ViewPort; |
||||
import com.jme3.renderer.queue.RenderQueue.Bucket; |
||||
import com.jme3.scene.Spatial; |
||||
import com.jme3.scene.CenterQuad; |
||||
import com.jme3.scene.Geometry; |
||||
import com.jme3.scene.Node; |
||||
import com.jme3.system.AppSettings; |
||||
import com.jme3.texture.FrameBuffer; |
||||
import com.jme3.texture.Image.Format; |
||||
import com.jme3.texture.Texture; |
||||
import com.jme3.texture.Texture2D; |
||||
import java.awt.GraphicsEnvironment; |
||||
import java.util.Iterator; |
||||
|
||||
/** |
||||
* A class dedicated to the management and the display of a Graphical User Interface (GUI) within a VR environment. |
||||
* @author reden - phr00t - https://github.com/phr00t
|
||||
* @author Julien Seinturier - (c) 2016 - JOrigin project - <a href="http://www.jorigin.org">http:/www.jorigin.org</a> |
||||
* |
||||
*/ |
||||
public class VRGuiManager { |
||||
|
||||
private Camera camLeft, camRight; |
||||
private float guiDistance = 1.5f; |
||||
private float guiScale = 1f; |
||||
private float guiPositioningElastic; |
||||
|
||||
private VRGUIPositioningMode posMode = VRGUIPositioningMode.AUTO_CAM_ALL; |
||||
|
||||
private final Matrix3f orient = new Matrix3f(); |
||||
private Vector2f screenSize; |
||||
protected boolean wantsReposition; |
||||
|
||||
private Vector2f ratio; |
||||
|
||||
private final Vector3f EoldPos = new Vector3f(); |
||||
|
||||
private final Quaternion EoldDir = new Quaternion(); |
||||
|
||||
private final Vector3f look = new Vector3f(); |
||||
private final Vector3f left = new Vector3f(); |
||||
private final Vector3f temppos = new Vector3f(); |
||||
private final Vector3f up = new Vector3f(); |
||||
|
||||
private boolean useCurvedSurface = false; |
||||
private boolean overdraw = false; |
||||
private Geometry guiQuad; |
||||
private Node guiQuadNode; |
||||
private ViewPort offView; |
||||
private Texture2D guiTexture; |
||||
|
||||
private final Quaternion tempq = new Quaternion(); |
||||
|
||||
private VREnvironment environment = null; |
||||
|
||||
/** |
||||
* Create a new GUI manager attached to the given app state. |
||||
* @param environment the VR environment to which this manager is attached to. |
||||
*/ |
||||
public VRGuiManager(VREnvironment environment){ |
||||
this.environment = environment; |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* Makes auto GUI positioning happen not immediately, but like an |
||||
* elastic connected to the headset. Setting to 0 disables (default) |
||||
* Higher settings make it track the headset quicker. |
||||
* |
||||
* @param elastic amount of elasticity |
||||
*/ |
||||
public void setPositioningElasticity(float elastic) { |
||||
guiPositioningElastic = elastic; |
||||
} |
||||
|
||||
public float getPositioningElasticity() { |
||||
return guiPositioningElastic; |
||||
} |
||||
|
||||
/** |
||||
* Get the GUI {@link VRGUIPositioningMode positioning mode}. |
||||
* @return the GUI {@link VRGUIPositioningMode positioning mode}. |
||||
* @see #setPositioningMode(VRGUIPositioningMode) |
||||
*/ |
||||
public VRGUIPositioningMode getPositioningMode() { |
||||
return posMode; |
||||
} |
||||
|
||||
/** |
||||
* Set the GUI {@link VRGUIPositioningMode positioning mode}. |
||||
* @param mode the GUI {@link VRGUIPositioningMode positioning mode}. |
||||
* @see #getPositioningMode() |
||||
*/ |
||||
public void setPositioningMode(VRGUIPositioningMode mode) { |
||||
posMode = mode; |
||||
} |
||||
|
||||
/** |
||||
* Get the GUI canvas size. This method return the size in pixels of the GUI available area within the VR view. |
||||
* @return the GUI canvas size. This method return the size in pixels of the GUI available area within the VR view. |
||||
*/ |
||||
public Vector2f getCanvasSize() { |
||||
|
||||
if (environment != null){ |
||||
|
||||
if (environment.getApplication() != null){ |
||||
if( screenSize == null ) { |
||||
if( environment.isInVR() && environment.getVRHardware() != null ) { |
||||
screenSize = new Vector2f(); |
||||
environment.getVRHardware().getRenderSize(screenSize); |
||||
screenSize.multLocal(environment.getVRViewManager().getResolutionMuliplier()); |
||||
} else { |
||||
AppSettings as = environment.getApplication().getContext().getSettings(); |
||||
screenSize = new Vector2f(as.getWidth(), as.getHeight()); |
||||
} |
||||
} |
||||
return screenSize; |
||||
} else { |
||||
throw new IllegalStateException("VR GUI manager underlying environment is not attached to any application."); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("VR GUI manager is not attached to any environment."); |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Get the ratio between the {@link #getCanvasSize() GUI canvas size} and the application main windows (if available) or the screen size. |
||||
* @return the ratio between the {@link #getCanvasSize() GUI canvas size} and the application main windows (if available). |
||||
* @see #getCanvasSize() |
||||
*/ |
||||
public Vector2f getCanvasToWindowRatio() { |
||||
|
||||
if (environment != null){ |
||||
|
||||
if (environment.getApplication() != null){ |
||||
if( ratio == null ) { |
||||
ratio = new Vector2f(); |
||||
Vector2f canvas = getCanvasSize(); |
||||
int width = Integer.min(GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode().getWidth(), |
||||
environment.getApplication().getContext().getSettings().getWidth()); |
||||
int height = Integer.min(GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode().getHeight(), |
||||
environment.getApplication().getContext().getSettings().getHeight()); |
||||
ratio.x = Float.max(1f, canvas.x / width); |
||||
ratio.y = Float.max(1f, canvas.y / height); |
||||
} |
||||
return ratio; |
||||
|
||||
} else { |
||||
throw new IllegalStateException("VR GUI manager underlying environment is not attached to any application."); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("VR GUI manager is not attached to any environment."); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Inform this manager that it has to position the GUI. |
||||
*/ |
||||
public void positionGui() { |
||||
wantsReposition = true; |
||||
} |
||||
|
||||
/** |
||||
* Position the GUI to the given location. |
||||
* @param pos the position of the GUI. |
||||
* @param dir the rotation of the GUI. |
||||
* @param tpf the time per frame. |
||||
*/ |
||||
private void positionTo(Vector3f pos, Quaternion dir, float tpf) { |
||||
|
||||
if (environment != null){ |
||||
Vector3f guiPos = guiQuadNode.getLocalTranslation(); |
||||
guiPos.set(0f, 0f, guiDistance); |
||||
dir.mult(guiPos, guiPos); |
||||
guiPos.x += pos.x; |
||||
guiPos.y += pos.y + environment.getVRHeightAdjustment(); |
||||
guiPos.z += pos.z; |
||||
if( guiPositioningElastic > 0f && posMode != VRGUIPositioningMode.MANUAL ) { |
||||
// mix pos & dir with current pos & dir
|
||||
guiPos.interpolateLocal(EoldPos, guiPos, Float.min(1f, tpf * guiPositioningElastic)); |
||||
EoldPos.set(guiPos); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("VR GUI manager is not attached to any environment."); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Update the GUI geometric state. This method should be called after GUI modification. |
||||
*/ |
||||
protected void updateGuiQuadGeometricState() { |
||||
guiQuadNode.updateGeometricState(); |
||||
} |
||||
|
||||
/** |
||||
* Position the GUI without delay. |
||||
* @param tpf the time per frame. |
||||
*/ |
||||
protected void positionGuiNow(float tpf) { |
||||
|
||||
if (environment != null){ |
||||
wantsReposition = false; |
||||
if( environment.isInVR() == false ){ |
||||
return; |
||||
} |
||||
|
||||
guiQuadNode.setLocalScale(guiDistance * guiScale * 4f, 4f * guiDistance * guiScale, 1f); |
||||
|
||||
switch( posMode ) { |
||||
case MANUAL: |
||||
case AUTO_CAM_ALL_SKIP_PITCH: |
||||
case AUTO_CAM_ALL: |
||||
if( camLeft != null && camRight != null ) { |
||||
// get middle point
|
||||
temppos.set(camLeft.getLocation()).interpolateLocal(camRight.getLocation(), 0.5f); |
||||
positionTo(temppos, camLeft.getRotation(), tpf); |
||||
} |
||||
rotateScreenTo(camLeft.getRotation(), tpf); |
||||
|
||||
break; |
||||
case AUTO_OBSERVER_POS_CAM_ROTATION: |
||||
Object obs = environment.getObserver(); |
||||
if( obs != null ) { |
||||
if( obs instanceof Camera ) { |
||||
positionTo(((Camera)obs).getLocation(), camLeft.getRotation(), tpf); |
||||
} else { |
||||
positionTo(((Spatial)obs).getWorldTranslation(), camLeft.getRotation(), tpf); |
||||
} |
||||
} |
||||
rotateScreenTo(camLeft.getRotation(), tpf); |
||||
|
||||
break; |
||||
case AUTO_OBSERVER_ALL: |
||||
case AUTO_OBSERVER_ALL_CAMHEIGHT: |
||||
obs = environment.getObserver(); |
||||
if( obs != null ) { |
||||
Quaternion q; |
||||
if( obs instanceof Camera ) { |
||||
q = ((Camera)obs).getRotation(); |
||||
temppos.set(((Camera)obs).getLocation()); |
||||
} else { |
||||
q = ((Spatial)obs).getWorldRotation(); |
||||
temppos.set(((Spatial)obs).getWorldTranslation()); |
||||
} |
||||
if( posMode == VRGUIPositioningMode.AUTO_OBSERVER_ALL_CAMHEIGHT ) { |
||||
temppos.y = camLeft.getLocation().y; |
||||
} |
||||
positionTo(temppos, q, tpf); |
||||
rotateScreenTo(q, tpf); |
||||
|
||||
} |
||||
break; |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("VR GUI manager is not attached to any environment."); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Rotate the GUI to the given direction. |
||||
* @param dir the direction to rotate to. |
||||
* @param tpf the time per frame. |
||||
*/ |
||||
private void rotateScreenTo(Quaternion dir, float tpf) { |
||||
dir.getRotationColumn(2, look).negateLocal(); |
||||
dir.getRotationColumn(0, left).negateLocal(); |
||||
orient.fromAxes(left, dir.getRotationColumn(1, up), look); |
||||
Quaternion rot = tempq.fromRotationMatrix(orient); |
||||
if( posMode == VRGUIPositioningMode.AUTO_CAM_ALL_SKIP_PITCH ){ |
||||
VRUtil.stripToYaw(rot); |
||||
} |
||||
|
||||
if( guiPositioningElastic > 0f && posMode != VRGUIPositioningMode.MANUAL ) { |
||||
// mix pos & dir with current pos & dir
|
||||
EoldDir.nlerp(rot, tpf * guiPositioningElastic); |
||||
guiQuadNode.setLocalRotation(EoldDir); |
||||
} else { |
||||
guiQuadNode.setLocalRotation(rot); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Get the GUI distance from the observer. |
||||
* @return the GUI distance from the observer. |
||||
* @see #setGuiDistance(float) |
||||
*/ |
||||
public float getGuiDistance() { |
||||
return guiDistance; |
||||
} |
||||
|
||||
/** |
||||
* Set the GUI distance from the observer. |
||||
* @param newGuiDistance the GUI distance from the observer. |
||||
* @see #getGuiDistance() |
||||
*/ |
||||
public void setGuiDistance(float newGuiDistance) { |
||||
guiDistance = newGuiDistance; |
||||
} |
||||
|
||||
/** |
||||
* Get the GUI scale. |
||||
* @return the GUI scale. |
||||
* @see #setGuiScale(float) |
||||
*/ |
||||
public float getGUIScale(){ |
||||
return guiScale; |
||||
} |
||||
|
||||
/** |
||||
* Set the GUI scale. |
||||
* @param scale the GUI scale. |
||||
* @see #getGUIScale() |
||||
*/ |
||||
public void setGuiScale(float scale) { |
||||
guiScale = scale; |
||||
} |
||||
|
||||
/** |
||||
* Adjust the GUI distance from the observer. |
||||
* This method increment / decrement the {@link #getGuiDistance() GUI distance} by the given value. |
||||
* @param adjustAmount the increment (if positive) / decrement (if negative) value of the GUI distance. |
||||
*/ |
||||
public void adjustGuiDistance(float adjustAmount) { |
||||
guiDistance += adjustAmount; |
||||
} |
||||
|
||||
/** |
||||
* Set up the GUI. |
||||
* @param leftcam the left eye camera. |
||||
* @param rightcam the right eye camera. |
||||
* @param left the left eye viewport. |
||||
* @param right the right eye viewport. |
||||
*/ |
||||
protected void setupGui(Camera leftcam, Camera rightcam, ViewPort left, ViewPort right) { |
||||
|
||||
if (environment != null){ |
||||
if( environment.hasTraditionalGUIOverlay() ) { |
||||
camLeft = leftcam; |
||||
camRight = rightcam; |
||||
Spatial guiScene = getGuiQuad(camLeft); |
||||
left.attachScene(guiScene); |
||||
if( right != null ) right.attachScene(guiScene); |
||||
setPositioningMode(posMode); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("VR GUI manager is not attached to any environment."); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Get if the GUI has to use curved surface. |
||||
* @return <code>true</code> if the GUI has to use curved surface and <code>false</code> otherwise. |
||||
* @see #setCurvedSurface(boolean) |
||||
*/ |
||||
public boolean isCurverSurface(){ |
||||
return useCurvedSurface; |
||||
} |
||||
|
||||
/** |
||||
* Set if the GUI has to use curved surface. |
||||
* @param set <code>true</code> if the GUI has to use curved surface and <code>false</code> otherwise. |
||||
* @see #isCurverSurface() |
||||
*/ |
||||
public void setCurvedSurface(boolean set) { |
||||
useCurvedSurface = set; |
||||
} |
||||
|
||||
/** |
||||
* Get if the GUI has to be displayed even if it is behind objects. |
||||
* @return <code>true</code> if the GUI has to use curved surface and <code>false</code> otherwise. |
||||
* @see #setGuiOverdraw(boolean) |
||||
*/ |
||||
public boolean isGuiOverdraw(){ |
||||
return overdraw; |
||||
} |
||||
|
||||
/** |
||||
* Set if the GUI has to be displayed even if it is behind objects. |
||||
* @param set <code>true</code> if the GUI has to use curved surface and <code>false</code> otherwise. |
||||
* @see #isGuiOverdraw() |
||||
*/ |
||||
public void setGuiOverdraw(boolean set) { |
||||
overdraw = set; |
||||
} |
||||
|
||||
/** |
||||
* Create a GUI quad for the given camera. |
||||
* @param sourceCam the camera |
||||
* @return a GUI quad for the given camera. |
||||
*/ |
||||
private Spatial getGuiQuad(Camera sourceCam){ |
||||
|
||||
if (environment != null){ |
||||
|
||||
if (environment.getApplication() != null){ |
||||
if( guiQuadNode == null ) { |
||||
Vector2f guiCanvasSize = getCanvasSize(); |
||||
Camera offCamera = sourceCam.clone(); |
||||
offCamera.setParallelProjection(true); |
||||
offCamera.setLocation(Vector3f.ZERO); |
||||
offCamera.lookAt(Vector3f.UNIT_Z, Vector3f.UNIT_Y); |
||||
|
||||
offView = environment.getApplication().getRenderManager().createPreView("GUI View", offCamera); |
||||
offView.setClearFlags(true, true, true); |
||||
offView.setBackgroundColor(ColorRGBA.BlackNoAlpha); |
||||
|
||||
// create offscreen framebuffer
|
||||
FrameBuffer offBuffer = new FrameBuffer((int)guiCanvasSize.x, (int)guiCanvasSize.y, 1); |
||||
|
||||
//setup framebuffer's texture
|
||||
guiTexture = new Texture2D((int)guiCanvasSize.x, (int)guiCanvasSize.y, Format.RGBA8); |
||||
guiTexture.setMinFilter(Texture.MinFilter.BilinearNoMipMaps); |
||||
guiTexture.setMagFilter(Texture.MagFilter.Bilinear); |
||||
|
||||
//setup framebuffer to use texture
|
||||
offBuffer.setDepthBuffer(Format.Depth); |
||||
offBuffer.setColorTexture(guiTexture); |
||||
|
||||
//set viewport to render to offscreen framebuffer
|
||||
offView.setOutputFrameBuffer(offBuffer); |
||||
|
||||
// setup framebuffer's scene
|
||||
Iterator<Spatial> spatialIter = environment.getApplication().getGuiViewPort().getScenes().iterator(); |
||||
while(spatialIter.hasNext()){ |
||||
offView.attachScene(spatialIter.next()); |
||||
} |
||||
|
||||
|
||||
if( useCurvedSurface ) { |
||||
guiQuad = (Geometry)environment.getApplication().getAssetManager().loadModel("Common/Util/gui_mesh.j3o"); |
||||
} else { |
||||
guiQuad = new Geometry("guiQuad", new CenterQuad(1f, 1f)); |
||||
} |
||||
|
||||
Material mat = new Material(environment.getApplication().getAssetManager(), "Common/MatDefs/VR/GuiOverlay.j3md"); |
||||
mat.getAdditionalRenderState().setDepthTest(!overdraw); |
||||
mat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha); |
||||
mat.getAdditionalRenderState().setDepthWrite(false); |
||||
mat.setTexture("ColorMap", guiTexture); |
||||
guiQuad.setQueueBucket(Bucket.Translucent); |
||||
guiQuad.setMaterial(mat); |
||||
|
||||
guiQuadNode = new Node("gui-quad-node"); |
||||
guiQuadNode.setQueueBucket(Bucket.Translucent); |
||||
guiQuadNode.attachChild(guiQuad); |
||||
} |
||||
return guiQuadNode; |
||||
} else { |
||||
throw new IllegalStateException("VR GUI manager underlying environment is not attached to any application."); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("VR GUI manager is not attached to any environment."); |
||||
} |
||||
|
||||
|
||||
|
||||
} |
||||
} |
@ -0,0 +1,334 @@ |
||||
/* |
||||
* 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.util; |
||||
|
||||
import java.util.logging.Logger; |
||||
|
||||
import org.lwjgl.glfw.GLFW; |
||||
|
||||
import com.jme3.app.VREnvironment; |
||||
import com.jme3.input.MouseInput; |
||||
import com.jme3.input.controls.AnalogListener; |
||||
import com.jme3.input.lwjgl.GlfwMouseInputVR; |
||||
import com.jme3.input.vr.VRInputType; |
||||
import com.jme3.material.RenderState.BlendMode; |
||||
import com.jme3.math.Vector2f; |
||||
import com.jme3.scene.Node; |
||||
import com.jme3.system.AppSettings; |
||||
import com.jme3.system.lwjgl.LwjglWindow; |
||||
import com.jme3.texture.Texture; |
||||
import com.jme3.texture.Texture2D; |
||||
import com.jme3.ui.Picture; |
||||
|
||||
/** |
||||
* A class dedicated to the handling of the mouse within VR environment. |
||||
* @author Julien Seinturier - (c) 2016 - JOrigin project - <a href="http://www.jorigin.org">http:/www.jorigin.org</a> |
||||
* |
||||
*/ |
||||
public class VRMouseManager { |
||||
|
||||
private static final Logger logger = Logger.getLogger(VRMouseManager.class.getName()); |
||||
|
||||
|
||||
private VREnvironment environment = null; |
||||
|
||||
private final int AVERAGE_AMNT = 4; |
||||
private int avgCounter; |
||||
|
||||
private Picture mouseImage; |
||||
private int recentCenterCount = 0; |
||||
private final Vector2f cursorPos = new Vector2f(); |
||||
private float ySize, sensitivity = 8f, acceleration = 2f; |
||||
private final float[] lastXmv = new float[AVERAGE_AMNT], lastYmv = new float[AVERAGE_AMNT]; |
||||
private boolean thumbstickMode; |
||||
private float moveScale = 1f; |
||||
|
||||
private float avg(float[] arr) { |
||||
float amt = 0f; |
||||
for(float f : arr) amt += f; |
||||
return amt / arr.length; |
||||
} |
||||
|
||||
/** |
||||
* Create a new VR mouse manager within the given {@link VREnvironment VR environment}. |
||||
* @param environment the VR environment of the mouse manager. |
||||
*/ |
||||
public VRMouseManager(VREnvironment environment){ |
||||
this.environment = environment; |
||||
} |
||||
|
||||
/** |
||||
* Initialize the VR mouse manager. |
||||
*/ |
||||
protected void initialize() { |
||||
|
||||
logger.config("Initializing VR mouse manager."); |
||||
|
||||
// load default mouseimage
|
||||
mouseImage = new Picture("mouse"); |
||||
setImage("Common/Util/mouse.png"); |
||||
// hide default cursor by making it invisible
|
||||
|
||||
MouseInput mi = environment.getApplication().getContext().getMouseInput(); |
||||
if( mi instanceof GlfwMouseInputVR ){ |
||||
((GlfwMouseInputVR)mi).hideActiveCursor(); |
||||
} |
||||
centerMouse(); |
||||
|
||||
logger.config("Initialized VR mouse manager [SUCCESS]"); |
||||
} |
||||
|
||||
public void setThumbstickMode(boolean set) { |
||||
thumbstickMode = set; |
||||
} |
||||
|
||||
public boolean isThumbstickMode() { |
||||
return thumbstickMode; |
||||
} |
||||
|
||||
/** |
||||
* Set the speed of the mouse. |
||||
* @param sensitivity the sensitivity of the mouse. |
||||
* @param acceleration the acceleration of the mouse. |
||||
* @see #getSpeedAcceleration() |
||||
* @see #getSpeedSensitivity() |
||||
*/ |
||||
public void setSpeed(float sensitivity, float acceleration) { |
||||
this.sensitivity = sensitivity; |
||||
this.acceleration = acceleration; |
||||
} |
||||
|
||||
/** |
||||
* Get the sensitivity of the mouse. |
||||
* @return the sensitivity of the mouse. |
||||
* @see #setSpeed(float, float) |
||||
*/ |
||||
public float getSpeedSensitivity() { |
||||
return sensitivity; |
||||
} |
||||
|
||||
/** |
||||
* Get the acceleration of the mouse. |
||||
* @return the acceleration of the mouse. |
||||
* @see #setSpeed(float, float) |
||||
*/ |
||||
public float getSpeedAcceleration() { |
||||
return acceleration; |
||||
} |
||||
|
||||
/** |
||||
* Set the mouse move scale. |
||||
* @param set the mouse move scale. |
||||
*/ |
||||
public void setMouseMoveScale(float set) { |
||||
moveScale = set; |
||||
} |
||||
|
||||
/** |
||||
* Set the image to use as mouse cursor. The given string describe an asset that the underlying application asset manager has to load. |
||||
* @param texture the image to use as mouse cursor. |
||||
*/ |
||||
public void setImage(String texture) { |
||||
|
||||
if (environment != null){ |
||||
|
||||
if (environment.getApplication() != null){ |
||||
if( environment.isInVR() == false ){ |
||||
Texture tex = environment.getApplication().getAssetManager().loadTexture(texture); |
||||
mouseImage.setTexture(environment.getApplication().getAssetManager(), (Texture2D)tex, true); |
||||
ySize = tex.getImage().getHeight(); |
||||
mouseImage.setHeight(ySize); |
||||
mouseImage.setWidth(tex.getImage().getWidth()); |
||||
mouseImage.getMaterial().getAdditionalRenderState().setBlendMode(BlendMode.Alpha); |
||||
mouseImage.getMaterial().getAdditionalRenderState().setDepthWrite(false); |
||||
} else { |
||||
Texture tex = environment.getApplication().getAssetManager().loadTexture(texture); |
||||
mouseImage.setTexture(environment.getApplication().getAssetManager(), (Texture2D)tex, true); |
||||
ySize = tex.getImage().getHeight(); |
||||
mouseImage.setHeight(ySize); |
||||
mouseImage.setWidth(tex.getImage().getWidth()); |
||||
mouseImage.getMaterial().getAdditionalRenderState().setBlendMode(BlendMode.Alpha); |
||||
mouseImage.getMaterial().getAdditionalRenderState().setDepthWrite(false); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR environment is not attached to any application."); |
||||
} |
||||
|
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Update analog controller as it was a mouse controller. |
||||
* @param inputIndex the index of the controller attached to the VR system. |
||||
* @param mouseListener the JMonkey mouse listener to trigger. |
||||
* @param mouseXName the mouseX identifier. |
||||
* @param mouseYName the mouseY identifier |
||||
* @param tpf the time per frame. |
||||
*/ |
||||
public void updateAnalogAsMouse(int inputIndex, AnalogListener mouseListener, String mouseXName, String mouseYName, float tpf) { |
||||
|
||||
if (environment != null){ |
||||
if (environment.getApplication() != null){ |
||||
// got a tracked controller to use as the "mouse"
|
||||
if( environment.isInVR() == false || |
||||
environment.getVRinput() == null || |
||||
environment.getVRinput().isInputDeviceTracking(inputIndex) == false ){ |
||||
return; |
||||
} |
||||
|
||||
Vector2f tpDelta; |
||||
if( thumbstickMode ) { |
||||
tpDelta = environment.getVRinput().getAxis(inputIndex, VRInputType.ViveTrackpadAxis); |
||||
} else { |
||||
tpDelta = environment.getVRinput().getAxisDeltaSinceLastCall(inputIndex, VRInputType.ViveTrackpadAxis); |
||||
} |
||||
|
||||
float Xamount = (float)Math.pow(Math.abs(tpDelta.x) * sensitivity, acceleration); |
||||
float Yamount = (float)Math.pow(Math.abs(tpDelta.y) * sensitivity, acceleration); |
||||
|
||||
if( tpDelta.x < 0f ){ |
||||
Xamount = -Xamount; |
||||
} |
||||
|
||||
if( tpDelta.y < 0f ){ |
||||
Yamount = -Yamount; |
||||
} |
||||
|
||||
Xamount *= moveScale; Yamount *= moveScale; |
||||
if( mouseListener != null ) { |
||||
if( tpDelta.x != 0f && mouseXName != null ) mouseListener.onAnalog(mouseXName, Xamount * 0.2f, tpf); |
||||
if( tpDelta.y != 0f && mouseYName != null ) mouseListener.onAnalog(mouseYName, Yamount * 0.2f, tpf); |
||||
} |
||||
|
||||
if( environment.getApplication().getInputManager().isCursorVisible() ) { |
||||
int index = (avgCounter+1) % AVERAGE_AMNT; |
||||
lastXmv[index] = Xamount * 133f; |
||||
lastYmv[index] = Yamount * 133f; |
||||
cursorPos.x -= avg(lastXmv); |
||||
cursorPos.y -= avg(lastYmv); |
||||
Vector2f maxsize = environment.getVRGUIManager().getCanvasSize(); |
||||
|
||||
if( cursorPos.x > maxsize.x ){ |
||||
cursorPos.x = maxsize.x; |
||||
} |
||||
|
||||
if( cursorPos.x < 0f ){ |
||||
cursorPos.x = 0f; |
||||
} |
||||
|
||||
if( cursorPos.y > maxsize.y ){ |
||||
cursorPos.y = maxsize.y; |
||||
} |
||||
|
||||
if( cursorPos.y < 0f ){ |
||||
cursorPos.y = 0f; |
||||
} |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR environment is not attached to any application."); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Get the actual cursor position. |
||||
* @return the actual cursor position. |
||||
*/ |
||||
public Vector2f getCursorPosition() { |
||||
|
||||
if (environment != null){ |
||||
if (environment.getApplication() != null){ |
||||
if( environment.isInVR() ) { |
||||
return cursorPos; |
||||
} |
||||
|
||||
return environment.getApplication().getInputManager().getCursorPosition(); |
||||
} else { |
||||
throw new IllegalStateException("This VR environment is not attached to any application."); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Center the mouse on the display. |
||||
*/ |
||||
public void centerMouse() { |
||||
|
||||
if (environment != null){ |
||||
if (environment.getApplication() != null){ |
||||
// set mouse in center of the screen if newly added
|
||||
Vector2f size = environment.getVRGUIManager().getCanvasSize(); |
||||
MouseInput mi = environment.getApplication().getContext().getMouseInput(); |
||||
AppSettings as = environment.getApplication().getContext().getSettings(); |
||||
if( mi instanceof GlfwMouseInputVR ) ((GlfwMouseInputVR)mi).setCursorPosition((int)(as.getWidth() / 2f), (int)(as.getHeight() / 2f)); |
||||
if( environment.isInVR() ) { |
||||
cursorPos.x = size.x / 2f; |
||||
cursorPos.y = size.y / 2f; |
||||
recentCenterCount = 2; |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR environment is not attached to any application."); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Update the mouse manager. This method should not be called manually. |
||||
* The standard behavior for this method is to be called from the {@link VRViewManager#update(float) update method} of the attached {@link VRViewManager VR view manager}. |
||||
* @param tpf the time per frame. |
||||
*/ |
||||
protected void update(float tpf) { |
||||
// if we are showing the cursor, add our picture as it
|
||||
|
||||
if( environment.getApplication().getInputManager().isCursorVisible() ) { |
||||
if( mouseImage.getParent() == null ) { |
||||
|
||||
environment.getApplication().getGuiViewPort().attachScene(mouseImage); |
||||
centerMouse(); |
||||
// the "real" mouse pointer should stay hidden
|
||||
if (environment.getApplication().getContext() instanceof LwjglWindow){ |
||||
GLFW.glfwSetInputMode(((LwjglWindow)environment.getApplication().getContext()).getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_DISABLED); |
||||
} |
||||
} |
||||
// handle mouse movements, which may be in addition to (or exclusive from) tracked movement
|
||||
MouseInput mi = environment.getApplication().getContext().getMouseInput(); |
||||
if( mi instanceof GlfwMouseInputVR ) { |
||||
if( recentCenterCount <= 0 ) { |
||||
//Vector2f winratio = VRGuiManager.getCanvasToWindowRatio();
|
||||
cursorPos.x += ((GlfwMouseInputVR)mi).getLastDeltaX();// * winratio.x;
|
||||
cursorPos.y += ((GlfwMouseInputVR)mi).getLastDeltaY();// * winratio.y;
|
||||
if( cursorPos.x < 0f ) cursorPos.x = 0f; |
||||
if( cursorPos.y < 0f ) cursorPos.y = 0f; |
||||
if( cursorPos.x > environment.getVRGUIManager().getCanvasSize().x ) cursorPos.x = environment.getVRGUIManager().getCanvasSize().x; |
||||
if( cursorPos.y > environment.getVRGUIManager().getCanvasSize().y ) cursorPos.y = environment.getVRGUIManager().getCanvasSize().y; |
||||
} else recentCenterCount--; |
||||
((GlfwMouseInputVR)mi).clearDeltas(); |
||||
} |
||||
// ok, update the cursor graphic position
|
||||
Vector2f currentPos = getCursorPosition(); |
||||
mouseImage.setLocalTranslation(currentPos.x, currentPos.y - ySize, environment.getVRGUIManager().getGuiDistance() + 1f); |
||||
|
||||
mouseImage.updateGeometricState(); |
||||
|
||||
} else if( mouseImage.getParent() != null ) { |
||||
Node n = mouseImage.getParent(); |
||||
mouseImage.removeFromParent(); |
||||
|
||||
if (n != null){ |
||||
n.updateGeometricState(); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,156 @@ |
||||
package com.jme3.util; |
||||
|
||||
import com.jme3.app.VRAppState; |
||||
import com.jme3.app.VREnvironment; |
||||
import com.jme3.app.state.AppState; |
||||
import com.jme3.post.FilterPostProcessor; |
||||
import com.jme3.renderer.Camera; |
||||
import com.jme3.renderer.ViewPort; |
||||
import com.jme3.texture.Texture2D; |
||||
|
||||
/** |
||||
* A VR view manager. This interface describes methods that enable to submit 3D views to the VR compositor. |
||||
* @author reden - phr00t - https://github.com/phr00t
|
||||
* @author Julien Seinturier - (c) 2016 - JOrigin project - <a href="http://www.jorigin.org">http:/www.jorigin.org</a> |
||||
*/ |
||||
public interface VRViewManager { |
||||
|
||||
/** |
||||
* The name of the left view. |
||||
*/ |
||||
public final static String LEFT_VIEW_NAME = "Left View"; |
||||
|
||||
/** |
||||
* The name of the right view. |
||||
*/ |
||||
public final static String RIGHT_VIEW_NAME = "Right View"; |
||||
|
||||
/** |
||||
* Get the {@link Camera camera} attached to the left eye. |
||||
* @return the {@link Camera camera} attached to the left eye. |
||||
* @see #getRightCamera() |
||||
*/ |
||||
public Camera getLeftCamera(); |
||||
|
||||
/** |
||||
* Get the {@link Camera camera} attached to the right eye. |
||||
* @return the {@link Camera camera} attached to the right eye. |
||||
* @see #getLeftCamera() |
||||
*/ |
||||
public Camera getRightCamera(); |
||||
|
||||
/** |
||||
* Get the {@link ViewPort viewport} attached to the left eye. |
||||
* @return the {@link ViewPort viewport} attached to the left eye. |
||||
* @see #getRightViewport() |
||||
*/ |
||||
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() |
||||
*/ |
||||
public ViewPort getRightViewport(); |
||||
|
||||
/** |
||||
* Get the texture attached to the left eye. |
||||
* @return the texture attached to the left eye. |
||||
* @see #getRightTexture() |
||||
*/ |
||||
public Texture2D getLeftTexture(); |
||||
|
||||
/** |
||||
* Get the texture attached to the right eye. |
||||
* @return the texture attached to the right eye. |
||||
* @see #getLeftTexture() |
||||
*/ |
||||
public Texture2D getRightTexture(); |
||||
|
||||
/** |
||||
* Get the depth texture attached to the left eye. |
||||
* @return the texture attached to the left eye. |
||||
* @see #getRightTexture() |
||||
*/ |
||||
public Texture2D getLeftDepth(); |
||||
|
||||
/** |
||||
* Get the depth texture attached to the right eye. |
||||
* @return the texture attached to the right eye. |
||||
* @see #getLeftTexture() |
||||
*/ |
||||
public Texture2D getRightDepth(); |
||||
|
||||
/** |
||||
* Get the {@link FilterPostProcessor filter post processor} attached to the left eye. |
||||
* @return the {@link FilterPostProcessor filter post processor} attached to the left eye. |
||||
* @see #getRightPostProcessor() |
||||
*/ |
||||
public FilterPostProcessor getLeftPostProcessor(); |
||||
|
||||
/** |
||||
* Get the {@link FilterPostProcessor filter post processor} attached to the right eye. |
||||
* @return the {@link FilterPostProcessor filter post processor} attached to the right eye. |
||||
* @see #getLeftPostProcessor() |
||||
*/ |
||||
public FilterPostProcessor getRightPostProcessor(); |
||||
|
||||
/** |
||||
* Get the resolution multiplier. |
||||
* @return the resolution multiplier. |
||||
* @see #setResolutionMultiplier(float) |
||||
*/ |
||||
public float getResolutionMuliplier(); |
||||
|
||||
/** |
||||
* Set the resolution multiplier. |
||||
* @param resMult the resolution multiplier. |
||||
* @see #getResolutionMuliplier() |
||||
*/ |
||||
public void setResolutionMultiplier(float resMult); |
||||
|
||||
/** |
||||
* Get the height adjustment to apply to the cameras before rendering. |
||||
* @return the height adjustment to apply to the cameras before rendering. |
||||
* @see #setHeightAdjustment(float) |
||||
*/ |
||||
public float getHeightAdjustment(); |
||||
|
||||
/** |
||||
* Set the height adjustment to apply to the cameras before rendering. |
||||
* @param amount the height adjustment to apply to the cameras before rendering. |
||||
* @see #getHeightAdjustment() |
||||
*/ |
||||
public void setHeightAdjustment(float amount); |
||||
|
||||
/** |
||||
* Get the {@link VREnvironment VR environment} to which the view manager is attached. |
||||
* @return the {@link VREnvironment VR environment} to which the view manager is attached. |
||||
*/ |
||||
public VREnvironment getVREnvironment(); |
||||
|
||||
/** |
||||
* Initialize the VR view manager. This method should be called after the attachment of a {@link VREnvironment VR environment} to an application. |
||||
*/ |
||||
public void initialize(); |
||||
|
||||
/** |
||||
* Update the VR view manager. |
||||
* This method is called by the attached {@link VRAppState app state} and should not be called manually. |
||||
* @param tpf the time per frame. |
||||
*/ |
||||
public void update(float tpf); |
||||
|
||||
/** |
||||
* Send the rendering result as textures to the two eyes. |
||||
* This method should be called after all the rendering operations |
||||
* (for example at the end of the {@link AppState#postRender() postRender()} method of the attached app state.) |
||||
*/ |
||||
public void postRender(); |
||||
|
||||
/** |
||||
* Handles moving filters from the main view to each eye. |
||||
*/ |
||||
public void moveScreenProcessingToEyes(); |
||||
} |
@ -0,0 +1,957 @@ |
||||
package com.jme3.util; |
||||
|
||||
import java.awt.GraphicsEnvironment; |
||||
import java.util.Iterator; |
||||
import java.util.logging.Logger; |
||||
|
||||
import com.jme3.app.VREnvironment; |
||||
import com.jme3.input.vr.OSVR; |
||||
import com.jme3.input.vr.VRAPI; |
||||
import com.jme3.material.Material; |
||||
import com.jme3.math.ColorRGBA; |
||||
import com.jme3.math.Quaternion; |
||||
import com.jme3.math.Vector2f; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.post.CartoonSSAO; |
||||
import com.jme3.post.Filter; |
||||
import com.jme3.post.FilterPostProcessor; |
||||
import com.jme3.post.FilterUtil; |
||||
import com.jme3.post.SceneProcessor; |
||||
import com.jme3.post.filters.FogFilter; |
||||
import com.jme3.post.filters.TranslucentBucketFilter; |
||||
import com.jme3.post.ssao.SSAOFilter; |
||||
import com.jme3.renderer.Camera; |
||||
import com.jme3.renderer.ViewPort; |
||||
import com.jme3.renderer.queue.RenderQueue.Bucket; |
||||
import com.jme3.scene.Geometry; |
||||
import com.jme3.scene.Mesh; |
||||
import com.jme3.scene.Node; |
||||
import com.jme3.scene.Spatial; |
||||
import com.jme3.scene.VertexBuffer; |
||||
import com.jme3.shadow.DirectionalLightShadowFilter; |
||||
import com.jme3.shadow.VRDirectionalLightShadowRenderer; |
||||
import com.jme3.system.jopenvr.DistortionCoordinates_t; |
||||
import com.jme3.system.jopenvr.JOpenVRLibrary; |
||||
import com.jme3.system.jopenvr.OpenVRUtil; |
||||
import com.jme3.system.jopenvr.Texture_t; |
||||
import com.jme3.system.jopenvr.VR_IVRSystem_FnTable; |
||||
import com.jme3.system.lwjgl.LwjglWindow; |
||||
import com.jme3.system.osvr.osvrrendermanageropengl.OSVR_RenderBufferOpenGL; |
||||
import com.jme3.system.osvr.osvrrendermanageropengl.OSVR_ViewportDescription; |
||||
import com.jme3.system.osvr.osvrrendermanageropengl.OsvrRenderManagerOpenGLLibrary; |
||||
import com.jme3.texture.FrameBuffer; |
||||
import com.jme3.texture.Image; |
||||
import com.jme3.texture.Texture; |
||||
import com.jme3.texture.Texture2D; |
||||
import com.jme3.ui.Picture; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.ptr.PointerByReference; |
||||
|
||||
public class VRViewManagerOSVR extends AbstractVRViewManager{ |
||||
private static final Logger logger = Logger.getLogger(VRViewManagerOpenVR.class.getName()); |
||||
|
||||
private Camera leftCamera; |
||||
private ViewPort leftViewport; |
||||
private FilterPostProcessor leftPostProcessor; |
||||
private Texture2D leftEyeTexture; |
||||
private Texture2D leftEyeDepth; |
||||
|
||||
private Camera rightCamera; |
||||
private ViewPort rightViewport; |
||||
private FilterPostProcessor rightPostProcessor; |
||||
private Texture2D rightEyeTexture; |
||||
private Texture2D rightEyeDepth; |
||||
|
||||
// OpenVR values
|
||||
private Texture_t leftTextureType; |
||||
private Texture_t rightTextureType; |
||||
|
||||
// OSVR values
|
||||
OSVR_RenderBufferOpenGL.ByValue[] osvr_renderBuffer; |
||||
OSVR_ViewportDescription.ByValue osvr_viewDescFull; |
||||
OSVR_ViewportDescription.ByValue osvr_viewDescLeft; |
||||
OSVR_ViewportDescription.ByValue osvr_viewDescRight; |
||||
Pointer osvr_rmBufferState; |
||||
|
||||
//private static boolean useCustomDistortion;
|
||||
private float heightAdjustment; |
||||
|
||||
private Texture2D dualEyeTex; |
||||
|
||||
private final PointerByReference grabRBS = new PointerByReference(); |
||||
|
||||
private float resMult = 1f; |
||||
|
||||
//final & temp values for camera calculations
|
||||
private final Vector3f finalPosition = new Vector3f(); |
||||
private final Quaternion finalRotation = new Quaternion(); |
||||
private final Vector3f hmdPos = new Vector3f(); |
||||
private final Quaternion hmdRot = new Quaternion(); |
||||
|
||||
/** |
||||
* Create a new VR view manager attached to the given {@link VREnvironment VR environment}. |
||||
* @param environment the {@link VREnvironment VR environment} to which this view manager is attached. |
||||
*/ |
||||
public VRViewManagerOSVR(VREnvironment environment){ |
||||
this.environment = environment; |
||||
} |
||||
|
||||
/** |
||||
* Get the {@link Camera camera} attached to the left eye. |
||||
* @return the {@link Camera camera} attached to the left eye. |
||||
* @see #getRightCamera() |
||||
*/ |
||||
public Camera getLeftCamera() { |
||||
return leftCamera; |
||||
} |
||||
|
||||
/** |
||||
* Get the {@link Camera camera} attached to the right eye. |
||||
* @return the {@link Camera camera} attached to the right eye. |
||||
* @see #getLeftCamera() |
||||
*/ |
||||
public Camera getRightCamera() { |
||||
return rightCamera; |
||||
} |
||||
|
||||
/** |
||||
* Get the {@link ViewPort viewport} attached to the left eye. |
||||
* @return the {@link ViewPort viewport} attached to the left eye. |
||||
* @see #getRightViewport() |
||||
*/ |
||||
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() |
||||
*/ |
||||
public ViewPort getRightViewport() { |
||||
return rightViewport; |
||||
} |
||||
|
||||
/** |
||||
* Get the identifier of the left eye texture. |
||||
* @return the identifier of the left eye texture. |
||||
* @see #getRightTexId() |
||||
* @see #getFullTexId() |
||||
*/ |
||||
protected int getLeftTexId() { |
||||
return (int)leftEyeTexture.getImage().getId(); |
||||
} |
||||
|
||||
/** |
||||
* Get the identifier of the right eye texture. |
||||
* @return the identifier of the right eye texture. |
||||
* @see #getLeftTexId() |
||||
* @see #getFullTexId() |
||||
*/ |
||||
protected int getRightTexId() { |
||||
return (int)rightEyeTexture.getImage().getId(); |
||||
} |
||||
|
||||
/** |
||||
* Get the identifier of the full (dual eye) texture. |
||||
* @return the identifier of the full (dual eye) texture. |
||||
* @see #getLeftTexId() |
||||
* @see #getRightTexId() |
||||
*/ |
||||
private int getFullTexId() { |
||||
return (int)dualEyeTex.getImage().getId(); |
||||
} |
||||
|
||||
/** |
||||
* Get the height adjustment to apply to the cameras before rendering. |
||||
* @return the height adjustment to apply to the cameras before rendering. |
||||
* @see #setHeightAdjustment(float) |
||||
*/ |
||||
public float getHeightAdjustment() { |
||||
return heightAdjustment; |
||||
} |
||||
|
||||
/** |
||||
* Set the height adjustment to apply to the cameras before rendering. |
||||
* @param amount the height adjustment to apply to the cameras before rendering. |
||||
* @see #getHeightAdjustment() |
||||
*/ |
||||
public void setHeightAdjustment(float amount) { |
||||
heightAdjustment = amount; |
||||
} |
||||
|
||||
/** |
||||
* Get the resolution multiplier. |
||||
* @return the resolution multiplier. |
||||
* @see #setResolutionMultiplier(float) |
||||
*/ |
||||
public float getResolutionMuliplier() { |
||||
return resMult; |
||||
} |
||||
|
||||
/** |
||||
* Set the resolution multiplier. |
||||
* @param resMult the resolution multiplier. |
||||
* @see #getResolutionMuliplier() |
||||
*/ |
||||
public void setResolutionMultiplier(float resMult) { |
||||
this.resMult = resMult; |
||||
} |
||||
|
||||
/** |
||||
* Initialize the system binds of the textures. |
||||
*/ |
||||
private void initTextureSubmitStructs() { |
||||
leftTextureType = new Texture_t(); |
||||
rightTextureType = new Texture_t(); |
||||
|
||||
// must be OSVR
|
||||
osvr_renderBuffer = new OSVR_RenderBufferOpenGL.ByValue[2]; |
||||
osvr_renderBuffer[OSVR.EYE_LEFT] = new OSVR_RenderBufferOpenGL.ByValue(); |
||||
osvr_renderBuffer[OSVR.EYE_RIGHT] = new OSVR_RenderBufferOpenGL.ByValue(); |
||||
osvr_renderBuffer[OSVR.EYE_LEFT].setAutoSynch(false); |
||||
osvr_renderBuffer[OSVR.EYE_RIGHT].setAutoSynch(false); |
||||
osvr_viewDescFull = new OSVR_ViewportDescription.ByValue(); |
||||
osvr_viewDescFull.setAutoSynch(false); |
||||
osvr_viewDescFull.left = osvr_viewDescFull.lower = 0.0; |
||||
osvr_viewDescFull.width = osvr_viewDescFull.height = 1.0; |
||||
osvr_viewDescLeft = new OSVR_ViewportDescription.ByValue(); |
||||
osvr_viewDescLeft.setAutoSynch(false); |
||||
osvr_viewDescLeft.left = osvr_viewDescLeft.lower = 0.0; |
||||
osvr_viewDescLeft.width = 0.5; |
||||
osvr_viewDescLeft.height = 1.0; |
||||
osvr_viewDescRight = new OSVR_ViewportDescription.ByValue(); |
||||
osvr_viewDescRight.setAutoSynch(false); |
||||
osvr_viewDescRight.left = 0.5; |
||||
osvr_viewDescRight.lower = 0.0; |
||||
osvr_viewDescRight.width = 0.5; |
||||
osvr_viewDescRight.height = 1.0; |
||||
osvr_viewDescRight.write(); |
||||
osvr_viewDescLeft.write(); |
||||
osvr_viewDescFull.write(); |
||||
osvr_renderBuffer[OSVR.EYE_LEFT].depthStencilBufferName = -1; |
||||
osvr_renderBuffer[OSVR.EYE_LEFT].colorBufferName = -1; |
||||
osvr_renderBuffer[OSVR.EYE_RIGHT].depthStencilBufferName = -1; |
||||
osvr_renderBuffer[OSVR.EYE_RIGHT].colorBufferName = -1; |
||||
} |
||||
|
||||
/** |
||||
* Register the OSVR OpenGL buffer. |
||||
* @param buf the OSVR OpenGL buffer. |
||||
*/ |
||||
private void registerOSVRBuffer(OSVR_RenderBufferOpenGL.ByValue buf) { |
||||
|
||||
if (environment != null){ |
||||
OsvrRenderManagerOpenGLLibrary.osvrRenderManagerStartRegisterRenderBuffers(grabRBS); |
||||
OsvrRenderManagerOpenGLLibrary.osvrRenderManagerRegisterRenderBufferOpenGL(grabRBS.getValue(), buf); |
||||
OsvrRenderManagerOpenGLLibrary.osvrRenderManagerFinishRegisterRenderBuffers(((OSVR)environment.getVRHardware()).getCompositor(), grabRBS.getValue(), (byte)0); |
||||
|
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Send the textures to the two eyes. |
||||
*/ |
||||
public void postRender() { |
||||
|
||||
if (environment != null){ |
||||
if( environment.isInVR() ) { |
||||
VRAPI api = environment.getVRHardware(); |
||||
if( api.getCompositor() != null ) { |
||||
// using the compositor...
|
||||
int errl = 0, errr = 0; |
||||
if( environment.isInstanceRendering() ) { |
||||
if( leftTextureType.handle == -1 || leftTextureType.handle != getFullTexId() ) { |
||||
leftTextureType.handle = getFullTexId(); |
||||
if( leftTextureType.handle != -1 ) { |
||||
leftTextureType.write(); |
||||
if( api instanceof OSVR ) { |
||||
osvr_renderBuffer[OSVR.EYE_LEFT].colorBufferName = leftTextureType.handle; |
||||
osvr_renderBuffer[OSVR.EYE_LEFT].depthStencilBufferName = dualEyeTex.getImage().getId(); |
||||
osvr_renderBuffer[OSVR.EYE_LEFT].write(); |
||||
registerOSVRBuffer(osvr_renderBuffer[OSVR.EYE_LEFT]); |
||||
} |
||||
} |
||||
} else { |
||||
if( api instanceof OSVR ) { |
||||
((OSVR)api).handleRenderBufferPresent(osvr_viewDescLeft, osvr_viewDescRight, |
||||
osvr_renderBuffer[OSVR.EYE_LEFT], osvr_renderBuffer[OSVR.EYE_LEFT]); |
||||
} |
||||
} |
||||
} else if( leftTextureType.handle == -1 || rightTextureType.handle == -1 || |
||||
leftTextureType.handle != getLeftTexId() || rightTextureType.handle != getRightTexId() ) { |
||||
leftTextureType.handle = getLeftTexId(); |
||||
if( leftTextureType.handle != -1 ) { |
||||
logger.fine("Writing Left texture to native memory at " + leftTextureType.getPointer()); |
||||
leftTextureType.write(); |
||||
if( api instanceof OSVR ) { |
||||
osvr_renderBuffer[OSVR.EYE_LEFT].colorBufferName = leftTextureType.handle; |
||||
if( leftEyeDepth != null ) osvr_renderBuffer[OSVR.EYE_LEFT].depthStencilBufferName = leftEyeDepth.getImage().getId(); |
||||
osvr_renderBuffer[OSVR.EYE_LEFT].write(); |
||||
registerOSVRBuffer(osvr_renderBuffer[OSVR.EYE_LEFT]); |
||||
} |
||||
} |
||||
rightTextureType.handle = getRightTexId(); |
||||
if( rightTextureType.handle != -1 ) { |
||||
logger.fine("Writing Right texture to native memory at " + leftTextureType.getPointer()); |
||||
rightTextureType.write(); |
||||
if( api instanceof OSVR ) { |
||||
osvr_renderBuffer[OSVR.EYE_RIGHT].colorBufferName = rightTextureType.handle; |
||||
if( rightEyeDepth != null ) osvr_renderBuffer[OSVR.EYE_RIGHT].depthStencilBufferName = rightEyeDepth.getImage().getId(); |
||||
osvr_renderBuffer[OSVR.EYE_RIGHT].write(); |
||||
registerOSVRBuffer(osvr_renderBuffer[OSVR.EYE_RIGHT]); |
||||
} |
||||
} |
||||
} else { |
||||
if( api instanceof OSVR ) { |
||||
((OSVR)api).handleRenderBufferPresent(osvr_viewDescFull, osvr_viewDescFull, |
||||
osvr_renderBuffer[OSVR.EYE_LEFT], osvr_renderBuffer[OSVR.EYE_RIGHT]); |
||||
} |
||||
} |
||||
|
||||
if( errl != 0 ){ |
||||
logger.severe("Submit to left compositor error: " + OpenVRUtil.getEVRCompositorErrorString(errl)+" ("+Integer.toString(errl)+")"); |
||||
logger.severe(" Texture color space: "+OpenVRUtil.getEColorSpaceString(leftTextureType.eColorSpace)); |
||||
logger.severe(" Texture type: "+OpenVRUtil.getETextureTypeString(leftTextureType.eType)); |
||||
logger.severe(" Texture handle: "+leftTextureType.handle); |
||||
|
||||
logger.severe(" Left eye texture "+leftEyeTexture.getName()+" ("+leftEyeTexture.getImage().getId()+")"); |
||||
logger.severe(" Type: "+leftEyeTexture.getType()); |
||||
logger.severe(" Size: "+leftEyeTexture.getImage().getWidth()+"x"+leftEyeTexture.getImage().getHeight()); |
||||
logger.severe(" Image depth: "+leftEyeTexture.getImage().getDepth()); |
||||
logger.severe(" Image format: "+leftEyeTexture.getImage().getFormat()); |
||||
logger.severe(" Image color space: "+leftEyeTexture.getImage().getColorSpace()); |
||||
|
||||
} |
||||
|
||||
if( errr != 0 ){ |
||||
logger.severe("Submit to right compositor error: " + OpenVRUtil.getEVRCompositorErrorString(errl)+" ("+Integer.toString(errl)+")"); |
||||
logger.severe(" Texture color space: "+OpenVRUtil.getEColorSpaceString(rightTextureType.eColorSpace)); |
||||
logger.severe(" Texture type: "+OpenVRUtil.getETextureTypeString(rightTextureType.eType)); |
||||
logger.severe(" Texture handle: "+rightTextureType.handle); |
||||
|
||||
logger.severe(" Right eye texture "+rightEyeTexture.getName()+" ("+rightEyeTexture.getImage().getId()+")"); |
||||
logger.severe(" Type: "+rightEyeTexture.getType()); |
||||
logger.severe(" Size: "+rightEyeTexture.getImage().getWidth()+"x"+rightEyeTexture.getImage().getHeight()); |
||||
logger.severe(" Image depth: "+rightEyeTexture.getImage().getDepth()); |
||||
logger.severe(" Image format: "+rightEyeTexture.getImage().getFormat()); |
||||
logger.severe(" Image color space: "+rightEyeTexture.getImage().getColorSpace()); |
||||
} |
||||
} |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Initialize the VR view manager. |
||||
*/ |
||||
public void initialize() { |
||||
|
||||
logger.config("Initializing VR view manager."); |
||||
|
||||
if (environment != null){ |
||||
initTextureSubmitStructs(); |
||||
setupCamerasAndViews(); |
||||
setupVRScene(); |
||||
moveScreenProcessingToEyes(); |
||||
if( environment.hasTraditionalGUIOverlay() ) { |
||||
|
||||
environment.getVRMouseManager().initialize(); |
||||
|
||||
// update the pose to position the gui correctly on start
|
||||
update(0f); |
||||
environment.getVRGUIManager().positionGui(); |
||||
} |
||||
|
||||
if (environment.getApplication() != null){ |
||||
// if we are OSVR, our primary mirror window needs to be the same size as the render manager's output...
|
||||
if( environment.getVRHardware() instanceof OSVR ) { |
||||
int origWidth = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode().getWidth(); |
||||
int origHeight = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode().getHeight(); |
||||
long window = ((LwjglWindow)environment.getApplication().getContext()).getWindowHandle(); |
||||
Vector2f windowSize = new Vector2f(); |
||||
((OSVR)environment.getVRHardware()).getRenderSize(windowSize); |
||||
windowSize.x = Math.max(windowSize.x * 2f, leftCamera.getWidth()); |
||||
org.lwjgl.glfw.GLFW.glfwSetWindowSize(window, (int)windowSize.x, (int)windowSize.y); |
||||
environment.getApplication().getContext().getSettings().setResolution((int)windowSize.x, (int)windowSize.y); |
||||
|
||||
if (environment.getApplication().getRenderManager() != null) { |
||||
environment.getApplication().getRenderManager().notifyReshape((int)windowSize.x, (int)windowSize.y); |
||||
} |
||||
|
||||
org.lwjgl.glfw.GLFW.glfwSetWindowPos(window, origWidth - (int)windowSize.x, 32); |
||||
|
||||
org.lwjgl.glfw.GLFW.glfwFocusWindow(window); |
||||
|
||||
org.lwjgl.glfw.GLFW.glfwSetCursorPos(window, origWidth / 2.0, origHeight / 2.0); |
||||
|
||||
logger.config("Initialized VR view manager [SUCCESS]"); |
||||
} else { |
||||
throw new IllegalStateException("Underlying VR hardware should be "+OSVR.class.getSimpleName()); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR environment is not attached to any application."); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
|
||||
|
||||
} |
||||
|
||||
/** |
||||
* Prepare the size of the given {@link Camera camera} to adapt it to the underlying rendering context. |
||||
* @param cam the {@link Camera camera} to prepare. |
||||
* @param xMult the camera width multiplier. |
||||
*/ |
||||
private void prepareCameraSize(Camera cam, float xMult) { |
||||
|
||||
if (environment != null){ |
||||
|
||||
if (environment.getApplication() != null){ |
||||
|
||||
} else { |
||||
throw new IllegalStateException("This VR environment is not attached to any application."); |
||||
} |
||||
|
||||
Vector2f size = new Vector2f(); |
||||
VRAPI vrhmd = environment.getVRHardware(); |
||||
|
||||
if( vrhmd == null ) { |
||||
size.x = 1280f; |
||||
size.y = 720f; |
||||
} else { |
||||
vrhmd.getRenderSize(size); |
||||
} |
||||
|
||||
if( size.x < environment.getApplication().getContext().getSettings().getWidth() ) { |
||||
size.x = environment.getApplication().getContext().getSettings().getWidth(); |
||||
} |
||||
if( size.y < environment.getApplication().getContext().getSettings().getHeight() ) { |
||||
size.y = environment.getApplication().getContext().getSettings().getHeight(); |
||||
} |
||||
|
||||
if( environment.isInstanceRendering() ){ |
||||
size.x *= 2f; |
||||
} |
||||
|
||||
// other adjustments
|
||||
size.x *= xMult; |
||||
size.x *= resMult; |
||||
size.y *= resMult; |
||||
|
||||
if( cam.getWidth() != size.x || cam.getHeight() != size.y ){ |
||||
cam.resize((int)size.x, (int)size.y, false); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Replaces rootNode as the main cameras scene with the distortion mesh |
||||
*/ |
||||
private void setupVRScene(){ |
||||
|
||||
if (environment != null){ |
||||
if (environment.getApplication() != null){ |
||||
// no special scene to setup if we are doing instancing
|
||||
if( environment.isInstanceRendering() ) { |
||||
// distortion has to be done with compositor here... we want only one pass on our end!
|
||||
if( environment.getApplication().getContext().getSettings().isSwapBuffers() ) { |
||||
setupMirrorBuffers(environment.getCamera(), dualEyeTex, true); |
||||
} |
||||
return; |
||||
} |
||||
|
||||
leftEyeTexture = (Texture2D) leftViewport.getOutputFrameBuffer().getColorBuffer().getTexture(); |
||||
rightEyeTexture = (Texture2D)rightViewport.getOutputFrameBuffer().getColorBuffer().getTexture(); |
||||
leftEyeDepth = (Texture2D) leftViewport.getOutputFrameBuffer().getDepthBuffer().getTexture(); |
||||
rightEyeDepth = (Texture2D)rightViewport.getOutputFrameBuffer().getDepthBuffer().getTexture(); |
||||
|
||||
// main viewport is either going to be a distortion scene or nothing
|
||||
// mirroring is handled by copying framebuffers
|
||||
Iterator<Spatial> spatialIter = environment.getApplication().getViewPort().getScenes().iterator(); |
||||
while(spatialIter.hasNext()){ |
||||
environment.getApplication().getViewPort().detachScene(spatialIter.next()); |
||||
} |
||||
|
||||
spatialIter = environment.getApplication().getGuiViewPort().getScenes().iterator(); |
||||
while(spatialIter.hasNext()){ |
||||
environment.getApplication().getGuiViewPort().detachScene(spatialIter.next()); |
||||
} |
||||
|
||||
// only setup distortion scene if compositor isn't running (or using custom mesh distortion option)
|
||||
if( environment.getVRHardware().getCompositor() == null ) { |
||||
Node distortionScene = new Node(); |
||||
Material leftMat = new Material(environment.getApplication().getAssetManager(), "Common/MatDefs/VR/OpenVR.j3md"); |
||||
leftMat.setTexture("Texture", leftEyeTexture); |
||||
Geometry leftEye = new Geometry("box", setupDistortionMesh(JOpenVRLibrary.EVREye.EVREye_Eye_Left, environment.getVRHardware())); |
||||
leftEye.setMaterial(leftMat); |
||||
distortionScene.attachChild(leftEye); |
||||
|
||||
Material rightMat = new Material(environment.getApplication().getAssetManager(), "Common/MatDefs/VR/OpenVR.j3md"); |
||||
rightMat.setTexture("Texture", rightEyeTexture); |
||||
Geometry rightEye = new Geometry("box", setupDistortionMesh(JOpenVRLibrary.EVREye.EVREye_Eye_Right, environment.getVRHardware())); |
||||
rightEye.setMaterial(rightMat); |
||||
distortionScene.attachChild(rightEye); |
||||
|
||||
distortionScene.updateGeometricState(); |
||||
|
||||
environment.getApplication().getViewPort().attachScene(distortionScene); |
||||
|
||||
//if( useCustomDistortion ) setupFinalFullTexture(app.getViewPort().getCamera());
|
||||
} |
||||
|
||||
if( environment.getApplication().getContext().getSettings().isSwapBuffers() ) { |
||||
setupMirrorBuffers(environment.getCamera(), leftEyeTexture, false); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR environment is not attached to any application."); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
|
||||
|
||||
} |
||||
|
||||
/** |
||||
* Update the VR view manager. |
||||
* This method is called by the attached {@link VRApplication VR application} and should not be called manually. |
||||
* @param tpf the time per frame. |
||||
*/ |
||||
public void update(float tpf) { |
||||
|
||||
if (environment != null){ |
||||
// grab the observer
|
||||
Object obs = environment.getObserver(); |
||||
Quaternion objRot; |
||||
Vector3f objPos; |
||||
if( obs instanceof Camera ) { |
||||
objRot = ((Camera)obs).getRotation(); |
||||
objPos = ((Camera)obs).getLocation(); |
||||
} else { |
||||
objRot = ((Spatial)obs).getWorldRotation(); |
||||
objPos = ((Spatial)obs).getWorldTranslation(); |
||||
} |
||||
// grab the hardware handle
|
||||
VRAPI dev = environment.getVRHardware(); |
||||
if( dev != null ) { |
||||
// update the HMD's position & orientation
|
||||
dev.updatePose(); |
||||
dev.getPositionAndOrientation(hmdPos, hmdRot); |
||||
if( obs != null ) { |
||||
// update hmdPos based on obs rotation
|
||||
finalRotation.set(objRot); |
||||
finalRotation.mult(hmdPos, hmdPos); |
||||
finalRotation.multLocal(hmdRot); |
||||
} |
||||
|
||||
finalizeCamera(dev.getHMDVectorPoseLeftEye(), objPos, leftCamera); |
||||
finalizeCamera(dev.getHMDVectorPoseRightEye(), objPos, rightCamera); |
||||
} else { |
||||
leftCamera.setFrame(objPos, objRot); |
||||
rightCamera.setFrame(objPos, objRot); |
||||
} |
||||
|
||||
if( environment.hasTraditionalGUIOverlay() ) { |
||||
// update the mouse?
|
||||
environment.getVRMouseManager().update(tpf); |
||||
|
||||
// update GUI position?
|
||||
if( environment.getVRGUIManager().wantsReposition || environment.getVRGUIManager().getPositioningMode() != VRGUIPositioningMode.MANUAL ) { |
||||
environment.getVRGUIManager().positionGuiNow(tpf); |
||||
environment.getVRGUIManager().updateGuiQuadGeometricState(); |
||||
} |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Place the camera within the scene. |
||||
* @param eyePos the eye position. |
||||
* @param obsPosition the observer position. |
||||
* @param cam the camera to place. |
||||
*/ |
||||
private void finalizeCamera(Vector3f eyePos, Vector3f obsPosition, Camera cam) { |
||||
finalRotation.mult(eyePos, finalPosition); |
||||
finalPosition.addLocal(hmdPos); |
||||
if( obsPosition != null ){ |
||||
finalPosition.addLocal(obsPosition); |
||||
} |
||||
finalPosition.y += heightAdjustment; |
||||
cam.setFrame(finalPosition, finalRotation); |
||||
} |
||||
|
||||
/** |
||||
* Handles moving filters from the main view to each eye |
||||
*/ |
||||
public void moveScreenProcessingToEyes() { |
||||
if( rightViewport == null ){ |
||||
return; |
||||
} |
||||
|
||||
if (environment != null){ |
||||
if (environment.getApplication() != null){ |
||||
|
||||
syncScreenProcessing(environment.getApplication().getViewPort()); |
||||
environment.getApplication().getViewPort().clearProcessors(); |
||||
} else { |
||||
throw new IllegalStateException("This VR environment is not attached to any application."); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Sets the two views to use the list of {@link SceneProcessor processors}. |
||||
* @param sourceViewport the {@link ViewPort viewport} that contains the processors to use. |
||||
*/ |
||||
public void syncScreenProcessing(ViewPort sourceViewport) { |
||||
if( rightViewport == null ){ |
||||
return; |
||||
} |
||||
|
||||
if (environment != null){ |
||||
if (environment.getApplication() != null){ |
||||
// setup post processing filters
|
||||
if( rightPostProcessor == null ) { |
||||
rightPostProcessor = new FilterPostProcessor(environment.getApplication().getAssetManager()); |
||||
leftPostProcessor = new FilterPostProcessor(environment.getApplication().getAssetManager()); |
||||
} |
||||
// clear out all filters & processors, to start from scratch
|
||||
rightPostProcessor.removeAllFilters(); |
||||
leftPostProcessor.removeAllFilters(); |
||||
leftViewport.clearProcessors(); |
||||
rightViewport.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
|
||||
leftViewport.addProcessor(leftPostProcessor); |
||||
rightViewport.addProcessor(rightPostProcessor); |
||||
// 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()) { |
||||
if (sceneProcessor instanceof FilterPostProcessor) { |
||||
for(Filter f : ((FilterPostProcessor)sceneProcessor).getFilterList() ) { |
||||
if( f instanceof TranslucentBucketFilter ) { |
||||
// just remove this filter, we will add it at the end manually
|
||||
((FilterPostProcessor)sceneProcessor).removeFilter(f); |
||||
} else { |
||||
leftPostProcessor.addFilter(f); |
||||
// clone to the right
|
||||
Filter f2; |
||||
if(f instanceof FogFilter){ |
||||
f2 = FilterUtil.cloneFogFilter((FogFilter)f); |
||||
} else if (f instanceof CartoonSSAO ) { |
||||
f2 = new CartoonSSAO((CartoonSSAO)f); |
||||
} else if (f instanceof SSAOFilter){ |
||||
f2 = FilterUtil.cloneSSAOFilter((SSAOFilter)f); |
||||
} else if (f instanceof DirectionalLightShadowFilter){ |
||||
f2 = FilterUtil.cloneDirectionalLightShadowFilter(environment.getApplication().getAssetManager(), (DirectionalLightShadowFilter)f); |
||||
} else { |
||||
f2 = f; // dof, bloom, lightscattering etc.
|
||||
} |
||||
rightPostProcessor.addFilter(f2); |
||||
} |
||||
} |
||||
} else if (sceneProcessor instanceof VRDirectionalLightShadowRenderer) { |
||||
// shadow processing
|
||||
// TODO: make right shadow processor use same left shadow maps for performance
|
||||
VRDirectionalLightShadowRenderer dlsr = (VRDirectionalLightShadowRenderer) sceneProcessor; |
||||
VRDirectionalLightShadowRenderer dlsrRight = dlsr.clone(); |
||||
dlsrRight.setLight(dlsr.getLight()); |
||||
rightViewport.getProcessors().add(0, dlsrRight); |
||||
leftViewport.getProcessors().add(0, sceneProcessor); |
||||
} |
||||
} |
||||
// make sure each has a translucent filter renderer
|
||||
leftPostProcessor.addFilter(new TranslucentBucketFilter()); |
||||
rightPostProcessor.addFilter(new TranslucentBucketFilter()); |
||||
} else { |
||||
throw new IllegalStateException("This VR environment is not attached to any application."); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
} |
||||
|
||||
private void setupCamerasAndViews() { |
||||
|
||||
if (environment != null){ |
||||
if (environment.getApplication() != null){ |
||||
// get desired frustrum from original camera
|
||||
Camera origCam = environment.getCamera(); |
||||
float fFar = origCam.getFrustumFar(); |
||||
float fNear = origCam.getFrustumNear(); |
||||
|
||||
// if we are using OSVR get the eye info here
|
||||
if( environment.getVRHardware() instanceof OSVR ) { |
||||
((OSVR)environment.getVRHardware()).getEyeInfo(); |
||||
} |
||||
|
||||
// restore frustrum on distortion scene cam, if needed
|
||||
if( environment.isInstanceRendering() ) { |
||||
leftCamera = origCam; |
||||
} else if( environment.compositorAllowed() == false ) { |
||||
origCam.setFrustumFar(100f); |
||||
origCam.setFrustumNear(1f); |
||||
leftCamera = origCam.clone(); |
||||
prepareCameraSize(origCam, 2f); |
||||
} else { |
||||
leftCamera = origCam.clone(); |
||||
} |
||||
|
||||
leftCamera.setFrustumPerspective(environment.getDefaultFOV(), environment.getDefaultAspect(), fNear, fFar); |
||||
|
||||
prepareCameraSize(leftCamera, 1f); |
||||
if( environment.getVRHardware() != null ) leftCamera.setProjectionMatrix(environment.getVRHardware().getHMDMatrixProjectionLeftEye(leftCamera)); |
||||
//org.lwjgl.opengl.GL11.glEnable(org.lwjgl.opengl.GL30.GL_FRAMEBUFFER_SRGB);
|
||||
|
||||
if( !environment.isInstanceRendering()) { |
||||
leftViewport = setupViewBuffers(leftCamera, LEFT_VIEW_NAME); |
||||
rightCamera = leftCamera.clone(); |
||||
if( environment.getVRHardware() != null ){ |
||||
rightCamera.setProjectionMatrix(environment.getVRHardware().getHMDMatrixProjectionRightEye(rightCamera)); |
||||
} |
||||
rightViewport = setupViewBuffers(rightCamera, RIGHT_VIEW_NAME); |
||||
} else { |
||||
|
||||
System.err.println("[VRViewManager] THIS CODE NEED CHANGES !!!"); |
||||
leftViewport = environment.getApplication().getViewPort(); |
||||
//leftViewport.attachScene(app.getRootNode());
|
||||
rightCamera = leftCamera.clone(); |
||||
if( environment.getVRHardware() != null ){ |
||||
rightCamera.setProjectionMatrix(environment.getVRHardware().getHMDMatrixProjectionRightEye(rightCamera)); |
||||
} |
||||
|
||||
org.lwjgl.opengl.GL11.glEnable(org.lwjgl.opengl.GL30.GL_CLIP_DISTANCE0); |
||||
|
||||
//FIXME: [jme-vr] Fix with JMonkey next release
|
||||
//RenderManager._VRInstancing_RightCamProjection = camRight.getViewProjectionMatrix();
|
||||
setupFinalFullTexture(environment.getApplication().getViewPort().getCamera()); |
||||
} |
||||
|
||||
// setup gui
|
||||
environment.getVRGUIManager().setupGui(leftCamera, rightCamera, leftViewport, rightViewport); |
||||
|
||||
if( environment.getVRHardware() != null ) { |
||||
// call these to cache the results internally
|
||||
environment.getVRHardware().getHMDMatrixPoseLeftEye(); |
||||
environment.getVRHardware().getHMDMatrixPoseRightEye(); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR environment is not attached to any application."); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
} |
||||
|
||||
private ViewPort setupMirrorBuffers(Camera cam, Texture tex, boolean expand) { |
||||
|
||||
if (environment != null){ |
||||
if (environment.getApplication() != null){ |
||||
Camera clonecam = cam.clone(); |
||||
ViewPort viewPort = environment.getApplication().getRenderManager().createPostView("MirrorView", clonecam); |
||||
clonecam.setParallelProjection(true); |
||||
viewPort.setClearFlags(true, true, true); |
||||
viewPort.setBackgroundColor(ColorRGBA.Black); |
||||
Picture pic = new Picture("fullscene"); |
||||
pic.setLocalTranslation(-0.75f, -0.5f, 0f); |
||||
if( expand ) { |
||||
pic.setLocalScale(3f, 1f, 1f); |
||||
} else { |
||||
pic.setLocalScale(1.5f, 1f, 1f); |
||||
} |
||||
pic.setQueueBucket(Bucket.Opaque); |
||||
pic.setTexture(environment.getApplication().getAssetManager(), (Texture2D)tex, false); |
||||
viewPort.attachScene(pic); |
||||
viewPort.setOutputFrameBuffer(null); |
||||
|
||||
pic.updateGeometricState(); |
||||
|
||||
return viewPort; |
||||
} else { |
||||
throw new IllegalStateException("This VR environment is not attached to any application."); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
} |
||||
|
||||
private void setupFinalFullTexture(Camera cam) { |
||||
|
||||
if (environment != null){ |
||||
if (environment.getApplication() != null){ |
||||
// create offscreen framebuffer
|
||||
FrameBuffer out = new FrameBuffer(cam.getWidth(), cam.getHeight(), 1); |
||||
//offBuffer.setSrgb(true);
|
||||
|
||||
//setup framebuffer's texture
|
||||
dualEyeTex = new Texture2D(cam.getWidth(), cam.getHeight(), Image.Format.RGBA8); |
||||
dualEyeTex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps); |
||||
dualEyeTex.setMagFilter(Texture.MagFilter.Bilinear); |
||||
|
||||
logger.config("Dual eye texture "+dualEyeTex.getName()+" ("+dualEyeTex.getImage().getId()+")"); |
||||
logger.config(" Type: "+dualEyeTex.getType()); |
||||
logger.config(" Size: "+dualEyeTex.getImage().getWidth()+"x"+dualEyeTex.getImage().getHeight()); |
||||
logger.config(" Image depth: "+dualEyeTex.getImage().getDepth()); |
||||
logger.config(" Image format: "+dualEyeTex.getImage().getFormat()); |
||||
logger.config(" Image color space: "+dualEyeTex.getImage().getColorSpace()); |
||||
|
||||
//setup framebuffer to use texture
|
||||
out.setDepthBuffer(Image.Format.Depth); |
||||
out.setColorTexture(dualEyeTex); |
||||
|
||||
ViewPort viewPort = environment.getApplication().getViewPort(); |
||||
viewPort.setClearFlags(true, true, true); |
||||
viewPort.setBackgroundColor(ColorRGBA.Black); |
||||
viewPort.setOutputFrameBuffer(out); |
||||
} else { |
||||
throw new IllegalStateException("This VR environment is not attached to any application."); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
} |
||||
|
||||
private ViewPort setupViewBuffers(Camera cam, String viewName){ |
||||
|
||||
if (environment != null){ |
||||
if (environment.getApplication() != null){ |
||||
// create offscreen framebuffer
|
||||
FrameBuffer offBufferLeft = new FrameBuffer(cam.getWidth(), cam.getHeight(), 1); |
||||
//offBufferLeft.setSrgb(true);
|
||||
|
||||
//setup framebuffer's texture
|
||||
Texture2D offTex = new Texture2D(cam.getWidth(), cam.getHeight(), Image.Format.RGBA8); |
||||
offTex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps); |
||||
offTex.setMagFilter(Texture.MagFilter.Bilinear); |
||||
|
||||
//setup framebuffer to use texture
|
||||
offBufferLeft.setDepthBuffer(Image.Format.Depth); |
||||
offBufferLeft.setColorTexture(offTex); |
||||
|
||||
ViewPort viewPort = environment.getApplication().getRenderManager().createPreView(viewName, cam); |
||||
viewPort.setClearFlags(true, true, true); |
||||
viewPort.setBackgroundColor(ColorRGBA.Black); |
||||
|
||||
Iterator<Spatial> spatialIter = environment.getApplication().getViewPort().getScenes().iterator(); |
||||
while(spatialIter.hasNext()){ |
||||
viewPort.attachScene(spatialIter.next()); |
||||
} |
||||
|
||||
//set viewport to render to offscreen framebuffer
|
||||
viewPort.setOutputFrameBuffer(offBufferLeft); |
||||
return viewPort; |
||||
} else { |
||||
throw new IllegalStateException("This VR environment is not attached to any application."); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Setup a distortion mesh for the stereo view. |
||||
* @param eye the eye to apply. |
||||
* @param api the underlying VR api |
||||
* @return the distorted mesh. |
||||
*/ |
||||
public static Mesh setupDistortionMesh(int eye, VRAPI api) { |
||||
Mesh distortionMesh = new Mesh(); |
||||
float m_iLensGridSegmentCountH = 43, m_iLensGridSegmentCountV = 43; |
||||
|
||||
float w = 1f / (m_iLensGridSegmentCountH - 1f); |
||||
float h = 1f / (m_iLensGridSegmentCountV - 1f); |
||||
|
||||
float u, v; |
||||
|
||||
float verts[] = new float[(int) (m_iLensGridSegmentCountV * m_iLensGridSegmentCountH) * 3]; |
||||
|
||||
float texcoordR[] = new float[(int) (m_iLensGridSegmentCountV * m_iLensGridSegmentCountH) * 2]; |
||||
float texcoordG[] = new float[(int) (m_iLensGridSegmentCountV * m_iLensGridSegmentCountH) * 2]; |
||||
float texcoordB[] = new float[(int) (m_iLensGridSegmentCountV * m_iLensGridSegmentCountH) * 2]; |
||||
|
||||
int vertPos = 0, coordPos = 0; |
||||
|
||||
float Xoffset = eye == JOpenVRLibrary.EVREye.EVREye_Eye_Left ? -1f : 0; |
||||
for (int y = 0; y < m_iLensGridSegmentCountV; y++) { |
||||
for (int x = 0; x < m_iLensGridSegmentCountH; x++) { |
||||
u = x * w; |
||||
v = 1 - y * h; |
||||
verts[vertPos] = Xoffset + u; // x
|
||||
verts[vertPos + 1] = -1 + 2 * y * h; // y
|
||||
verts[vertPos + 2] = 0f; // z
|
||||
vertPos += 3; |
||||
|
||||
DistortionCoordinates_t dc0 = new DistortionCoordinates_t(); |
||||
if( api.getVRSystem() == null ) { |
||||
// default to no distortion
|
||||
texcoordR[coordPos] = u; |
||||
texcoordR[coordPos + 1] = 1 - v; |
||||
texcoordG[coordPos] = u; |
||||
texcoordG[coordPos + 1] = 1 - v; |
||||
texcoordB[coordPos] = u; |
||||
texcoordB[coordPos + 1] = 1 - v; |
||||
} else { |
||||
((VR_IVRSystem_FnTable)api.getVRSystem()).ComputeDistortion.apply(eye, u, v, dc0); |
||||
|
||||
texcoordR[coordPos] = dc0.rfRed[0]; |
||||
texcoordR[coordPos + 1] = 1 - dc0.rfRed[1]; |
||||
texcoordG[coordPos] = dc0.rfGreen[0]; |
||||
texcoordG[coordPos + 1] = 1 - dc0.rfGreen[1]; |
||||
texcoordB[coordPos] = dc0.rfBlue[0]; |
||||
texcoordB[coordPos + 1] = 1 - dc0.rfBlue[1]; |
||||
} |
||||
|
||||
coordPos += 2; |
||||
} |
||||
} |
||||
|
||||
// have UV coordinates & positions, now to setup indices
|
||||
|
||||
int[] indices = new int[(int) ((m_iLensGridSegmentCountV - 1) * (m_iLensGridSegmentCountH - 1)) * 6]; |
||||
int indexPos = 0; |
||||
int a, b, c, d; |
||||
|
||||
int offset = 0; |
||||
for (int y = 0; y < m_iLensGridSegmentCountV - 1; y++) { |
||||
for (int x = 0; x < m_iLensGridSegmentCountH - 1; x++) { |
||||
a = (int) (m_iLensGridSegmentCountH * y + x + offset); |
||||
b = (int) (m_iLensGridSegmentCountH * y + x + 1 + offset); |
||||
c = (int) ((y + 1) * m_iLensGridSegmentCountH + x + 1 + offset); |
||||
d = (int) ((y + 1) * m_iLensGridSegmentCountH + x + offset); |
||||
|
||||
indices[indexPos] = a; |
||||
indices[indexPos + 1] = b; |
||||
indices[indexPos + 2] = c; |
||||
|
||||
indices[indexPos + 3] = a; |
||||
indices[indexPos + 4] = c; |
||||
indices[indexPos + 5] = d; |
||||
|
||||
indexPos += 6; |
||||
} |
||||
} |
||||
|
||||
// OK, create the mesh
|
||||
distortionMesh.setBuffer(VertexBuffer.Type.Position, 3, verts); |
||||
distortionMesh.setBuffer(VertexBuffer.Type.Index, 1, indices); |
||||
distortionMesh.setBuffer(VertexBuffer.Type.TexCoord, 2, texcoordR); |
||||
distortionMesh.setBuffer(VertexBuffer.Type.TexCoord2, 2, texcoordG); |
||||
distortionMesh.setBuffer(VertexBuffer.Type.TexCoord3, 2, texcoordB); |
||||
distortionMesh.setStatic(); |
||||
return distortionMesh; |
||||
} |
||||
} |
@ -0,0 +1,732 @@ |
||||
/* |
||||
* To change this template, choose Tools | Templates |
||||
* and open the template in the editor. |
||||
*/ |
||||
package com.jme3.util; |
||||
|
||||
import com.jme3.app.VREnvironment; |
||||
import com.jme3.input.vr.OpenVR; |
||||
import com.jme3.input.vr.VRAPI; |
||||
import com.jme3.material.Material; |
||||
import com.jme3.math.ColorRGBA; |
||||
import com.jme3.math.Quaternion; |
||||
import com.jme3.math.Vector2f; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.renderer.Camera; |
||||
import com.jme3.renderer.ViewPort; |
||||
import com.jme3.renderer.queue.RenderQueue.Bucket; |
||||
import com.jme3.scene.Geometry; |
||||
import com.jme3.scene.Mesh; |
||||
import com.jme3.scene.Node; |
||||
import com.jme3.scene.Spatial; |
||||
import com.jme3.scene.VertexBuffer; |
||||
import com.jme3.system.jopenvr.DistortionCoordinates_t; |
||||
import com.jme3.system.jopenvr.JOpenVRLibrary; |
||||
import com.jme3.system.jopenvr.OpenVRUtil; |
||||
import com.jme3.system.jopenvr.Texture_t; |
||||
import com.jme3.system.jopenvr.VRTextureBounds_t; |
||||
import com.jme3.system.jopenvr.VR_IVRSystem_FnTable; |
||||
import com.jme3.texture.FrameBuffer; |
||||
import com.jme3.texture.Image; |
||||
import com.jme3.texture.Texture; |
||||
import com.jme3.texture.Texture2D; |
||||
import com.jme3.ui.Picture; |
||||
|
||||
import java.util.Iterator; |
||||
import java.util.logging.Logger; |
||||
|
||||
/** |
||||
* A VR view manager based on OpenVR. This class enable to submit 3D views to the VR compositor. |
||||
* @author reden - phr00t - https://github.com/phr00t
|
||||
* @author Julien Seinturier - (c) 2016 - JOrigin project - <a href="http://www.jorigin.org">http:/www.jorigin.org</a> |
||||
*/ |
||||
public class VRViewManagerOpenVR extends AbstractVRViewManager { |
||||
|
||||
private static final Logger logger = Logger.getLogger(VRViewManagerOpenVR.class.getName()); |
||||
|
||||
// OpenVR values
|
||||
private VRTextureBounds_t leftTextureBounds; |
||||
private Texture_t leftTextureType; |
||||
|
||||
private VRTextureBounds_t rightTextureBounds; |
||||
private Texture_t rightTextureType; |
||||
|
||||
private Texture2D dualEyeTex; |
||||
|
||||
//final & temp values for camera calculations
|
||||
private final Vector3f finalPosition = new Vector3f(); |
||||
private final Quaternion finalRotation = new Quaternion(); |
||||
private final Vector3f hmdPos = new Vector3f(); |
||||
private final Quaternion hmdRot = new Quaternion(); |
||||
|
||||
/** |
||||
* Create a new VR view manager attached to the given {@link VREnvironment VR environment}. |
||||
* @param environment the {@link VREnvironment VR environment} to which this view manager is attached. |
||||
*/ |
||||
public VRViewManagerOpenVR(VREnvironment environment){ |
||||
this.environment = environment; |
||||
} |
||||
|
||||
/** |
||||
* Get the identifier of the left eye texture. |
||||
* @return the identifier of the left eye texture. |
||||
* @see #getRightTexId() |
||||
* @see #getFullTexId() |
||||
*/ |
||||
protected int getLeftTexId() { |
||||
return (int)getLeftTexture().getImage().getId(); |
||||
} |
||||
|
||||
/** |
||||
* Get the identifier of the right eye texture. |
||||
* @return the identifier of the right eye texture. |
||||
* @see #getLeftTexId() |
||||
* @see #getFullTexId() |
||||
*/ |
||||
protected int getRightTexId() { |
||||
return (int)getRightTexture().getImage().getId(); |
||||
} |
||||
|
||||
/** |
||||
* Get the identifier of the full (dual eye) texture. |
||||
* @return the identifier of the full (dual eye) texture. |
||||
* @see #getLeftTexId() |
||||
* @see #getRightTexId() |
||||
*/ |
||||
private int getFullTexId() { |
||||
return (int)dualEyeTex.getImage().getId(); |
||||
} |
||||
|
||||
/** |
||||
* Initialize the system binds of the textures. |
||||
*/ |
||||
private void initTextureSubmitStructs() { |
||||
leftTextureType = new Texture_t(); |
||||
rightTextureType = new Texture_t(); |
||||
|
||||
if (environment != null){ |
||||
if( environment.getVRHardware() instanceof OpenVR ) { |
||||
leftTextureBounds = new VRTextureBounds_t(); |
||||
rightTextureBounds = new VRTextureBounds_t(); |
||||
// left eye
|
||||
leftTextureBounds.uMax = 0.5f; |
||||
leftTextureBounds.uMin = 0f; |
||||
leftTextureBounds.vMax = 1f; |
||||
leftTextureBounds.vMin = 0f; |
||||
leftTextureBounds.setAutoSynch(false); |
||||
leftTextureBounds.setAutoRead(false); |
||||
leftTextureBounds.setAutoWrite(false); |
||||
leftTextureBounds.write(); |
||||
// right eye
|
||||
rightTextureBounds.uMax = 1f; |
||||
rightTextureBounds.uMin = 0.5f; |
||||
rightTextureBounds.vMax = 1f; |
||||
rightTextureBounds.vMin = 0f; |
||||
rightTextureBounds.setAutoSynch(false); |
||||
rightTextureBounds.setAutoRead(false); |
||||
rightTextureBounds.setAutoWrite(false); |
||||
rightTextureBounds.write(); |
||||
// texture type
|
||||
leftTextureType.eColorSpace = JOpenVRLibrary.EColorSpace.EColorSpace_ColorSpace_Gamma; |
||||
leftTextureType.eType = JOpenVRLibrary.ETextureType.ETextureType_TextureType_OpenGL; |
||||
leftTextureType.setAutoSynch(false); |
||||
leftTextureType.setAutoRead(false); |
||||
leftTextureType.setAutoWrite(false); |
||||
leftTextureType.handle = -1; |
||||
rightTextureType.eColorSpace = JOpenVRLibrary.EColorSpace.EColorSpace_ColorSpace_Gamma; |
||||
rightTextureType.eType = JOpenVRLibrary.ETextureType.ETextureType_TextureType_OpenGL; |
||||
rightTextureType.setAutoSynch(false); |
||||
rightTextureType.setAutoRead(false); |
||||
rightTextureType.setAutoWrite(false); |
||||
rightTextureType.handle = -1; |
||||
|
||||
|
||||
logger.config("Init eyes native texture binds"); |
||||
logger.config(" Left eye texture"); |
||||
logger.config(" address: "+leftTextureType.getPointer()); |
||||
logger.config(" size: "+leftTextureType.size()+" bytes"); |
||||
logger.config(" color space: "+OpenVRUtil.getEColorSpaceString(leftTextureType.eColorSpace)); |
||||
logger.config(" type: "+OpenVRUtil.getETextureTypeString(leftTextureType.eType)); |
||||
logger.config(" auto read: "+leftTextureType.getAutoRead()); |
||||
logger.config(" auto write: "+leftTextureType.getAutoWrite()); |
||||
logger.config(" handle address: "+leftTextureType.handle); |
||||
logger.config(" handle value: "+leftTextureType.handle); |
||||
logger.config(""); |
||||
logger.config(" Right eye texture"); |
||||
logger.config(" address: "+rightTextureType.getPointer()); |
||||
logger.config(" size: "+rightTextureType.size()+" bytes"); |
||||
logger.config(" color space: "+OpenVRUtil.getEColorSpaceString(rightTextureType.eColorSpace)); |
||||
logger.config(" type: "+OpenVRUtil.getETextureTypeString(rightTextureType.eType)); |
||||
logger.config(" auto read: "+rightTextureType.getAutoRead()); |
||||
logger.config(" auto write: "+rightTextureType.getAutoWrite()); |
||||
logger.config(" handle address: "+rightTextureType.handle); |
||||
logger.config(" handle value: "+rightTextureType.handle); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void postRender() { |
||||
|
||||
if (environment != null){ |
||||
if( environment.isInVR() ) { |
||||
VRAPI api = environment.getVRHardware(); |
||||
if( api.getCompositor() != null ) { |
||||
// using the compositor...
|
||||
int errl = 0, errr = 0; |
||||
if( environment.isInstanceRendering() ) { |
||||
if( leftTextureType.handle == -1 || leftTextureType.handle != getFullTexId() ) { |
||||
leftTextureType.handle = getFullTexId(); |
||||
if( leftTextureType.handle != -1 ) { |
||||
leftTextureType.write(); |
||||
} |
||||
} else { |
||||
if( api instanceof OpenVR ) { |
||||
int submitFlag = JOpenVRLibrary.EVRSubmitFlags.EVRSubmitFlags_Submit_Default; |
||||
errr = ((OpenVR)api).getCompositor().Submit.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Right, leftTextureType, rightTextureBounds, submitFlag); |
||||
errl = ((OpenVR)api).getCompositor().Submit.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Left, leftTextureType, leftTextureBounds, submitFlag); |
||||
} |
||||
} |
||||
} else if( leftTextureType.handle == -1 || rightTextureType.handle == -1 || |
||||
leftTextureType.handle != getLeftTexId() || rightTextureType.handle != getRightTexId() ) { |
||||
leftTextureType.handle = getLeftTexId(); |
||||
if( leftTextureType.handle != -1 ) { |
||||
logger.fine("Writing Left texture to native memory at " + leftTextureType.getPointer()); |
||||
leftTextureType.write(); |
||||
} |
||||
rightTextureType.handle = getRightTexId(); |
||||
if( rightTextureType.handle != -1 ) { |
||||
logger.fine("Writing Right texture to native memory at " + leftTextureType.getPointer()); |
||||
rightTextureType.write(); |
||||
} |
||||
} else { |
||||
if( api instanceof OpenVR ) { |
||||
errl = ((OpenVR)api).getCompositor().Submit.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Left, leftTextureType, null, |
||||
JOpenVRLibrary.EVRSubmitFlags.EVRSubmitFlags_Submit_Default); |
||||
errr = ((OpenVR)api).getCompositor().Submit.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Right, rightTextureType, null, |
||||
JOpenVRLibrary.EVRSubmitFlags.EVRSubmitFlags_Submit_Default); |
||||
} else { |
||||
|
||||
} |
||||
} |
||||
|
||||
if( errl != 0 ){ |
||||
logger.severe("Submit to left compositor error: " + OpenVRUtil.getEVRCompositorErrorString(errl)+" ("+Integer.toString(errl)+")"); |
||||
logger.severe(" Texture color space: "+OpenVRUtil.getEColorSpaceString(leftTextureType.eColorSpace)); |
||||
logger.severe(" Texture type: "+OpenVRUtil.getETextureTypeString(leftTextureType.eType)); |
||||
logger.severe(" Texture handle: "+leftTextureType.handle); |
||||
|
||||
logger.severe(" Left eye texture "+leftEyeTexture.getName()+" ("+leftEyeTexture.getImage().getId()+")"); |
||||
logger.severe(" Type: "+leftEyeTexture.getType()); |
||||
logger.severe(" Size: "+leftEyeTexture.getImage().getWidth()+"x"+leftEyeTexture.getImage().getHeight()); |
||||
logger.severe(" Image depth: "+leftEyeTexture.getImage().getDepth()); |
||||
logger.severe(" Image format: "+leftEyeTexture.getImage().getFormat()); |
||||
logger.severe(" Image color space: "+leftEyeTexture.getImage().getColorSpace()); |
||||
|
||||
} |
||||
|
||||
if( errr != 0 ){ |
||||
logger.severe("Submit to right compositor error: " + OpenVRUtil.getEVRCompositorErrorString(errl)+" ("+Integer.toString(errl)+")"); |
||||
logger.severe(" Texture color space: "+OpenVRUtil.getEColorSpaceString(rightTextureType.eColorSpace)); |
||||
logger.severe(" Texture type: "+OpenVRUtil.getETextureTypeString(rightTextureType.eType)); |
||||
logger.severe(" Texture handle: "+rightTextureType.handle); |
||||
|
||||
logger.severe(" Right eye texture "+rightEyeTexture.getName()+" ("+rightEyeTexture.getImage().getId()+")"); |
||||
logger.severe(" Type: "+rightEyeTexture.getType()); |
||||
logger.severe(" Size: "+rightEyeTexture.getImage().getWidth()+"x"+rightEyeTexture.getImage().getHeight()); |
||||
logger.severe(" Image depth: "+rightEyeTexture.getImage().getDepth()); |
||||
logger.severe(" Image format: "+rightEyeTexture.getImage().getFormat()); |
||||
logger.severe(" Image color space: "+rightEyeTexture.getImage().getColorSpace()); |
||||
} |
||||
} |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
|
||||
|
||||
|
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void initialize() { |
||||
|
||||
logger.config("Initializing VR view manager."); |
||||
|
||||
if (environment != null){ |
||||
|
||||
initTextureSubmitStructs(); |
||||
setupCamerasAndViews(); |
||||
setupVRScene(); |
||||
moveScreenProcessingToEyes(); |
||||
|
||||
if( environment.hasTraditionalGUIOverlay() ) { |
||||
|
||||
environment.getVRMouseManager().initialize(); |
||||
|
||||
// update the pose to position the gui correctly on start
|
||||
update(0f); |
||||
environment.getVRGUIManager().positionGui(); |
||||
} |
||||
|
||||
logger.config("Initialized VR view manager [SUCCESS]"); |
||||
|
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Prepare the size of the given {@link Camera camera} to adapt it to the underlying rendering context. |
||||
* @param cam the {@link Camera camera} to prepare. |
||||
* @param xMult the camera width multiplier. |
||||
*/ |
||||
private void prepareCameraSize(Camera cam, float xMult) { |
||||
|
||||
if (environment != null){ |
||||
|
||||
if (environment.getApplication() != null){ |
||||
Vector2f size = new Vector2f(); |
||||
VRAPI vrhmd = environment.getVRHardware(); |
||||
|
||||
if( vrhmd == null ) { |
||||
size.x = 1280f; |
||||
size.y = 720f; |
||||
} else { |
||||
vrhmd.getRenderSize(size); |
||||
} |
||||
|
||||
if( size.x < environment.getApplication().getContext().getSettings().getWidth() ) { |
||||
size.x = environment.getApplication().getContext().getSettings().getWidth(); |
||||
} |
||||
if( size.y < environment.getApplication().getContext().getSettings().getHeight() ) { |
||||
size.y = environment.getApplication().getContext().getSettings().getHeight(); |
||||
} |
||||
|
||||
if( environment.isInstanceRendering() ){ |
||||
size.x *= 2f; |
||||
} |
||||
|
||||
// other adjustments
|
||||
size.x *= xMult; |
||||
size.x *= getResolutionMuliplier(); |
||||
size.y *= getResolutionMuliplier(); |
||||
|
||||
if( cam.getWidth() != size.x || cam.getHeight() != size.y ){ |
||||
cam.resize((int)size.x, (int)size.y, false); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR environment is not attached to any application."); |
||||
} |
||||
|
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
|
||||
|
||||
} |
||||
|
||||
/** |
||||
* Replaces rootNode as the main cameras scene with the distortion mesh |
||||
*/ |
||||
private void setupVRScene(){ |
||||
|
||||
|
||||
if (environment != null){ |
||||
if (environment.getApplication() != null){ |
||||
// no special scene to setup if we are doing instancing
|
||||
if( environment.isInstanceRendering() ) { |
||||
// distortion has to be done with compositor here... we want only one pass on our end!
|
||||
if( environment.getApplication().getContext().getSettings().isSwapBuffers() ) { |
||||
setupMirrorBuffers(environment.getCamera(), dualEyeTex, true); |
||||
} |
||||
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(); |
||||
|
||||
// main viewport is either going to be a distortion scene or nothing
|
||||
// mirroring is handled by copying framebuffers
|
||||
Iterator<Spatial> spatialIter = environment.getApplication().getViewPort().getScenes().iterator(); |
||||
while(spatialIter.hasNext()){ |
||||
environment.getApplication().getViewPort().detachScene(spatialIter.next()); |
||||
} |
||||
|
||||
spatialIter = environment.getApplication().getGuiViewPort().getScenes().iterator(); |
||||
while(spatialIter.hasNext()){ |
||||
environment.getApplication().getGuiViewPort().detachScene(spatialIter.next()); |
||||
} |
||||
|
||||
// only setup distortion scene if compositor isn't running (or using custom mesh distortion option)
|
||||
if( environment.getVRHardware().getCompositor() == null ) { |
||||
Node distortionScene = new Node(); |
||||
Material leftMat = new Material(environment.getApplication().getAssetManager(), "Common/MatDefs/VR/OpenVR.j3md"); |
||||
leftMat.setTexture("Texture", leftEyeTexture); |
||||
Geometry leftEye = new Geometry("box", setupDistortionMesh(JOpenVRLibrary.EVREye.EVREye_Eye_Left, environment.getVRHardware())); |
||||
leftEye.setMaterial(leftMat); |
||||
distortionScene.attachChild(leftEye); |
||||
|
||||
Material rightMat = new Material(environment.getApplication().getAssetManager(), "Common/MatDefs/VR/OpenVR.j3md"); |
||||
rightMat.setTexture("Texture", rightEyeTexture); |
||||
Geometry rightEye = new Geometry("box", setupDistortionMesh(JOpenVRLibrary.EVREye.EVREye_Eye_Right, environment.getVRHardware())); |
||||
rightEye.setMaterial(rightMat); |
||||
distortionScene.attachChild(rightEye); |
||||
|
||||
distortionScene.updateGeometricState(); |
||||
|
||||
environment.getApplication().getViewPort().attachScene(distortionScene); |
||||
|
||||
//if( useCustomDistortion ) setupFinalFullTexture(app.getViewPort().getCamera());
|
||||
} |
||||
|
||||
if( environment.getApplication().getContext().getSettings().isSwapBuffers() ) { |
||||
setupMirrorBuffers(environment.getCamera(), leftEyeTexture, false); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR environment is not attached to any application."); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void update(float tpf) { |
||||
|
||||
if (environment != null){ |
||||
// grab the observer
|
||||
Object obs = environment.getObserver(); |
||||
Quaternion objRot; |
||||
Vector3f objPos; |
||||
if( obs instanceof Camera ) { |
||||
objRot = ((Camera)obs).getRotation(); |
||||
objPos = ((Camera)obs).getLocation(); |
||||
} else { |
||||
objRot = ((Spatial)obs).getWorldRotation(); |
||||
objPos = ((Spatial)obs).getWorldTranslation(); |
||||
} |
||||
// grab the hardware handle
|
||||
VRAPI dev = environment.getVRHardware(); |
||||
if( dev != null ) { |
||||
// update the HMD's position & orientation
|
||||
dev.updatePose(); |
||||
dev.getPositionAndOrientation(hmdPos, hmdRot); |
||||
if( obs != null ) { |
||||
// update hmdPos based on obs rotation
|
||||
finalRotation.set(objRot); |
||||
finalRotation.mult(hmdPos, hmdPos); |
||||
finalRotation.multLocal(hmdRot); |
||||
} |
||||
|
||||
finalizeCamera(dev.getHMDVectorPoseLeftEye(), objPos, getLeftCamera()); |
||||
finalizeCamera(dev.getHMDVectorPoseRightEye(), objPos, getRightCamera()); |
||||
} else { |
||||
getLeftCamera().setFrame(objPos, objRot); |
||||
getRightCamera().setFrame(objPos, objRot); |
||||
} |
||||
|
||||
if( environment.hasTraditionalGUIOverlay() ) { |
||||
// update the mouse?
|
||||
environment.getVRMouseManager().update(tpf); |
||||
|
||||
// update GUI position?
|
||||
if( environment.getVRGUIManager().wantsReposition || environment.getVRGUIManager().getPositioningMode() != VRGUIPositioningMode.MANUAL ) { |
||||
environment.getVRGUIManager().positionGuiNow(tpf); |
||||
environment.getVRGUIManager().updateGuiQuadGeometricState(); |
||||
} |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Place the camera within the scene. |
||||
* @param eyePos the eye position. |
||||
* @param obsPosition the observer position. |
||||
* @param cam the camera to place. |
||||
*/ |
||||
private void finalizeCamera(Vector3f eyePos, Vector3f obsPosition, Camera cam) { |
||||
finalRotation.mult(eyePos, finalPosition); |
||||
finalPosition.addLocal(hmdPos); |
||||
if( obsPosition != null ) finalPosition.addLocal(obsPosition); |
||||
finalPosition.y += getHeightAdjustment(); |
||||
cam.setFrame(finalPosition, finalRotation); |
||||
} |
||||
|
||||
|
||||
private void setupCamerasAndViews() { |
||||
|
||||
if (environment != null){ |
||||
// get desired frustrum from original camera
|
||||
Camera origCam = environment.getCamera(); |
||||
float fFar = origCam.getFrustumFar(); |
||||
float fNear = origCam.getFrustumNear(); |
||||
|
||||
// restore frustrum on distortion scene cam, if needed
|
||||
if( environment.isInstanceRendering() ) { |
||||
leftCamera = origCam; |
||||
} else if( environment.compositorAllowed() == false ) { |
||||
origCam.setFrustumFar(100f); |
||||
origCam.setFrustumNear(1f); |
||||
leftCamera = origCam.clone(); |
||||
prepareCameraSize(origCam, 2f); |
||||
} else { |
||||
leftCamera = origCam.clone(); |
||||
} |
||||
|
||||
getLeftCamera().setFrustumPerspective(environment.getDefaultFOV(), environment.getDefaultAspect(), fNear, fFar); |
||||
|
||||
prepareCameraSize(getLeftCamera(), 1f); |
||||
if( environment.getVRHardware() != null ) { |
||||
getLeftCamera().setProjectionMatrix(environment.getVRHardware().getHMDMatrixProjectionLeftEye(getLeftCamera())); |
||||
} |
||||
//org.lwjgl.opengl.GL11.glEnable(org.lwjgl.opengl.GL30.GL_FRAMEBUFFER_SRGB);
|
||||
|
||||
if( !environment.isInstanceRendering()) { |
||||
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); |
||||
} else { |
||||
|
||||
if (environment.getApplication() != null){ |
||||
|
||||
logger.severe("THIS CODE NEED CHANGES !!!"); |
||||
leftViewport = environment.getApplication().getViewPort(); |
||||
//leftViewport.attachScene(app.getRootNode());
|
||||
rightCamera = getLeftCamera().clone(); |
||||
if( environment.getVRHardware() != null ){ |
||||
getRightCamera().setProjectionMatrix(environment.getVRHardware().getHMDMatrixProjectionRightEye(getRightCamera())); |
||||
} |
||||
|
||||
org.lwjgl.opengl.GL11.glEnable(org.lwjgl.opengl.GL30.GL_CLIP_DISTANCE0); |
||||
|
||||
//FIXME: [jme-vr] Fix with JMonkey next release
|
||||
//RenderManager._VRInstancing_RightCamProjection = camRight.getViewProjectionMatrix();
|
||||
setupFinalFullTexture(environment.getApplication().getViewPort().getCamera()); |
||||
} else { |
||||
throw new IllegalStateException("This VR environment is not attached to any application."); |
||||
} |
||||
|
||||
} |
||||
|
||||
// setup gui
|
||||
environment.getVRGUIManager().setupGui(getLeftCamera(), getRightCamera(), getLeftViewport(), getRightViewport()); |
||||
|
||||
if( environment.getVRHardware() != null ) { |
||||
// call these to cache the results internally
|
||||
environment.getVRHardware().getHMDMatrixPoseLeftEye(); |
||||
environment.getVRHardware().getHMDMatrixPoseRightEye(); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
} |
||||
|
||||
private ViewPort setupMirrorBuffers(Camera cam, Texture tex, boolean expand) { |
||||
|
||||
if (environment != null){ |
||||
if (environment.getApplication() != null){ |
||||
Camera clonecam = cam.clone(); |
||||
ViewPort viewPort = environment.getApplication().getRenderManager().createPostView("MirrorView", clonecam); |
||||
clonecam.setParallelProjection(true); |
||||
viewPort.setClearFlags(true, true, true); |
||||
viewPort.setBackgroundColor(ColorRGBA.Black); |
||||
Picture pic = new Picture("fullscene"); |
||||
pic.setLocalTranslation(-0.75f, -0.5f, 0f); |
||||
if( expand ) { |
||||
pic.setLocalScale(3f, 1f, 1f); |
||||
} else { |
||||
pic.setLocalScale(1.5f, 1f, 1f); |
||||
} |
||||
pic.setQueueBucket(Bucket.Opaque); |
||||
pic.setTexture(environment.getApplication().getAssetManager(), (Texture2D)tex, false); |
||||
viewPort.attachScene(pic); |
||||
viewPort.setOutputFrameBuffer(null); |
||||
|
||||
pic.updateGeometricState(); |
||||
|
||||
return viewPort; |
||||
} else { |
||||
throw new IllegalStateException("This VR environment is not attached to any application."); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
} |
||||
|
||||
private void setupFinalFullTexture(Camera cam) { |
||||
|
||||
if (environment != null){ |
||||
if (environment.getApplication() != null){ |
||||
// create offscreen framebuffer
|
||||
FrameBuffer out = new FrameBuffer(cam.getWidth(), cam.getHeight(), 1); |
||||
//offBuffer.setSrgb(true);
|
||||
|
||||
//setup framebuffer's texture
|
||||
dualEyeTex = new Texture2D(cam.getWidth(), cam.getHeight(), Image.Format.RGBA8); |
||||
dualEyeTex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps); |
||||
dualEyeTex.setMagFilter(Texture.MagFilter.Bilinear); |
||||
|
||||
logger.config("Dual eye texture "+dualEyeTex.getName()+" ("+dualEyeTex.getImage().getId()+")"); |
||||
logger.config(" Type: "+dualEyeTex.getType()); |
||||
logger.config(" Size: "+dualEyeTex.getImage().getWidth()+"x"+dualEyeTex.getImage().getHeight()); |
||||
logger.config(" Image depth: "+dualEyeTex.getImage().getDepth()); |
||||
logger.config(" Image format: "+dualEyeTex.getImage().getFormat()); |
||||
logger.config(" Image color space: "+dualEyeTex.getImage().getColorSpace()); |
||||
|
||||
//setup framebuffer to use texture
|
||||
out.setDepthBuffer(Image.Format.Depth); |
||||
out.setColorTexture(dualEyeTex); |
||||
|
||||
ViewPort viewPort = environment.getApplication().getViewPort(); |
||||
viewPort.setClearFlags(true, true, true); |
||||
viewPort.setBackgroundColor(ColorRGBA.Black); |
||||
viewPort.setOutputFrameBuffer(out); |
||||
} else { |
||||
throw new IllegalStateException("This VR environment is not attached to any application."); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
} |
||||
|
||||
private ViewPort setupViewBuffers(Camera cam, String viewName){ |
||||
|
||||
if (environment != null){ |
||||
if (environment.getApplication() != null){ |
||||
// create offscreen framebuffer
|
||||
FrameBuffer offBufferLeft = new FrameBuffer(cam.getWidth(), cam.getHeight(), 1); |
||||
//offBufferLeft.setSrgb(true);
|
||||
|
||||
//setup framebuffer's texture
|
||||
Texture2D offTex = new Texture2D(cam.getWidth(), cam.getHeight(), Image.Format.RGBA8); |
||||
offTex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps); |
||||
offTex.setMagFilter(Texture.MagFilter.Bilinear); |
||||
|
||||
//setup framebuffer to use texture
|
||||
offBufferLeft.setDepthBuffer(Image.Format.Depth); |
||||
offBufferLeft.setColorTexture(offTex); |
||||
|
||||
ViewPort viewPort = environment.getApplication().getRenderManager().createPreView(viewName, cam); |
||||
viewPort.setClearFlags(true, true, true); |
||||
viewPort.setBackgroundColor(ColorRGBA.Black); |
||||
|
||||
Iterator<Spatial> spatialIter = environment.getApplication().getViewPort().getScenes().iterator(); |
||||
while(spatialIter.hasNext()){ |
||||
viewPort.attachScene(spatialIter.next()); |
||||
} |
||||
|
||||
//set viewport to render to offscreen framebuffer
|
||||
viewPort.setOutputFrameBuffer(offBufferLeft); |
||||
return viewPort; |
||||
} else { |
||||
throw new IllegalStateException("This VR environment is not attached to any application."); |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("This VR view manager is not attached to any VR environment."); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Setup a distortion mesh for the stereo view. |
||||
* @param eye the eye to apply. |
||||
* @param api the underlying VR api |
||||
* @return the distorted mesh. |
||||
*/ |
||||
public static Mesh setupDistortionMesh(int eye, VRAPI api) { |
||||
Mesh distortionMesh = new Mesh(); |
||||
float m_iLensGridSegmentCountH = 43, m_iLensGridSegmentCountV = 43; |
||||
|
||||
float w = 1f / (m_iLensGridSegmentCountH - 1f); |
||||
float h = 1f / (m_iLensGridSegmentCountV - 1f); |
||||
|
||||
float u, v; |
||||
|
||||
float verts[] = new float[(int) (m_iLensGridSegmentCountV * m_iLensGridSegmentCountH) * 3]; |
||||
|
||||
float texcoordR[] = new float[(int) (m_iLensGridSegmentCountV * m_iLensGridSegmentCountH) * 2]; |
||||
float texcoordG[] = new float[(int) (m_iLensGridSegmentCountV * m_iLensGridSegmentCountH) * 2]; |
||||
float texcoordB[] = new float[(int) (m_iLensGridSegmentCountV * m_iLensGridSegmentCountH) * 2]; |
||||
|
||||
int vertPos = 0, coordPos = 0; |
||||
|
||||
float Xoffset = eye == JOpenVRLibrary.EVREye.EVREye_Eye_Left ? -1f : 0; |
||||
for (int y = 0; y < m_iLensGridSegmentCountV; y++) { |
||||
for (int x = 0; x < m_iLensGridSegmentCountH; x++) { |
||||
u = x * w; |
||||
v = 1 - y * h; |
||||
verts[vertPos] = Xoffset + u; // x
|
||||
verts[vertPos + 1] = -1 + 2 * y * h; // y
|
||||
verts[vertPos + 2] = 0f; // z
|
||||
vertPos += 3; |
||||
|
||||
DistortionCoordinates_t dc0 = new DistortionCoordinates_t(); |
||||
if( api.getVRSystem() == null ) { |
||||
// default to no distortion
|
||||
texcoordR[coordPos] = u; |
||||
texcoordR[coordPos + 1] = 1 - v; |
||||
texcoordG[coordPos] = u; |
||||
texcoordG[coordPos + 1] = 1 - v; |
||||
texcoordB[coordPos] = u; |
||||
texcoordB[coordPos + 1] = 1 - v; |
||||
} else { |
||||
((VR_IVRSystem_FnTable)api.getVRSystem()).ComputeDistortion.apply(eye, u, v, dc0); |
||||
|
||||
texcoordR[coordPos] = dc0.rfRed[0]; |
||||
texcoordR[coordPos + 1] = 1 - dc0.rfRed[1]; |
||||
texcoordG[coordPos] = dc0.rfGreen[0]; |
||||
texcoordG[coordPos + 1] = 1 - dc0.rfGreen[1]; |
||||
texcoordB[coordPos] = dc0.rfBlue[0]; |
||||
texcoordB[coordPos + 1] = 1 - dc0.rfBlue[1]; |
||||
} |
||||
|
||||
coordPos += 2; |
||||
} |
||||
} |
||||
|
||||
// have UV coordinates & positions, now to setup indices
|
||||
|
||||
int[] indices = new int[(int) ((m_iLensGridSegmentCountV - 1) * (m_iLensGridSegmentCountH - 1)) * 6]; |
||||
int indexPos = 0; |
||||
int a, b, c, d; |
||||
|
||||
int offset = 0; |
||||
for (int y = 0; y < m_iLensGridSegmentCountV - 1; y++) { |
||||
for (int x = 0; x < m_iLensGridSegmentCountH - 1; x++) { |
||||
a = (int) (m_iLensGridSegmentCountH * y + x + offset); |
||||
b = (int) (m_iLensGridSegmentCountH * y + x + 1 + offset); |
||||
c = (int) ((y + 1) * m_iLensGridSegmentCountH + x + 1 + offset); |
||||
d = (int) ((y + 1) * m_iLensGridSegmentCountH + x + offset); |
||||
|
||||
indices[indexPos] = a; |
||||
indices[indexPos + 1] = b; |
||||
indices[indexPos + 2] = c; |
||||
|
||||
indices[indexPos + 3] = a; |
||||
indices[indexPos + 4] = c; |
||||
indices[indexPos + 5] = d; |
||||
|
||||
indexPos += 6; |
||||
} |
||||
} |
||||
|
||||
// OK, create the mesh
|
||||
distortionMesh.setBuffer(VertexBuffer.Type.Position, 3, verts); |
||||
distortionMesh.setBuffer(VertexBuffer.Type.Index, 1, indices); |
||||
distortionMesh.setBuffer(VertexBuffer.Type.TexCoord, 2, texcoordR); |
||||
distortionMesh.setBuffer(VertexBuffer.Type.TexCoord2, 2, texcoordG); |
||||
distortionMesh.setBuffer(VertexBuffer.Type.TexCoord3, 2, texcoordB); |
||||
distortionMesh.setStatic(); |
||||
return distortionMesh; |
||||
} |
||||
} |
@ -1,107 +0,0 @@ |
||||
/* |
||||
* To change this template, choose Tools | Templates |
||||
* and open the template in the editor. |
||||
*/ |
||||
package jmevr.util; |
||||
|
||||
import com.jme3.app.VRApplication; |
||||
import com.jme3.input.vr.VRAPI; |
||||
import com.jme3.scene.Mesh; |
||||
import com.jme3.scene.VertexBuffer; |
||||
import com.jme3.system.jopenvr.DistortionCoordinates_t; |
||||
import com.jme3.system.jopenvr.JOpenVRLibrary; |
||||
import com.jme3.system.jopenvr.VR_IVRSystem_FnTable; |
||||
|
||||
/** |
||||
* |
||||
* @author reden |
||||
*/ |
||||
public class MeshUtil { |
||||
|
||||
public static Mesh setupDistortionMesh(int eye, VRAPI api) { |
||||
Mesh distortionMesh = new Mesh(); |
||||
float m_iLensGridSegmentCountH = 43, m_iLensGridSegmentCountV = 43; |
||||
|
||||
float w = 1f / (m_iLensGridSegmentCountH - 1f); |
||||
float h = 1f / (m_iLensGridSegmentCountV - 1f); |
||||
|
||||
float u, v; |
||||
|
||||
float verts[] = new float[(int) (m_iLensGridSegmentCountV * m_iLensGridSegmentCountH) * 3]; |
||||
|
||||
float texcoordR[] = new float[(int) (m_iLensGridSegmentCountV * m_iLensGridSegmentCountH) * 2]; |
||||
float texcoordG[] = new float[(int) (m_iLensGridSegmentCountV * m_iLensGridSegmentCountH) * 2]; |
||||
float texcoordB[] = new float[(int) (m_iLensGridSegmentCountV * m_iLensGridSegmentCountH) * 2]; |
||||
|
||||
int vertPos = 0, coordPos = 0; |
||||
|
||||
float Xoffset = eye == JOpenVRLibrary.EVREye.EVREye_Eye_Left ? -1f : 0; |
||||
for (int y = 0; y < m_iLensGridSegmentCountV; y++) { |
||||
for (int x = 0; x < m_iLensGridSegmentCountH; x++) { |
||||
u = x * w; |
||||
v = 1 - y * h; |
||||
verts[vertPos] = Xoffset + u; // x
|
||||
verts[vertPos + 1] = -1 + 2 * y * h; // y
|
||||
verts[vertPos + 2] = 0f; // z
|
||||
vertPos += 3; |
||||
|
||||
DistortionCoordinates_t dc0 = new DistortionCoordinates_t(); |
||||
if( api.getVRSystem() == null ) { |
||||
// default to no distortion
|
||||
texcoordR[coordPos] = u; |
||||
texcoordR[coordPos + 1] = 1 - v; |
||||
texcoordG[coordPos] = u; |
||||
texcoordG[coordPos + 1] = 1 - v; |
||||
texcoordB[coordPos] = u; |
||||
texcoordB[coordPos + 1] = 1 - v; |
||||
} else { |
||||
((VR_IVRSystem_FnTable)api.getVRSystem()).ComputeDistortion.apply(eye, u, v, dc0); |
||||
|
||||
texcoordR[coordPos] = dc0.rfRed[0]; |
||||
texcoordR[coordPos + 1] = 1 - dc0.rfRed[1]; |
||||
texcoordG[coordPos] = dc0.rfGreen[0]; |
||||
texcoordG[coordPos + 1] = 1 - dc0.rfGreen[1]; |
||||
texcoordB[coordPos] = dc0.rfBlue[0]; |
||||
texcoordB[coordPos + 1] = 1 - dc0.rfBlue[1]; |
||||
} |
||||
|
||||
coordPos += 2; |
||||
} |
||||
} |
||||
|
||||
// have UV coordinates & positions, now to setup indices
|
||||
|
||||
int[] indices = new int[(int) ((m_iLensGridSegmentCountV - 1) * (m_iLensGridSegmentCountH - 1)) * 6]; |
||||
int indexPos = 0; |
||||
int a, b, c, d; |
||||
|
||||
int offset = 0; |
||||
for (int y = 0; y < m_iLensGridSegmentCountV - 1; y++) { |
||||
for (int x = 0; x < m_iLensGridSegmentCountH - 1; x++) { |
||||
a = (int) (m_iLensGridSegmentCountH * y + x + offset); |
||||
b = (int) (m_iLensGridSegmentCountH * y + x + 1 + offset); |
||||
c = (int) ((y + 1) * m_iLensGridSegmentCountH + x + 1 + offset); |
||||
d = (int) ((y + 1) * m_iLensGridSegmentCountH + x + offset); |
||||
|
||||
indices[indexPos] = a; |
||||
indices[indexPos + 1] = b; |
||||
indices[indexPos + 2] = c; |
||||
|
||||
indices[indexPos + 3] = a; |
||||
indices[indexPos + 4] = c; |
||||
indices[indexPos + 5] = d; |
||||
|
||||
indexPos += 6; |
||||
} |
||||
} |
||||
|
||||
// OK, create the mesh
|
||||
distortionMesh.setBuffer(VertexBuffer.Type.Position, 3, verts); |
||||
distortionMesh.setBuffer(VertexBuffer.Type.Index, 1, indices); |
||||
distortionMesh.setBuffer(VertexBuffer.Type.TexCoord, 2, texcoordR); |
||||
distortionMesh.setBuffer(VertexBuffer.Type.TexCoord2, 2, texcoordG); |
||||
distortionMesh.setBuffer(VertexBuffer.Type.TexCoord3, 2, texcoordB); |
||||
distortionMesh.setStatic(); |
||||
return distortionMesh; |
||||
} |
||||
} |
@ -1,334 +0,0 @@ |
||||
/* |
||||
* To change this template, choose Tools | Templates |
||||
* and open the template in the editor. |
||||
*/ |
||||
package jmevr.util; |
||||
|
||||
import com.jme3.app.Application; |
||||
import com.jme3.app.VRAppState; |
||||
import com.jme3.app.VRApplication; |
||||
import com.jme3.app.state.AppState; |
||||
import com.jme3.material.Material; |
||||
import com.jme3.material.RenderState.BlendMode; |
||||
import com.jme3.math.ColorRGBA; |
||||
import com.jme3.math.Matrix3f; |
||||
import com.jme3.math.Quaternion; |
||||
import com.jme3.math.Vector2f; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.renderer.Camera; |
||||
import com.jme3.renderer.ViewPort; |
||||
import com.jme3.renderer.queue.RenderQueue.Bucket; |
||||
import com.jme3.scene.Spatial; |
||||
import com.jme3.scene.Geometry; |
||||
import com.jme3.scene.Node; |
||||
import com.jme3.system.AppSettings; |
||||
import com.jme3.texture.FrameBuffer; |
||||
import com.jme3.texture.Image.Format; |
||||
import com.jme3.texture.Texture; |
||||
import com.jme3.texture.Texture2D; |
||||
import java.awt.GraphicsEnvironment; |
||||
import java.util.Iterator; |
||||
|
||||
/** |
||||
* |
||||
* @author |
||||
* phr00t |
||||
*/ |
||||
public class VRGuiManager { |
||||
|
||||
public enum POSITIONING_MODE { |
||||
MANUAL, AUTO_CAM_ALL, AUTO_CAM_ALL_SKIP_PITCH, AUTO_OBSERVER_POS_CAM_ROTATION, AUTO_OBSERVER_ALL, AUTO_OBSERVER_ALL_CAMHEIGHT |
||||
} |
||||
|
||||
private Camera camLeft, camRight; |
||||
private float guiDistance = 1.5f, guiScale = 1f, guiPositioningElastic; |
||||
private POSITIONING_MODE posMode = POSITIONING_MODE.AUTO_CAM_ALL; |
||||
|
||||
private final Matrix3f orient = new Matrix3f(); |
||||
private Vector2f screenSize; |
||||
protected boolean wantsReposition; |
||||
|
||||
private VRAppState app = null; |
||||
private Application application = null; |
||||
|
||||
/** |
||||
* Create a new GUI manager attached to the given app state. |
||||
* @param app the VR app state that this manager is attached to. |
||||
*/ |
||||
public VRGuiManager(){ |
||||
} |
||||
|
||||
/** |
||||
* Get the VR app state to which this GUI manager is attached. |
||||
* @return the VR app state to which this GUI manager is attached. |
||||
*/ |
||||
public VRAppState getVRAppState(){ |
||||
return app; |
||||
} |
||||
|
||||
/** |
||||
* Attach the GUI manager to an app state and an Application. |
||||
* The application has to be the one that the app state is attached. |
||||
* This method should be called from the {@link AppState#initialize(com.jme3.app.state.AppStateManager, Application) initialize} |
||||
* method of the {@link AppState} instance. |
||||
* @param app the VR app state that this manager is attached to. |
||||
* @param application the application to whitch the app state is attcached. |
||||
*/ |
||||
public void attach(VRAppState app, Application application){ |
||||
this.app = app; |
||||
this.application = application; |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* Makes auto GUI positioning happen not immediately, but like an |
||||
* elastic connected to the headset. Setting to 0 disables (default) |
||||
* Higher settings make it track the headset quicker. |
||||
* |
||||
* @param elastic amount of elasticity |
||||
*/ |
||||
public void setPositioningElasticity(float elastic) { |
||||
guiPositioningElastic = elastic; |
||||
} |
||||
|
||||
public float getPositioningElasticity() { |
||||
return guiPositioningElastic; |
||||
} |
||||
|
||||
public void setPositioningMode(POSITIONING_MODE mode) { |
||||
posMode = mode; |
||||
} |
||||
|
||||
public Vector2f getCanvasSize() { |
||||
if( screenSize == null ) { |
||||
if( app.isInVR() && app.getVRHardware() != null ) { |
||||
screenSize = new Vector2f(); |
||||
app.getVRHardware().getRenderSize(screenSize); |
||||
screenSize.multLocal(app.getVRViewManager().getResolutionMuliplier()); |
||||
} else { |
||||
AppSettings as = application.getContext().getSettings(); |
||||
screenSize = new Vector2f(as.getWidth(), as.getHeight()); |
||||
} |
||||
} |
||||
return screenSize; |
||||
} |
||||
|
||||
private Vector2f ratio; |
||||
|
||||
public Vector2f getCanvasToWindowRatio() { |
||||
if( ratio == null ) { |
||||
ratio = new Vector2f(); |
||||
Vector2f canvas = getCanvasSize(); |
||||
int width = Integer.min(GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode().getWidth(), |
||||
application.getContext().getSettings().getWidth()); |
||||
int height = Integer.min(GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode().getHeight(), |
||||
application.getContext().getSettings().getHeight()); |
||||
ratio.x = Float.max(1f, canvas.x / width); |
||||
ratio.y = Float.max(1f, canvas.y / height); |
||||
} |
||||
return ratio; |
||||
} |
||||
|
||||
public POSITIONING_MODE getPositioningMode() { |
||||
return posMode; |
||||
} |
||||
|
||||
public void positionGui() { |
||||
wantsReposition = true; |
||||
} |
||||
|
||||
private final Vector3f EoldPos = new Vector3f(); |
||||
private final Quaternion EoldDir = new Quaternion(); |
||||
private void positionTo(Vector3f pos, Quaternion dir, float tpf) { |
||||
Vector3f guiPos = guiQuadNode.getLocalTranslation(); |
||||
guiPos.set(0f, 0f, guiDistance); |
||||
dir.mult(guiPos, guiPos); |
||||
guiPos.x += pos.x; |
||||
guiPos.y += pos.y + app.getVRHeightAdjustment(); |
||||
guiPos.z += pos.z; |
||||
if( guiPositioningElastic > 0f && posMode != POSITIONING_MODE.MANUAL ) { |
||||
// mix pos & dir with current pos & dir
|
||||
guiPos.interpolateLocal(EoldPos, guiPos, Float.min(1f, tpf * guiPositioningElastic)); |
||||
EoldPos.set(guiPos); |
||||
} |
||||
} |
||||
|
||||
protected void updateGuiQuadGeometricState() { |
||||
guiQuadNode.updateGeometricState(); |
||||
} |
||||
|
||||
protected void positionGuiNow(float tpf) { |
||||
wantsReposition = false; |
||||
if( app.isInVR() == false ) return; |
||||
guiQuadNode.setLocalScale(guiDistance * guiScale * 4f, 4f * guiDistance * guiScale, 1f); |
||||
|
||||
switch( posMode ) { |
||||
case MANUAL: |
||||
case AUTO_CAM_ALL_SKIP_PITCH: |
||||
case AUTO_CAM_ALL: |
||||
if( camLeft != null && camRight != null ) { |
||||
// get middle point
|
||||
temppos.set(camLeft.getLocation()).interpolateLocal(camRight.getLocation(), 0.5f); |
||||
positionTo(temppos, camLeft.getRotation(), tpf); |
||||
} |
||||
rotateScreenTo(camLeft.getRotation(), tpf); |
||||
|
||||
break; |
||||
case AUTO_OBSERVER_POS_CAM_ROTATION: |
||||
Object obs = app.getObserver(); |
||||
if( obs != null ) { |
||||
if( obs instanceof Camera ) { |
||||
positionTo(((Camera)obs).getLocation(), camLeft.getRotation(), tpf); |
||||
} else { |
||||
positionTo(((Spatial)obs).getWorldTranslation(), camLeft.getRotation(), tpf); |
||||
} |
||||
} |
||||
rotateScreenTo(camLeft.getRotation(), tpf); |
||||
|
||||
break; |
||||
case AUTO_OBSERVER_ALL: |
||||
case AUTO_OBSERVER_ALL_CAMHEIGHT: |
||||
obs = app.getObserver(); |
||||
if( obs != null ) { |
||||
Quaternion q; |
||||
if( obs instanceof Camera ) { |
||||
q = ((Camera)obs).getRotation(); |
||||
temppos.set(((Camera)obs).getLocation()); |
||||
} else { |
||||
q = ((Spatial)obs).getWorldRotation(); |
||||
temppos.set(((Spatial)obs).getWorldTranslation()); |
||||
} |
||||
if( posMode == POSITIONING_MODE.AUTO_OBSERVER_ALL_CAMHEIGHT ) { |
||||
temppos.y = camLeft.getLocation().y; |
||||
} |
||||
positionTo(temppos, q, tpf); |
||||
rotateScreenTo(q, tpf); |
||||
|
||||
} |
||||
break; |
||||
|
||||
} |
||||
|
||||
|
||||
} |
||||
|
||||
private final Vector3f look = new Vector3f(), left = new Vector3f(), temppos = new Vector3f(), up = new Vector3f(); |
||||
private final Quaternion tempq = new Quaternion(); |
||||
|
||||
private void rotateScreenTo(Quaternion dir, float tpf) { |
||||
dir.getRotationColumn(2, look).negateLocal(); |
||||
dir.getRotationColumn(0, left).negateLocal(); |
||||
orient.fromAxes(left, dir.getRotationColumn(1, up), look); |
||||
Quaternion rot = tempq.fromRotationMatrix(orient); |
||||
if( posMode == POSITIONING_MODE.AUTO_CAM_ALL_SKIP_PITCH ) VRUtil.stripToYaw(rot); |
||||
if( guiPositioningElastic > 0f && posMode != POSITIONING_MODE.MANUAL ) { |
||||
// mix pos & dir with current pos & dir
|
||||
EoldDir.nlerp(rot, tpf * guiPositioningElastic); |
||||
guiQuadNode.setLocalRotation(EoldDir); |
||||
} else { |
||||
guiQuadNode.setLocalRotation(rot); |
||||
} |
||||
} |
||||
|
||||
public void setGuiDistance(float newGuiDistance) { |
||||
guiDistance = newGuiDistance; |
||||
} |
||||
|
||||
public void setGuiScale(float scale) { |
||||
guiScale = scale; |
||||
} |
||||
|
||||
public float getGuiDistance() { |
||||
return guiDistance; |
||||
} |
||||
|
||||
public void adjustGuiDistance(float adjustAmount) { |
||||
guiDistance += adjustAmount; |
||||
} |
||||
|
||||
protected void setupGui(Camera leftcam, Camera rightcam, ViewPort left, ViewPort right) { |
||||
if( app.hasTraditionalGUIOverlay() ) { |
||||
camLeft = leftcam; |
||||
camRight = rightcam; |
||||
Spatial guiScene = getGuiQuad(camLeft); |
||||
left.attachScene(guiScene); |
||||
if( right != null ) right.attachScene(guiScene); |
||||
setPositioningMode(posMode); |
||||
} |
||||
} |
||||
|
||||
/* |
||||
do not use, set by preconfigure routine in VRApplication |
||||
*/ |
||||
public void _enableCurvedSuface(boolean set) { |
||||
useCurvedSurface = set; |
||||
} |
||||
|
||||
/* |
||||
do not use, set by preconfigure routine in VRApplication |
||||
*/ |
||||
public void _enableGuiOverdraw(boolean set) { |
||||
overdraw = set; |
||||
} |
||||
|
||||
private boolean useCurvedSurface = false, overdraw = false; |
||||
private Geometry guiQuad; |
||||
private Node guiQuadNode; |
||||
private ViewPort offView; |
||||
private Texture2D guiTexture; |
||||
|
||||
private Spatial getGuiQuad(Camera sourceCam){ |
||||
if( guiQuadNode == null ) { |
||||
Vector2f guiCanvasSize = getCanvasSize(); |
||||
Camera offCamera = sourceCam.clone(); |
||||
offCamera.setParallelProjection(true); |
||||
offCamera.setLocation(Vector3f.ZERO); |
||||
offCamera.lookAt(Vector3f.UNIT_Z, Vector3f.UNIT_Y); |
||||
|
||||
offView = application.getRenderManager().createPreView("GUI View", offCamera); |
||||
offView.setClearFlags(true, true, true); |
||||
offView.setBackgroundColor(ColorRGBA.BlackNoAlpha); |
||||
|
||||
// create offscreen framebuffer
|
||||
FrameBuffer offBuffer = new FrameBuffer((int)guiCanvasSize.x, (int)guiCanvasSize.y, 1); |
||||
|
||||
//setup framebuffer's texture
|
||||
guiTexture = new Texture2D((int)guiCanvasSize.x, (int)guiCanvasSize.y, Format.RGBA8); |
||||
guiTexture.setMinFilter(Texture.MinFilter.BilinearNoMipMaps); |
||||
guiTexture.setMagFilter(Texture.MagFilter.Bilinear); |
||||
|
||||
//setup framebuffer to use texture
|
||||
offBuffer.setDepthBuffer(Format.Depth); |
||||
offBuffer.setColorTexture(guiTexture); |
||||
|
||||
//set viewport to render to offscreen framebuffer
|
||||
offView.setOutputFrameBuffer(offBuffer); |
||||
|
||||
// setup framebuffer's scene
|
||||
Iterator<Spatial> spatialIter = application.getGuiViewPort().getScenes().iterator(); |
||||
while(spatialIter.hasNext()){ |
||||
offView.attachScene(spatialIter.next()); |
||||
} |
||||
|
||||
|
||||
if( useCurvedSurface ) { |
||||
guiQuad = (Geometry)application.getAssetManager().loadModel("Common/Util/gui_mesh.j3o"); |
||||
} else { |
||||
guiQuad = new Geometry("guiQuad", new CenterQuad(1f, 1f)); |
||||
} |
||||
|
||||
Material mat = new Material(application.getAssetManager(), "Common/MatDefs/VR/GuiOverlay.j3md"); |
||||
mat.getAdditionalRenderState().setDepthTest(!overdraw); |
||||
mat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha); |
||||
mat.getAdditionalRenderState().setDepthWrite(false); |
||||
mat.setTexture("ColorMap", guiTexture); |
||||
guiQuad.setQueueBucket(Bucket.Translucent); |
||||
guiQuad.setMaterial(mat); |
||||
|
||||
guiQuadNode = new Node("gui-quad-node"); |
||||
guiQuadNode.setQueueBucket(Bucket.Translucent); |
||||
guiQuadNode.attachChild(guiQuad); |
||||
} |
||||
return guiQuadNode; |
||||
} |
||||
} |
@ -1,234 +0,0 @@ |
||||
/* |
||||
* 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 jmevr.util; |
||||
|
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
import org.lwjgl.glfw.GLFW; |
||||
|
||||
import com.jme3.app.Application; |
||||
import com.jme3.app.VRAppState; |
||||
import com.jme3.app.VRApplication; |
||||
import com.jme3.app.state.AppState; |
||||
import com.jme3.input.MouseInput; |
||||
import com.jme3.input.controls.AnalogListener; |
||||
import com.jme3.input.lwjgl.GlfwMouseInputVR; |
||||
import com.jme3.input.vr.VRInputType; |
||||
import com.jme3.material.RenderState.BlendMode; |
||||
import com.jme3.math.Vector2f; |
||||
import com.jme3.scene.Node; |
||||
import com.jme3.system.AppSettings; |
||||
import com.jme3.system.lwjgl.LwjglWindow; |
||||
import com.jme3.system.lwjgl.LwjglWindowVR; |
||||
import com.jme3.texture.Texture; |
||||
import com.jme3.texture.Texture2D; |
||||
import com.jme3.ui.Picture; |
||||
|
||||
/** |
||||
* |
||||
* @author Phr00t |
||||
*/ |
||||
public class VRMouseManager { |
||||
|
||||
private static final Logger logger = Logger.getLogger(VRMouseManager.class.getName()); |
||||
|
||||
private Application application = null; |
||||
private VRAppState app = null; |
||||
|
||||
private final int AVERAGE_AMNT = 4; |
||||
private int avgCounter; |
||||
|
||||
private Picture mouseImage; |
||||
private int recentCenterCount = 0; |
||||
private final Vector2f cursorPos = new Vector2f(); |
||||
private float ySize, sensitivity = 8f, acceleration = 2f; |
||||
private final float[] lastXmv = new float[AVERAGE_AMNT], lastYmv = new float[AVERAGE_AMNT]; |
||||
private boolean thumbstickMode; |
||||
private float moveScale = 1f; |
||||
|
||||
private float avg(float[] arr) { |
||||
float amt = 0f; |
||||
for(float f : arr) amt += f; |
||||
return amt / arr.length; |
||||
} |
||||
|
||||
public VRMouseManager(){ |
||||
} |
||||
|
||||
/** |
||||
* Attach the mouse manager to an app state and an Application. |
||||
* The application has to be the one that the app state is attached. |
||||
* This method should be called from the {@link AppState#initialize(com.jme3.app.state.AppStateManager, Application) initialize} |
||||
* method of the {@link AppState} instance. |
||||
* @param app the VR app state that this manager is attached to. |
||||
* @param application the application to whitch the app state is attcached. |
||||
*/ |
||||
public void attach(VRAppState app, Application application){ |
||||
this.app = app; |
||||
this.application = application; |
||||
} |
||||
|
||||
protected void init() { |
||||
|
||||
logger.config("Initializing VR mouse manager."); |
||||
|
||||
// load default mouseimage
|
||||
mouseImage = new Picture("mouse"); |
||||
setImage("Common/Util/mouse.png"); |
||||
// hide default cursor by making it invisible
|
||||
MouseInput mi = application.getContext().getMouseInput(); |
||||
if( mi instanceof GlfwMouseInputVR ){ |
||||
((GlfwMouseInputVR)mi).hideActiveCursor(); |
||||
} |
||||
centerMouse(); |
||||
|
||||
logger.config("Initialized VR mouse manager [SUCCESS]"); |
||||
} |
||||
|
||||
public void setThumbstickMode(boolean set) { |
||||
thumbstickMode = set; |
||||
} |
||||
|
||||
public boolean isThumbstickMode() { |
||||
return thumbstickMode; |
||||
} |
||||
|
||||
public void setSpeed(float sensitivity, float acceleration) { |
||||
this.sensitivity = sensitivity; |
||||
this.acceleration = acceleration; |
||||
} |
||||
|
||||
public float getSpeedSensitivity() { |
||||
return sensitivity; |
||||
} |
||||
|
||||
public float getSpeedAcceleration() { |
||||
return acceleration; |
||||
} |
||||
|
||||
public void setMouseMoveScale(float set) { |
||||
moveScale = set; |
||||
} |
||||
|
||||
public void setImage(String texture) { |
||||
if( app.isInVR() == false ){ |
||||
Texture tex = application.getAssetManager().loadTexture(texture); |
||||
mouseImage.setTexture(application.getAssetManager(), (Texture2D)tex, true); |
||||
ySize = tex.getImage().getHeight(); |
||||
mouseImage.setHeight(ySize); |
||||
mouseImage.setWidth(tex.getImage().getWidth()); |
||||
mouseImage.getMaterial().getAdditionalRenderState().setBlendMode(BlendMode.Alpha); |
||||
mouseImage.getMaterial().getAdditionalRenderState().setDepthWrite(false); |
||||
} else { |
||||
Texture tex = application.getAssetManager().loadTexture(texture); |
||||
mouseImage.setTexture(application.getAssetManager(), (Texture2D)tex, true); |
||||
ySize = tex.getImage().getHeight(); |
||||
mouseImage.setHeight(ySize); |
||||
mouseImage.setWidth(tex.getImage().getWidth()); |
||||
mouseImage.getMaterial().getAdditionalRenderState().setBlendMode(BlendMode.Alpha); |
||||
mouseImage.getMaterial().getAdditionalRenderState().setDepthWrite(false); |
||||
} |
||||
|
||||
} |
||||
|
||||
public void updateAnalogAsMouse(int inputIndex, AnalogListener mouseListener, String mouseXName, String mouseYName, float tpf) { |
||||
// got a tracked controller to use as the "mouse"
|
||||
if( app.isInVR() == false || |
||||
app.getVRinput() == null || |
||||
app.getVRinput().isInputDeviceTracking(inputIndex) == false ) return; |
||||
Vector2f tpDelta; |
||||
if( thumbstickMode ) { |
||||
tpDelta = app.getVRinput().getAxis(inputIndex, VRInputType.ViveTrackpadAxis); |
||||
} else { |
||||
tpDelta = app.getVRinput().getAxisDeltaSinceLastCall(inputIndex, VRInputType.ViveTrackpadAxis); |
||||
} |
||||
float Xamount = (float)Math.pow(Math.abs(tpDelta.x) * sensitivity, acceleration); |
||||
float Yamount = (float)Math.pow(Math.abs(tpDelta.y) * sensitivity, acceleration); |
||||
if( tpDelta.x < 0f ) Xamount = -Xamount; |
||||
if( tpDelta.y < 0f ) Yamount = -Yamount; |
||||
Xamount *= moveScale; Yamount *= moveScale; |
||||
if( mouseListener != null ) { |
||||
if( tpDelta.x != 0f && mouseXName != null ) mouseListener.onAnalog(mouseXName, Xamount * 0.2f, tpf); |
||||
if( tpDelta.y != 0f && mouseYName != null ) mouseListener.onAnalog(mouseYName, Yamount * 0.2f, tpf); |
||||
} |
||||
if( application.getInputManager().isCursorVisible() ) { |
||||
int index = (avgCounter+1) % AVERAGE_AMNT; |
||||
lastXmv[index] = Xamount * 133f; |
||||
lastYmv[index] = Yamount * 133f; |
||||
cursorPos.x -= avg(lastXmv); |
||||
cursorPos.y -= avg(lastYmv); |
||||
Vector2f maxsize = app.getVRGUIManager().getCanvasSize(); |
||||
if( cursorPos.x > maxsize.x ) cursorPos.x = maxsize.x; |
||||
if( cursorPos.x < 0f ) cursorPos.x = 0f; |
||||
if( cursorPos.y > maxsize.y ) cursorPos.y = maxsize.y; |
||||
if( cursorPos.y < 0f ) cursorPos.y = 0f; |
||||
} |
||||
} |
||||
|
||||
public Vector2f getCursorPosition() { |
||||
if( app.isInVR() ) { |
||||
return cursorPos; |
||||
} |
||||
return application.getInputManager().getCursorPosition(); |
||||
} |
||||
|
||||
public void centerMouse() { |
||||
// set mouse in center of the screen if newly added
|
||||
Vector2f size = app.getVRGUIManager().getCanvasSize(); |
||||
MouseInput mi = application.getContext().getMouseInput(); |
||||
AppSettings as = application.getContext().getSettings(); |
||||
if( mi instanceof GlfwMouseInputVR ) ((GlfwMouseInputVR)mi).setCursorPosition((int)(as.getWidth() / 2f), (int)(as.getHeight() / 2f)); |
||||
if( app.isInVR() ) { |
||||
cursorPos.x = size.x / 2f; |
||||
cursorPos.y = size.y / 2f; |
||||
recentCenterCount = 2; |
||||
} |
||||
} |
||||
|
||||
protected void update(float tpf) { |
||||
// if we are showing the cursor, add our picture as it
|
||||
|
||||
if( application.getInputManager().isCursorVisible() ) { |
||||
if( mouseImage.getParent() == null ) { |
||||
|
||||
application.getGuiViewPort().attachScene(mouseImage); |
||||
centerMouse(); |
||||
// the "real" mouse pointer should stay hidden
|
||||
if (application.getContext() instanceof LwjglWindow){ |
||||
GLFW.glfwSetInputMode(((LwjglWindow)application.getContext()).getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_DISABLED); |
||||
} |
||||
} |
||||
// handle mouse movements, which may be in addition to (or exclusive from) tracked movement
|
||||
MouseInput mi = application.getContext().getMouseInput(); |
||||
if( mi instanceof GlfwMouseInputVR ) { |
||||
if( recentCenterCount <= 0 ) { |
||||
//Vector2f winratio = VRGuiManager.getCanvasToWindowRatio();
|
||||
cursorPos.x += ((GlfwMouseInputVR)mi).getLastDeltaX();// * winratio.x;
|
||||
cursorPos.y += ((GlfwMouseInputVR)mi).getLastDeltaY();// * winratio.y;
|
||||
if( cursorPos.x < 0f ) cursorPos.x = 0f; |
||||
if( cursorPos.y < 0f ) cursorPos.y = 0f; |
||||
if( cursorPos.x > app.getVRGUIManager().getCanvasSize().x ) cursorPos.x = app.getVRGUIManager().getCanvasSize().x; |
||||
if( cursorPos.y > app.getVRGUIManager().getCanvasSize().y ) cursorPos.y = app.getVRGUIManager().getCanvasSize().y; |
||||
} else recentCenterCount--; |
||||
((GlfwMouseInputVR)mi).clearDeltas(); |
||||
} |
||||
// ok, update the cursor graphic position
|
||||
Vector2f currentPos = getCursorPosition(); |
||||
mouseImage.setLocalTranslation(currentPos.x, currentPos.y - ySize, app.getVRGUIManager().getGuiDistance() + 1f); |
||||
|
||||
mouseImage.updateGeometricState(); |
||||
|
||||
} else if( mouseImage.getParent() != null ) { |
||||
Node n = mouseImage.getParent(); |
||||
mouseImage.removeFromParent(); |
||||
|
||||
if (n != null){ |
||||
n.updateGeometricState(); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,863 +0,0 @@ |
||||
/* |
||||
* To change this template, choose Tools | Templates |
||||
* and open the template in the editor. |
||||
*/ |
||||
package jmevr.util; |
||||
|
||||
import com.jme3.app.Application; |
||||
import com.jme3.app.VRAppState; |
||||
import com.jme3.app.VRApplication; |
||||
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.material.Material; |
||||
import com.jme3.math.ColorRGBA; |
||||
import com.jme3.math.Quaternion; |
||||
import com.jme3.math.Vector2f; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.post.CartoonSSAO; |
||||
import com.jme3.post.Filter; |
||||
import com.jme3.post.FilterPostProcessor; |
||||
import com.jme3.post.SceneProcessor; |
||||
import com.jme3.post.filters.FogFilter; |
||||
import com.jme3.post.filters.TranslucentBucketFilter; |
||||
import com.jme3.post.ssao.SSAOFilter; |
||||
import com.jme3.renderer.Camera; |
||||
import com.jme3.renderer.ViewPort; |
||||
import com.jme3.renderer.queue.RenderQueue.Bucket; |
||||
import com.jme3.scene.Geometry; |
||||
import com.jme3.scene.Node; |
||||
import com.jme3.scene.Spatial; |
||||
import com.jme3.shadow.DirectionalLightShadowFilter; |
||||
import com.jme3.shadow.VRDirectionalLightShadowRenderer; |
||||
import com.jme3.system.jopenvr.JOpenVRLibrary; |
||||
import com.jme3.system.jopenvr.OpenVRUtil; |
||||
import com.jme3.system.jopenvr.Texture_t; |
||||
import com.jme3.system.jopenvr.VRTextureBounds_t; |
||||
import com.jme3.system.lwjgl.LwjglWindow; |
||||
import com.jme3.texture.FrameBuffer; |
||||
import com.jme3.texture.Image; |
||||
import com.jme3.texture.Texture; |
||||
import com.jme3.texture.Texture2D; |
||||
import com.jme3.ui.Picture; |
||||
import com.sun.jna.Pointer; |
||||
import com.sun.jna.ptr.PointerByReference; |
||||
|
||||
import java.awt.GraphicsEnvironment; |
||||
import java.util.Iterator; |
||||
import java.util.logging.Logger; |
||||
|
||||
import osvrrendermanageropengl.OSVR_RenderBufferOpenGL; |
||||
import osvrrendermanageropengl.OSVR_ViewportDescription; |
||||
import osvrrendermanageropengl.OsvrRenderManagerOpenGLLibrary; |
||||
|
||||
/** |
||||
* A VR view manager. This class enable to submit 3D views to the VR compositor. |
||||
* @author reden - phr00t - https://github.com/phr00t
|
||||
* @author Julien Seinturier - (c) 2016 - JOrigin project - <a href="http://www.jorigin.org">http:/www.jorigin.org</a> |
||||
*/ |
||||
public class VRViewManager { |
||||
|
||||
private static final Logger logger = Logger.getLogger(VRViewManager.class.getName()); |
||||
|
||||
/** |
||||
* The name of the left view. |
||||
*/ |
||||
public final static String LEFT_VIEW_NAME = "Left View"; |
||||
|
||||
/** |
||||
* The name of the right view. |
||||
*/ |
||||
public final static String RIGHT_VIEW_NAME = "Right View"; |
||||
|
||||
private VRAppState app; |
||||
private Application application; |
||||
|
||||
private Camera leftCamera; |
||||
private ViewPort leftViewport; |
||||
private FilterPostProcessor leftPostProcessor; |
||||
private Texture2D leftEyeTexture; |
||||
private Texture2D leftEyeDepth; |
||||
|
||||
private Camera rightCamera; |
||||
private ViewPort rightViewport; |
||||
private FilterPostProcessor rightPostProcessor; |
||||
private Texture2D rightEyeTexture; |
||||
private Texture2D rightEyeDepth; |
||||
|
||||
// OpenVR values
|
||||
private VRTextureBounds_t leftTextureBounds; |
||||
private Texture_t leftTextureType; |
||||
|
||||
private VRTextureBounds_t rightTextureBounds; |
||||
private Texture_t rightTextureType; |
||||
|
||||
// OSVR values
|
||||
OSVR_RenderBufferOpenGL.ByValue[] osvr_renderBuffer; |
||||
OSVR_ViewportDescription.ByValue osvr_viewDescFull; |
||||
OSVR_ViewportDescription.ByValue osvr_viewDescLeft; |
||||
OSVR_ViewportDescription.ByValue osvr_viewDescRight; |
||||
Pointer osvr_rmBufferState; |
||||
|
||||
//private static boolean useCustomDistortion;
|
||||
private float heightAdjustment; |
||||
|
||||
private Texture2D dualEyeTex; |
||||
|
||||
private final PointerByReference grabRBS = new PointerByReference(); |
||||
|
||||
private float resMult = 1f; |
||||
|
||||
//final & temp values for camera calculations
|
||||
private final Vector3f finalPosition = new Vector3f(); |
||||
private final Quaternion finalRotation = new Quaternion(); |
||||
private final Vector3f hmdPos = new Vector3f(); |
||||
private final Quaternion hmdRot = new Quaternion(); |
||||
|
||||
/** |
||||
* Create a new VR view manager attached to the given {@link VRAppState VR app state}.<br> |
||||
* in order to be used, this manager has to be attached to an app state and to an application. |
||||
*/ |
||||
public VRViewManager(){ |
||||
} |
||||
|
||||
/** |
||||
* Attach this manager to the given {@link VRAppState app state} and the given {@link Application application}. |
||||
* The application has to be the one that the app state is attached. |
||||
* This method should be called from the {@link AppState#initialize(com.jme3.app.state.AppStateManager, Application) initialize} |
||||
* method of the {@link AppState} instance. |
||||
* @param app the {@link VRAppState VR app state} to which this manager is linked. |
||||
* @param application the {@link Application} which the app state is attached. |
||||
*/ |
||||
public void attach(VRAppState app, Application application){ |
||||
this.app = app; |
||||
this.application = application; |
||||
} |
||||
|
||||
/** |
||||
* Get the {@link Camera camera} attached to the left eye. |
||||
* @return the {@link Camera camera} attached to the left eye. |
||||
* @see #getRightCamera() |
||||
*/ |
||||
public Camera getLeftCamera() { |
||||
return leftCamera; |
||||
} |
||||
|
||||
/** |
||||
* Get the {@link Camera camera} attached to the right eye. |
||||
* @return the {@link Camera camera} attached to the right eye. |
||||
* @see #getLeftCamera() |
||||
*/ |
||||
public Camera getRightCamera() { |
||||
return rightCamera; |
||||
} |
||||
|
||||
/** |
||||
* Get the {@link ViewPort viewport} attached to the left eye. |
||||
* @return the {@link ViewPort viewport} attached to the left eye. |
||||
* @see #getRightViewport() |
||||
*/ |
||||
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() |
||||
*/ |
||||
public ViewPort getRightViewport() { |
||||
return rightViewport; |
||||
} |
||||
|
||||
/** |
||||
* Get the identifier of the left eye texture. |
||||
* @return the identifier of the left eye texture. |
||||
* @see #getRightTexId() |
||||
* @see #getFullTexId() |
||||
*/ |
||||
private int getLeftTexId() { |
||||
return (int)leftEyeTexture.getImage().getId(); |
||||
} |
||||
|
||||
/** |
||||
* Get the identifier of the right eye texture. |
||||
* @return the identifier of the right eye texture. |
||||
* @see #getLeftTexId() |
||||
* @see #getFullTexId() |
||||
*/ |
||||
private int getRightTexId() { |
||||
return (int)rightEyeTexture.getImage().getId(); |
||||
} |
||||
|
||||
/** |
||||
* Get the identifier of the full (dual eye) texture. |
||||
* @return the identifier of the full (dual eye) texture. |
||||
* @see #getLeftTexId() |
||||
* @see #getRightTexId() |
||||
*/ |
||||
private int getFullTexId() { |
||||
return (int)dualEyeTex.getImage().getId(); |
||||
} |
||||
|
||||
/** |
||||
* Get the height adjustment to apply to the cameras before rendering. |
||||
* @return the height adjustment to apply to the cameras before rendering. |
||||
* @see #setHeightAdjustment(float) |
||||
*/ |
||||
public float getHeightAdjustment() { |
||||
return heightAdjustment; |
||||
} |
||||
|
||||
/** |
||||
* Set the height adjustment to apply to the cameras before rendering. |
||||
* @param amount the height adjustment to apply to the cameras before rendering. |
||||
* @see #getHeightAdjustment() |
||||
*/ |
||||
public void setHeightAdjustment(float amount) { |
||||
heightAdjustment = amount; |
||||
} |
||||
|
||||
/** |
||||
* Get the resolution multiplier. |
||||
* @return the resolution multiplier. |
||||
* @see #setResolutionMultiplier(float) |
||||
*/ |
||||
public float getResolutionMuliplier() { |
||||
return resMult; |
||||
} |
||||
|
||||
/** |
||||
* Set the resolution multiplier. |
||||
* @param resMult the resolution multiplier. |
||||
* @see #getResolutionMuliplier() |
||||
*/ |
||||
public void setResolutionMultiplier(float resMult) { |
||||
this.resMult = resMult; |
||||
} |
||||
|
||||
/** |
||||
* Initialize the system binds of the textures. |
||||
*/ |
||||
private void initTextureSubmitStructs() { |
||||
leftTextureType = new Texture_t(); |
||||
rightTextureType = new Texture_t(); |
||||
|
||||
|
||||
if( app.getVRHardware() instanceof OpenVR ) { |
||||
leftTextureBounds = new VRTextureBounds_t(); |
||||
rightTextureBounds = new VRTextureBounds_t(); |
||||
// left eye
|
||||
leftTextureBounds.uMax = 0.5f; |
||||
leftTextureBounds.uMin = 0f; |
||||
leftTextureBounds.vMax = 1f; |
||||
leftTextureBounds.vMin = 0f; |
||||
leftTextureBounds.setAutoSynch(false); |
||||
leftTextureBounds.setAutoRead(false); |
||||
leftTextureBounds.setAutoWrite(false); |
||||
leftTextureBounds.write(); |
||||
// right eye
|
||||
rightTextureBounds.uMax = 1f; |
||||
rightTextureBounds.uMin = 0.5f; |
||||
rightTextureBounds.vMax = 1f; |
||||
rightTextureBounds.vMin = 0f; |
||||
rightTextureBounds.setAutoSynch(false); |
||||
rightTextureBounds.setAutoRead(false); |
||||
rightTextureBounds.setAutoWrite(false); |
||||
rightTextureBounds.write(); |
||||
// texture type
|
||||
// FIXME: Synchronize with JMonkey given texture (at this time is linear but was Gamma with phr00t implementation)
|
||||
leftTextureType.eColorSpace = JOpenVRLibrary.EColorSpace.EColorSpace_ColorSpace_Gamma; |
||||
//leftTextureType.eColorSpace = JOpenVRLibrary.EColorSpace.EColorSpace_ColorSpace_Linear;
|
||||
leftTextureType.eType = JOpenVRLibrary.ETextureType.ETextureType_TextureType_OpenGL; |
||||
leftTextureType.setAutoSynch(false); |
||||
leftTextureType.setAutoRead(false); |
||||
leftTextureType.setAutoWrite(false); |
||||
leftTextureType.handle = -1; |
||||
// FIXME: Synchronize with JMonkey given texture (at this time is linear but was Gamma with phr00t implementation)
|
||||
rightTextureType.eColorSpace = JOpenVRLibrary.EColorSpace.EColorSpace_ColorSpace_Gamma; |
||||
//rightTextureType.eColorSpace = JOpenVRLibrary.EColorSpace.EColorSpace_ColorSpace_Linear;
|
||||
rightTextureType.eType = JOpenVRLibrary.ETextureType.ETextureType_TextureType_OpenGL; |
||||
rightTextureType.setAutoSynch(false); |
||||
rightTextureType.setAutoRead(false); |
||||
rightTextureType.setAutoWrite(false); |
||||
rightTextureType.handle = -1; |
||||
|
||||
|
||||
logger.config("Init eyes native texture binds"); |
||||
logger.config(" Left eye texture"); |
||||
logger.config(" address: "+leftTextureType.getPointer()); |
||||
logger.config(" size: "+leftTextureType.size()+" bytes"); |
||||
logger.config(" color space: "+OpenVRUtil.getEColorSpaceString(leftTextureType.eColorSpace)); |
||||
logger.config(" type: "+OpenVRUtil.getETextureTypeString(leftTextureType.eType)); |
||||
logger.config(" auto read: "+leftTextureType.getAutoRead()); |
||||
logger.config(" auto write: "+leftTextureType.getAutoWrite()); |
||||
logger.config(" handle address: "+leftTextureType.handle); |
||||
logger.config(" handle value: "+leftTextureType.handle); |
||||
logger.config(""); |
||||
logger.config(" Right eye texture"); |
||||
logger.config(" address: "+rightTextureType.getPointer()); |
||||
logger.config(" size: "+rightTextureType.size()+" bytes"); |
||||
logger.config(" color space: "+OpenVRUtil.getEColorSpaceString(rightTextureType.eColorSpace)); |
||||
logger.config(" type: "+OpenVRUtil.getETextureTypeString(rightTextureType.eType)); |
||||
logger.config(" auto read: "+rightTextureType.getAutoRead()); |
||||
logger.config(" auto write: "+rightTextureType.getAutoWrite()); |
||||
logger.config(" handle address: "+rightTextureType.handle); |
||||
logger.config(" handle value: "+rightTextureType.handle); |
||||
|
||||
|
||||
} else if( app.getVRHardware() instanceof OSVR ) { |
||||
// must be OSVR
|
||||
osvr_renderBuffer = new OSVR_RenderBufferOpenGL.ByValue[2]; |
||||
osvr_renderBuffer[OSVR.EYE_LEFT] = new OSVR_RenderBufferOpenGL.ByValue(); |
||||
osvr_renderBuffer[OSVR.EYE_RIGHT] = new OSVR_RenderBufferOpenGL.ByValue(); |
||||
osvr_renderBuffer[OSVR.EYE_LEFT].setAutoSynch(false); |
||||
osvr_renderBuffer[OSVR.EYE_RIGHT].setAutoSynch(false); |
||||
osvr_viewDescFull = new OSVR_ViewportDescription.ByValue(); |
||||
osvr_viewDescFull.setAutoSynch(false); |
||||
osvr_viewDescFull.left = osvr_viewDescFull.lower = 0.0; |
||||
osvr_viewDescFull.width = osvr_viewDescFull.height = 1.0; |
||||
osvr_viewDescLeft = new OSVR_ViewportDescription.ByValue(); |
||||
osvr_viewDescLeft.setAutoSynch(false); |
||||
osvr_viewDescLeft.left = osvr_viewDescLeft.lower = 0.0; |
||||
osvr_viewDescLeft.width = 0.5; |
||||
osvr_viewDescLeft.height = 1.0; |
||||
osvr_viewDescRight = new OSVR_ViewportDescription.ByValue(); |
||||
osvr_viewDescRight.setAutoSynch(false); |
||||
osvr_viewDescRight.left = 0.5; |
||||
osvr_viewDescRight.lower = 0.0; |
||||
osvr_viewDescRight.width = 0.5; |
||||
osvr_viewDescRight.height = 1.0; |
||||
osvr_viewDescRight.write(); |
||||
osvr_viewDescLeft.write(); |
||||
osvr_viewDescFull.write(); |
||||
osvr_renderBuffer[OSVR.EYE_LEFT].depthStencilBufferName = -1; |
||||
osvr_renderBuffer[OSVR.EYE_LEFT].colorBufferName = -1; |
||||
osvr_renderBuffer[OSVR.EYE_RIGHT].depthStencilBufferName = -1; |
||||
osvr_renderBuffer[OSVR.EYE_RIGHT].colorBufferName = -1; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Register the OSVR OpenGL buffer. |
||||
* @param buf the OSVR OpenGL buffer. |
||||
*/ |
||||
private void registerOSVRBuffer(OSVR_RenderBufferOpenGL.ByValue buf) { |
||||
OsvrRenderManagerOpenGLLibrary.osvrRenderManagerStartRegisterRenderBuffers(grabRBS); |
||||
OsvrRenderManagerOpenGLLibrary.osvrRenderManagerRegisterRenderBufferOpenGL(grabRBS.getValue(), buf); |
||||
OsvrRenderManagerOpenGLLibrary.osvrRenderManagerFinishRegisterRenderBuffers(((OSVR)app.getVRHardware()).getCompositor(), grabRBS.getValue(), (byte)0); |
||||
} |
||||
|
||||
/** |
||||
* Send the textures to the two eyes. |
||||
*/ |
||||
public void sendTextures() { |
||||
if( app.isInVR() ) { |
||||
VRAPI api = app.getVRHardware(); |
||||
if( api.getCompositor() != null ) { |
||||
// using the compositor...
|
||||
int errl = 0, errr = 0; |
||||
if( app.isInstanceVRRendering() ) { |
||||
if( leftTextureType.handle == -1 || leftTextureType.handle != getFullTexId() ) { |
||||
leftTextureType.handle = getFullTexId(); |
||||
if( leftTextureType.handle != -1 ) { |
||||
leftTextureType.write(); |
||||
if( api instanceof OSVR ) { |
||||
osvr_renderBuffer[OSVR.EYE_LEFT].colorBufferName = leftTextureType.handle; |
||||
osvr_renderBuffer[OSVR.EYE_LEFT].depthStencilBufferName = dualEyeTex.getImage().getId(); |
||||
osvr_renderBuffer[OSVR.EYE_LEFT].write(); |
||||
registerOSVRBuffer(osvr_renderBuffer[OSVR.EYE_LEFT]); |
||||
} |
||||
} |
||||
} else { |
||||
if( api instanceof OpenVR ) { |
||||
int submitFlag = JOpenVRLibrary.EVRSubmitFlags.EVRSubmitFlags_Submit_Default; |
||||
errr = ((OpenVR)api).getCompositor().Submit.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Right, leftTextureType, rightTextureBounds, submitFlag); |
||||
errl = ((OpenVR)api).getCompositor().Submit.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Left, leftTextureType, leftTextureBounds, submitFlag); |
||||
} else if( api instanceof OSVR ) { |
||||
((OSVR)api).handleRenderBufferPresent(osvr_viewDescLeft, osvr_viewDescRight, |
||||
osvr_renderBuffer[OSVR.EYE_LEFT], osvr_renderBuffer[OSVR.EYE_LEFT]); |
||||
} |
||||
} |
||||
} else if( leftTextureType.handle == -1 || rightTextureType.handle == -1 || |
||||
leftTextureType.handle != getLeftTexId() || rightTextureType.handle != getRightTexId() ) { |
||||
leftTextureType.handle = getLeftTexId(); |
||||
if( leftTextureType.handle != -1 ) { |
||||
logger.fine("Writing Left texture to native memory at " + leftTextureType.getPointer()); |
||||
leftTextureType.write(); |
||||
if( api instanceof OSVR ) { |
||||
osvr_renderBuffer[OSVR.EYE_LEFT].colorBufferName = leftTextureType.handle; |
||||
if( leftEyeDepth != null ) osvr_renderBuffer[OSVR.EYE_LEFT].depthStencilBufferName = leftEyeDepth.getImage().getId(); |
||||
osvr_renderBuffer[OSVR.EYE_LEFT].write(); |
||||
registerOSVRBuffer(osvr_renderBuffer[OSVR.EYE_LEFT]); |
||||
} |
||||
} |
||||
rightTextureType.handle = getRightTexId(); |
||||
if( rightTextureType.handle != -1 ) { |
||||
logger.fine("Writing Right texture to native memory at " + leftTextureType.getPointer()); |
||||
rightTextureType.write(); |
||||
if( api instanceof OSVR ) { |
||||
osvr_renderBuffer[OSVR.EYE_RIGHT].colorBufferName = rightTextureType.handle; |
||||
if( rightEyeDepth != null ) osvr_renderBuffer[OSVR.EYE_RIGHT].depthStencilBufferName = rightEyeDepth.getImage().getId(); |
||||
osvr_renderBuffer[OSVR.EYE_RIGHT].write(); |
||||
registerOSVRBuffer(osvr_renderBuffer[OSVR.EYE_RIGHT]); |
||||
} |
||||
} |
||||
} else { |
||||
if( api instanceof OpenVR ) { |
||||
errl = ((OpenVR)api).getCompositor().Submit.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Left, leftTextureType, null, |
||||
JOpenVRLibrary.EVRSubmitFlags.EVRSubmitFlags_Submit_Default); |
||||
errr = ((OpenVR)api).getCompositor().Submit.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Right, rightTextureType, null, |
||||
JOpenVRLibrary.EVRSubmitFlags.EVRSubmitFlags_Submit_Default); |
||||
} else if( api instanceof OSVR ) { |
||||
((OSVR)api).handleRenderBufferPresent(osvr_viewDescFull, osvr_viewDescFull, |
||||
osvr_renderBuffer[OSVR.EYE_LEFT], osvr_renderBuffer[OSVR.EYE_RIGHT]); |
||||
} |
||||
} |
||||
|
||||
if( errl != 0 ){ |
||||
logger.severe("Submit to left compositor error: " + OpenVRUtil.getEVRCompositorErrorString(errl)+" ("+Integer.toString(errl)+")"); |
||||
logger.severe(" Texture color space: "+OpenVRUtil.getEColorSpaceString(leftTextureType.eColorSpace)); |
||||
logger.severe(" Texture type: "+OpenVRUtil.getETextureTypeString(leftTextureType.eType)); |
||||
logger.severe(" Texture handle: "+leftTextureType.handle); |
||||
|
||||
logger.severe(" Left eye texture "+leftEyeTexture.getName()+" ("+leftEyeTexture.getImage().getId()+")"); |
||||
logger.severe(" Type: "+leftEyeTexture.getType()); |
||||
logger.severe(" Size: "+leftEyeTexture.getImage().getWidth()+"x"+leftEyeTexture.getImage().getHeight()); |
||||
logger.severe(" Image depth: "+leftEyeTexture.getImage().getDepth()); |
||||
logger.severe(" Image format: "+leftEyeTexture.getImage().getFormat()); |
||||
logger.severe(" Image color space: "+leftEyeTexture.getImage().getColorSpace()); |
||||
|
||||
} |
||||
|
||||
if( errr != 0 ){ |
||||
logger.severe("Submit to right compositor error: " + OpenVRUtil.getEVRCompositorErrorString(errl)+" ("+Integer.toString(errl)+")"); |
||||
logger.severe(" Texture color space: "+OpenVRUtil.getEColorSpaceString(rightTextureType.eColorSpace)); |
||||
logger.severe(" Texture type: "+OpenVRUtil.getETextureTypeString(rightTextureType.eType)); |
||||
logger.severe(" Texture handle: "+rightTextureType.handle); |
||||
|
||||
logger.severe(" Right eye texture "+rightEyeTexture.getName()+" ("+rightEyeTexture.getImage().getId()+")"); |
||||
logger.severe(" Type: "+rightEyeTexture.getType()); |
||||
logger.severe(" Size: "+rightEyeTexture.getImage().getWidth()+"x"+rightEyeTexture.getImage().getHeight()); |
||||
logger.severe(" Image depth: "+rightEyeTexture.getImage().getDepth()); |
||||
logger.severe(" Image format: "+rightEyeTexture.getImage().getFormat()); |
||||
logger.severe(" Image color space: "+rightEyeTexture.getImage().getColorSpace()); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Initialize the VR view manager. |
||||
*/ |
||||
public void initialize() { |
||||
|
||||
logger.config("Initializing VR view manager."); |
||||
|
||||
initTextureSubmitStructs(); |
||||
setupCamerasAndViews(); |
||||
setupVRScene(); |
||||
moveScreenProcessingToEyes(); |
||||
if( app.hasTraditionalGUIOverlay() ) { |
||||
|
||||
app.getVRMouseManager().init(); |
||||
|
||||
// update the pose to position the gui correctly on start
|
||||
update(0f); |
||||
app.getVRGUIManager().positionGui(); |
||||
} |
||||
// if we are OSVR, our primary mirror window needs to be the same size as the render manager's output...
|
||||
if( app.getVRHardware() instanceof OSVR ) { |
||||
int origWidth = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode().getWidth(); |
||||
int origHeight = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode().getHeight(); |
||||
long window = ((LwjglWindow)application.getContext()).getWindowHandle(); |
||||
Vector2f windowSize = new Vector2f(); |
||||
((OSVR)app.getVRHardware()).getRenderSize(windowSize); |
||||
windowSize.x = Math.max(windowSize.x * 2f, leftCamera.getWidth()); |
||||
org.lwjgl.glfw.GLFW.glfwSetWindowSize(window, (int)windowSize.x, (int)windowSize.y); |
||||
application.getContext().getSettings().setResolution((int)windowSize.x, (int)windowSize.y); |
||||
|
||||
if (application.getRenderManager() != null) { |
||||
application.getRenderManager().notifyReshape((int)windowSize.x, (int)windowSize.y); |
||||
} |
||||
|
||||
org.lwjgl.glfw.GLFW.glfwSetWindowPos(window, origWidth - (int)windowSize.x, 32); |
||||
|
||||
org.lwjgl.glfw.GLFW.glfwFocusWindow(window); |
||||
|
||||
org.lwjgl.glfw.GLFW.glfwSetCursorPos(window, origWidth / 2.0, origHeight / 2.0); |
||||
} |
||||
|
||||
logger.config("Initialized VR view manager [SUCCESS]"); |
||||
} |
||||
|
||||
/** |
||||
* Prepare the size of the given {@link Camera camera} to adapt it to the underlying rendering context. |
||||
* @param cam the {@link Camera camera} to prepare. |
||||
* @param xMult the camera width multiplier. |
||||
*/ |
||||
private void prepareCameraSize(Camera cam, float xMult) { |
||||
Vector2f size = new Vector2f(); |
||||
VRAPI vrhmd = app.getVRHardware(); |
||||
|
||||
if( vrhmd == null ) { |
||||
size.x = 1280f; |
||||
size.y = 720f; |
||||
} else { |
||||
vrhmd.getRenderSize(size); |
||||
} |
||||
|
||||
if( size.x < application.getContext().getSettings().getWidth() ) { |
||||
size.x = application.getContext().getSettings().getWidth(); |
||||
} |
||||
if( size.y < application.getContext().getSettings().getHeight() ) { |
||||
size.y = application.getContext().getSettings().getHeight(); |
||||
} |
||||
|
||||
if( app.isInstanceVRRendering() ) size.x *= 2f; |
||||
|
||||
// other adjustments
|
||||
size.x *= xMult; |
||||
size.x *= resMult; |
||||
size.y *= resMult; |
||||
|
||||
if( cam.getWidth() != size.x || cam.getHeight() != size.y ) cam.resize((int)size.x, (int)size.y, false); |
||||
} |
||||
|
||||
/** |
||||
* Replaces rootNode as the main cameras scene with the distortion mesh |
||||
*/ |
||||
private void setupVRScene(){ |
||||
// no special scene to setup if we are doing instancing
|
||||
if( app.isInstanceVRRendering() ) { |
||||
// distortion has to be done with compositor here... we want only one pass on our end!
|
||||
if( application.getContext().getSettings().isSwapBuffers() ) { |
||||
setupMirrorBuffers(app.getCamera(), dualEyeTex, true); |
||||
} |
||||
return; |
||||
} |
||||
|
||||
leftEyeTexture = (Texture2D) leftViewport.getOutputFrameBuffer().getColorBuffer().getTexture(); |
||||
rightEyeTexture = (Texture2D)rightViewport.getOutputFrameBuffer().getColorBuffer().getTexture(); |
||||
leftEyeDepth = (Texture2D) leftViewport.getOutputFrameBuffer().getDepthBuffer().getTexture(); |
||||
rightEyeDepth = (Texture2D)rightViewport.getOutputFrameBuffer().getDepthBuffer().getTexture(); |
||||
|
||||
// main viewport is either going to be a distortion scene or nothing
|
||||
// mirroring is handled by copying framebuffers
|
||||
Iterator<Spatial> spatialIter = application.getViewPort().getScenes().iterator(); |
||||
while(spatialIter.hasNext()){ |
||||
application.getViewPort().detachScene(spatialIter.next()); |
||||
} |
||||
|
||||
spatialIter = application.getGuiViewPort().getScenes().iterator(); |
||||
while(spatialIter.hasNext()){ |
||||
application.getGuiViewPort().detachScene(spatialIter.next()); |
||||
} |
||||
|
||||
// only setup distortion scene if compositor isn't running (or using custom mesh distortion option)
|
||||
if( app.getVRHardware().getCompositor() == null ) { |
||||
Node distortionScene = new Node(); |
||||
Material leftMat = new Material(application.getAssetManager(), "Common/MatDefs/VR/OpenVR.j3md"); |
||||
leftMat.setTexture("Texture", leftEyeTexture); |
||||
Geometry leftEye = new Geometry("box", MeshUtil.setupDistortionMesh(JOpenVRLibrary.EVREye.EVREye_Eye_Left, app.getVRHardware())); |
||||
leftEye.setMaterial(leftMat); |
||||
distortionScene.attachChild(leftEye); |
||||
|
||||
Material rightMat = new Material(application.getAssetManager(), "Common/MatDefs/VR/OpenVR.j3md"); |
||||
rightMat.setTexture("Texture", rightEyeTexture); |
||||
Geometry rightEye = new Geometry("box", MeshUtil.setupDistortionMesh(JOpenVRLibrary.EVREye.EVREye_Eye_Right, app.getVRHardware())); |
||||
rightEye.setMaterial(rightMat); |
||||
distortionScene.attachChild(rightEye); |
||||
|
||||
distortionScene.updateGeometricState(); |
||||
|
||||
application.getViewPort().attachScene(distortionScene); |
||||
|
||||
//if( useCustomDistortion ) setupFinalFullTexture(app.getViewPort().getCamera());
|
||||
} |
||||
|
||||
if( application.getContext().getSettings().isSwapBuffers() ) { |
||||
setupMirrorBuffers(app.getCamera(), leftEyeTexture, false); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Update the VR view manager. |
||||
* This method is called by the attached {@link VRApplication VR application} and should not be called manually. |
||||
* @param tpf the time per frame. |
||||
*/ |
||||
public void update(float tpf) { |
||||
|
||||
// grab the observer
|
||||
Object obs = app.getObserver(); |
||||
Quaternion objRot; |
||||
Vector3f objPos; |
||||
if( obs instanceof Camera ) { |
||||
objRot = ((Camera)obs).getRotation(); |
||||
objPos = ((Camera)obs).getLocation(); |
||||
} else { |
||||
objRot = ((Spatial)obs).getWorldRotation(); |
||||
objPos = ((Spatial)obs).getWorldTranslation(); |
||||
} |
||||
// grab the hardware handle
|
||||
VRAPI dev = app.getVRHardware(); |
||||
if( dev != null ) { |
||||
// update the HMD's position & orientation
|
||||
dev.updatePose(); |
||||
dev.getPositionAndOrientation(hmdPos, hmdRot); |
||||
if( obs != null ) { |
||||
// update hmdPos based on obs rotation
|
||||
finalRotation.set(objRot); |
||||
finalRotation.mult(hmdPos, hmdPos); |
||||
finalRotation.multLocal(hmdRot); |
||||
} |
||||
|
||||
finalizeCamera(dev.getHMDVectorPoseLeftEye(), objPos, leftCamera); |
||||
finalizeCamera(dev.getHMDVectorPoseRightEye(), objPos, rightCamera); |
||||
} else { |
||||
leftCamera.setFrame(objPos, objRot); |
||||
rightCamera.setFrame(objPos, objRot); |
||||
} |
||||
|
||||
if( app.hasTraditionalGUIOverlay() ) { |
||||
// update the mouse?
|
||||
app.getVRMouseManager().update(tpf); |
||||
|
||||
// update GUI position?
|
||||
if( app.getVRGUIManager().wantsReposition || app.getVRGUIManager().getPositioningMode() != VRGuiManager.POSITIONING_MODE.MANUAL ) { |
||||
app.getVRGUIManager().positionGuiNow(tpf); |
||||
app.getVRGUIManager().updateGuiQuadGeometricState(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Place the camera within the scene. |
||||
* @param eyePos the eye position. |
||||
* @param obsPosition the observer position. |
||||
* @param cam the camera to place. |
||||
*/ |
||||
private void finalizeCamera(Vector3f eyePos, Vector3f obsPosition, Camera cam) { |
||||
finalRotation.mult(eyePos, finalPosition); |
||||
finalPosition.addLocal(hmdPos); |
||||
if( obsPosition != null ) finalPosition.addLocal(obsPosition); |
||||
finalPosition.y += heightAdjustment; |
||||
cam.setFrame(finalPosition, finalRotation); |
||||
} |
||||
|
||||
/** |
||||
* Handles moving filters from the main view to each eye |
||||
*/ |
||||
public void moveScreenProcessingToEyes() { |
||||
if( rightViewport == null ) return; |
||||
syncScreenProcessing(application.getViewPort()); |
||||
application.getViewPort().clearProcessors(); |
||||
} |
||||
|
||||
/** |
||||
* Sets the two views to use the list of {@link SceneProcessor processors}. |
||||
* @param sourceViewport the {@link ViewPort viewport} that contains the processors to use. |
||||
*/ |
||||
public void syncScreenProcessing(ViewPort sourceViewport) { |
||||
if( rightViewport == null ) return; |
||||
// setup post processing filters
|
||||
if( rightPostProcessor == null ) { |
||||
rightPostProcessor = new FilterPostProcessor(application.getAssetManager()); |
||||
leftPostProcessor = new FilterPostProcessor(application.getAssetManager()); |
||||
} |
||||
// clear out all filters & processors, to start from scratch
|
||||
rightPostProcessor.removeAllFilters(); |
||||
leftPostProcessor.removeAllFilters(); |
||||
leftViewport.clearProcessors(); |
||||
rightViewport.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
|
||||
leftViewport.addProcessor(leftPostProcessor); |
||||
rightViewport.addProcessor(rightPostProcessor); |
||||
// 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()) { |
||||
if (sceneProcessor instanceof FilterPostProcessor) { |
||||
for(Filter f : ((FilterPostProcessor)sceneProcessor).getFilterList() ) { |
||||
if( f instanceof TranslucentBucketFilter ) { |
||||
// just remove this filter, we will add it at the end manually
|
||||
((FilterPostProcessor)sceneProcessor).removeFilter(f); |
||||
} else { |
||||
leftPostProcessor.addFilter(f); |
||||
// clone to the right
|
||||
Filter f2; |
||||
if(f instanceof FogFilter){ |
||||
f2 = FilterUtil.cloneFogFilter((FogFilter)f); |
||||
} else if (f instanceof CartoonSSAO ) { |
||||
f2 = new CartoonSSAO((CartoonSSAO)f); |
||||
} else if (f instanceof SSAOFilter){ |
||||
f2 = FilterUtil.cloneSSAOFilter((SSAOFilter)f); |
||||
} else if (f instanceof DirectionalLightShadowFilter){ |
||||
f2 = FilterUtil.cloneDirectionalLightShadowFilter(application.getAssetManager(), (DirectionalLightShadowFilter)f); |
||||
} else { |
||||
f2 = f; // dof, bloom, lightscattering etc.
|
||||
} |
||||
rightPostProcessor.addFilter(f2); |
||||
} |
||||
} |
||||
} else if (sceneProcessor instanceof VRDirectionalLightShadowRenderer) { |
||||
// shadow processing
|
||||
// TODO: make right shadow processor use same left shadow maps for performance
|
||||
VRDirectionalLightShadowRenderer dlsr = (VRDirectionalLightShadowRenderer) sceneProcessor; |
||||
VRDirectionalLightShadowRenderer dlsrRight = dlsr.clone(); |
||||
dlsrRight.setLight(dlsr.getLight()); |
||||
rightViewport.getProcessors().add(0, dlsrRight); |
||||
leftViewport.getProcessors().add(0, sceneProcessor); |
||||
} |
||||
} |
||||
// make sure each has a translucent filter renderer
|
||||
leftPostProcessor.addFilter(new TranslucentBucketFilter()); |
||||
rightPostProcessor.addFilter(new TranslucentBucketFilter()); |
||||
} |
||||
|
||||
private void setupCamerasAndViews() { |
||||
// get desired frustrum from original camera
|
||||
Camera origCam = app.getCamera(); |
||||
float fFar = origCam.getFrustumFar(); |
||||
float fNear = origCam.getFrustumNear(); |
||||
|
||||
// if we are using OSVR get the eye info here
|
||||
if( app.getVRHardware() instanceof OSVR ) { |
||||
((OSVR)app.getVRHardware()).getEyeInfo(); |
||||
} |
||||
|
||||
// restore frustrum on distortion scene cam, if needed
|
||||
if( app.isInstanceVRRendering() ) { |
||||
leftCamera = origCam; |
||||
} else if( app.compositorAllowed() == false ) { |
||||
origCam.setFrustumFar(100f); |
||||
origCam.setFrustumNear(1f); |
||||
leftCamera = origCam.clone(); |
||||
prepareCameraSize(origCam, 2f); |
||||
} else { |
||||
leftCamera = origCam.clone(); |
||||
} |
||||
|
||||
leftCamera.setFrustumPerspective(app.getDefaultFOV(), app.getDefaultAspect(), fNear, fFar); |
||||
|
||||
prepareCameraSize(leftCamera, 1f); |
||||
if( app.getVRHardware() != null ) leftCamera.setProjectionMatrix(app.getVRHardware().getHMDMatrixProjectionLeftEye(leftCamera)); |
||||
//org.lwjgl.opengl.GL11.glEnable(org.lwjgl.opengl.GL30.GL_FRAMEBUFFER_SRGB);
|
||||
|
||||
if( !app.isInstanceVRRendering()) { |
||||
leftViewport = setupViewBuffers(leftCamera, LEFT_VIEW_NAME); |
||||
rightCamera = leftCamera.clone(); |
||||
if( app.getVRHardware() != null ){ |
||||
rightCamera.setProjectionMatrix(app.getVRHardware().getHMDMatrixProjectionRightEye(rightCamera)); |
||||
} |
||||
rightViewport = setupViewBuffers(rightCamera, RIGHT_VIEW_NAME); |
||||
} else { |
||||
|
||||
System.err.println("[VRViewManager] THIS CODE NEED CHANGES !!!"); |
||||
leftViewport = application.getViewPort(); |
||||
//leftViewport.attachScene(app.getRootNode());
|
||||
rightCamera = leftCamera.clone(); |
||||
if( app.getVRHardware() != null ){ |
||||
rightCamera.setProjectionMatrix(app.getVRHardware().getHMDMatrixProjectionRightEye(rightCamera)); |
||||
} |
||||
|
||||
org.lwjgl.opengl.GL11.glEnable(org.lwjgl.opengl.GL30.GL_CLIP_DISTANCE0); |
||||
|
||||
//FIXME: [jme-vr] Fix with JMonkey next release
|
||||
//RenderManager._VRInstancing_RightCamProjection = camRight.getViewProjectionMatrix();
|
||||
setupFinalFullTexture(application.getViewPort().getCamera()); |
||||
} |
||||
|
||||
// setup gui
|
||||
app.getVRGUIManager().setupGui(leftCamera, rightCamera, leftViewport, rightViewport); |
||||
|
||||
if( app.getVRHardware() != null ) { |
||||
// call these to cache the results internally
|
||||
app.getVRHardware().getHMDMatrixPoseLeftEye(); |
||||
app.getVRHardware().getHMDMatrixPoseRightEye(); |
||||
} |
||||
|
||||
} |
||||
|
||||
private ViewPort setupMirrorBuffers(Camera cam, Texture tex, boolean expand) { |
||||
Camera clonecam = cam.clone(); |
||||
ViewPort viewPort = application.getRenderManager().createPostView("MirrorView", clonecam); |
||||
clonecam.setParallelProjection(true); |
||||
viewPort.setClearFlags(true, true, true); |
||||
viewPort.setBackgroundColor(ColorRGBA.Black); |
||||
Picture pic = new Picture("fullscene"); |
||||
pic.setLocalTranslation(-0.75f, -0.5f, 0f); |
||||
if( expand ) { |
||||
pic.setLocalScale(3f, 1f, 1f); |
||||
} else { |
||||
pic.setLocalScale(1.5f, 1f, 1f); |
||||
} |
||||
pic.setQueueBucket(Bucket.Opaque); |
||||
pic.setTexture(application.getAssetManager(), (Texture2D)tex, false); |
||||
viewPort.attachScene(pic); |
||||
viewPort.setOutputFrameBuffer(null); |
||||
|
||||
pic.updateGeometricState(); |
||||
|
||||
return viewPort; |
||||
} |
||||
|
||||
private void setupFinalFullTexture(Camera cam) { |
||||
// create offscreen framebuffer
|
||||
FrameBuffer out = new FrameBuffer(cam.getWidth(), cam.getHeight(), 1); |
||||
//offBuffer.setSrgb(true);
|
||||
|
||||
//setup framebuffer's texture
|
||||
dualEyeTex = new Texture2D(cam.getWidth(), cam.getHeight(), Image.Format.RGBA8); |
||||
dualEyeTex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps); |
||||
dualEyeTex.setMagFilter(Texture.MagFilter.Bilinear); |
||||
|
||||
logger.config("Dual eye texture "+dualEyeTex.getName()+" ("+dualEyeTex.getImage().getId()+")"); |
||||
logger.config(" Type: "+dualEyeTex.getType()); |
||||
logger.config(" Size: "+dualEyeTex.getImage().getWidth()+"x"+dualEyeTex.getImage().getHeight()); |
||||
logger.config(" Image depth: "+dualEyeTex.getImage().getDepth()); |
||||
logger.config(" Image format: "+dualEyeTex.getImage().getFormat()); |
||||
logger.config(" Image color space: "+dualEyeTex.getImage().getColorSpace()); |
||||
|
||||
//setup framebuffer to use texture
|
||||
out.setDepthBuffer(Image.Format.Depth); |
||||
out.setColorTexture(dualEyeTex); |
||||
|
||||
ViewPort viewPort = application.getViewPort(); |
||||
viewPort.setClearFlags(true, true, true); |
||||
viewPort.setBackgroundColor(ColorRGBA.Black); |
||||
viewPort.setOutputFrameBuffer(out); |
||||
|
||||
} |
||||
|
||||
private ViewPort setupViewBuffers(Camera cam, String viewName){ |
||||
// create offscreen framebuffer
|
||||
FrameBuffer offBufferLeft = new FrameBuffer(cam.getWidth(), cam.getHeight(), 1); |
||||
//offBufferLeft.setSrgb(true);
|
||||
|
||||
//setup framebuffer's texture
|
||||
Texture2D offTex = new Texture2D(cam.getWidth(), cam.getHeight(), Image.Format.RGBA8); |
||||
offTex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps); |
||||
offTex.setMagFilter(Texture.MagFilter.Bilinear); |
||||
|
||||
//setup framebuffer to use texture
|
||||
offBufferLeft.setDepthBuffer(Image.Format.Depth); |
||||
offBufferLeft.setColorTexture(offTex); |
||||
|
||||
ViewPort viewPort = application.getRenderManager().createPreView(viewName, cam); |
||||
viewPort.setClearFlags(true, true, true); |
||||
viewPort.setBackgroundColor(ColorRGBA.Black); |
||||
|
||||
Iterator<Spatial> spatialIter = application.getViewPort().getScenes().iterator(); |
||||
while(spatialIter.hasNext()){ |
||||
viewPort.attachScene(spatialIter.next()); |
||||
} |
||||
|
||||
//set viewport to render to offscreen framebuffer
|
||||
viewPort.setOutputFrameBuffer(offBufferLeft); |
||||
return viewPort; |
||||
} |
||||
} |
Loading…
Reference in new issue