Hello VR monkeys!
After pulling the latest changes for jme3-vr, we noticed that the speed of particles in our scene had increased.
It seems the scene is updated twice. Once by simpleUpdate() and another time by VRAppState.update().
The following test code should reproduce the issue. It uses an image from jme3-test-data.
import com.jme3.app.LostFocusBehavior;
import com.jme3.app.SimpleApplication;
import com.jme3.app.VRAppState;
import com.jme3.app.VRConstants;
import com.jme3.app.VREnvironment;
import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.ParticleMesh.Type;
import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Geometry;
import com.jme3.scene.control.AbstractControl;
import com.jme3.scene.shape.Box;
import com.jme3.system.AppSettings;
public class VRParticles extends SimpleApplication {
private class DebugEmitter extends ParticleEmitter {
private long lastUpdate = 0;
public DebugEmitter(String name, Type type, int numParticles) {
super(name, type, numParticles);
}
@Override
public void updateFromControl(float tpf) {
long t = System.nanoTime();
long dt = t - lastUpdate;
lastUpdate = t;
if(dt < 100000) // 100 microseconds
System.out.println("DebugEmitter dt = " + dt);
super.updateFromControl(tpf);
}
}
private class TestControl extends AbstractControl {
private long lastUpdate = 0;
public int updateCount = 0;
@Override
protected void controlUpdate(float tpf) {
long t = System.nanoTime();
long dt = t - lastUpdate;
lastUpdate = t;
updateCount++;
if(dt < 100000) // 100 microseconds
System.out.println("TestControl: dt = " + dt);
}
protected void controlRender(RenderManager rm, ViewPort vp) {}
}
private final TestControl ctrl = new TestControl();
private VRParticles(VRAppState vrAppState) {
stateManager.attach(vrAppState);
}
@Override
public void simpleInitApp() {
Material mat = new Material(assetManager, "Common/MatDefs/Misc/ShowNormals.j3md");
Geometry geom = new Geometry("Box", new Box(.5f, .5f, .5f));
geom.setMaterial(mat);
mat.getAdditionalRenderState().setWireframe(true);
mat.getAdditionalRenderState().setFaceCullMode(RenderState.FaceCullMode.Off);
rootNode.attachChild(geom);
DebugEmitter em = new DebugEmitter("my particle effect", Type.Triangle, 60);
Material emMat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
emMat.setTexture("Texture", assetManager.loadTexture("Effects/Explosion/shockwave.png"));
em.setMaterial(emMat);
em.setParticlesPerSec(2);
em.setLowLife(4);
em.setHighLife(4);
rootNode.attachChild(em);
rootNode.addControl(ctrl);
cam.setLocation(new Vector3f(0, 0, 0));
}
private int updateCount = 0;
@Override
public void simpleUpdate(float tpf) {
System.out.println("simple update count: " + updateCount + ", control update count: " + ctrl.updateCount);
updateCount++;
}
public static void main(String[] args) {
AppSettings settings = new AppSettings(true);
settings.put(VRConstants.SETTING_VRAPI, VRConstants.SETTING_VRAPI_OPENVR_VALUE); // The VR api to use (need to be present on the system)
settings.put(VRConstants.SETTING_DISABLE_VR, false); // Enable VR
settings.put(VRConstants.SETTING_USE_COMPOSITOR, true); // disable the SteamVR compositor (kinda needed at the moment)
settings.put(VRConstants.SETTING_ENABLE_MIRROR_WINDOW, true); // runs faster when set to false, but will allow mirroring
settings.put(VRConstants.SETTING_VR_FORCE, false); // render two eyes, regardless of API detection
settings.put(VRConstants.SETTING_GUI_CURVED_SURFACE, true);
settings.put(VRConstants.SETTING_GUI_OVERDRAW, true); // show gui even if it is behind things
settings.put(VRConstants.SETTING_INSTANCE_RENDERING, false); // faster VR rendering, requires some vertex shader changes (see jmevr/shaders/Unshaded.j3md)
settings.put(VRConstants.SETTING_NO_GUI, false);
settings.put(VRConstants.SETTING_FLIP_EYES, false); // Is the HMD eyes have to be inverted.
settings.put(VRConstants.SETTING_DEFAULT_FOV, 108f); // The default ield Of View (FOV)
settings.put(VRConstants.SETTING_DEFAULT_ASPECT_RATIO, 1f); // The default aspect ratio.
settings.setSamples(1);
settings.setSwapBuffers(true);
VREnvironment env = new VREnvironment(settings);
env.initialize();
if(env.isInitialized()) {
VRAppState vrAppState = new VRAppState(settings, env);
VRParticles app = new VRParticles(vrAppState);
settings.setWidth(800);
settings.setHeight(600);
app.setLostFocusBehavior(LostFocusBehavior.Disabled);
app.setSettings(settings);
app.setShowSettings(false);
app.start();
}
}
}
Output:
simple update count: 1, control update count: 3
TestControl: dt = 43670
DebugEmitter dt = 77401
simple update count: 2, control update count: 5
simple update count: 3, control update count: 7
TestControl: dt = 58427
DebugEmitter dt = 73184
simple update count: 4, control update count: 9
TestControl: dt = 65052
DebugEmitter dt = 77702
simple update count: 5, control update count: 11
TestControl: dt = 75895
DebugEmitter dt = 96375
simple update count: 6, control update count: 13
TestControl: dt = 56017
DebugEmitter dt = 68666
simple update count: 7, control update count: 15
TestControl: dt = 73787
DebugEmitter dt = 93663
simple update count: 8, control update count: 17
TestControl: dt = 54210
DebugEmitter dt = 67160
simple update count: 9, control update count: 19
TestControl: dt = 72582
DebugEmitter dt = 93062
simple update count: 10, control update count: 21
Particle spawn rate and movement speed is also visibly affected.
I noticed this change in VRAppState: OculusVR: Add basic camera positioning · jMonkeyEngine/jmonkeyengine@601ba1c · GitHub
Reverting that one line back to
Iterator<Spatial> spatialIter = application.getViewPort().getScenes().iterator();
fixes the issue because it doesn’t iterate over anything. No spatials are processed in this loop so the duplicate call to update() falls away.
There’s a comment saying //FIXME: check if this code is necessary.
So can we maybe just remove this loop? The loop for the GUI below makes me suspicious though. What’s “manual mode”? Does it require manual updateLogicalState()
calls?
What was the reason for your change @ZNix?