-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature openxr #2012
base: master
Are you sure you want to change the base?
Feature openxr #2012
Changes from all commits
ddbb1e7
be1f14f
723f811
45a57e6
355646c
bf7399c
49f7bbb
27d21bf
58edcd4
1db9534
a3af4d7
1155eb7
75b5602
7e07b49
28ff3d7
d374d46
81ae772
c2ed049
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
dependencies { | ||
api project(':jme3-core') | ||
api project(':jme3-lwjgl3') | ||
api project(':jme3-desktop') | ||
api project(':jme3-effects') | ||
|
||
// https://mvnrepository.com/artifact/net.java.dev.jna/jna | ||
implementation 'net.java.dev.jna:jna:5.10.0' | ||
implementation 'com.nativelibs4java:jnaerator-runtime:0.12' | ||
|
||
// Native OpenXR/LWJGL support | ||
api "org.lwjgl:lwjgl-openxr:${lwjgl3Version}" | ||
// implementation "org.lwjgl:lwjgl-openxr:${lwjgl3Version}:natives-linux" | ||
// implementation "org.lwjgl:lwjgl-openxr:${lwjgl3Version}:natives-macos" | ||
runtimeOnly "org.lwjgl:lwjgl-openxr:${lwjgl3Version}:natives-windows" | ||
runtimeOnly "org.lwjgl:lwjgl-openxr:${lwjgl3Version}:natives-linux" | ||
// runtimeOnly "org.lwjgl:lwjgl-openxr:${lwjgl3Version}:natives-macos" | ||
|
||
// Necessary by lwjgl-openxr | ||
api "org.joml:joml:1.10.4" | ||
api "org.lwjgl:lwjgl-egl:${lwjgl3Version}" | ||
// api "org.lwjgl:lwjgl-vulkan:${lwjgl3Version}" | ||
} | ||
|
||
javadoc { | ||
// Disable doclint for JDK8+. | ||
if (JavaVersion.current().isJava8Compatible()){ | ||
options.addStringOption('Xdoclint:none', '-quiet') | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package com.jme3.input.xr; | ||
|
||
import com.jme3.app.SimpleApplication; | ||
import com.jme3.material.Material; | ||
import com.jme3.math.ColorRGBA; | ||
import com.jme3.math.Quaternion; | ||
import com.jme3.math.Vector3f; | ||
import com.jme3.renderer.Camera; | ||
import com.jme3.renderer.ViewPort; | ||
import com.jme3.scene.Geometry; | ||
import com.jme3.scene.shape.Box; | ||
import com.jme3.texture.FrameBuffer; | ||
import com.jme3.texture.Texture; | ||
import com.jme3.texture.Texture2D; | ||
import com.jme3.texture.FrameBuffer.FrameBufferTarget; | ||
import com.jme3.texture.Image.Format; | ||
|
||
public class Eye { | ||
static int index = 0; | ||
SimpleApplication app; | ||
float posX; | ||
Vector3f tmpVec = new Vector3f(); | ||
Texture2D offTex; | ||
Geometry offGeo; | ||
Camera offCamera; | ||
Vector3f centerPos = new Vector3f(0f, 0f, -5f); | ||
Quaternion centerRot = new Quaternion(); | ||
|
||
public Eye(SimpleApplication app) | ||
{ | ||
this.app = app; | ||
setupOffscreenView(app); | ||
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); | ||
mat.setTexture("ColorMap", offTex); | ||
|
||
offGeo = new Geometry("box", new Box(1, 1, 1)); | ||
offGeo.setMaterial(mat); | ||
} | ||
|
||
public void setPosX(float posX) { this.posX = posX; } | ||
public float getPosX() { return posX; } | ||
|
||
/** Moves the camera. | ||
* @param The new absolute center position. */ | ||
public void moveAbs(Vector3f newPos) | ||
{ | ||
centerPos.set(newPos); | ||
rotateAbs(offCamera.getRotation()); | ||
} | ||
|
||
/** Rotates the camera, and moves left/right. | ||
* @param The new rotation. */ | ||
public void rotateAbs(Quaternion newRot) | ||
{ | ||
tmpVec.set(posX, 0.0f, 0.0f); | ||
newRot.multLocal(tmpVec); | ||
offCamera.setLocation(tmpVec.addLocal(centerPos)); | ||
offCamera.setRotation(newRot); | ||
} | ||
|
||
private void setupOffscreenView(SimpleApplication app) | ||
{ | ||
int w = app.getContext().getSettings().getWidth(); | ||
int h = app.getContext().getSettings().getHeight(); | ||
offCamera = new Camera(w, h); | ||
|
||
ViewPort offView = app.getRenderManager().createPreView("OffscreenViewX" + (index++), offCamera); | ||
offView.setClearFlags(true, true, true); | ||
offView.setBackgroundColor(ColorRGBA.DarkGray); | ||
FrameBuffer offBuffer = new FrameBuffer(w, h, 1); | ||
|
||
//setup framebuffer's cam | ||
offCamera.setFrustumPerspective(45f, 1f, 1f, 1000f); | ||
offCamera.lookAt(new Vector3f(0f, 0f, 0f), Vector3f.UNIT_Y); | ||
|
||
//setup framebuffer's texture | ||
offTex = new Texture2D(w, h, Format.RGBA8); | ||
offTex.setMinFilter(Texture.MinFilter.Trilinear); | ||
offTex.setMagFilter(Texture.MagFilter.Bilinear); | ||
|
||
//setup framebuffer to use texture | ||
offBuffer.setDepthTarget(FrameBufferTarget.newTarget(Format.Depth)); | ||
offBuffer.addColorTarget(FrameBufferTarget.newTarget(offTex)); | ||
|
||
//set viewport to render to offscreen framebuffer | ||
offView.setOutputFrameBuffer(offBuffer); | ||
offView.attachScene(app.getRootNode()); | ||
} | ||
|
||
public void render() | ||
{ | ||
app.getRenderManager().renderGeometry(offGeo); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is this offgeo for exactly? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rendering offscreen onto texture of offgeo, and then render this offgeo on framebuffer of openxr was the only way I found, to get a 3d picture onto the HMD. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right. I tried removing it and it stopped working. I wonder if rendering the geometry triggers all the dependant materials and textures to be rendered and that's what actually needed? I'm going to have a play and see if that can be streamlined |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
package com.jme3.input.xr; | ||
|
||
import java.util.ArrayList; | ||
|
||
import com.jme3.app.SimpleApplication; | ||
import com.jme3.math.Quaternion; | ||
import com.jme3.math.Vector3f; | ||
import com.jme3.system.AppSettings; | ||
import com.jme3.system.lwjgl.LwjglWindowXr; | ||
|
||
public class XrHmd | ||
{ | ||
public static final float DEFAULT_X_DIST = 0.4f; | ||
public static final float DEFAULT_X_ROT = 0.028f; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is DEFAULT_X_ROT for? It looks to be 1.6 degrees which is a tiny amount. O, its making the eyes not face exactly forwards. Is that right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it is Rotation. |
||
public static final float DEFAULT_POS_MULT = 10.0f; | ||
Quaternion tmpQ = new Quaternion(); | ||
float[] tmpArr = new float[3]; | ||
Quaternion tmpQdistRotL = new Quaternion().fromAngles(0, -DEFAULT_X_ROT, 0); | ||
Quaternion tmpQdistRotR = new Quaternion().fromAngles(0, DEFAULT_X_ROT, 0); | ||
SimpleApplication app; | ||
Eye leftEye; | ||
Eye rightEye; | ||
ArrayList<XrListener.OrientationListener> hmdListeners = new ArrayList<XrListener.OrientationListener>(); | ||
ArrayList<XrListener.ButtonPressedListener> contr1Listeners = new ArrayList<XrListener.ButtonPressedListener>(); | ||
ArrayList<XrListener.ButtonPressedListener> contr2Listeners = new ArrayList<XrListener.ButtonPressedListener>(); | ||
|
||
public XrHmd(SimpleApplication app) | ||
{ | ||
this.app = app; | ||
leftEye = new Eye(app); | ||
rightEye = new Eye(app); | ||
setXDistance(DEFAULT_X_DIST); | ||
hmdListeners.add((p,r) -> doMoveRotate(p,r)); | ||
} | ||
|
||
Vector3f initPos; | ||
Vector3f diffPos = new Vector3f(); | ||
private void doMoveRotate(Vector3f p, Quaternion r) | ||
{ | ||
if (initPos == null) { initPos = new Vector3f(p.getX(), p.getY(), p.getZ()); } | ||
initPos.subtract(p,diffPos); | ||
leftEye.moveAbs(diffPos); | ||
rightEye.moveAbs(diffPos); | ||
tmpQ.set(r); | ||
tmpQ.inverseLocal(); | ||
tmpQ.set(tmpQ.getX(), tmpQ.getY(), -tmpQ.getZ(), tmpQ.getW()); | ||
leftEye.rotateAbs(tmpQ.multLocal(tmpQdistRotL)); | ||
tmpQ.set(r); | ||
tmpQ.inverseLocal(); | ||
tmpQ.set(tmpQ.getX(), tmpQ.getY(), -tmpQ.getZ(), tmpQ.getW()); | ||
rightEye.rotateAbs(tmpQ.multLocal(tmpQdistRotR)); | ||
} | ||
|
||
public Eye getLeftEye() { return leftEye; } | ||
public Eye getRightEye() { return rightEye; } | ||
|
||
public ArrayList<XrListener.OrientationListener> getHmdOrientationListeners() { return hmdListeners; } | ||
public ArrayList<XrListener.ButtonPressedListener> getContr1ButtonPressedListeners() { return contr1Listeners; } | ||
public ArrayList<XrListener.ButtonPressedListener> getContr2ButtonPressedListeners() { return contr2Listeners; } | ||
|
||
Vector3f multPos = new Vector3f(); | ||
public void onUpdateHmdOrientation(Vector3f viewPos, Quaternion viewRot) | ||
{ | ||
viewPos.mult(DEFAULT_POS_MULT, multPos); | ||
multPos.setX(0.0f-multPos.getX()); | ||
multPos.setY(0.0f-multPos.getY()); | ||
for (XrListener.OrientationListener l : hmdListeners) { l.onUpdateOrientation(multPos, viewRot); } | ||
} | ||
|
||
/** Must be called in main function before init. | ||
* @param s The appSettings that must be used with app.setSettings(s). */ | ||
public static void setRendererForSettings(AppSettings s) | ||
{ | ||
s.setRenderer("CUSTOM" + com.jme3.system.lwjgl.LwjglWindowXr.class.getName()); //see JmeDesktopSystem.newContext(...) | ||
} | ||
|
||
/** Must be called in simpleInitApp-function of SimpleApplication. | ||
* @return The head-mounted-device object. */ | ||
public static XrHmd initHmd(SimpleApplication app) | ||
{ | ||
XrHmd xrHmd = new XrHmd(app); | ||
((LwjglWindowXr)app.getContext()).getXr().setHmd(xrHmd); | ||
return xrHmd; | ||
} | ||
|
||
/** Gets the distance between the eyes. Default is DEFAULT_X_DIST */ | ||
public float getXDistance() { return rightEye.getPosX() * 2f; } | ||
|
||
/** Sets the distance between the eyes. Default is DEFAULT_X_DIST */ | ||
public void setXDistance(float xDist) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd suggest calling this the interpupillary distance as that seems to be the formal term for this |
||
{ | ||
rightEye.setPosX(xDist / 2f); | ||
leftEye.setPosX(xDist / -2f); | ||
} | ||
|
||
/** Gets the rotation angle between the eyes. Default is DEFAULT_X_ROT */ | ||
public float getXRotation() { return tmpQdistRotR.toAngles(tmpArr)[1]; } | ||
|
||
/** Sets the rotation angle between the eyes. Default is DEFAULT_X_ROT */ | ||
public void setXRotation(float xRot) | ||
{ | ||
tmpQdistRotR.fromAngles(0, xRot, 0); | ||
tmpQdistRotL.fromAngles(0, xRot, 0); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package com.jme3.input.xr; | ||
|
||
import com.jme3.math.Quaternion; | ||
import com.jme3.math.Vector3f; | ||
|
||
public interface XrListener { | ||
public interface OrientationListener | ||
{ | ||
public void onUpdateOrientation(Vector3f pos, Quaternion rot); | ||
} | ||
|
||
public interface ButtonPressedListener | ||
{ | ||
public void onButtonPressed(int num); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package com.jme3.system.lwjgl; | ||
|
||
import static org.lwjgl.glfw.GLFW.glfwGetTime; | ||
|
||
import com.jme3.input.KeyInput; | ||
import com.jme3.input.RawInputListener; | ||
|
||
public class EmptyKeyInput implements KeyInput { | ||
|
||
public EmptyKeyInput() { } | ||
@Override | ||
public void initialize() {} | ||
|
||
@Override | ||
public boolean isInitialized() {return false;} | ||
|
||
@Override | ||
public void update() {} | ||
@Override | ||
public void destroy() { | ||
// TODO Auto-generated method stub | ||
|
||
} | ||
@Override | ||
public void setInputListener(RawInputListener listener) { | ||
// TODO Auto-generated method stub | ||
|
||
} | ||
@Override | ||
public long getInputTimeNanos() { | ||
return (long) (glfwGetTime() * 1000000000); | ||
} | ||
@Override | ||
public String getKeyName(int key) { | ||
// TODO Auto-generated method stub | ||
return null; | ||
} | ||
|
||
public void resetContext() {} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package com.jme3.system.lwjgl; | ||
|
||
import static org.lwjgl.glfw.GLFW.glfwGetTime; | ||
|
||
import com.jme3.cursors.plugins.JmeCursor; | ||
import com.jme3.input.MouseInput; | ||
import com.jme3.input.RawInputListener; | ||
|
||
public class EmptyMouseInput implements MouseInput { | ||
|
||
public EmptyMouseInput() { } | ||
@Override | ||
public void initialize() {} | ||
|
||
@Override | ||
public boolean isInitialized() {return false;} | ||
@Override | ||
public void update() { | ||
// TODO Auto-generated method stub | ||
|
||
} | ||
@Override | ||
public void destroy() { | ||
// TODO Auto-generated method stub | ||
|
||
} | ||
@Override | ||
public void setInputListener(RawInputListener listener) { | ||
// TODO Auto-generated method stub | ||
|
||
} | ||
@Override | ||
public long getInputTimeNanos() { | ||
return (long) (glfwGetTime() * 1000000000); | ||
} | ||
@Override | ||
public void setCursorVisible(boolean visible) { | ||
// TODO Auto-generated method stub | ||
|
||
} | ||
@Override | ||
public int getButtonCount() { | ||
// TODO Auto-generated method stub | ||
return 3; | ||
} | ||
@Override | ||
public void setNativeCursor(JmeCursor cursor) { | ||
// TODO Auto-generated method stub | ||
} | ||
|
||
public void resetContext() {} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like there may be some tabs vs spaces issues going on here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can fix this, when I am back from holiday next week.
Have no access to my account currently.