Hello, I am trying to check some feature of the engine in order to decide whether and how to properly advance in some of my case studies.
Right now I am checking for how properly you can toggle VR back and forth and still maintain all the states as expected, ie returning to the same as they were before, and being able to toggle at will and always (without moving the VR headset of course) keep oscillating between the two behaviors.
The dependencies I am using are, engine version: 3.6.0-beta1, artifacts: jme3-core, jme3-desktop, jme3-lwjgl3, jme3-vr.
Here is the source code:
package mk.test;
import com.jme3.app.*;
import com.jme3.input.*;
import com.jme3.input.controls.*;
import com.jme3.material.*;
import com.jme3.scene.*;
import com.jme3.scene.shape.*;
import com.jme3.system.*;
import java.util.*;
import static com.jme3.app.VRConstants.SETTING_ENABLE_MIRROR_WINDOW;
import static com.jme3.app.VRConstants.SETTING_VRAPI;
import static com.jme3.app.VRConstants.SETTING_VRAPI_OPENVR_LWJGL_VALUE;
import static java.util.Optional.empty;
import static java.util.function.Predicate.not;
public class VRToggleTest extends SimpleApplication {
public static void main(String[] args) {
new VRToggleTest().start();
}
@Override
public void simpleInitApp() {
inputManager.addMapping("V", new KeyTrigger(KeyInput.KEY_V));
inputManager.addListener((ActionListener) (String name, boolean pressed, float timePerFrame) -> {
if (!pressed)
VRTOGGLE();
}, "V");
var b = new Box(1, 1, 1);
var geom = new Geometry("Box", b);
var mat = new Material(assetManager, "Common/MatDefs/Misc/ShowNormals.j3md");
geom.setMaterial(mat);
rootNode.attachChild(geom);
}
Optional<VRAppState> vrState = empty();
private void VRTOGGLE() {
if (vrState.isEmpty())
VRON();
else
VROFF();
}
private void VRON() {
if (vrState.isEmpty())
vrState = createVR();
vrState
.filter(not(stateManager::hasState))
.ifPresent(stateManager::attach);
}
private void VROFF() {
vrState.ifPresent(stateManager::detach);
vrState.ifPresent(VRAppState::cleanup);
vrState = empty();
}
private Optional<VRAppState> createVR() {
try {
var vrSettings = new AppSettings(true);
vrSettings.put(SETTING_VRAPI, SETTING_VRAPI_OPENVR_LWJGL_VALUE);
vrSettings.put(SETTING_ENABLE_MIRROR_WINDOW, true);
return Optional.of(new VRAppState(new VREnvironment(vrSettings)));
} catch (RuntimeException e) {
return Optional.empty();
}
}
}
Sorry needed to mention: it’s not doing what I was expecting it to do.
The first time I return from VR controls are blocked and the camera remains from the last VR position. Then I enable VR again and I can’t see the cube anymore, no idea where the PoV ends up.
Mainly for user experience and my own standards of quality, I expect things to be properly toggleable.
I am just assuming that there is something missing that I need to do.
Games I’ve seen that support both VR and PC (which are quite rare to begin with¹) usually present that as launch options rather than toggling mid game. I really would suggest you look at what the use case is for making it toggleable mid game, it may be effort for something end users don’t want if you do it just for the sake of it unless you have a use case.
¹ what makes a good VR game and a good desktop game are usually quite different, both in terms of controls but also pacing
If you are making a VR game you may find this example project useful
It uses a library i wrote, Tamarin, to try to make VR easier in JMonkey. It does a lot of the hand interactions for you and supports OpenVR (which is a relatively modern VR framework, although OpenXR is the new kid on the block)
One example is a game like Demeo. Or any long running game where one player is the host or required to be present otherwise the whole party won’t continue. You need for any reason to put down VR and go back to desktop. Battery reason, dizziness reasons, and so on. However, closing steamvr (or steam) while open vr is still attached to the game will force close the game. Now you lost everything.
More cases involve social VR platforms where longer sessions are also usually common, and people do switch back and forth in VR. I can confirm the use case here because I saw it happening myself, but the problem is the application needs to be started in steamvr to begin with, forcing to attach the process again and risk termination.
Other situations may also be if you don’t want to pump GPU for no reason while switching to normal PC game mode.
And so on…
I may remind just like I tried to explain in the beginning, that I am not making a game right now but exploring the engine for capabilities.
PS. Yes I know of Tamarin, I tried it both with and without before posting, the issue is just the same.
For the Demeo example I’d suggest separate server and client applications (even if one client is running on the same machine as the server), the two have clearly separate concerns and lifecycles and that feels cleaner. Possibly that is the solution to all your use cases, a locally running server that is connected to by either a VR or desktop client?
For non multi-party applications I’d suggest just saving the state and restarting the JME context (which you could probably make semi transparent to the user).
(Enabling and disabling the VRState will toggle VR control, but I don’t think that gives you what you want)
As far as app states go, I make the pretty general assumption that if an app state does not extend BaseAppState that it automatically must have some lifecycle bugs.
It’s certainly possible to mess up lifecycle when subclassing BaseAppState but usually those extending BaseAppState are already thinking about lifecycle (which is the main reason to use it) and those who directly implement AppState or extend the thin AbstractAppState are not thinking beyond “attach”.
For those that remember, there used to be a VRApplication. Some effort was put into making VRAppState instead, because that theoretically meant the same launcher application could be used.
In practice, this pretty much meant the functionality was shoved into the appstate, instead.
There’s so much underlying stuff going on to make VR work that it will just not be possible to revert it back (imo). Not the way it’s implemented today.
But I’m often wrong, so anyone is welcome to try But first check what’s going on in the VREnvironment and VRViewManager classes. VRAppState is just the tip.