Creating a VRAppState

fix-456
Julien Seinturier 8 years ago
parent b9071513e7
commit 41bead60e3
  1. 121
      jme3-core/src/main/java/com/jme3/scene/Spatial.java
  2. 843
      jme3-vr/src/main/java/com/jme3/app/VRAppState.java
  3. 34
      jme3-vr/src/main/java/com/jme3/app/VRApplication.java
  4. 147
      jme3-vr/src/main/java/com/jme3/app/VRConstants.java
  5. 17
      jme3-vr/src/main/java/com/jme3/input/vr/OSVR.java
  6. 25
      jme3-vr/src/main/java/com/jme3/input/vr/OSVRInput.java
  7. 26
      jme3-vr/src/main/java/com/jme3/input/vr/OpenVR.java
  8. 33
      jme3-vr/src/main/java/com/jme3/input/vr/OpenVRInput.java
  9. 8
      jme3-vr/src/main/java/com/jme3/input/vr/VRAPI.java
  10. 7
      jme3-vr/src/main/java/com/jme3/input/vr/VRInputAPI.java
  11. 7
      jme3-vr/src/main/java/jmevr/util/MeshUtil.java
  12. 58
      jme3-vr/src/main/java/jmevr/util/VRGuiManager.java
  13. 71
      jme3-vr/src/main/java/jmevr/util/VRMouseManager.java
  14. 1715
      jme3-vr/src/main/java/jmevr/util/VRViewManager.java

@ -170,6 +170,60 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
*/
protected transient int refreshFlags = 0;
public void refreshFlagOr(int flag){
refreshFlags |= flag;
//logger.warning("Changing refresh flags for spatial " + getName()+", flags: "+getRefreshFlagsDescription());
}
public void refreshFlagAnd(int flag){
refreshFlags &= flag;
//logger.warning("Changing refresh flags for spatial " + getName()+", flags: "+getRefreshFlagsDescription());
}
public int refreshFlagGetAnd(int flag){
return (refreshFlags & flag);
}
public int getRefreshFlags(){
return refreshFlags;
}
public String getRefreshFlagsDescription(){
String str = "";
if (refreshFlags == 0){
str += "OK";
} else {
// need light resort + combine transforms
if ((refreshFlags & RF_TRANSFORM) == RF_TRANSFORM){
str += "RF_TRANSFORM ";
}
// need light resort + combine transforms
if ((refreshFlags & RF_BOUND) == RF_BOUND){
str += "RF_BOUND ";
}
// changes in light lists
if ((refreshFlags & RF_LIGHTLIST) == RF_LIGHTLIST){
str += "RF_LIGHTLIST ";
}
// some child need geometry update
if ((refreshFlags & RF_CHILD_LIGHTLIST) == RF_CHILD_LIGHTLIST){
str += "RF_CHILD_LIGHTLIST ";
}
// some child need geometry update
if ((refreshFlags & RF_MATPARAM_OVERRIDE) == RF_MATPARAM_OVERRIDE){
str += "RF_MATPARAM_OVERRIDE ";
}
}
return str;
}
/**
* Set to true if a subclass requires updateLogicalState() even
* if it doesn't have any controls. Defaults to true thus implementing
@ -209,7 +263,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
localOverrides = new SafeArrayList<>(MatParamOverride.class);
worldOverrides = new SafeArrayList<>(MatParamOverride.class);
refreshFlags |= RF_BOUND;
refreshFlagOr(RF_BOUND);
}
public void setKey(AssetKey key) {
@ -272,35 +326,35 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
* a refresh is required.
*/
protected void setTransformRefresh() {
refreshFlags |= RF_TRANSFORM;
refreshFlagOr(RF_TRANSFORM);
setBoundRefresh();
}
protected void setLightListRefresh() {
refreshFlags |= RF_LIGHTLIST;
refreshFlagOr(RF_LIGHTLIST);
// Make sure next updateGeometricState() visits this branch
// to update lights.
Spatial p = parent;
while (p != null) {
if ((p.refreshFlags & RF_CHILD_LIGHTLIST) != 0) {
if ((p.refreshFlagGetAnd(RF_CHILD_LIGHTLIST)) != 0) {
// The parent already has this flag,
// so must all ancestors.
return;
}
p.refreshFlags |= RF_CHILD_LIGHTLIST;
p.refreshFlagOr(RF_CHILD_LIGHTLIST);
p = p.parent;
}
}
protected void setMatParamOverrideRefresh() {
refreshFlags |= RF_MATPARAM_OVERRIDE;
refreshFlagOr(RF_MATPARAM_OVERRIDE);
Spatial p = parent;
while (p != null) {
if ((p.refreshFlags & RF_MATPARAM_OVERRIDE) != 0) {
if ((p.refreshFlagGetAnd(RF_MATPARAM_OVERRIDE)) != 0) {
return;
}
p.refreshFlags |= RF_MATPARAM_OVERRIDE;
p.refreshFlagOr(RF_MATPARAM_OVERRIDE);
p = p.parent;
}
}
@ -310,15 +364,15 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
* a refresh is required.
*/
protected void setBoundRefresh() {
refreshFlags |= RF_BOUND;
refreshFlagOr(RF_BOUND);
Spatial p = parent;
while (p != null) {
if ((p.refreshFlags & RF_BOUND) != 0) {
if ((p.refreshFlagGetAnd(RF_BOUND)) != 0) {
return;
}
p.refreshFlags |= RF_BOUND;
p.refreshFlagOr(RF_BOUND);
p = p.parent;
}
}
@ -353,11 +407,14 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
* (should be rendered), false if outside.
*/
public boolean checkCulling(Camera cam) {
if (refreshFlags != 0) {
if (getRefreshFlags() != 0) {
/*
throw new IllegalStateException("Scene graph is not properly updated for rendering.\n"
+ "State was changed after rootNode.updateGeometricState() call. \n"
+ "Make sure you do not modify the scene from another thread!\n"
+ "Problem spatial name: " + getName());
+ "Problem spatial name: " + getName()+", flags: "+getRefreshFlagsDescription());
*/
logger.warning("Invalid refresh flags for spatial " + getName()+", flags: "+getRefreshFlagsDescription());
}
CullHint cm = getCullHint();
@ -571,28 +628,28 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
// for a node, the world bound is a combination of all it's children
// bounds
// -> handled by subclass
refreshFlags &= ~RF_BOUND;
refreshFlagAnd(~RF_BOUND);
}
protected void updateWorldLightList() {
if (parent == null) {
worldLights.update(localLights, null);
refreshFlags &= ~RF_LIGHTLIST;
refreshFlagAnd(~RF_LIGHTLIST);
} else {
assert (parent.refreshFlags & RF_LIGHTLIST) == 0;
assert (parent.refreshFlagGetAnd(RF_LIGHTLIST)) == 0;
worldLights.update(localLights, parent.worldLights);
refreshFlags &= ~RF_LIGHTLIST;
refreshFlagAnd(~RF_LIGHTLIST);
}
}
protected void updateMatParamOverrides() {
refreshFlags &= ~RF_MATPARAM_OVERRIDE;
refreshFlagAnd(~RF_MATPARAM_OVERRIDE);
worldOverrides.clear();
if (parent == null) {
worldOverrides.addAll(localOverrides);
} else {
assert (parent.refreshFlags & RF_MATPARAM_OVERRIDE) == 0;
assert (parent.refreshFlagGetAnd(RF_MATPARAM_OVERRIDE)) == 0;
worldOverrides.addAll(parent.worldOverrides);
worldOverrides.addAll(localOverrides);
}
@ -643,13 +700,13 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
protected void updateWorldTransforms() {
if (parent == null) {
worldTransform.set(localTransform);
refreshFlags &= ~RF_TRANSFORM;
refreshFlagAnd(~RF_TRANSFORM);
} else {
// check if transform for parent is updated
assert ((parent.refreshFlags & RF_TRANSFORM) == 0);
assert ((parent.refreshFlagGetAnd(RF_TRANSFORM)) == 0);
worldTransform.set(localTransform);
worldTransform.combineWithParent(parent.worldTransform);
refreshFlags &= ~RF_TRANSFORM;
refreshFlagAnd(~RF_TRANSFORM);
}
}
@ -658,13 +715,13 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
* efficient manner possible.
*/
void checkDoTransformUpdate() {
if ((refreshFlags & RF_TRANSFORM) == 0) {
if ((refreshFlagGetAnd(RF_TRANSFORM)) == 0) {
return;
}
if (parent == null) {
worldTransform.set(localTransform);
refreshFlags &= ~RF_TRANSFORM;
refreshFlagAnd(~RF_TRANSFORM);
} else {
TempVars vars = TempVars.get();
@ -675,14 +732,14 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
Spatial hisParent = rootNode.parent;
if (hisParent == null) {
rootNode.worldTransform.set(rootNode.localTransform);
rootNode.refreshFlags &= ~RF_TRANSFORM;
rootNode.refreshFlagAnd(~RF_TRANSFORM);
i--;
break;
}
stack[i] = rootNode;
if ((hisParent.refreshFlags & RF_TRANSFORM) == 0) {
if ((hisParent.refreshFlagGetAnd(RF_TRANSFORM)) == 0) {
break;
}
@ -707,7 +764,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
* manner possible.
*/
void checkDoBoundUpdate() {
if ((refreshFlags & RF_BOUND) == 0) {
if ((refreshFlagGetAnd(RF_BOUND)) == 0) {
return;
}
@ -897,20 +954,20 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
// NOTE: Update world transforms first because
// bound transform depends on them.
if ((refreshFlags & RF_LIGHTLIST) != 0) {
if ((refreshFlagGetAnd(RF_LIGHTLIST)) != 0) {
updateWorldLightList();
}
if ((refreshFlags & RF_TRANSFORM) != 0) {
if ((refreshFlagGetAnd(RF_TRANSFORM)) != 0) {
updateWorldTransforms();
}
if ((refreshFlags & RF_BOUND) != 0) {
if ((refreshFlagGetAnd(RF_BOUND)) != 0) {
updateWorldBound();
}
if ((refreshFlags & RF_MATPARAM_OVERRIDE) != 0) {
if ((refreshFlagGetAnd(RF_MATPARAM_OVERRIDE)) != 0) {
updateMatParamOverrides();
}
assert refreshFlags == 0;
assert getRefreshFlags() == 0;
}
/**

@ -0,0 +1,843 @@
package com.jme3.app;
import com.jme3.app.Application;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
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.math.ColorRGBA;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.post.PreNormalCaching;
import com.jme3.renderer.Camera;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Spatial;
import com.jme3.system.AppSettings;
import com.jme3.system.jopenvr.JOpenVRLibrary;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.Iterator;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
import jmevr.util.VRGuiManager;
import jmevr.util.VRMouseManager;
import jmevr.util.VRViewManager;
import jmevr.util.VRGuiManager.POSITIONING_MODE;
/**
* A JMonkey app state dedicated to Virtual Reality.
* An application that want to use VR devices (HTC vive, ...) has to use this app state.<br>
* As this app state and the main {@link Application application} have to share {@link AppSettings application settings},
* the common way to use this app state is:<br>
* <ul>
* <li>To create {@link AppSettings application settings} and set the VR related settings (see {@link VRConstants}).
* <li>To instantiate this app state with the created settings.
* <li>To instantiate the main {@link Application application} and to attach it to the created settings (with {@link Application#setSettings(AppSettings) setSettings(AppSettings)}).
* <li>To start the main {@link Application application}.
* </ul>
* Attaching an instance of this app state to an already started application may cause crashes.
* @author Julien Seinturier - JOrigin project - <a href="http://www.jorigin.org">http:/www.jorigin.org</a>
*/
public class VRAppState extends AbstractAppState {
private static final Logger logger = Logger.getLogger(VRAppState.class.getName());
/**
* The underlying system VR API. By default set to {@link VRConstants#SETTING_VRAPI_OPENVR_VALUE}.
*/
public int vrBinding = VRConstants.SETTING_VRAPI_OPENVR_VALUE;
/**
* Is the application has not to start within VR mode (default is <code>false</code>).
*/
public boolean DISABLE_VR = false;
private VRAPI VRhardware = null;
private VRGuiManager guiManager = null;
private VRMouseManager mouseManager = null;
private VRViewManager viewmanager = null;
private String OS;
private Camera dummyCam;
private Spatial observer = null;
private boolean VRSupportedOS;
private boolean forceVR = false;;
private boolean disableSwapBuffers = true;
private boolean disableVR = false;
private boolean seated;
private boolean nogui;
private boolean instanceVR = false;
private float defaultFOV = 108f;
private float defaultAspect = 1f;
private float fFar = 1000f;
private float fNear = 0.1f;
private int xWin = 1920;
private int yWin = 1080;
private float resMult = 1f;
private boolean useCompositor = true;
private boolean compositorOS;
/*
where is the headset pointing, after all rotations are combined?
depends on observer rotation, if any
*/
private Quaternion tempq = new Quaternion();
private Application application = null;
private AppStateManager stateManager = null;
private AppSettings settings = null;
/**
* Create a new default VR app state.
*/
public VRAppState() {
super();
dummyCam = new Camera();
// Create the GUI manager.
guiManager = new VRGuiManager();
// Create a new view manager.
viewmanager = new VRViewManager();
// Create a new mouse manager.
mouseManager = new VRMouseManager();
}
/**
* Create a new VR app state with given settings.
* @param settings the settings to use.
*/
public VRAppState(AppSettings settings){
this();
this.settings = settings;
processSettings(settings);
}
/**
* Simple update of the app state, this method should contains any spatial updates.
* This method is called by the {@link #update(float) update()} method and should not be called manually.
* @param tpf the application time.
*/
public void simpleUpdate(float tpf) {
return;
}
/**
* Rendering callback of the app state. This method is called by the {@link #update(float) update()} method and should not be called manually.
* @param renderManager the {@link RenderManager render manager}.
*/
public void simpleRender(RenderManager renderManager) {
PreNormalCaching.resetCache(isInVR());
}
/**
* Set the frustrum values for the application.
* @param near the frustrum near value.
* @param far the frustrum far value.
*/
public void setFrustrumNearFar(float near, float far) {
fNear = near;
fFar = far;
}
/**
* Set the mirror window size in pixel.
* @param width the width of the mirror window in pixel.
* @param height the height of the mirror window in pixel.
*/
public void setMirrorWindowSize(int width, int height) {
xWin = width;
yWin = height;
}
/**
* Set the resolution multiplier.
* @param val the resolution multiplier.
*/
public void setResolutionMultiplier(float val) {
resMult = val;
if( viewmanager != null ){
viewmanager.setResolutionMultiplier(resMult);
}
}
/**
* 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;
}
/**
* 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;
}
/**
* Get the {@link Camera camera} attached to this application state.
* 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 to this application state.
*/
public Camera getCamera() {
if( isInVR() && viewmanager != null && viewmanager.getLeftCamera() != null ) {
return dummyCam;
}
return application.getCamera();
}
/**
* 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( VRhardware instanceof OpenVR ) {
if( VRhardware.getCompositor() == null ) return;
if( seated ) {
((OpenVR)VRhardware).getCompositor().SetTrackingSpace.apply(JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseSeated);
} else {
((OpenVR)VRhardware).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;
}
/**
* Reset headset pose if seating experience.
*/
public void resetSeatedPose(){
if( VRSupportedOS == false || isSeatedExperience() == false ) return;
VRhardware.reset();
}
/**
* 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 isInstanceVRRendering() {
return instanceVR && isInVR();
}
/**
* 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 DISABLE_VR == false && (forceVR || VRSupportedOS && VRhardware != null && VRhardware.isInitialized());
}
/**
* 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;
}
/**
* Move filters from the main scene into the eye's.
* This removes filters from the main scene.
*/
public void moveScreenProcessingToVR() {
if( isInVR() ) {
viewmanager.moveScreenProcessingToEyes();
}
}
/**
* 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;
}
/**
* 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 ) {
return getCamera();
}
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 observer final rotation within the scene.
* @return the observer final rotation within the scene.
* @see #getFinalObserverPosition()
*/
public Quaternion getFinalObserverRotation() {
if( viewmanager == null ) {
if( observer == null ) {
return getCamera().getRotation();
} else return observer.getWorldRotation();
}
if( observer == null ) {
tempq.set(dummyCam.getRotation());
} else {
tempq.set(observer.getWorldRotation());
}
return tempq.multLocal(VRhardware.getOrientation());
}
/**
* Get the observer final position within the scene.
* @return the observer position.
* @see #getFinalObserverRotation()
*/
public Vector3f getFinalObserverPosition() {
if( viewmanager == null ) {
if( observer == null ) {
return getCamera().getLocation();
} else return observer.getWorldTranslation();
}
Vector3f pos = VRhardware.getPosition();
if( observer == null ) {
dummyCam.getRotation().mult(pos, pos);
return pos.addLocal(dummyCam.getLocation());
} else {
observer.getWorldRotation().mult(pos, pos);
return pos.addLocal(observer.getWorldTranslation());
}
}
/**
* 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 VR headset left viewport.
* @return the VR headset left viewport.
* @see #getRightViewPort()
*/
public ViewPort getLeftViewPort() {
if( viewmanager == null ) return application.getViewPort();
return viewmanager.getLeftViewport();
}
/**
* Get the VR headset right viewport.
* @return the VR headset right viewport.
* @see #getLeftViewPort()
*/
public ViewPort getRightViewPort() {
if( viewmanager == null ) return application.getViewPort();
return viewmanager.getRightViewport();
}
/**
* Set the background color for both left and right view ports.
* @param clr the background color.
*/
public void setBackgroundColors(ColorRGBA clr) {
if( viewmanager == null ) {
application.getViewPort().setBackgroundColor(clr);
} else if( viewmanager.getLeftViewport() != null ) {
viewmanager.getLeftViewport().setBackgroundColor(clr);
if( viewmanager.getRightViewport() != null ) viewmanager.getRightViewport().setBackgroundColor(clr);
}
}
/**
* Get the {@link Application} to which this app state is attached.
* @return the {@link Application} to which this app state is attached.
* @see #getStateManager()
*/
public Application getApplication(){
return application;
}
/**
* Get the {@link AppStateManager state manager} to which this app state is attached.
* @return the {@link AppStateManager state manager} to which this app state is attached.
* @see #getApplication()
*/
public AppStateManager getStateManager(){
return stateManager;
}
/**
* Get the VR underlying hardware.
* @return the VR underlying hardware.
*/
public VRAPI getVRHardware() {
return VRhardware;
}
/**
* Get the VR dedicated input.
* @return the VR dedicated input.
*/
public VRInputAPI getVRinput() {
if( VRhardware == null ){
return null;
}
return VRhardware.getVRinput();
}
/**
* Get the VR view manager.
* @return the VR view manager.
*/
public VRViewManager getVRViewManager() {
return viewmanager;
}
/**
* Get the GUI manager attached to this application.
* @return the GUI manager attached to this application.
*/
public VRGuiManager getVRGUIManager(){
return guiManager;
}
/**
* Get the VR mouse manager attached to this application.
* @return the VR mouse manager attached to this application.
*/
public VRMouseManager getVRMouseManager(){
return mouseManager;
}
/**
* Get the {@link AppSettings settings} attached to this app state.
* @return the {@link AppSettings settings} attached to this app state.
* @see #setSettings(AppSettings)
*/
public AppSettings getSettings(){
return settings;
}
/**
* Set the {@link AppSettings settings} attached to this app state.
* @param settings the {@link AppSettings settings} attached to this app state.
* @see #getSettings()
*/
public void setSettings(AppSettings settings){
this.settings = settings;
processSettings(settings);
}
@Override
public void update(float tpf) {
// update VR pose & cameras
if( viewmanager != null ) {
viewmanager.update(tpf);
} else if( observer != null ) {
getCamera().setFrame(observer.getWorldTranslation(), observer.getWorldRotation());
}
//FIXME: check if this code is necessary.
// Updates scene and gui states.
Iterator<Spatial> spatialIter = application.getViewPort().getScenes().iterator();
Spatial spatial = null;
while(spatialIter.hasNext()){
spatial = spatialIter.next();
spatial.updateLogicalState(tpf);
spatial.updateGeometricState();
}
if( isInVR() == false || guiManager.getPositioningMode() == POSITIONING_MODE.MANUAL ) {
// only update geometric state here if GUI is in manual mode, or not in VR
// it will get updated automatically in the viewmanager update otherwise
spatialIter = application.getGuiViewPort().getScenes().iterator();
spatial = null;
while(spatialIter.hasNext()){
spatial = spatialIter.next();
spatial.updateGeometricState();
}
}
// use the analog control on the first tracked controller to push around the mouse
getVRMouseManager().updateAnalogAsMouse(0, null, null, null, tpf);
}
@Override
public void postRender() {
super.postRender();
// update compositor?
if( viewmanager != null ) {
viewmanager.sendTextures();
}
}
@Override
public void initialize(AppStateManager stateManager, Application app) {
super.initialize(stateManager, app);
this.application = app;
this.stateManager = stateManager;
// disable annoying warnings about GUI stuff being updated, which is normal behavior
// for late GUI placement for VR purposes
Logger.getLogger("com.jme3").setLevel(Level.SEVERE);
// VR module attch
guiManager.attach(this, app);
viewmanager.attach(this, app);
mouseManager.attach(this, app);
app.getCamera().setFrustumFar(fFar);
app.getCamera().setFrustumNear(fNear);
dummyCam = app.getCamera().clone();
if( isInVR() ) {
logger.config("VR mode enabled.");
if( VRhardware != null ) {
VRhardware.initVRCompositor(compositorAllowed());
} else {
logger.warning("No VR system found.");
}
viewmanager.setResolutionMultiplier(resMult);
//inputManager.addMapping(RESET_HMD, new KeyTrigger(KeyInput.KEY_F9));
//setLostFocusBehavior(LostFocusBehavior.Disabled);
} else {
logger.config("VR mode disabled.");
//viewPort.attachScene(rootNode);
//guiViewPort.attachScene(guiNode);
}
if( viewmanager != null ) {
viewmanager.initialize();
}
}
@Override
public void stateAttached(AppStateManager stateManager) {
super.stateAttached(stateManager); //To change body of generated methods, choose Tools | Templates.
if (settings == null) {
settings = new AppSettings(true);
logger.config("Using default settings.");
} else {
logger.config("Using given settings.");
}
// we are going to use OpenVR now, not the Oculus Rift
// OpenVR does support the Rift
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 && disableVR == false ) {
if( vrBinding == VRConstants.SETTING_VRAPI_OSVR_VALUE ) {
VRhardware = new OSVR(this);
logger.config("Creating OSVR wrapper [SUCCESS]");
} else if( vrBinding == VRConstants.SETTING_VRAPI_OPENVR_VALUE ) {
VRhardware = new OpenVR(this);
logger.config("Creating OpenVR wrapper [SUCCESS]");
} else {
logger.config("Cannot create VR binding: "+vrBinding+" [FAILED]");
}
if( VRhardware.initialize() ) {
logger.config("VR native wrapper initialized [SUCCESS]");
} else {
logger.warning("VR native wrapper initialized [FAILED]");
}
}
GraphicsDevice defDev = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
if( isInVR() && !compositorAllowed() ) {
// "easy extended" mode
// setup experimental JFrame on external device
// first, find the VR device
GraphicsDevice VRdev = null;
GraphicsDevice[] devs = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices();
// pick the display that isn't the default one
for(GraphicsDevice gd : devs) {
if( gd != defDev ) {
VRdev = gd;
break;
}
}
// did we get the VR device?
if( VRdev != null ) {
// set properties for VR acceleration
try {
java.awt.DisplayMode useDM = null;
int max = 0;
for(java.awt.DisplayMode dm : VRdev.getDisplayModes()) {
int check = dm.getHeight() + dm.getWidth() + dm.getRefreshRate() + dm.getBitDepth();
if( check > max ) {
max = check;
useDM = dm;
}
}
// create a window for the VR device
settings.setWidth(useDM.getWidth());
settings.setHeight(useDM.getHeight());
settings.setBitsPerPixel(useDM.getBitDepth());
settings.setFrequency(useDM.getRefreshRate());
settings.setSwapBuffers(true);
settings.setVSync(true); // allow vsync on this display
stateManager.getApplication().setSettings(settings);
logger.config("Updated underlying application settings.");
//VRdev.setFullScreenWindow(VRwindow);
// make sure we are in the right display mode
if( VRdev.getDisplayMode().equals(useDM) == false ) {
VRdev.setDisplayMode(useDM);
}
return;
} catch(Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
}
} else {
logger.config("Cannot access to external screen.");
}
} else {
if (!isInVR()){
logger.config("Cannot switch to VR mode (VR disabled by user).");
} else if (!compositorAllowed()){
logger.warning("Cannot switch to VR mode (VR not supported).");
}
}
if( !isInVR() ) {
//FIXME: Handling GLFW workaround on MacOS
boolean macOs = false;
if (macOs) {
// GLFW workaround on macs
settings.setFrequency(defDev.getDisplayMode().getRefreshRate());
settings.setDepthBits(24);
settings.setVSync(true);
// try and read resolution from file in local dir
File resfile = new File("resolution.txt");
if( resfile.exists() ) {
try {
BufferedReader br = new BufferedReader(new FileReader(resfile));
settings.setWidth(Integer.parseInt(br.readLine()));
settings.setHeight(Integer.parseInt(br.readLine()));
try {
settings.setFullscreen(br.readLine().toLowerCase(Locale.ENGLISH).contains("full"));
} catch(Exception e) {
settings.setFullscreen(false);
}
br.close();
} catch(Exception e) {
settings.setWidth(1280);
settings.setHeight(720);
}
} else {
settings.setWidth(1280);
settings.setHeight(720);
settings.setFullscreen(false);
}
settings.setResizable(false);
}
settings.setSwapBuffers(true);
} else {
// use basic mirroring window, skip settings window
settings.setSamples(1);
settings.setWidth(xWin);
settings.setHeight(yWin);
settings.setBitsPerPixel(32);
settings.setFrameRate(0);
settings.setFrequency(VRhardware.getDisplayFrequency());
settings.setFullscreen(false);
settings.setVSync(false); // stop vsyncing on primary monitor!
settings.setSwapBuffers(disableSwapBuffers);
}
// Updating application settings
stateManager.getApplication().setSettings(settings);
logger.config("Updated underlying application settings.");
}
@Override
public void cleanup() {
if( VRhardware != null ) {
VRhardware.destroy();
VRhardware = null;
}
disableVR = true;
this.application = null;
this.stateManager = null;
}
@Override
public void stateDetached(AppStateManager stateManager) {
super.stateDetached(stateManager);
}
/**
* Process the attached settings and apply changes to this app state.
* @param settings the app settings to process.
*/
protected void processSettings(AppSettings settings){
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_VR_FORCE) != null){
forceVR = settings.getBoolean(VRConstants.SETTING_VR_FORCE);
}
if (settings.get(VRConstants.SETTING_FLIP_EYES) != null){
if( VRhardware != null ){
VRhardware._setFlipEyes(settings.getBoolean(VRConstants.SETTING_FLIP_EYES));
}
}
if (settings.get(VRConstants.SETTING_GUI_OVERDRAW) != null){
guiManager._enableGuiOverdraw(settings.getBoolean(VRConstants.SETTING_GUI_OVERDRAW));
}
if (settings.get(VRConstants.SETTING_GUI_CURVED_SURFACE) != null){
guiManager._enableCurvedSuface(settings.getBoolean(VRConstants.SETTING_GUI_CURVED_SURFACE));
}
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_DISABLE_VR) != null){
DISABLE_VR = settings.getBoolean(VRConstants.SETTING_DISABLE_VR);
}
if (settings.get(VRConstants.SETTING_SEATED_EXPERIENCE) != null){
seated = settings.getBoolean(VRConstants.SETTING_SEATED_EXPERIENCE);
}
if (settings.get(VRConstants.SETTING_NO_GUI) != null){
nogui = settings.getBoolean(VRConstants.SETTING_NO_GUI);
}
if (settings.get(VRConstants.SETTING_INSTANCE_RENDERING) != null){
instanceVR = 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_VRAPI) != null){
vrBinding = settings.getInteger(VRConstants.SETTING_VRAPI);
}
}
}
}

@ -255,13 +255,13 @@ public abstract class VRApplication implements Application, SystemListener {
initStateManager();
// Create the GUI manager.
guiManager = new VRGuiManager(this);
guiManager = new VRGuiManager();
// Create a new view manager.
viewmanager = new VRViewManager(this);
viewmanager = new VRViewManager();
// Create a new mouse manager.
mouseManager = new VRMouseManager(this);
mouseManager = new VRMouseManager();
// we are going to use OpenVR now, not the Oculus Rift
// OpenVR does support the Rift
@ -275,10 +275,12 @@ public abstract class VRApplication implements Application, SystemListener {
logger.warning("VR disabled via code.");
} else if( VRSupportedOS && DISABLE_VR == false ) {
if( CONSTRUCT_WITH_OSVR ) {
VRhardware = new OSVR(this);
//FIXME: WARNING !!
VRhardware = new OSVR(null);
logger.config("Creating OSVR wrapper [SUCCESS]");
} else {
VRhardware = new OpenVR(this);
//FIXME: WARNING !!
VRhardware = new OpenVR(null);
logger.config("Creating OpenVR wrapper [SUCCESS]");
}
if( VRhardware.initialize() ) {
@ -820,6 +822,16 @@ public abstract class VRApplication implements Application, SystemListener {
context.create(waitFor);
}
/**
* Move filters from the main scene into the eye's.
* This removes filters from the main scene.
*/
public void moveScreenProcessingToVR() {
if( isInVR() ) {
viewmanager.moveScreenProcessingToEyes();
}
}
/**
* Set VR application {@link PreconfigParameter specific parameter}.
* If making changes to default values, this must be called before the VRApplication starts
@ -924,15 +936,6 @@ public abstract class VRApplication implements Application, SystemListener {
return DISABLE_VR == false && (forceVR || VRSupportedOS && VRhardware != null && VRhardware.isInitialized());
}
/**
* Move filters from the main scene into the eye's.
* This removes filters from the main scene.
*/
public void moveScreenProcessingToVR() {
if( isInVR() ) {
viewmanager.moveScreenProcessingToEyes();
}
}
/**
* Get the GUI node from the application.
@ -1354,7 +1357,8 @@ public abstract class VRApplication implements Application, SystemListener {
logger.warning("No VR system found.");
}
viewmanager = new VRViewManager(this);
//FIXME: WARNING !!
viewmanager = new VRViewManager();
viewmanager.setResolutionMultiplier(resMult);
inputManager.addMapping(RESET_HMD, new KeyTrigger(KeyInput.KEY_F9));
setLostFocusBehavior(LostFocusBehavior.Disabled);

@ -0,0 +1,147 @@
package com.jme3.app;
import java.util.HashMap;
import com.jme3.system.AppSettings;
/**
* Some constants dedicated to the VR module.
* @author Julien Seinturier - JOrigin project - <a href="http://www.jorigin.org">http:/www.jorigin.org</a>
* @since 3.1.0
*/
public class VRConstants {
/**
* An AppSettings parameter that set if the VR compositor has to be used.
* <p>
* <b>Type: </b><code>boolean</code><br>
* <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_USE_COMPOSITOR, value)</code>
*/
public static final String SETTING_USE_COMPOSITOR = "VRUseCompositor";
/**
* An AppSettings parameter that set if the rendering has to use two eyes,
* regardless of VR API detection (turning this setting on without a VR system should lead to errors).
* <p>
* <b>Type: </b><code>boolean</code><br>
* <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_VR_FORCE, value)</code>
*/
public static final String SETTING_VR_FORCE = "VRForce";
/**
* An AppSettings parameter that set to invert the eyes of the HMD.
* <b>Type: </b><code>boolean</code><br>
* <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_FLIP_EYES, value)</code>
*/
public static final String SETTING_FLIP_EYES = "VRFlipEyes";
/**
* An AppSettings parameter that set if the GUI has to be displayed even if it is behind objects.
* <b>Type: </b><code>boolean</code><br>
* <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_GUI_OVERDRAW, value)</code>
*
*/
public static final String SETTING_GUI_OVERDRAW = "VRGUIOverdraw";
/**
* An AppSettings parameter that set if the GUI surface has to be curved.
* <b>Type: </b><code>boolean</code><br>
* <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_GUI_CURVED_SURFACE, value)</code>
*/
public static final String SETTING_GUI_CURVED_SURFACE = "VRGUICurvedSurface";
/**
* An AppSettings parameter that set if a mirror rendering has to be displayed on the screen.
* Runs faster when set to <code>false</code>.
* <b>Type: </b><code>boolean</code><br>
* <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_ENABLE_MIRROR_WINDOW, value)</code>
*/
public static final String SETTING_ENABLE_MIRROR_WINDOW = "VREnableMirrorWindow";
/**
* An AppSettings parameter that set if the VR rendering has to be disabled,
* regardless VR API and devices are presents.
* <b>Type: </b><code>boolean</code><br>
* <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_DISABLE_VR, value)</code>
*/
public static final String SETTING_DISABLE_VR = "VRDisable";
/**
* An AppSettings parameter that set if the VR user is seated.
* <b>Type: </b><code>boolean</code><br>
* <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_SEATED_EXPERIENCE, value)</code>
*/
public static final String SETTING_SEATED_EXPERIENCE = "VRSeatedExperience";
/**
* An AppSettings parameter that set if the GUI has to be ignored.
* <b>Type: </b><code>boolean</code><br>
* <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_NO_GUI, value)</code>
*/
public static final String SETTING_NO_GUI = "VRNoGUI";
/**
* An AppSettings parameter that set if instance rendering has to be used.
* This setting requires some vertex shader changes (see Common/MatDefs/VR/Unshaded.j3md).
* <b>Type: </b><code>boolean</code><br>
* <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_INSTANCE_RENDERING, value)</code>
*/
public static final String SETTING_INSTANCE_RENDERING = "VRInstanceRendering";
/**
* An AppSettings parameter that set if Multi Sample Anti Aliasing has to be enabled.
* <b>Type: </b><code>boolean</code><br>
* <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_DISABLE_MSAA, value)</code>
*/
public static final String SETTING_DISABLE_MSAA = "VRDisableMSAA";
/**
* An AppSettings parameter that set the default field of view (FOV) value.
* <b>Type: </b><code>float</code><br>
* <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_DEFAULT_FOV, value)</code>
*/
public static final String SETTING_DEFAULT_FOV = "VRDefaultFOV";
/**
* An AppSettings parameter that set the default aspect ratio.
* <b>Type: </b><code>float</code><br>
* <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_DEFAULT_ASPECT_RATIO, value)</code>
*/
public static final String SETTING_DEFAULT_ASPECT_RATIO = "VRDefaultAspectRatio";
/**
* An AppSettings parameter that specifies the underlying VR API. Possible values are:<br>
* <ul>
* <li>{@link VRConstants#SETTING_VRAPI_OPENVR_VALUE SETTING_VRAPI_OPENVR_VALUE}: Use OpenVR binding.
* <li>{@link VRConstants#SETTING_VRAPI_OSVR_VALUE SETTING_VRAPI_OSVR_VALUE}: Use OSVR binding.
* <li>{@link VRConstants#SETTING_VRAPI_OPENVR_LWJGL_VALUE SETTING_VRAPI_OPENVR_LWJGL_VALUE}: Use OpenVR binding from LWJGL.
* </ul>
* <b>Type: </b><code>int</code><br>
* <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_VRAPI, value)</code>
*/
public static final String SETTING_VRAPI = "VRAPI";
/**
* The identifier of the OpenVR system.
* @see #SETTING_VRAPI
*/
public static final int SETTING_VRAPI_OPENVR_VALUE = 1;
/**
* The identifier of the OSVR system.
* @see #SETTING_VRAPI
*/
public static final int SETTING_VRAPI_OSVR_VALUE = 2;
/**
* The identifier of the OpenVR from LWJGL system.
* @see #SETTING_VRAPI
*/
public static final int SETTING_VRAPI_OPENVR_LWJGL_VALUE = 3;
}

@ -9,6 +9,7 @@ https://github.com/sensics/OSVR-RenderManager/blob/master/examples/RenderManager
*/
package com.jme3.input.vr;
import com.jme3.app.VRAppState;
import com.jme3.app.VRApplication;
import com.jme3.math.Matrix4f;
import com.jme3.math.Quaternion;
@ -110,14 +111,14 @@ public class OSVR implements VRAPI {
boolean initSuccess = false;
boolean flipEyes = false;
private VRApplication application = null;
private VRAppState app = null;
/**
* Create a new <a href="http://www.osvr.org/">OSVR</a> system attached to the given application.
* @param application the application to which the input is attached.
* Create a new <a href="http://www.osvr.org/">OSVR</a> system attached to the given {@link VRAppState app state}.
* @param app the app state to which the input is attached.
*/
public OSVR(VRApplication application){
this.application = application;
public OSVR(VRAppState app){
this.app = app;
}
/**
@ -149,7 +150,7 @@ public class OSVR implements VRAPI {
hmdPose.setAutoSynch(false);
context = OsvrClientKitLibrary.osvrClientInit(defaultJString, 0);
VRinput = new OSVRInput(application);
VRinput = new OSVRInput(app);
initSuccess = context != null && VRinput.init();
if( initSuccess ) {
PointerByReference grabDisplay = new PointerByReference();
@ -462,8 +463,8 @@ public class OSVR implements VRAPI {
}
@Override
public VRApplication getApplication() {
return application;
public VRAppState getVRAppState() {
return app;
}
}

@ -7,6 +7,7 @@ package com.jme3.input.vr;
import java.util.logging.Logger;
import com.jme3.app.VRAppState;
import com.jme3.app.VRApplication;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector2f;
@ -63,7 +64,7 @@ public class OSVRInput implements VRInputAPI {
private static final Vector2f lastCallAxis[] = new Vector2f[16];
private static float axisMultiplier = 1f;
private VRApplication application = null;
private VRAppState app = null;
/**
* Get the system String that identifies a controller.
@ -90,11 +91,11 @@ public class OSVRInput implements VRInputAPI {
/**
* Create a new <a href="http://www.osvr.org/">OSVR</a> input attached to the given application.
* @param application the application to which the input is attached.
* Create a new <a href="http://www.osvr.org/">OSVR</a> input attached to the given {@link VRAppState app state}.
* @param app the app state to which the input is attached.
*/
public OSVRInput(VRApplication application){
this.application = application;
public OSVRInput(VRAppState app){
this.app = app;
}
@ -166,7 +167,7 @@ public class OSVRInput implements VRInputAPI {
private OSVR_ClientInterface getInterface(byte[] str) {
PointerByReference pbr = new PointerByReference();
OsvrClientKitLibrary.osvrClientGetInterface((OsvrClientKitLibrary.OSVR_ClientContext)application.getVRHardware().getVRSystem(), str, pbr);
OsvrClientKitLibrary.osvrClientGetInterface((OsvrClientKitLibrary.OSVR_ClientContext)app.getVRHardware().getVRSystem(), str, pbr);
return new OSVR_ClientInterface(pbr.getValue());
}
@ -302,9 +303,9 @@ public class OSVRInput implements VRInputAPI {
@Override
public Quaternion getFinalObserverRotation(int index) {
VRViewManager vrvm = application.getVRViewManager();
VRViewManager vrvm = app.getVRViewManager();
if( vrvm == null || isInputDeviceTracking(index) == false ) return null;
Object obs = application.getObserver();
Object obs = app.getObserver();
if( obs instanceof Camera ) {
tempq.set(((Camera)obs).getRotation());
} else {
@ -315,9 +316,9 @@ public class OSVRInput implements VRInputAPI {
@Override
public Vector3f getFinalObserverPosition(int index) {
VRViewManager vrvm = application.getVRViewManager();
VRViewManager vrvm = app.getVRViewManager();
if( vrvm == null || isInputDeviceTracking(index) == false ) return null;
Object obs = application.getObserver();
Object obs = app.getObserver();
Vector3f pos = getPosition(index);
if( obs instanceof Camera ) {
((Camera)obs).getRotation().mult(pos, pos);
@ -350,8 +351,8 @@ public class OSVRInput implements VRInputAPI {
@Override
public VRApplication getApplication() {
return application;
public VRAppState getVRAppState() {
return app;
}

@ -5,6 +5,7 @@
*/
package com.jme3.input.vr;
import com.jme3.app.VRAppState;
import com.jme3.app.VRApplication;
import com.jme3.math.Matrix4f;
import com.jme3.math.Quaternion;
@ -83,14 +84,15 @@ public class OpenVR implements VRAPI {
private static long frameCount;
private static OpenVRInput VRinput;
private VRApplication application = null;
private VRAppState app = null;
/**
* Create a new <a href="https://github.com/ValveSoftware/openvr/wiki/API-Documentation">OpenVR</a> system attached to the given application.
* @param application the application to which the input is attached.
* Create a new <a href="https://github.com/ValveSoftware/openvr/wiki/API-Documentation">OpenVR</a> system
* attached to the given {@link VRAppState VR app state}.
* @param appState the VR app state to which the api is attached.
*/
public OpenVR(VRApplication application){
this.application = application;
public OpenVR(VRAppState appState){
this.app = appState;
}
@Override
@ -178,7 +180,7 @@ public class OpenVR implements VRAPI {
}
// init controllers for the first time
VRinput = new OpenVRInput(application);
VRinput = new OpenVRInput(app);
VRinput.init();
VRinput.updateConnectedControllers();
@ -204,7 +206,7 @@ public class OpenVR implements VRAPI {
if(compositorFunctions != null && hmdErrorStore.getValue() == 0 ){
compositorFunctions.setAutoSynch(false);
compositorFunctions.read();
if( application.isSeatedExperience() ) {
if( app.isSeatedExperience() ) {
compositorFunctions.SetTrackingSpace.apply(JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseSeated);
} else {
compositorFunctions.SetTrackingSpace.apply(JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseStanding);
@ -354,7 +356,7 @@ public class OpenVR implements VRAPI {
frameCount = nowCount;
vrsystemFunctions.GetDeviceToAbsoluteTrackingPose.apply(
application.isSeatedExperience()?JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseSeated:
app.isSeatedExperience()?JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseSeated:
JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseStanding,
fSecondsUntilPhotons, hmdTrackedDevicePoseReference, JOpenVRLibrary.k_unMaxTrackedDeviceCount);
}
@ -371,7 +373,7 @@ public class OpenVR implements VRAPI {
VRInput._updateConnectedControllers();
}*/
//update controllers pose information
application.getVRinput().updateControllerStates();
app.getVRinput().updateControllerStates();
// read pose data from native
for (int nDevice = 0; nDevice < JOpenVRLibrary.k_unMaxTrackedDeviceCount; ++nDevice ){
@ -445,7 +447,7 @@ public class OpenVR implements VRAPI {
@Override
public Vector3f getSeatedToAbsolutePosition() {
if( application.isSeatedExperience() == false ) return Vector3f.ZERO;
if( app.isSeatedExperience() == false ) return Vector3f.ZERO;
if( hmdSeatToStand == null ) {
hmdSeatToStand = new Vector3f();
HmdMatrix34_t mat = vrsystemFunctions.GetSeatedZeroPoseToStandingAbsoluteTrackingPose.apply();
@ -524,8 +526,8 @@ public class OpenVR implements VRAPI {
}
@Override
public VRApplication getApplication() {
return application;
public VRAppState getVRAppState() {
return app;
}
}

@ -9,6 +9,7 @@ import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.app.VRAppState;
import com.jme3.app.VRApplication;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector2f;
@ -97,7 +98,7 @@ public class OpenVRInput implements VRInputAPI {
private final Quaternion tempq = new Quaternion();
private VRApplication application;
private VRAppState app;
private List<VRTrackedController> trackedControllers = null;
@ -105,8 +106,8 @@ public class OpenVRInput implements VRInputAPI {
* Create a new <a href="https://github.com/ValveSoftware/openvr/wiki/API-Documentation">OpenVR</a> input attached to the given application.
* @param application the application to which the input is attached.
*/
public OpenVRInput(VRApplication application){
this.application = application;
public OpenVRInput(VRAppState appState){
this.app = appState;
}
@Override
@ -296,7 +297,7 @@ public class OpenVRInput implements VRInputAPI {
@Override
public boolean isInputFocused() {
return ((VR_IVRSystem_FnTable)application.getVRHardware().getVRSystem()).IsInputFocusCapturedByAnotherProcess.apply() == 0;
return ((VR_IVRSystem_FnTable)app.getVRHardware().getVRSystem()).IsInputFocusCapturedByAnotherProcess.apply() == 0;
}
@Override
@ -326,9 +327,9 @@ public class OpenVRInput implements VRInputAPI {
@Override
public Quaternion getFinalObserverRotation(int index) {
VRViewManager vrvm = application.getVRViewManager();
VRViewManager vrvm = app.getVRViewManager();
if( vrvm == null || isInputDeviceTracking(index) == false ) return null;
Object obs = application.getObserver();
Object obs = app.getObserver();
if( obs instanceof Camera ) {
tempq.set(((Camera)obs).getRotation());
} else {
@ -339,9 +340,9 @@ public class OpenVRInput implements VRInputAPI {
@Override
public Vector3f getFinalObserverPosition(int index) {
VRViewManager vrvm = application.getVRViewManager();
VRViewManager vrvm = app.getVRViewManager();
if( vrvm == null || isInputDeviceTracking(index) == false ) return null;
Object obs = application.getObserver();
Object obs = app.getObserver();
Vector3f pos = getPosition(index);
if( obs instanceof Camera ) {
((Camera)obs).getRotation().mult(pos, pos);
@ -354,9 +355,9 @@ public class OpenVRInput implements VRInputAPI {
@Override
public void triggerHapticPulse(int controllerIndex, float seconds) {
if( application.isInVR() == false || isInputDeviceTracking(controllerIndex) == false ) return;
if( app.isInVR() == false || isInputDeviceTracking(controllerIndex) == false ) return;
// apparently only axis ID of 0 works
((VR_IVRSystem_FnTable)application.getVRHardware().getVRSystem()).TriggerHapticPulse.apply(OpenVRInput.controllerIndex[controllerIndex],
((VR_IVRSystem_FnTable)app.getVRHardware().getVRSystem()).TriggerHapticPulse.apply(OpenVRInput.controllerIndex[controllerIndex],
0, (short)Math.round(3f * seconds / 1e-3f));
}
@ -365,13 +366,13 @@ public class OpenVRInput implements VRInputAPI {
logger.config("Updating connected controllers.");
controllerCount = 0;
for(int i=0;i<JOpenVRLibrary.k_unMaxTrackedDeviceCount;i++) {
if( ((OpenVR)application.getVRHardware()).getVRSystem().GetTrackedDeviceClass.apply(i) == JOpenVRLibrary.ETrackedDeviceClass.ETrackedDeviceClass_TrackedDeviceClass_Controller ) {
if( ((OpenVR)app.getVRHardware()).getVRSystem().GetTrackedDeviceClass.apply(i) == JOpenVRLibrary.ETrackedDeviceClass.ETrackedDeviceClass_TrackedDeviceClass_Controller ) {
String controllerName = "Unknown";
String manufacturerName = "Unknown";
try {
controllerName = OpenVRUtil.getTrackedDeviceStringProperty(((OpenVR)application.getVRHardware()).getVRSystem(), i, JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_TrackingSystemName_String);
manufacturerName = OpenVRUtil.getTrackedDeviceStringProperty(((OpenVR)application.getVRHardware()).getVRSystem(), i, JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_ManufacturerName_String);
controllerName = OpenVRUtil.getTrackedDeviceStringProperty(((OpenVR)app.getVRHardware()).getVRSystem(), i, JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_TrackingSystemName_String);
manufacturerName = OpenVRUtil.getTrackedDeviceStringProperty(((OpenVR)app.getVRHardware()).getVRSystem(), i, JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_ManufacturerName_String);
} catch (Exception e) {
logger.log(Level.WARNING, e.getMessage(), e);
}
@ -393,7 +394,7 @@ public class OpenVRInput implements VRInputAPI {
public void updateControllerStates() {
for(int i=0;i<controllerCount;i++) {
int index = controllerIndex[i];
((OpenVR)application.getVRHardware()).getVRSystem().GetControllerState.apply(index, cStates[index], 5);
((OpenVR)app.getVRHardware()).getVRSystem().GetControllerState.apply(index, cStates[index], 5);
cStates[index].readField("ulButtonPressed");
cStates[index].readField("rAxis");
needsNewVelocity[index] = true;
@ -402,8 +403,8 @@ public class OpenVRInput implements VRInputAPI {
}
@Override
public VRApplication getApplication() {
return application;
public VRAppState getVRAppState() {
return app;
}
}

@ -5,7 +5,7 @@
*/
package com.jme3.input.vr;
import com.jme3.app.VRApplication;
import com.jme3.app.VRAppState;
import com.jme3.math.Matrix4f;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector2f;
@ -33,10 +33,10 @@ public interface VRAPI {
public boolean initVRCompositor(boolean allowed);
/**
* Get the VR application to which this input is attached.
* @return the VR application to which this input is attached.
* Get the {@link VRAppState VR app state} to which this api is attached.
* @return the VR app state to which this input is attached.
*/
public VRApplication getApplication();
public VRAppState getVRAppState();
/**
* Get the object that wraps natively the VR system.

@ -5,6 +5,7 @@
*/
package com.jme3.input.vr;
import com.jme3.app.VRAppState;
import com.jme3.app.VRApplication;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector2f;
@ -197,8 +198,8 @@ public interface VRInputAPI {
public void triggerHapticPulse(int controllerIndex, float seconds);
/**
* Get the VR application to which this input is attached.
* @return the VR application to which this input is attached.
* Get the {@link VRAppState VR app state} to which this api is attached.
* @return the VR app state to which this input is attached.
*/
public VRApplication getApplication();
public VRAppState getVRAppState();
}

@ -5,6 +5,7 @@
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;
@ -17,7 +18,7 @@ import com.jme3.system.jopenvr.VR_IVRSystem_FnTable;
*/
public class MeshUtil {
public static Mesh setupDistortionMesh(int eye, VRApplication application) {
public static Mesh setupDistortionMesh(int eye, VRAPI api) {
Mesh distortionMesh = new Mesh();
float m_iLensGridSegmentCountH = 43, m_iLensGridSegmentCountV = 43;
@ -45,7 +46,7 @@ public class MeshUtil {
vertPos += 3;
DistortionCoordinates_t dc0 = new DistortionCoordinates_t();
if( application.getVRHardware().getVRSystem() == null ) {
if( api.getVRSystem() == null ) {
// default to no distortion
texcoordR[coordPos] = u;
texcoordR[coordPos + 1] = 1 - v;
@ -54,7 +55,7 @@ public class MeshUtil {
texcoordB[coordPos] = u;
texcoordB[coordPos + 1] = 1 - v;
} else {
((VR_IVRSystem_FnTable)application.getVRHardware().getVRSystem()).ComputeDistortion.apply(eye, u, v, dc0);
((VR_IVRSystem_FnTable)api.getVRSystem()).ComputeDistortion.apply(eye, u, v, dc0);
texcoordR[coordPos] = dc0.rfRed[0];
texcoordR[coordPos + 1] = 1 - dc0.rfRed[1];

@ -4,7 +4,10 @@
*/
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;
@ -24,6 +27,7 @@ import com.jme3.texture.Image.Format;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture2D;
import java.awt.GraphicsEnvironment;
import java.util.Iterator;
/**
*
@ -44,22 +48,35 @@ public class VRGuiManager {
private Vector2f screenSize;
protected boolean wantsReposition;
private VRApplication application = null;
private VRAppState app = null;
private Application application = null;
/**
* Create a new GUI manager attached to the given application.
* @param application the VR application that this manager is attached to.
* 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(VRApplication application){
this.application = application;
public VRGuiManager(){
}
/**
* Get the VR application to which this GUI manager is attached.
* @return the VR application to which this GUI manager is attached.
* 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 VRApplication getApplication(){
return application;
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;
}
/**
@ -84,10 +101,10 @@ public class VRGuiManager {
public Vector2f getCanvasSize() {
if( screenSize == null ) {
if( application.isInVR() && application.getVRHardware() != null ) {
if( app.isInVR() && app.getVRHardware() != null ) {
screenSize = new Vector2f();
application.getVRHardware().getRenderSize(screenSize);
screenSize.multLocal(application.getVRViewManager().getResolutionMuliplier());
app.getVRHardware().getRenderSize(screenSize);
screenSize.multLocal(app.getVRViewManager().getResolutionMuliplier());
} else {
AppSettings as = application.getContext().getSettings();
screenSize = new Vector2f(as.getWidth(), as.getHeight());
@ -127,7 +144,7 @@ public class VRGuiManager {
guiPos.set(0f, 0f, guiDistance);
dir.mult(guiPos, guiPos);
guiPos.x += pos.x;
guiPos.y += pos.y + application.getVRHeightAdjustment();
guiPos.y += pos.y + app.getVRHeightAdjustment();
guiPos.z += pos.z;
if( guiPositioningElastic > 0f && posMode != POSITIONING_MODE.MANUAL ) {
// mix pos & dir with current pos & dir
@ -142,7 +159,7 @@ public class VRGuiManager {
protected void positionGuiNow(float tpf) {
wantsReposition = false;
if( application.isInVR() == false ) return;
if( app.isInVR() == false ) return;
guiQuadNode.setLocalScale(guiDistance * guiScale * 4f, 4f * guiDistance * guiScale, 1f);
switch( posMode ) {
@ -158,7 +175,7 @@ public class VRGuiManager {
break;
case AUTO_OBSERVER_POS_CAM_ROTATION:
Object obs = application.getObserver();
Object obs = app.getObserver();
if( obs != null ) {
if( obs instanceof Camera ) {
positionTo(((Camera)obs).getLocation(), camLeft.getRotation(), tpf);
@ -171,7 +188,7 @@ public class VRGuiManager {
break;
case AUTO_OBSERVER_ALL:
case AUTO_OBSERVER_ALL_CAMHEIGHT:
obs = application.getObserver();
obs = app.getObserver();
if( obs != null ) {
Quaternion q;
if( obs instanceof Camera ) {
@ -230,7 +247,7 @@ public class VRGuiManager {
}
protected void setupGui(Camera leftcam, Camera rightcam, ViewPort left, ViewPort right) {
if( application.hasTraditionalGUIOverlay() ) {
if( app.hasTraditionalGUIOverlay() ) {
camLeft = leftcam;
camRight = rightcam;
Spatial guiScene = getGuiQuad(camLeft);
@ -259,6 +276,7 @@ public class VRGuiManager {
private Node guiQuadNode;
private ViewPort offView;
private Texture2D guiTexture;
private Spatial getGuiQuad(Camera sourceCam){
if( guiQuadNode == null ) {
Vector2f guiCanvasSize = getCanvasSize();
@ -287,7 +305,11 @@ public class VRGuiManager {
offView.setOutputFrameBuffer(offBuffer);
// setup framebuffer's scene
offView.attachScene(application.getGuiNode());
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");

@ -5,9 +5,15 @@
*/
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;
@ -16,6 +22,7 @@ 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;
@ -29,8 +36,9 @@ public class VRMouseManager {
private static final Logger logger = Logger.getLogger(VRMouseManager.class.getName());
private VRApplication application = null;
private Application application = null;
private VRAppState app = null;
private final int AVERAGE_AMNT = 4;
private int avgCounter;
@ -48,16 +56,20 @@ public class VRMouseManager {
return amt / arr.length;
}
public VRMouseManager(VRApplication application){
this.application = application;
public VRMouseManager(){
}
/**
* Get the VR application to which this mouse manager is attached.
* @return the VR application to which this mouse manager is attached.
* 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 VRApplication getApplication(){
return application;
public void attach(VRAppState app, Application application){
this.app = app;
this.application = application;
}
protected void init() {
@ -73,6 +85,8 @@ public class VRMouseManager {
((GlfwMouseInputVR)mi).hideActiveCursor();
}
centerMouse();
logger.config("Initialized VR mouse manager [SUCCESS]");
}
public void setThumbstickMode(boolean set) {
@ -101,7 +115,7 @@ public class VRMouseManager {
}
public void setImage(String texture) {
if( application.isInVR() == false ){
if( app.isInVR() == false ){
Texture tex = application.getAssetManager().loadTexture(texture);
mouseImage.setTexture(application.getAssetManager(), (Texture2D)tex, true);
ySize = tex.getImage().getHeight();
@ -123,14 +137,14 @@ public class VRMouseManager {
public void updateAnalogAsMouse(int inputIndex, AnalogListener mouseListener, String mouseXName, String mouseYName, float tpf) {
// got a tracked controller to use as the "mouse"
if( application.isInVR() == false ||
application.getVRinput() == null ||
application.getVRinput().isInputDeviceTracking(inputIndex) == false ) return;
if( app.isInVR() == false ||
app.getVRinput() == null ||
app.getVRinput().isInputDeviceTracking(inputIndex) == false ) return;
Vector2f tpDelta;
if( thumbstickMode ) {
tpDelta = application.getVRinput().getAxis(inputIndex, VRInputType.ViveTrackpadAxis);
tpDelta = app.getVRinput().getAxis(inputIndex, VRInputType.ViveTrackpadAxis);
} else {
tpDelta = application.getVRinput().getAxisDeltaSinceLastCall(inputIndex, VRInputType.ViveTrackpadAxis);
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);
@ -147,7 +161,7 @@ public class VRMouseManager {
lastYmv[index] = Yamount * 133f;
cursorPos.x -= avg(lastXmv);
cursorPos.y -= avg(lastYmv);
Vector2f maxsize = application.getVRGUIManager().getCanvasSize();
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;
@ -156,7 +170,7 @@ public class VRMouseManager {
}
public Vector2f getCursorPosition() {
if( application.isInVR() ) {
if( app.isInVR() ) {
return cursorPos;
}
return application.getInputManager().getCursorPosition();
@ -164,11 +178,11 @@ public class VRMouseManager {
public void centerMouse() {
// set mouse in center of the screen if newly added
Vector2f size = application.getVRGUIManager().getCanvasSize();
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( application.isInVR() ) {
if( app.isInVR() ) {
cursorPos.x = size.x / 2f;
cursorPos.y = size.y / 2f;
recentCenterCount = 2;
@ -180,11 +194,13 @@ public class VRMouseManager {
if( application.getInputManager().isCursorVisible() ) {
if( mouseImage.getParent() == null ) {
application.getGuiNode().attachChild(mouseImage);
application.getGuiViewPort().attachScene(mouseImage);
centerMouse();
// the "real" mouse pointer should stay hidden
org.lwjgl.glfw.GLFW.glfwSetInputMode(((LwjglWindowVR)application.getContext()).getWindowHandle(),
org.lwjgl.glfw.GLFW.GLFW_CURSOR, org.lwjgl.glfw.GLFW.GLFW_CURSOR_DISABLED);
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();
@ -195,21 +211,24 @@ public class VRMouseManager {
cursorPos.y += ((GlfwMouseInputVR)mi).getLastDeltaY();// * winratio.y;
if( cursorPos.x < 0f ) cursorPos.x = 0f;
if( cursorPos.y < 0f ) cursorPos.y = 0f;
if( cursorPos.x > application.getVRGUIManager().getCanvasSize().x ) cursorPos.x = application.getVRGUIManager().getCanvasSize().x;
if( cursorPos.y > application.getVRGUIManager().getCanvasSize().y ) cursorPos.y = application.getVRGUIManager().getCanvasSize().y;
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, application.getVRGUIManager().getGuiDistance() + 1f);
mouseImage.setLocalTranslation(currentPos.x, currentPos.y - ySize, app.getVRGUIManager().getGuiDistance() + 1f);
mouseImage.updateGeometricState();
mouseImage.getParent().updateGeometricState();
} else if( mouseImage.getParent() != null ) {
Node n = mouseImage.getParent();
mouseImage.removeFromParent();
n.updateGeometricState();
if (n != null){
n.updateGeometricState();
}
}
}
}

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save