Added Slime Commons dependency, move VR brdige code from test project

This commit is contained in:
Eiren Rain
2021-01-05 18:15:29 +03:00
parent d26468da8e
commit 51508e0380
8 changed files with 326 additions and 2 deletions

View File

@@ -28,5 +28,6 @@
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
<classpathentry combineaccessrules="false" kind="src" path="/Slime Java Commons"/>
<classpathentry kind="output" path="bin/default"/>
</classpath>

5
.gitignore vendored
View File

@@ -3,3 +3,8 @@
# Ignore Gradle build output directory
build
/bin/
# Syncthing ignore file
.stignore

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>SlimeVR-Server</name>
<name>SlimeVR Server</name>
<comment>SlimeVR Server</comment>
<projects>
</projects>

View File

@@ -7,4 +7,5 @@
* in the user manual at https://docs.gradle.org/6.3/userguide/multi_project_builds.html
*/
rootProject.name = 'SlimeVR-Server'
rootProject.name = 'SlimeVR Server'
include('Slime Java Commons')

View File

@@ -0,0 +1,204 @@
package eiren.io.vr.bridge;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.WinBase;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.ptr.IntByReference;
import essentia.util.collections.FastList;
import io.eiren.util.StringUtils;
import io.eiren.util.logging.LogManager;
public class NamedPipeVRBridge extends Thread implements VRBridge {
public static final String HMDPipeName = "\\\\.\\pipe\\HMDPipe";
public static final String TrackersPipeName = "\\\\.\\pipe\\TrackPipe";
public static final int TRACKERS = 3;
private static final byte[] buffer = new byte[1024];
private Pipe hmdPipe;
private List<Pipe> trackerPipes = new FastList<>();
protected VRBridgeState bridgeState = VRBridgeState.NOT_STARTED;
public NamedPipeVRBridge() {
super("Named Pipe VR Bridge");
}
@Override
public VRBridgeState getBridgeState() {
return bridgeState;
}
@Override
public void run() {
try {
createPipes();
bridgeState = VRBridgeState.STARTED;
while(true) {
if(bridgeState == VRBridgeState.STARTED) {
if(hmdPipe != null && hmdPipe.state == PipeState.CREATED) {
if(tryOpeningPipe(hmdPipe))
initHMDPipe(hmdPipe);
}
for(int i = 0; i < trackerPipes.size(); ++i) {
Pipe trackerPipe = trackerPipes.get(i);
if(trackerPipe.state == PipeState.CREATED)
if(tryOpeningPipe(trackerPipe))
initTrackerPipe(trackerPipe, i);
}
if(areAllPipesOpen()) {
bridgeState = VRBridgeState.CONNECTED;
LogManager.log.info("[VRBridge] All pipes are connected!");
} else {
Thread.sleep(200L);
}
} else {
updateHMD();
for(int i = 0; i < trackerPipes.size(); ++i) {
updateTracker(trackerPipes.get(i), i);
}
}
}
} catch(Exception e) {
e.printStackTrace();
bridgeState = VRBridgeState.ERROR;
}
}
public void updateHMD() {
IntByReference bytesAvailable = new IntByReference(0);
if(Kernel32.INSTANCE.PeekNamedPipe(hmdPipe.pipeHandle, null, 0, null, bytesAvailable, null)) {
if(bytesAvailable.getValue() > 0) {
if(Kernel32.INSTANCE.ReadFile(hmdPipe.pipeHandle, buffer, buffer.length, bytesAvailable, null)) {
String str = new String(buffer, 0, bytesAvailable.getValue() - 1, Charset.forName("ASCII"));
String[] split = str.split("\n")[0].split(" ");
try {
double x = Double.parseDouble(split[0]);
double y = Double.parseDouble(split[1]);
double z = Double.parseDouble(split[2]);
double qw = Double.parseDouble(split[3]);
double qx = Double.parseDouble(split[4]);
double qy = Double.parseDouble(split[5]);
double qz = Double.parseDouble(split[6]);
LogManager.log.info("[VRBridge] New HMD position:"
+ " " + StringUtils.prettyNumber((float) x, 2)
+ " " + StringUtils.prettyNumber((float) y, 2)
+ " " + StringUtils.prettyNumber((float) z, 2)
+ " " + StringUtils.prettyNumber((float) qw, 2)
+ " " + StringUtils.prettyNumber((float) qx, 2)
+ " " + StringUtils.prettyNumber((float) qy, 2)
+ " " + StringUtils.prettyNumber((float) qz, 2));
} catch(NumberFormatException e) {
e.printStackTrace();
}
}
}
}
}
public void updateTracker(Pipe pipe, int trackerId) {
}
private void initHMDPipe(Pipe pipe) {
}
private void initTrackerPipe(Pipe pipe, int trackerId) {
String trackerHello = TRACKERS + " 0";
byte[] buff = new byte[trackerHello.length() + 1];// = length of string + terminating '\0' !!!
System.arraycopy(trackerHello.getBytes(Charset.forName("ASCII")), 0, buff, 0, trackerHello.length());
IntByReference lpNumberOfBytesWritten = new IntByReference(0);
Kernel32.INSTANCE.WriteFile(pipe.pipeHandle,
buff,
buff.length,
lpNumberOfBytesWritten,
null);
}
private boolean tryOpeningPipe(Pipe pipe) {
if(Kernel32.INSTANCE.ConnectNamedPipe(pipe.pipeHandle, null)) {
pipe.state = NamedPipeVRBridge.PipeState.OPEN;
LogManager.log.info("[VRBridge] Pipe " + pipe.name + " is open");
return true;
}
return false;
}
private boolean areAllPipesOpen() {
if(hmdPipe == null || hmdPipe.state == PipeState.CREATED) {
return false;
}
for(int i = 0; i < trackerPipes.size(); ++i) {
if(trackerPipes.get(i).state == PipeState.CREATED)
return false;
}
return true;
}
private void createPipes() throws IOException {
try {
hmdPipe = new Pipe(Kernel32.INSTANCE.CreateNamedPipe(HMDPipeName, WinBase.PIPE_ACCESS_DUPLEX, // dwOpenMode
WinBase.PIPE_TYPE_BYTE | WinBase.PIPE_READMODE_BYTE | WinBase.PIPE_WAIT, // dwPipeMode
1, // nMaxInstances,
1024 * 16, // nOutBufferSize,
1024 * 16, // nInBufferSize,
0, // nDefaultTimeOut,
null), HMDPipeName); // lpSecurityAttributes
LogManager.log.info("[VRBridge] Pipe " + hmdPipe.name + " created");
if(WinBase.INVALID_HANDLE_VALUE.equals(hmdPipe.pipeHandle))
throw new IOException("Can't open " + HMDPipeName + " pipe: " + Kernel32.INSTANCE.GetLastError());
for(int i = 0; i < TRACKERS; ++i) {
String pipeName = TrackersPipeName + i;
HANDLE pipeHandle = Kernel32.INSTANCE.CreateNamedPipe(pipeName, WinBase.PIPE_ACCESS_DUPLEX, // dwOpenMode
WinBase.PIPE_TYPE_BYTE | WinBase.PIPE_READMODE_BYTE | WinBase.PIPE_WAIT, // dwPipeMode
1, // nMaxInstances,
1024 * 16, // nOutBufferSize,
1024 * 16, // nInBufferSize,
0, // nDefaultTimeOut,
null); // lpSecurityAttributes
if(WinBase.INVALID_HANDLE_VALUE.equals(pipeHandle))
throw new IOException("Can't open " + pipeName + " pipe: " + Kernel32.INSTANCE.GetLastError());
LogManager.log.info("[VRBridge] Pipe " + pipeName + " created");
trackerPipes.add(new Pipe(pipeHandle, pipeName));
}
LogManager.log.info("[VRBridge] Pipes are open");
} catch(IOException e) {
safeDisconnect(hmdPipe);
for(int i = 0; i < trackerPipes.size(); ++i)
safeDisconnect(trackerPipes.get(i));
trackerPipes.clear();
throw e;
}
}
public static void safeDisconnect(Pipe pipe) {
try {
if(pipe != null && pipe.pipeHandle != null)
Kernel32.INSTANCE.DisconnectNamedPipe(pipe.pipeHandle);
} catch(Exception e) {
}
}
private static class Pipe {
final String name;
final HANDLE pipeHandle;
PipeState state = PipeState.CREATED;
public Pipe(HANDLE pipeHandle, String name) {
this.pipeHandle = pipeHandle;
this.name = name;
}
}
private static enum PipeState {
CREATED,
OPEN,
ERROR;
}
}

View File

@@ -0,0 +1,13 @@
package eiren.io.vr.bridge;
public interface VRBridge {
public VRBridgeState getBridgeState();
public static enum VRBridgeState {
NOT_STARTED,
STARTED,
CONNECTED,
ERROR
}
}

View File

@@ -0,0 +1,11 @@
package eiren.io.vr.sensors;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
public class RotationSensor {
public final Vector3f gyroVector = new Vector3f();
public final Vector3f accelVector = new Vector3f();
public final Vector3f magVector = new Vector3f();
public final Quaternion rotQuaternion = new Quaternion();
}

View File

@@ -0,0 +1,89 @@
package eiren.io.vr.sensors;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import io.eiren.util.Util;
public class SensorUDPServer extends Thread {
DatagramSocket socket = null;
byte[] sendBuffer = new byte[64];
long lastKeepup = System.currentTimeMillis();
boolean connected = false;
private final RotationSensor sensor;
private final int port;
public SensorUDPServer(int port, String name, RotationSensor sensor) {
super(name);
this.sensor = sensor;
this.port = port;
}
@Override
public void run() {
try {
socket = new DatagramSocket(port);
socket.setSoTimeout(250);
while(true) {
try {
byte[] rcvBuffer = new byte[64];
ByteBuffer bb = ByteBuffer.wrap(rcvBuffer).order(ByteOrder.BIG_ENDIAN);
//ByteBuffer bb2 = bb.asReadOnlyBuffer().order(ByteOrder.BIG_ENDIAN);
DatagramPacket recieve = new DatagramPacket(rcvBuffer, 64);
socket.receive(recieve);
bb.rewind();
//System.out.println(StringUtils.toHexString(rcvBuffer));
switch(bb.getInt()) {
case 3:
System.out.println("Handshake");
sendBuffer[0] = 3;
byte[] str = "Hey OVR =D 5".getBytes("ASCII");
System.arraycopy(str, 0, sendBuffer, 1, str.length);
socket.send(new DatagramPacket(sendBuffer, 64, recieve.getAddress(), recieve.getPort()));
connected = true;
break;
case 1:
bb.getLong();
sensor.rotQuaternion.set(bb.getFloat(), bb.getFloat(), bb.getFloat(), bb.getFloat());
//rotQuaternion.set(-rotQuaternion.getY(), rotQuaternion.getX(), rotQuaternion.getZ(), rotQuaternion.getW());
//System.out.println("Rot: " + rotQuaternion.getX() + "," + rotQuaternion.getY() + "," + rotQuaternion.getZ() + "," + rotQuaternion.getW());
break;
case 2:
bb.getLong();
sensor.gyroVector.set(bb.getFloat(), bb.getFloat(), bb.getFloat());
//System.out.println("Gyro: " + bb.getFloat() + "," + bb.getFloat() + "," + bb.getFloat());
break;
case 4:
bb.getLong();
sensor.accelVector.set(bb.get(), bb.getFloat(), bb.getFloat());
//System.out.println("Accel: " + bb.getFloat() + "," + bb.getFloat() + "," + bb.getFloat());
break;
case 5:
bb.getLong();
sensor.magVector.set(bb.get(), bb.getFloat(), bb.getFloat());
//System.out.println("Accel: " + bb.getFloat() + "," + bb.getFloat() + "," + bb.getFloat());
break;
}
if(lastKeepup + 500 < System.currentTimeMillis()) {
lastKeepup = System.currentTimeMillis();
if(connected) {
sendBuffer[0] = 1;
socket.send(new DatagramPacket(sendBuffer, 64, recieve.getAddress(), recieve.getPort()));
}
}
} catch(SocketTimeoutException e) {
} catch(Exception e) {
e.printStackTrace();
}
}
} catch(Exception e) {
e.printStackTrace();
} finally {
Util.close(socket);
}
}
}