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.Callback; |
||||||
import com.sun.jna.Library; |
import com.sun.jna.Library; |
||||||
import com.sun.jna.Native; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Library; |
||||||
import com.sun.jna.Native; |
import com.sun.jna.Native; |
||||||
import com.sun.jna.NativeLibrary; |
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.Library; |
||||||
import com.sun.jna.Native; |
import com.sun.jna.Native; |
||||||
import com.sun.jna.NativeLibrary; |
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> |
* JNA Wrapper for library <b>osvrInterface</b><br> |
||||||
* This file was autogenerated by <a href="http://jnaerator.googlecode.com/">JNAerator</a>,<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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Library; |
||||||
import com.sun.jna.Native; |
import com.sun.jna.Native; |
||||||
import com.sun.jna.NativeLibrary; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.ochafik.lang.jnaerator.runtime.NativeSize; |
||||||
import com.sun.jna.Callback; |
import com.sun.jna.Callback; |
||||||
import com.sun.jna.Pointer; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
import java.util.Arrays; |
||||||
import java.util.List; |
import java.util.List; |
||||||
import osvrmatrixconventions.OSVR_Pose3; |
|
||||||
/** |
/** |
||||||
* This file was autogenerated by <a href="http://jnaerator.googlecode.com/">JNAerator</a>,<br> |
* 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> |
* 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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Pointer; |
||||||
import com.sun.jna.Structure; |
import com.sun.jna.Structure; |
||||||
import java.util.Arrays; |
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.Library; |
||||||
import com.sun.jna.Native; |
import com.sun.jna.Native; |
||||||
import com.sun.jna.NativeLibrary; |
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