DummyVRBridge.java 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. package io.eiren.vr.bridge;
  2. import java.io.IOException;
  3. import java.nio.charset.Charset;
  4. import java.util.List;
  5. import java.util.concurrent.atomic.AtomicBoolean;
  6. import com.jme3.math.Quaternion;
  7. import com.jme3.math.Vector3f;
  8. import com.sun.jna.ptr.IntByReference;
  9. import io.eiren.util.collections.FastList;
  10. import io.eiren.util.logging.LogManager;
  11. import io.eiren.vr.VRServer;
  12. import io.eiren.vr.trackers.ComputedTracker;
  13. import io.eiren.vr.trackers.HMDTracker;
  14. import io.eiren.vr.trackers.Tracker;
  15. import io.eiren.vr.trackers.TrackerStatus;
  16. import java.io.FileWriter;
  17. import java.io.BufferedWriter;
  18. public class DummyVRBridge extends Thread implements VRBridge {
  19. private static final int MAX_COMMAND_LENGTH = 2048;
  20. public static final String HMDPipeName = "\\\\.\\pipe\\HMDPipe";
  21. public static final String TrackersPipeName = "\\\\.\\pipe\\TrackPipe";
  22. public static final Charset ASCII = Charset.forName("ASCII");
  23. private final byte[] buffArray = new byte[1024];
  24. private final StringBuilder commandBuilder = new StringBuilder(1024);
  25. private final StringBuilder sbBuffer = new StringBuilder(1024);
  26. private final Vector3f vBuffer = new Vector3f();
  27. private final Vector3f vBuffer2 = new Vector3f();
  28. private final Quaternion qBuffer = new Quaternion();
  29. private final Quaternion qBuffer2 = new Quaternion();
  30. private final HMDTracker hmd;
  31. //private final List<Pipe> trackerPipes;
  32. private int trackerPipes_size = 0;
  33. private FileWriter debugFile;
  34. private BufferedWriter debugWriter;
  35. private void writeDmsg(String dmsg)
  36. {
  37. try {
  38. debugWriter.write(dmsg+"\n");
  39. debugWriter.flush();
  40. }
  41. catch (Exception e)
  42. {
  43. e.getStackTrace();
  44. }
  45. }
  46. private final List<? extends Tracker> shareTrackers;
  47. private final List<ComputedTracker> internalTrackers;
  48. private final HMDTracker internalHMDTracker = new HMDTracker("itnernal://HMD");
  49. private final AtomicBoolean newHMDData = new AtomicBoolean(false);
  50. public DummyVRBridge(HMDTracker hmd, List<? extends Tracker> shareTrackers, VRServer server) {
  51. super("Named Pipe VR Bridge");
  52. this.hmd = hmd;
  53. this.shareTrackers = new FastList<>(shareTrackers);
  54. //this.trackerPipes = new FastList<>(shareTrackers.size());
  55. trackerPipes_size = shareTrackers.size();
  56. this.internalTrackers = new FastList<>(shareTrackers.size());
  57. for(int i = 0; i < shareTrackers.size(); ++i) {
  58. Tracker t = shareTrackers.get(i);
  59. ComputedTracker ct = new ComputedTracker("internal://" + t.getName(), true, true);
  60. ct.setStatus(TrackerStatus.OK);
  61. this.internalTrackers.add(ct);
  62. }
  63. try {
  64. debugFile = new FileWriter("/tmp/slimevr.txt");
  65. debugWriter = new BufferedWriter(debugFile);
  66. debugWriter.write("SlimeVR GNU/Linux port\n");
  67. debugWriter.flush();
  68. }
  69. catch (Exception e)
  70. {
  71. e.getStackTrace();
  72. }
  73. sbBuffer.setLength(0);
  74. sbBuffer.append("trackerPipes_size= ").append(trackerPipes_size); //only in dummy
  75. String str = sbBuffer.toString();
  76. writeDmsg(str);
  77. }
  78. @Override
  79. public void run() {
  80. try {
  81. writeDmsg("createPipes");
  82. createPipes();
  83. while(true) {
  84. writeDmsg("waitForPipesToOpen");
  85. waitForPipesToOpen();
  86. if(areAllPipesOpen()) {
  87. boolean hmdUpdated = updateHMD(); // Update at HMDs frequency
  88. for(int i = 0; i < trackerPipes_size; ++i) {
  89. updateTracker(i, hmdUpdated);
  90. }
  91. if(!hmdUpdated) {
  92. Thread.sleep(5); // Up to 200Hz
  93. writeDmsg("sleeping");
  94. }
  95. }
  96. }
  97. } catch(Exception e) {
  98. e.printStackTrace();
  99. }
  100. }
  101. @Override
  102. public void dataRead() {
  103. if(newHMDData.compareAndSet(true, false)) {
  104. hmd.position.set(internalHMDTracker.position);
  105. hmd.rotation.set(internalHMDTracker.rotation);
  106. hmd.dataTick();
  107. }
  108. }
  109. @Override
  110. public void dataWrite() {
  111. for(int i = 0; i < shareTrackers.size(); ++i) {
  112. Tracker t = shareTrackers.get(i);
  113. ComputedTracker it = this.internalTrackers.get(i);
  114. if(t.getPosition(vBuffer2))
  115. it.position.set(vBuffer2);
  116. if(t.getRotation(qBuffer2))
  117. it.rotation.set(qBuffer2);
  118. }
  119. }
  120. private void waitForPipesToOpen() {
  121. /*
  122. if(hmdPipe.state == PipeState.CREATED) {
  123. if(tryOpeningPipe(hmdPipe))
  124. initHMDPipe(hmdPipe);
  125. }
  126. for(int i = 0; i < trackerPipes.size(); ++i) {
  127. Pipe trackerPipe = trackerPipes.get(i);
  128. if(trackerPipe.state == PipeState.CREATED) {
  129. if(tryOpeningPipe(trackerPipe))
  130. initTrackerPipe(trackerPipe, i);
  131. }
  132. }*/
  133. }
  134. public boolean updateHMD() throws IOException {
  135. /* wait for incoming command via pipe
  136. if(hmdPipe.state == PipeState.OPEN) {
  137. IntByReference bytesAvailable = new IntByReference(0);
  138. if(Kernel32.INSTANCE.PeekNamedPipe(hmdPipe.pipeHandle, null, 0, null, bytesAvailable, null)) {
  139. if(bytesAvailable.getValue() > 0) {
  140. while(Kernel32.INSTANCE.ReadFile(hmdPipe.pipeHandle, buffArray, buffArray.length, bytesAvailable, null)) {
  141. int bytesRead = bytesAvailable.getValue();
  142. for(int i = 0; i < bytesRead; ++i) {
  143. char c = (char) buffArray[i];
  144. if(c == '\n') {
  145. executeHMDInput();
  146. commandBuilder.setLength(0);
  147. } else {
  148. commandBuilder.append(c);
  149. if(commandBuilder.length() >= MAX_COMMAND_LENGTH) {
  150. LogManager.log.severe("[VRBridge] Command from the pipe is too long, flushing buffer");
  151. commandBuilder.setLength(0);
  152. }
  153. }
  154. }
  155. if(bytesRead < buffArray.length)
  156. break; // Don't repeat, we read all available bytes
  157. }
  158. return true;
  159. }
  160. }
  161. }
  162. */
  163. return false;
  164. }
  165. private void executeHMDInput() throws IOException {
  166. String[] split = commandBuilder.toString().split(" ");
  167. if(split.length < 7) {
  168. LogManager.log.severe("[VRBridge] Short HMD data recieved: " + commandBuilder.toString());
  169. return;
  170. }
  171. try {
  172. double x = Double.parseDouble(split[0]);
  173. double y = Double.parseDouble(split[1]);
  174. double z = Double.parseDouble(split[2]);
  175. double qw = Double.parseDouble(split[3]);
  176. double qx = Double.parseDouble(split[4]);
  177. double qy = Double.parseDouble(split[5]);
  178. double qz = Double.parseDouble(split[6]);
  179. internalHMDTracker.position.set((float) x, (float) y, (float) z);
  180. internalHMDTracker.rotation.set((float) qx, (float) qy, (float) qz, (float) qw);
  181. internalHMDTracker.dataTick();
  182. newHMDData.set(true);
  183. } catch(NumberFormatException e) {
  184. e.printStackTrace();
  185. }
  186. }
  187. public void updateTracker(int trackerId, boolean hmdUpdated) {
  188. Tracker sensor = internalTrackers.get(trackerId);
  189. if(sensor.getStatus().sendData) {
  190. //Pipe trackerPipe = trackerPipes.get(trackerId);
  191. //if(hmdUpdated /*&& trackerPipe.state == PipeState.OPEN*/) {
  192. sbBuffer.setLength(0);
  193. sensor.getPosition(vBuffer);
  194. sensor.getRotation(qBuffer);
  195. sbBuffer.append("updateTracker ").append(trackerId).append(' '); //only in dummy
  196. sbBuffer.append(vBuffer.x).append(' ').append(vBuffer.y).append(' ').append(vBuffer.z).append(' ');
  197. sbBuffer.append(qBuffer.getW()).append(' ').append(qBuffer.getX()).append(' ').append(qBuffer.getY()).append(' ').append(qBuffer.getZ());//.append('\n');
  198. String str = sbBuffer.toString();
  199. writeDmsg(str);
  200. //System.arraycopy(str.getBytes(ASCII), 0, buffArray, 0, str.length());
  201. //buffArray[str.length()] = '\0';
  202. //IntByReference lpNumberOfBytesWritten = new IntByReference(0);
  203. //Kernel32.INSTANCE.WriteFile(trackerPipe.pipeHandle, buffArray, str.length() + 1, lpNumberOfBytesWritten, null);
  204. //}
  205. }
  206. else
  207. writeDmsg("updateTracker: nothing to do");
  208. }
  209. /*
  210. private void initHMDPipe(Pipe pipe) {
  211. hmd.setStatus(TrackerStatus.OK);
  212. }
  213. private void initTrackerPipe(Pipe pipe, int trackerId) {
  214. String trackerHello = this.shareTrackers.size() + " 0";
  215. System.arraycopy(trackerHello.getBytes(ASCII), 0, buffArray, 0, trackerHello.length());
  216. buffArray[trackerHello.length()] = '\0';
  217. IntByReference lpNumberOfBytesWritten = new IntByReference(0);
  218. Kernel32.INSTANCE.WriteFile(pipe.pipeHandle,
  219. buffArray,
  220. trackerHello.length() + 1,
  221. lpNumberOfBytesWritten,
  222. null);
  223. }
  224. private boolean tryOpeningPipe(Pipe pipe) {
  225. if(Kernel32.INSTANCE.ConnectNamedPipe(pipe.pipeHandle, null)) {
  226. pipe.state = PipeState.OPEN;
  227. LogManager.log.info("[VRBridge] Pipe " + pipe.name + " is open");
  228. return true;
  229. }
  230. LogManager.log.info("[VRBridge] Error connecting to pipe " + pipe.name + ": " + Kernel32.INSTANCE.GetLastError());
  231. return false;
  232. }
  233. private boolean areAllPipesOpen() {
  234. if(hmdPipe == null || hmdPipe.state == PipeState.CREATED) {
  235. return false;
  236. }
  237. for(int i = 0; i < trackerPipes.size(); ++i) {
  238. if(trackerPipes.get(i).state == PipeState.CREATED)
  239. return false;
  240. }
  241. return true;
  242. }
  243. private void createPipes() throws IOException {
  244. try {
  245. hmdPipe = new Pipe(Kernel32.INSTANCE.CreateNamedPipe(HMDPipeName, WinBase.PIPE_ACCESS_DUPLEX, // dwOpenMode
  246. WinBase.PIPE_TYPE_BYTE | WinBase.PIPE_READMODE_BYTE | WinBase.PIPE_WAIT, // dwPipeMode
  247. 1, // nMaxInstances,
  248. 1024 * 16, // nOutBufferSize,
  249. 1024 * 16, // nInBufferSize,
  250. 0, // nDefaultTimeOut,
  251. null), HMDPipeName); // lpSecurityAttributes
  252. LogManager.log.info("[VRBridge] Pipe " + hmdPipe.name + " created");
  253. if(WinBase.INVALID_HANDLE_VALUE.equals(hmdPipe.pipeHandle))
  254. throw new IOException("Can't open " + HMDPipeName + " pipe: " + Kernel32.INSTANCE.GetLastError());
  255. for(int i = 0; i < this.shareTrackers.size(); ++i) {
  256. String pipeName = TrackersPipeName + i;
  257. HANDLE pipeHandle = Kernel32.INSTANCE.CreateNamedPipe(pipeName, WinBase.PIPE_ACCESS_DUPLEX, // dwOpenMode
  258. WinBase.PIPE_TYPE_BYTE | WinBase.PIPE_READMODE_BYTE | WinBase.PIPE_WAIT, // dwPipeMode
  259. 1, // nMaxInstances,
  260. 1024 * 16, // nOutBufferSize,
  261. 1024 * 16, // nInBufferSize,
  262. 0, // nDefaultTimeOut,
  263. null); // lpSecurityAttributes
  264. if(WinBase.INVALID_HANDLE_VALUE.equals(pipeHandle))
  265. throw new IOException("Can't open " + pipeName + " pipe: " + Kernel32.INSTANCE.GetLastError());
  266. LogManager.log.info("[VRBridge] Pipe " + pipeName + " created");
  267. trackerPipes.add(new Pipe(pipeHandle, pipeName));
  268. }
  269. LogManager.log.info("[VRBridge] Pipes are open");
  270. } catch(IOException e) {
  271. safeDisconnect(hmdPipe);
  272. for(int i = 0; i < trackerPipes.size(); ++i)
  273. safeDisconnect(trackerPipes.get(i));
  274. trackerPipes.clear();
  275. throw e;
  276. }
  277. }
  278. public static void safeDisconnect(Pipe pipe) {
  279. try {
  280. if(pipe != null && pipe.pipeHandle != null)
  281. Kernel32.INSTANCE.DisconnectNamedPipe(pipe.pipeHandle);
  282. } catch(Exception e) {
  283. }
  284. }
  285. */
  286. private void createPipes() throws IOException {}
  287. private boolean areAllPipesOpen() {return true;}
  288. }