Refactored TrackerPosition (#181)

This commit is contained in:
Ryan Butler
2022-06-01 10:08:56 -04:00
committed by GitHub
parent 215f3b9f44
commit d40e2c2ce2
34 changed files with 1884 additions and 1977 deletions

View File

@@ -8,7 +8,7 @@ import dev.slimevr.protocol.ProtocolAPI;
import dev.slimevr.serial.SerialHandler;
import dev.slimevr.util.ann.VRServerThread;
import dev.slimevr.vr.processor.HumanPoseProcessor;
import dev.slimevr.vr.processor.skeleton.HumanSkeleton;
import dev.slimevr.vr.processor.skeleton.Skeleton;
import dev.slimevr.vr.trackers.*;
import dev.slimevr.vr.trackers.udp.TrackersUDPServer;
import dev.slimevr.websocketapi.WebSocketVRBridge;
@@ -194,7 +194,7 @@ public class VRServer extends Thread {
}
@ThreadSafe
public void addSkeletonUpdatedCallback(Consumer<HumanSkeleton> consumer) {
public void addSkeletonUpdatedCallback(Consumer<Skeleton> consumer) {
queueTask(() -> {
humanPoseProcessor.addSkeletonUpdatedCallback(consumer);
});

View File

@@ -5,7 +5,7 @@ import com.jme3.math.Vector3f;
import dev.slimevr.VRServer;
import dev.slimevr.poserecorder.*;
import dev.slimevr.vr.processor.HumanPoseProcessor;
import dev.slimevr.vr.processor.skeleton.HumanSkeleton;
import dev.slimevr.vr.processor.skeleton.Skeleton;
import dev.slimevr.vr.processor.skeleton.SkeletonConfig;
import dev.slimevr.vr.processor.skeleton.SkeletonConfigValue;
import dev.slimevr.vr.trackers.TrackerPosition;
@@ -187,14 +187,14 @@ public class AutoBone {
}
/**
* A simple utility method to get the {@link HumanSkeleton} from the
* A simple utility method to get the {@link Skeleton} from the
* {@link VRServer}
*
* @return The {@link HumanSkeleton} associated with the {@link VRServer},
* or null if there is none available
* @see {@link VRServer}, {@link HumanSkeleton}
* @return The {@link Skeleton} associated with the {@link VRServer}, or
* null if there is none available
* @see {@link VRServer}, {@link Skeleton}
*/
private HumanSkeleton getSkeleton() {
private Skeleton getSkeleton() {
HumanPoseProcessor humanPoseProcessor = server != null ? server.humanPoseProcessor : null;
return humanPoseProcessor != null ? humanPoseProcessor.getSkeleton() : null;
}
@@ -206,7 +206,7 @@ public class AutoBone {
}
}
public boolean applyConfigToSkeleton(HumanSkeleton skeleton) {
public boolean applyConfigToSkeleton(Skeleton skeleton) {
if (skeleton == null) {
return false;
}
@@ -351,7 +351,7 @@ public class AutoBone {
// If target height isn't specified, auto-detect
if (targetHeight < 0f) {
// Get the current skeleton from the server
HumanSkeleton skeleton = getSkeleton();
Skeleton skeleton = getSkeleton();
if (skeleton != null) {
// If there is a skeleton available, calculate the target height
// from its
@@ -647,7 +647,7 @@ public class AutoBone {
}
Vector3f nodePos = skeleton
.getComputedTracker(trackerFrame.designation.trackerRole).position;
.getComputedTracker(trackerFrame.designation.trackerRole.get()).position;
if (nodePos != null) {
offset += FastMath.abs(nodePos.distance(trackerFrame.position));
offsetCount++;
@@ -682,13 +682,13 @@ public class AutoBone {
}
Vector3f nodePos1 = skeleton1
.getComputedTracker(trackerFrame1.designation.trackerRole).position;
.getComputedTracker(trackerFrame1.designation.trackerRole.get()).position;
if (nodePos1 == null) {
continue;
}
Vector3f nodePos2 = skeleton2
.getComputedTracker(trackerFrame2.designation.trackerRole).position;
.getComputedTracker(trackerFrame2.designation.trackerRole.get()).position;
if (nodePos2 == null) {
continue;
}

View File

@@ -3,7 +3,7 @@ package dev.slimevr.gui;
import dev.slimevr.VRServer;
import dev.slimevr.gui.swing.ButtonTimer;
import dev.slimevr.gui.swing.EJBagNoStretch;
import dev.slimevr.vr.processor.skeleton.HumanSkeleton;
import dev.slimevr.vr.processor.skeleton.Skeleton;
import dev.slimevr.vr.processor.skeleton.SkeletonConfigValue;
import io.eiren.util.StringUtils;
import io.eiren.util.ann.ThreadSafe;
@@ -35,7 +35,7 @@ public class SkeletonConfigGUI extends EJBagNoStretch {
}
@ThreadSafe
public void skeletonUpdated(HumanSkeleton newSkeleton) {
public void skeletonUpdated(Skeleton newSkeleton) {
java.awt.EventQueue.invokeLater(() -> {
removeAll();

View File

@@ -7,7 +7,7 @@ import dev.slimevr.VRServer;
import dev.slimevr.gui.swing.EJBagNoStretch;
import dev.slimevr.util.ann.VRServerThread;
import dev.slimevr.vr.processor.TransformNode;
import dev.slimevr.vr.processor.skeleton.HumanSkeleton;
import dev.slimevr.vr.processor.skeleton.Skeleton;
import io.eiren.util.StringUtils;
import io.eiren.util.ann.ThreadSafe;
import io.eiren.util.collections.FastList;
@@ -36,7 +36,7 @@ public class SkeletonList extends EJBagNoStretch {
}
@ThreadSafe
public void skeletonUpdated(HumanSkeleton newSkeleton) {
public void skeletonUpdated(Skeleton newSkeleton) {
java.awt.EventQueue.invokeLater(() -> {
removeAll();
nodes.clear();

View File

@@ -185,7 +185,7 @@ public class TrackersList extends EJBoxNoStretch {
desSelect.addItem(p.name());
}
if (cfg.designation != null) {
TrackerPosition p = TrackerPosition.getByDesignation(cfg.designation);
TrackerPosition p = TrackerPosition.getByDesignation(cfg.designation).get();
if (p != null)
desSelect.setSelectedItem(p.name());
}

View File

@@ -114,7 +114,7 @@ public class WindowsNamedPipeBridge extends ProtobufBridge<VRTracker> implements
);
TrackerRole role = TrackerRole.getById(trackerAdded.getTrackerRole());
if (role != null) {
tracker.setBodyPosition(TrackerPosition.getByRole(role));
tracker.setBodyPosition(TrackerPosition.getByTrackerRole(role).get());
}
return tracker;
}

View File

@@ -1,360 +0,0 @@
package dev.slimevr.platform.windows;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.WinBase;
import com.sun.jna.platform.win32.WinError;
import com.sun.jna.ptr.IntByReference;
import dev.slimevr.VRServer;
import dev.slimevr.bridge.Bridge;
import dev.slimevr.bridge.PipeState;
import dev.slimevr.vr.trackers.ShareableTracker;
import dev.slimevr.vr.trackers.TrackerPosition;
import dev.slimevr.vr.trackers.TrackerStatus;
import dev.slimevr.vr.trackers.VRTracker;
import io.eiren.util.collections.FastList;
import io.eiren.util.logging.LogManager;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
public class WindowsSteamVRPipeInputBridge extends Thread implements Bridge {
public static final String PipeName = "\\\\.\\pipe\\SlimeVRInput";
private static final int MAX_COMMAND_LENGTH = 2048;
private final byte[] buffArray = new byte[1024];
private final VRServer server;
private final StringBuilder commandBuilder = new StringBuilder(1024);
private final List<VRTracker> trackers = new FastList<>();
private final Map<Integer, VRTracker> trackersInternal = new HashMap<>();
private final Vector3f vBuffer = new Vector3f();
private final Quaternion qBuffer = new Quaternion();
private final AtomicBoolean newData = new AtomicBoolean(false);
private WindowsPipe pipe;
public WindowsSteamVRPipeInputBridge(VRServer server) {
this.server = server;
}
@Override
public void run() {
try {
createPipes();
while (true) {
boolean pipesUpdated = false;
if (pipe.state == PipeState.CREATED) {
tryOpeningPipe(pipe);
}
if (pipe.state == PipeState.OPEN) {
pipesUpdated = updatePipes();
}
if (pipe.state == PipeState.ERROR) {
resetPipe();
}
if (!pipesUpdated) {
try {
Thread.sleep(5); // Up to 200Hz
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public boolean updatePipes() throws IOException {
if (pipe.state == PipeState.OPEN) {
IntByReference bytesAvailable = new IntByReference(0);
if (
Kernel32.INSTANCE
.PeekNamedPipe(pipe.pipeHandle, null, 0, null, bytesAvailable, null)
) {
if (bytesAvailable.getValue() > 0) {
while (
Kernel32.INSTANCE
.ReadFile(
pipe.pipeHandle,
buffArray,
buffArray.length,
bytesAvailable,
null
)
) {
int bytesRead = bytesAvailable.getValue();
for (int i = 0; i < bytesRead; ++i) {
char c = (char) buffArray[i];
if (c == '\n') {
executeInputCommand();
commandBuilder.setLength(0);
} else {
commandBuilder.append(c);
if (commandBuilder.length() >= MAX_COMMAND_LENGTH) {
LogManager
.severe(
"[SteamVRPipeInputBridge] Command from the pipe is too long, flushing buffer"
);
commandBuilder.setLength(0);
}
}
}
if (bytesRead < buffArray.length)
return true; // All pipe data read
}
} else {
return false; // Pipe was empty, it's okay
}
}
// PeekNamedPipe or ReadFile returned an error
pipe.state = PipeState.ERROR;
LogManager
.severe("[SteamVRPipeInputBridge] Pipe error: " + Kernel32.INSTANCE.GetLastError());
}
return false;
}
private void executeInputCommand() throws IOException {
String[] command = commandBuilder.toString().split(" ");
switch (command[0]) {
case "ADD": // Add new tracker
if (command.length < 4) {
LogManager
.severe(
"[SteamVRPipeInputBridge] Error in ADD command. Command requires at least 4 arguments. Supplied: "
+ commandBuilder
);
return;
}
VRTracker internalTracker = new VRTracker(
Integer.parseInt(command[1]),
StringUtils.join(command, " ", 3, command.length),
true,
true
);
int roleId = Integer.parseInt(command[2]);
if (roleId >= 0 && roleId < SteamVRInputRoles.values.length) {
SteamVRInputRoles svrRole = SteamVRInputRoles.values[roleId];
internalTracker.bodyPosition = svrRole.bodyPosition;
}
VRTracker oldTracker;
synchronized (trackersInternal) {
oldTracker = trackersInternal
.put(internalTracker.getTrackerId(), internalTracker);
}
if (oldTracker != null) {
LogManager
.severe(
"[SteamVRPipeInputBridge] New tracker added with the same id. Supplied: "
+ commandBuilder
);
return;
}
newData.set(true);
break;
case "UPD": // Update tracker data
if (command.length < 9) {
LogManager
.severe(
"[SteamVRPipeInputBridge] Error in UPD command. Command requires at least 9 arguments. Supplied: "
+ commandBuilder
);
return;
}
int id = Integer.parseInt(command[1]);
double x = Double.parseDouble(command[2]);
double y = Double.parseDouble(command[3]);
double z = Double.parseDouble(command[4]);
double qw = Double.parseDouble(command[5]);
double qx = Double.parseDouble(command[6]);
double qy = Double.parseDouble(command[7]);
double qz = Double.parseDouble(command[8]);
internalTracker = trackersInternal.get(id);
if (internalTracker != null) {
internalTracker.position.set((float) x, (float) y, (float) z);
internalTracker.rotation.set((float) qx, (float) qy, (float) qz, (float) qw);
internalTracker.dataTick();
newData.set(true);
}
break;
case "STA": // Update tracker status
if (command.length < 3) {
LogManager
.severe(
"[SteamVRPipeInputBridge] Error in STA command. Command requires at least 3 arguments. Supplied: "
+ commandBuilder
);
return;
}
id = Integer.parseInt(command[1]);
int status = Integer.parseInt(command[2]);
TrackerStatus st = TrackerStatus.getById(status);
if (st == null) {
LogManager
.severe(
"[SteamVRPipeInputBridge] Unrecognized status id. Supplied: "
+ commandBuilder
);
return;
}
internalTracker = trackersInternal.get(id);
if (internalTracker != null) {
internalTracker.setStatus(st);
newData.set(true);
}
break;
}
}
@Override
public void dataRead() {
if (newData.getAndSet(false)) {
if (trackers.size() < trackersInternal.size()) {
// Add new trackers
synchronized (trackersInternal) {
Iterator<VRTracker> iterator = trackersInternal.values().iterator();
internal: while (iterator.hasNext()) {
VRTracker internalTracker = iterator.next();
for (VRTracker t : trackers) {
if (t.getTrackerId() == internalTracker.getTrackerId())
continue internal;
}
// Tracker is not found in current trackers
VRTracker tracker = new VRTracker(
internalTracker.getTrackerId(),
internalTracker.getName(),
true,
true
);
tracker.bodyPosition = internalTracker.bodyPosition;
trackers.add(tracker);
server.registerTracker(tracker);
}
}
}
for (VRTracker tracker : trackers) {
VRTracker internal = trackersInternal.get(tracker.getTrackerId());
if (internal == null)
throw new NullPointerException(
"Lost internal tracker somehow: " + tracker.getTrackerId()
); // Shouln't
// really
// happen
// even,
// but
// better
// to
// catch
// it
// like
// this
if (internal.getPosition(vBuffer))
tracker.position.set(vBuffer);
if (internal.getRotation(qBuffer))
tracker.rotation.set(qBuffer);
tracker.setStatus(internal.getStatus());
tracker.dataTick();
}
}
}
@Override
public void dataWrite() {
// Not used, only input
}
private void resetPipe() {
WindowsPipe.safeDisconnect(pipe);
pipe.state = PipeState.CREATED;
// Main.vrServer.queueTask(this::disconnected);
}
private boolean tryOpeningPipe(WindowsPipe pipe) {
if (
Kernel32.INSTANCE.ConnectNamedPipe(pipe.pipeHandle, null)
|| Kernel32.INSTANCE.GetLastError() == WinError.ERROR_PIPE_CONNECTED
) {
pipe.state = PipeState.OPEN;
LogManager.info("[SteamVRPipeInputBridge] Pipe " + pipe.name + " is open");
return true;
}
LogManager
.info(
"[SteamVRPipeInputBridge] Error connecting to pipe "
+ pipe.name
+ ": "
+ Kernel32.INSTANCE.GetLastError()
);
return false;
}
private void createPipes() throws IOException {
try {
pipe = new WindowsPipe(
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
),
PipeName
); // lpSecurityAttributes
LogManager.info("[SteamVRPipeInputBridge] Pipe " + pipe.name + " created");
if (WinBase.INVALID_HANDLE_VALUE.equals(pipe.pipeHandle))
throw new IOException(
"Can't open " + PipeName + " pipe: " + Kernel32.INSTANCE.GetLastError()
);
LogManager.info("[SteamVRPipeInputBridge] Pipes are open");
} catch (IOException e) {
WindowsPipe.safeDisconnect(pipe);
throw e;
}
}
@Override
public void addSharedTracker(ShareableTracker tracker) {
// TODO Auto-generated method stub
}
@Override
public void removeSharedTracker(ShareableTracker tracker) {
// TODO Auto-generated method stub
}
@Override
public void startBridge() {
start();
}
public enum SteamVRInputRoles {
HEAD(TrackerPosition.HMD), LEFT_HAND(TrackerPosition.LEFT_CONTROLLER),
RIGHT_HAND(TrackerPosition.RIGHT_CONTROLLER), LEFT_FOOT(TrackerPosition.LEFT_FOOT),
RIGHT_FOOT(TrackerPosition.RIGHT_FOOT), LEFT_SHOULDER(TrackerPosition.NONE),
RIGHT_SHOULDER(TrackerPosition.NONE), LEFT_ELBOW(TrackerPosition.LEFT_FOREARM),
RIGHT_ELBOW(TrackerPosition.RIGHT_FOREARM), LEFT_KNEE(TrackerPosition.LEFT_KNEE),
RIGHT_KNEE(TrackerPosition.RIGHT_KNEE), WAIST(TrackerPosition.WAIST),
CHEST(TrackerPosition.CHEST),;
private static final SteamVRInputRoles[] values = values();
public final TrackerPosition bodyPosition;
SteamVRInputRoles(TrackerPosition slimeVrPosition) {
this.bodyPosition = slimeVrPosition;
}
}
}

View File

@@ -92,7 +92,7 @@ public final class PoseFrameIO {
TrackerPosition designation = null;
if (TrackerFrameData.DESIGNATION.check(dataFlags)) {
designation = TrackerPosition.getByDesignation(inputStream.readUTF());
designation = TrackerPosition.getByDesignation(inputStream.readUTF()).get();
}
Quaternion rotation = null;

View File

@@ -2,7 +2,7 @@ package dev.slimevr.poserecorder;
import dev.slimevr.VRServer;
import dev.slimevr.vr.processor.ComputedHumanPoseTracker;
import dev.slimevr.vr.processor.skeleton.SimpleSkeleton;
import dev.slimevr.vr.processor.skeleton.HumanSkeleton;
import dev.slimevr.vr.processor.skeleton.SkeletonConfigValue;
import dev.slimevr.vr.trackers.Tracker;
@@ -10,7 +10,7 @@ import java.util.List;
import java.util.Map;
public class PoseFrameSkeleton extends SimpleSkeleton {
public class PoseFrameSkeleton extends HumanSkeleton {
private int frameCursor = 0;

View File

@@ -5,7 +5,7 @@ import com.jme3.math.Quaternion;
import com.jme3.math.Transform;
import com.jme3.math.Vector3f;
import dev.slimevr.vr.processor.TransformNode;
import dev.slimevr.vr.processor.skeleton.HumanSkeleton;
import dev.slimevr.vr.processor.skeleton.Skeleton;
import org.apache.commons.lang3.StringUtils;
import java.io.*;
@@ -23,7 +23,7 @@ public class BVHFileStream extends PoseDataStream {
private float[] angleBuf = new float[3];
private Quaternion rotBuf = new Quaternion();
private HumanSkeleton wrappedSkeleton;
private Skeleton wrappedSkeleton;
private TransformNodeWrapper rootNode;
public BVHFileStream(OutputStream outputStream) {
@@ -48,7 +48,7 @@ public class BVHFileStream extends PoseDataStream {
return bufferCount > 0 ? frameString + StringUtils.repeat(' ', bufferCount) : frameString;
}
private TransformNodeWrapper wrapSkeletonIfNew(HumanSkeleton skeleton) {
private TransformNodeWrapper wrapSkeletonIfNew(Skeleton skeleton) {
TransformNodeWrapper wrapper = rootNode;
// If the wrapped skeleton is missing or the skeleton is updated
@@ -59,7 +59,7 @@ public class BVHFileStream extends PoseDataStream {
return wrapper;
}
private TransformNodeWrapper wrapSkeleton(HumanSkeleton skeleton) {
private TransformNodeWrapper wrapSkeleton(Skeleton skeleton) {
TransformNodeWrapper wrapper = wrapSkeletonNodes(skeleton.getRootNode());
wrappedSkeleton = skeleton;
@@ -134,7 +134,7 @@ public class BVHFileStream extends PoseDataStream {
}
@Override
public void writeHeader(HumanSkeleton skeleton, PoseStreamer streamer) throws IOException {
public void writeHeader(Skeleton skeleton, PoseStreamer streamer) throws IOException {
if (skeleton == null) {
throw new NullPointerException("skeleton must not be null");
}
@@ -249,7 +249,7 @@ public class BVHFileStream extends PoseDataStream {
}
@Override
public void writeFrame(HumanSkeleton skeleton) throws IOException {
public void writeFrame(Skeleton skeleton) throws IOException {
if (skeleton == null) {
throw new NullPointerException("skeleton must not be null");
}
@@ -276,7 +276,7 @@ public class BVHFileStream extends PoseDataStream {
}
@Override
public void writeFooter(HumanSkeleton skeleton) throws IOException {
public void writeFooter(Skeleton skeleton) throws IOException {
// Write the final frame count for files
if (outputStream instanceof FileOutputStream) {
FileOutputStream fileOutputStream = (FileOutputStream) outputStream;

View File

@@ -1,6 +1,6 @@
package dev.slimevr.posestreamer;
import dev.slimevr.vr.processor.skeleton.HumanSkeleton;
import dev.slimevr.vr.processor.skeleton.Skeleton;
import java.io.*;
@@ -22,12 +22,12 @@ public abstract class PoseDataStream implements AutoCloseable {
this(new FileOutputStream(file));
}
public void writeHeader(HumanSkeleton skeleton, PoseStreamer streamer) throws IOException {
public void writeHeader(Skeleton skeleton, PoseStreamer streamer) throws IOException {
}
abstract void writeFrame(HumanSkeleton skeleton) throws IOException;
abstract void writeFrame(Skeleton skeleton) throws IOException;
public void writeFooter(HumanSkeleton skeleton) throws IOException {
public void writeFooter(Skeleton skeleton) throws IOException {
}
public boolean isClosed() {

View File

@@ -1,6 +1,6 @@
package dev.slimevr.posestreamer;
import dev.slimevr.vr.processor.skeleton.HumanSkeleton;
import dev.slimevr.vr.processor.skeleton.Skeleton;
import io.eiren.util.logging.LogManager;
import java.io.IOException;
@@ -10,10 +10,10 @@ public class PoseStreamer {
protected long frameRecordingInterval = 60L;
protected HumanSkeleton skeleton;
protected Skeleton skeleton;
protected PoseDataStream poseFileStream;
public PoseStreamer(HumanSkeleton skeleton) {
public PoseStreamer(Skeleton skeleton) {
this.skeleton = skeleton;
}
@@ -43,7 +43,7 @@ public class PoseStreamer {
this.frameRecordingInterval = intervalMs;
}
public synchronized HumanSkeleton getSkeleton() {
public synchronized Skeleton getSkeleton() {
return skeleton;
}

View File

@@ -2,7 +2,7 @@ package dev.slimevr.posestreamer;
import dev.slimevr.VRServer;
import dev.slimevr.util.ann.VRServerThread;
import dev.slimevr.vr.processor.skeleton.HumanSkeleton;
import dev.slimevr.vr.processor.skeleton.Skeleton;
public class ServerPoseStreamer extends TickPoseStreamer {
@@ -19,7 +19,7 @@ public class ServerPoseStreamer extends TickPoseStreamer {
}
@VRServerThread
public void onSkeletonUpdated(HumanSkeleton skeleton) {
public void onSkeletonUpdated(Skeleton skeleton) {
this.skeleton = skeleton;
}

View File

@@ -1,6 +1,6 @@
package dev.slimevr.posestreamer;
import dev.slimevr.vr.processor.skeleton.HumanSkeleton;
import dev.slimevr.vr.processor.skeleton.Skeleton;
import java.io.IOException;
@@ -9,7 +9,7 @@ public class TickPoseStreamer extends PoseStreamer {
protected long nextFrameTimeMs = -1L;
public TickPoseStreamer(HumanSkeleton skeleton) {
public TickPoseStreamer(Skeleton skeleton) {
super(skeleton);
}
@@ -19,7 +19,7 @@ public class TickPoseStreamer extends PoseStreamer {
return;
}
HumanSkeleton skeleton = this.skeleton;
Skeleton skeleton = this.skeleton;
if (skeleton == null) {
return;
}

View File

@@ -59,7 +59,7 @@ public class DataFeedBuilder {
TrackerInfo.startTrackerInfo(fbb);
if (tracker.getBodyPosition() != null)
TrackerInfo.addBodyPart(fbb, tracker.getBodyPosition().id);
TrackerInfo.addBodyPart(fbb, tracker.getBodyPosition().bodyPart);
TrackerInfo.addEditable(fbb, tracker.userEditable());
TrackerInfo.addComputed(fbb, tracker.isComputed());
// TODO need support: TrackerInfo.addImuType(fbb, tracker.im);

View File

@@ -197,7 +197,7 @@ public class RPCHandler extends ProtocolHandler<RpcMessageHeader>
if (tracker == null)
return;
tracker.setBodyPosition(TrackerPosition.getById(req.bodyPosition()));
tracker.setBodyPosition(TrackerPosition.getByBodyPart(req.bodyPosition()).get());
if (tracker instanceof ReferenceAdjustedTracker) {
ReferenceAdjustedTracker refTracker = (ReferenceAdjustedTracker) tracker;

View File

@@ -24,7 +24,8 @@ public class ComputedHumanPoseTracker extends ComputedTracker
super(trackerId, "human://" + skeletonPosition.name(), true, true);
this.skeletonPosition = skeletonPosition;
this.trackerRole = role;
this.bodyPosition = TrackerPosition.getByRole(role);
// TODO: Use `TrackerPosition` instead of `TrackerRole`
this.bodyPosition = TrackerPosition.getByTrackerRole(role).get();
}
@Override

View File

@@ -2,8 +2,8 @@ package dev.slimevr.vr.processor;
import dev.slimevr.VRServer;
import dev.slimevr.util.ann.VRServerThread;
import dev.slimevr.vr.processor.skeleton.Skeleton;
import dev.slimevr.vr.processor.skeleton.HumanSkeleton;
import dev.slimevr.vr.processor.skeleton.SimpleSkeleton;
import dev.slimevr.vr.processor.skeleton.SkeletonConfig;
import dev.slimevr.vr.processor.skeleton.SkeletonConfigValue;
import dev.slimevr.vr.trackers.*;
@@ -18,8 +18,8 @@ public class HumanPoseProcessor {
private final VRServer server;
private final List<ComputedHumanPoseTracker> computedTrackers = new FastList<>();
private final List<Consumer<HumanSkeleton>> onSkeletonUpdated = new FastList<>();
private HumanSkeleton skeleton;
private final List<Consumer<Skeleton>> onSkeletonUpdated = new FastList<>();
private Skeleton skeleton;
public HumanPoseProcessor(VRServer server, HMDTracker hmd) {
this.server = server;
@@ -105,12 +105,12 @@ public class HumanPoseProcessor {
);
}
public HumanSkeleton getSkeleton() {
public Skeleton getSkeleton() {
return skeleton;
}
@VRServerThread
public void addSkeletonUpdatedCallback(Consumer<HumanSkeleton> consumer) {
public void addSkeletonUpdatedCallback(Consumer<Skeleton> consumer) {
onSkeletonUpdated.add(consumer);
if (skeleton != null)
consumer.accept(skeleton);
@@ -165,8 +165,8 @@ public class HumanPoseProcessor {
@VRServerThread
private void updateSekeltonModel() {
disconnectAllTrackers();
skeleton = new SimpleSkeleton(server, computedTrackers);
for (Consumer<HumanSkeleton> sc : onSkeletonUpdated)
skeleton = new HumanSkeleton(server, computedTrackers);
for (Consumer<Skeleton> sc : onSkeletonUpdated)
sc.accept(skeleton);
}

View File

@@ -0,0 +1,22 @@
package dev.slimevr.vr.processor.skeleton;
import dev.slimevr.vr.processor.TransformNode;
public class BoneInfo {
public final BoneType boneType;
public final TransformNode node;
public float length;
public BoneInfo(BoneType boneType, TransformNode node) {
this.boneType = boneType;
this.node = node;
updateLength();
}
public void updateLength() {
this.length = node.localTransform.getTranslation().length();
}
}

View File

@@ -0,0 +1,83 @@
package dev.slimevr.vr.processor.skeleton;
import solarxr_protocol.datatypes.BodyPart;
/**
* Keys for all bones in the skeleton. TODO: Some bones are deprecated because
* they are still used as SkeletonNodeOffset, and represent both left and right
* offsets, but they should be split into two.
*/
public enum BoneType {
HEAD(BodyPart.HMD),
NECK(BodyPart.NECK),
CHEST(BodyPart.CHEST),
CHEST_TRACKER(BodyPart.CHEST),
WAIST(BodyPart.WAIST),
WAIST_TRACKER(BodyPart.WAIST),
HIP(BodyPart.HIP),
HIP_TRACKER(BodyPart.HIP),
LEFT_HIP,
RIGHT_HIP,
@Deprecated
UPPER_LEG,
LEFT_UPPER_LEG(BodyPart.LEFT_KNEE),
RIGHT_UPPER_LEG(BodyPart.RIGHT_KNEE),
@Deprecated
KNEE_TRACKER,
LEFT_KNEE_TRACKER,
RIGHT_KNEE_TRACKER,
@Deprecated
LOWER_LEG,
LEFT_LOWER_LEG(BodyPart.LEFT_ANKLE),
RIGHT_LOWER_LEG(BodyPart.RIGHT_ANKLE),
@Deprecated
FOOT,
LEFT_FOOT(BodyPart.LEFT_FOOT),
RIGHT_FOOT(BodyPart.RIGHT_FOOT),
@Deprecated
FOOT_TRACKER,
LEFT_FOOT_TRACKER(BodyPart.LEFT_FOOT),
RIGHT_FOOT_TRACKER(BodyPart.RIGHT_FOOT),
@Deprecated
CONTROLLER,
LEFT_CONTROLLER(BodyPart.LEFT_CONTROLLER),
RIGHT_CONTROLLER(BodyPart.RIGHT_CONTROLLER),
@Deprecated
LOWER_ARM,
LEFT_LOWER_ARM(BodyPart.LEFT_FOREARM),
RIGHT_LOWER_ARM(BodyPart.RIGHT_FOREARM),
@Deprecated
LOWER_ARM_HMD,
@Deprecated
ELBOW_TRACKER,
LEFT_ELBOW_TRACKER,
RIGHT_ELBOW_TRACKER,
@Deprecated
UPPER_ARM,
LEFT_UPPER_ARM,
RIGHT_UPPER_ARM,
LEFT_SHOULDER,
RIGHT_SHOULDER,
@Deprecated
HAND,
LEFT_HAND,
RIGHT_HAND,
@Deprecated
HAND_TRACKER,
LEFT_HAND_TRACKER,
RIGHT_HAND_TRACKER;
public static final BoneType[] values = values();
public final int bodyPart;
private BoneType() {
this.bodyPart = BodyPart.NONE;
}
private BoneType(int associatedBodyPart) {
this.bodyPart = associatedBodyPart;
}
}

View File

@@ -0,0 +1,37 @@
package dev.slimevr.vr.processor.skeleton;
import dev.slimevr.util.ann.VRServerThread;
import dev.slimevr.vr.processor.TransformNode;
import io.eiren.util.ann.ThreadSafe;
public abstract class Skeleton {
@VRServerThread
public abstract void updatePose();
@ThreadSafe
public abstract TransformNode getRootNode();
@ThreadSafe
public abstract TransformNode[] getAllNodes();
@ThreadSafe
public abstract SkeletonConfig getSkeletonConfig();
@ThreadSafe
public abstract void resetSkeletonConfig(SkeletonConfigValue config);
@ThreadSafe
public void resetAllSkeletonConfigs() {
for (SkeletonConfigValue config : SkeletonConfigValue.values) {
resetSkeletonConfig(config);
}
}
@VRServerThread
public abstract void resetTrackersFull();
@VRServerThread
public abstract void resetTrackersYaw();
}

View File

@@ -16,8 +16,8 @@ public class SkeletonConfig {
protected final EnumMap<SkeletonConfigToggle, Boolean> toggles = new EnumMap<SkeletonConfigToggle, Boolean>(
SkeletonConfigToggle.class
);
protected final EnumMap<SkeletonNodeOffset, Vector3f> nodeOffsets = new EnumMap<SkeletonNodeOffset, Vector3f>(
SkeletonNodeOffset.class
protected final EnumMap<BoneType, Vector3f> nodeOffsets = new EnumMap<BoneType, Vector3f>(
BoneType.class
);
protected final boolean autoUpdateOffsets;
@@ -144,7 +144,7 @@ public class SkeletonConfig {
// Re-compute the affected offsets
if (computeOffsets && autoUpdateOffsets && config.affectedOffsets != null) {
for (SkeletonNodeOffset offset : config.affectedOffsets) {
for (BoneType offset : config.affectedOffsets) {
computeNodeOffset(offset);
}
}
@@ -227,7 +227,7 @@ public class SkeletonConfig {
return getToggle(SkeletonConfigToggle.getByStringValue(config));
}
protected void setNodeOffset(SkeletonNodeOffset nodeOffset, float x, float y, float z) {
protected void setNodeOffset(BoneType nodeOffset, float x, float y, float z) {
Vector3f offset = nodeOffsets.get(nodeOffset);
if (offset == null) {
@@ -246,7 +246,7 @@ public class SkeletonConfig {
}
}
protected void setNodeOffset(SkeletonNodeOffset nodeOffset, Vector3f offset) {
protected void setNodeOffset(BoneType nodeOffset, Vector3f offset) {
if (offset == null) {
setNodeOffset(nodeOffset, 0f, 0f, 0f);
return;
@@ -255,11 +255,11 @@ public class SkeletonConfig {
setNodeOffset(nodeOffset, offset.x, offset.y, offset.z);
}
public Vector3f getNodeOffset(SkeletonNodeOffset nodeOffset) {
public Vector3f getNodeOffset(BoneType nodeOffset) {
return nodeOffsets.getOrDefault(nodeOffset, Vector3f.ZERO);
}
public void computeNodeOffset(SkeletonNodeOffset nodeOffset) {
public void computeNodeOffset(BoneType nodeOffset) {
switch (nodeOffset) {
case HEAD:
setNodeOffset(nodeOffset, 0, 0, getConfig(SkeletonConfigValue.HEAD));
@@ -302,7 +302,7 @@ public class SkeletonConfig {
setNodeOffset(nodeOffset, getConfig(SkeletonConfigValue.HIPS_WIDTH) / 2f, 0, 0);
break;
case KNEE:
case UPPER_LEG:
setNodeOffset(
nodeOffset,
0,
@@ -314,7 +314,7 @@ public class SkeletonConfig {
case KNEE_TRACKER:
setNodeOffset(nodeOffset, 0, 0, -getConfig(SkeletonConfigValue.SKELETON_OFFSET));
break;
case ANKLE:
case LOWER_LEG:
setNodeOffset(
nodeOffset,
0,
@@ -345,10 +345,10 @@ public class SkeletonConfig {
-getConfig(SkeletonConfigValue.CONTROLLER_DISTANCE_Z)
);
break;
case FOREARM:
case LOWER_ARM:
setNodeOffset(nodeOffset, 0, getConfig(SkeletonConfigValue.FOREARM_LENGTH), 0);
break;
case FOREARM_HMD:
case LOWER_ARM_HMD:
setNodeOffset(nodeOffset, 0, -getConfig(SkeletonConfigValue.FOREARM_LENGTH), 0);
break;
case ELBOW_TRACKER:
@@ -379,7 +379,7 @@ public class SkeletonConfig {
}
public void computeAllNodeOffsets() {
for (SkeletonNodeOffset offset : SkeletonNodeOffset.values) {
for (BoneType offset : BoneType.values) {
computeNodeOffset(offset);
}
}

View File

@@ -9,5 +9,5 @@ public interface SkeletonConfigCallback {
void updateToggleState(SkeletonConfigToggle configToggle, boolean newValue);
void updateNodeOffset(SkeletonNodeOffset nodeOffset, Vector3f offset);
void updateNodeOffset(BoneType nodeOffset, Vector3f offset);
}

View File

@@ -11,7 +11,7 @@ public enum SkeletonConfigValue {
"headShift",
"Head shift",
0.1f,
new SkeletonNodeOffset[] { SkeletonNodeOffset.HEAD }
new BoneType[] { BoneType.HEAD }
),
NECK(
2,
@@ -19,7 +19,7 @@ public enum SkeletonConfigValue {
"neckLength",
"Neck length",
0.1f,
new SkeletonNodeOffset[] { SkeletonNodeOffset.NECK }
new BoneType[] { BoneType.NECK }
),
TORSO(
3,
@@ -27,7 +27,7 @@ public enum SkeletonConfigValue {
"torsoLength",
"Torso length",
0.56f,
new SkeletonNodeOffset[] { SkeletonNodeOffset.WAIST }
new BoneType[] { BoneType.WAIST }
),
CHEST(
4,
@@ -35,8 +35,8 @@ public enum SkeletonConfigValue {
"chestDistance",
"Chest distance",
0.32f,
new SkeletonNodeOffset[] { SkeletonNodeOffset.CHEST, SkeletonNodeOffset.WAIST,
SkeletonNodeOffset.LEFT_SHOULDER, SkeletonNodeOffset.RIGHT_SHOULDER }
new BoneType[] { BoneType.CHEST, BoneType.WAIST,
BoneType.LEFT_SHOULDER, BoneType.RIGHT_SHOULDER }
),
WAIST(
5,
@@ -44,7 +44,7 @@ public enum SkeletonConfigValue {
"waistDistance",
"Waist distance",
0.04f,
new SkeletonNodeOffset[] { SkeletonNodeOffset.WAIST, SkeletonNodeOffset.HIP }
new BoneType[] { BoneType.WAIST, BoneType.HIP }
),
HIP_OFFSET(
6,
@@ -52,7 +52,7 @@ public enum SkeletonConfigValue {
"hipOffset",
"Hip offset",
0.0f,
new SkeletonNodeOffset[] { SkeletonNodeOffset.HIP_TRACKER }
new BoneType[] { BoneType.HIP_TRACKER }
),
HIPS_WIDTH(
7,
@@ -60,7 +60,7 @@ public enum SkeletonConfigValue {
"hipsWidth",
"Hips width",
0.26f,
new SkeletonNodeOffset[] { SkeletonNodeOffset.LEFT_HIP, SkeletonNodeOffset.RIGHT_HIP }
new BoneType[] { BoneType.LEFT_HIP, BoneType.RIGHT_HIP }
),
LEGS_LENGTH(
8,
@@ -68,7 +68,7 @@ public enum SkeletonConfigValue {
"legsLength",
"Legs length",
0.92f,
new SkeletonNodeOffset[] { SkeletonNodeOffset.KNEE }
new BoneType[] { BoneType.UPPER_LEG }
),
KNEE_HEIGHT(
9,
@@ -76,7 +76,7 @@ public enum SkeletonConfigValue {
"kneeHeight",
"Knee height",
0.50f,
new SkeletonNodeOffset[] { SkeletonNodeOffset.KNEE, SkeletonNodeOffset.ANKLE }
new BoneType[] { BoneType.UPPER_LEG, BoneType.LOWER_LEG }
),
FOOT_LENGTH(
10,
@@ -84,7 +84,7 @@ public enum SkeletonConfigValue {
"footLength",
"Foot length",
0.05f,
new SkeletonNodeOffset[] { SkeletonNodeOffset.FOOT }
new BoneType[] { BoneType.FOOT }
),
FOOT_OFFSET(
11,
@@ -92,7 +92,7 @@ public enum SkeletonConfigValue {
"footOffset",
"Foot offset",
-0.05f,
new SkeletonNodeOffset[] { SkeletonNodeOffset.ANKLE }
new BoneType[] { BoneType.LOWER_LEG }
),
SKELETON_OFFSET(
12,
@@ -100,8 +100,8 @@ public enum SkeletonConfigValue {
"skeletonOffset",
"Skeleton offset",
0.0f,
new SkeletonNodeOffset[] { SkeletonNodeOffset.CHEST_TRACKER, SkeletonNodeOffset.HIP_TRACKER,
SkeletonNodeOffset.KNEE_TRACKER, SkeletonNodeOffset.FOOT_TRACKER }
new BoneType[] { BoneType.CHEST_TRACKER, BoneType.HIP_TRACKER,
BoneType.KNEE_TRACKER, BoneType.FOOT_TRACKER }
),
CONTROLLER_DISTANCE_Z(
13,
@@ -109,7 +109,7 @@ public enum SkeletonConfigValue {
"controllerDistanceZ",
"Controller distance z",
0.15f,
new SkeletonNodeOffset[] { SkeletonNodeOffset.CONTROLLER, SkeletonNodeOffset.HAND }
new BoneType[] { BoneType.CONTROLLER, BoneType.HAND }
),
CONTROLLER_DISTANCE_Y(
14,
@@ -117,7 +117,7 @@ public enum SkeletonConfigValue {
"controllerDistanceY",
"Controller distance y",
0.05f,
new SkeletonNodeOffset[] { SkeletonNodeOffset.CONTROLLER, SkeletonNodeOffset.HAND }
new BoneType[] { BoneType.CONTROLLER, BoneType.HAND }
),
FOREARM_LENGTH(
15,
@@ -125,8 +125,8 @@ public enum SkeletonConfigValue {
"forearmLength",
"Forearm length",
0.25f,
new SkeletonNodeOffset[] { SkeletonNodeOffset.FOREARM,
SkeletonNodeOffset.FOREARM_HMD }
new BoneType[] { BoneType.LOWER_ARM,
BoneType.LOWER_ARM_HMD }
),
SHOULDERS_DISTANCE(
16,
@@ -134,8 +134,8 @@ public enum SkeletonConfigValue {
"shoulersDistance",
"Shoulders distance",
0.08f,
new SkeletonNodeOffset[] { SkeletonNodeOffset.LEFT_SHOULDER,
SkeletonNodeOffset.RIGHT_SHOULDER }
new BoneType[] { BoneType.LEFT_SHOULDER,
BoneType.RIGHT_SHOULDER }
),
SHOULDERS_WIDTH(
17,
@@ -143,8 +143,8 @@ public enum SkeletonConfigValue {
"shoulersWidth",
"Shoulders width",
0.36f,
new SkeletonNodeOffset[] { SkeletonNodeOffset.LEFT_SHOULDER,
SkeletonNodeOffset.RIGHT_SHOULDER }
new BoneType[] { BoneType.LEFT_SHOULDER,
BoneType.RIGHT_SHOULDER }
),
UPPER_ARM_LENGTH(
18,
@@ -152,7 +152,7 @@ public enum SkeletonConfigValue {
"upperArmLength",
"Upper arm length",
0.25f,
new SkeletonNodeOffset[] { SkeletonNodeOffset.UPPER_ARM }
new BoneType[] { BoneType.UPPER_ARM }
),
ELBOW_OFFSET(
19,
@@ -160,7 +160,7 @@ public enum SkeletonConfigValue {
"elbowOffset",
"Elbow offset",
0f,
new SkeletonNodeOffset[] { SkeletonNodeOffset.ELBOW_TRACKER }
new BoneType[] { BoneType.ELBOW_TRACKER }
),;
public static final SkeletonConfigValue[] values = values();
@@ -180,7 +180,7 @@ public enum SkeletonConfigValue {
public final String configKey;
public final String label;
public final float defaultValue;
public final SkeletonNodeOffset[] affectedOffsets;
public final BoneType[] affectedOffsets;
SkeletonConfigValue(
int id,
@@ -188,7 +188,7 @@ public enum SkeletonConfigValue {
String configKey,
String label,
float defaultValue,
SkeletonNodeOffset[] affectedOffsets
BoneType[] affectedOffsets
) {
this.id = id;
this.stringVal = stringVal;
@@ -198,7 +198,7 @@ public enum SkeletonConfigValue {
this.defaultValue = defaultValue;
this.affectedOffsets = affectedOffsets
== null ? new SkeletonNodeOffset[0] : affectedOffsets;
== null ? new BoneType[0] : affectedOffsets;
}
public static SkeletonConfigValue getByStringValue(String stringVal) {

View File

@@ -9,14 +9,18 @@ import java.util.Map;
import dev.slimevr.vr.processor.TransformNode;
/**
* WIP Saved as part of skeleton rework. Is not finished.
*/
@Deprecated
public class SkeletonData {
public final Joint[] joints;
public final List<Bone> bones = new ArrayList<>();
private final SkeletonConfig config;
private Map<SkeletonNodeOffset, List<Bone>> bonesByOffsetKey = new EnumMap<>(
SkeletonNodeOffset.class
private Map<BoneType, List<Bone>> bonesByOffsetKey = new EnumMap<>(
BoneType.class
);
// #region Upper body nodes (torso)
@@ -70,68 +74,68 @@ public class SkeletonData {
// Forward is Negative Z, left iz Negative X. See head joint and hip
// joints as examples.
// #region Assemble skeleton from hmd to hip
hmdJoint.attachJoint(headJoint, SkeletonNodeOffset.HEAD);
headJoint.attachJoint(neckJoint, SkeletonNodeOffset.NECK);
neckJoint.attachJoint(chestJoint, SkeletonNodeOffset.CHEST);
chestJoint.attachJoint(waistJoint, SkeletonNodeOffset.WAIST);
waistJoint.attachJoint(hipJoint, SkeletonNodeOffset.HIP);
hmdJoint.attachJoint(headJoint, BoneType.HEAD);
headJoint.attachJoint(neckJoint, BoneType.NECK);
neckJoint.attachJoint(chestJoint, BoneType.CHEST);
chestJoint.attachJoint(waistJoint, BoneType.WAIST);
waistJoint.attachJoint(hipJoint, BoneType.HIP);
// #endregion
// #region Assemble skeleton from hips to feet
hipJoint.attachJoint(leftHipJoint, SkeletonNodeOffset.LEFT_HIP);
hipJoint.attachJoint(rightHipJoint, SkeletonNodeOffset.RIGHT_HIP);
hipJoint.attachJoint(leftHipJoint, BoneType.LEFT_HIP);
hipJoint.attachJoint(rightHipJoint, BoneType.RIGHT_HIP);
leftHipJoint.attachJoint(leftKneeJoint, SkeletonNodeOffset.KNEE);
rightHipJoint.attachJoint(rightKneeJoint, SkeletonNodeOffset.KNEE);
leftHipJoint.attachJoint(leftKneeJoint, BoneType.UPPER_LEG);
rightHipJoint.attachJoint(rightKneeJoint, BoneType.UPPER_LEG);
leftKneeJoint.attachJoint(leftAnkleJoint, SkeletonNodeOffset.ANKLE);
rightKneeJoint.attachJoint(rightAnkleJoint, SkeletonNodeOffset.ANKLE);
leftKneeJoint.attachJoint(leftAnkleJoint, BoneType.LOWER_LEG);
rightKneeJoint.attachJoint(rightAnkleJoint, BoneType.LOWER_LEG);
leftAnkleJoint.attachJoint(leftFootJoint, SkeletonNodeOffset.FOOT);
rightAnkleJoint.attachJoint(rightFootJoint, SkeletonNodeOffset.FOOT);
leftAnkleJoint.attachJoint(leftFootJoint, BoneType.FOOT);
rightAnkleJoint.attachJoint(rightFootJoint, BoneType.FOOT);
// #endregion
// #region Assemble skeleton arms from controllers
// TODO : Rebuild skeleton depending on if it's from controllers or from
// shoulders
// if (fromControllers)
leftHandJoint.attachJoint(leftWristJoint, SkeletonNodeOffset.HAND);
rightHandJoint.attachJoint(rightWristJoint, SkeletonNodeOffset.HAND);
rightWristJoint.attachJoint(leftElbowJoint, SkeletonNodeOffset.FOREARM);
leftWristJoint.attachJoint(rightElbowJoint, SkeletonNodeOffset.FOREARM);
leftHandJoint.attachJoint(leftWristJoint, BoneType.HAND);
rightHandJoint.attachJoint(rightWristJoint, BoneType.HAND);
rightWristJoint.attachJoint(leftElbowJoint, BoneType.LOWER_ARM);
leftWristJoint.attachJoint(rightElbowJoint, BoneType.LOWER_ARM);
// } else {
// #endregion
// #region Assemble skeleton arms from chest
chestJoint.attachJoint(leftShoulderJoint, SkeletonNodeOffset.LEFT_SHOULDER);
chestJoint.attachJoint(rightShoulderJoint, SkeletonNodeOffset.RIGHT_SHOULDER);
chestJoint.attachJoint(leftShoulderJoint, BoneType.LEFT_SHOULDER);
chestJoint.attachJoint(rightShoulderJoint, BoneType.RIGHT_SHOULDER);
leftShoulderJoint.attachJoint(leftElbowJoint, SkeletonNodeOffset.FOREARM);
rightShoulderJoint.attachJoint(rightElbowJoint, SkeletonNodeOffset.FOREARM);
leftShoulderJoint.attachJoint(leftElbowJoint, BoneType.LOWER_ARM);
rightShoulderJoint.attachJoint(rightElbowJoint, BoneType.LOWER_ARM);
leftElbowJoint.attachJoint(leftWristJoint, SkeletonNodeOffset.HAND);
rightElbowJoint.attachJoint(rightWristJoint, SkeletonNodeOffset.HAND);
leftElbowJoint.attachJoint(leftWristJoint, BoneType.HAND);
rightElbowJoint.attachJoint(rightWristJoint, BoneType.HAND);
leftWristJoint.attachJoint(leftHandJoint, SkeletonNodeOffset.HAND);
rightWristJoint.attachJoint(rightHandJoint, SkeletonNodeOffset.HAND);
leftWristJoint.attachJoint(leftHandJoint, BoneType.HAND);
rightWristJoint.attachJoint(rightHandJoint, BoneType.HAND);
// }
// #endregion
// #region Attach tracker nodes for offsets
chestJoint.attachJoint(trackerChestJoint, SkeletonNodeOffset.CHEST_TRACKER);
hipJoint.attachJoint(trackerWaistJoint, SkeletonNodeOffset.WAIST_TRACKER);
chestJoint.attachJoint(trackerChestJoint, BoneType.CHEST_TRACKER);
hipJoint.attachJoint(trackerWaistJoint, BoneType.WAIST_TRACKER);
leftKneeJoint.attachJoint(trackerLeftKneeJoint, SkeletonNodeOffset.KNEE_TRACKER);
rightKneeJoint.attachJoint(trackerRightKneeJoint, SkeletonNodeOffset.KNEE_TRACKER);
leftKneeJoint.attachJoint(trackerLeftKneeJoint, BoneType.KNEE_TRACKER);
rightKneeJoint.attachJoint(trackerRightKneeJoint, BoneType.KNEE_TRACKER);
leftFootJoint.attachJoint(trackerLeftFootJoint, SkeletonNodeOffset.FOOT_TRACKER);
rightFootJoint.attachJoint(trackerRightFootJoint, SkeletonNodeOffset.FOOT_TRACKER);
leftFootJoint.attachJoint(trackerLeftFootJoint, BoneType.FOOT_TRACKER);
rightFootJoint.attachJoint(trackerRightFootJoint, BoneType.FOOT_TRACKER);
leftElbowJoint.attachJoint(trackerLeftElbowJoint, SkeletonNodeOffset.ELBOW_TRACKER);
rightElbowJoint.attachJoint(trackerRightElbowJoint, SkeletonNodeOffset.ELBOW_TRACKER);
leftElbowJoint.attachJoint(trackerLeftElbowJoint, BoneType.ELBOW_TRACKER);
rightElbowJoint.attachJoint(trackerRightElbowJoint, BoneType.ELBOW_TRACKER);
leftHandJoint.attachJoint(trackerLeftHandJoint, SkeletonNodeOffset.HAND_TRACKER);
rightHandJoint.attachJoint(trackerRightHandJoint, SkeletonNodeOffset.HAND_TRACKER);
leftHandJoint.attachJoint(trackerLeftHandJoint, BoneType.HAND_TRACKER);
rightHandJoint.attachJoint(trackerRightHandJoint, BoneType.HAND_TRACKER);
// #endregion
}
@@ -144,7 +148,7 @@ public class SkeletonData {
node = new TransformNode(name, false);
}
public Bone attachJoint(Joint childJoint, SkeletonNodeOffset offsetKey) {
public Bone attachJoint(Joint childJoint, BoneType offsetKey) {
Bone bone = childrenBones.get(childJoint);
if (bone == null) {
bone = new Bone(
@@ -193,13 +197,13 @@ public class SkeletonData {
public class Bone {
public final Joint parent;
public final Joint child;
public SkeletonNodeOffset offsetKey;
public BoneType offsetKey;
public Bone(
String name,
Joint firstJoint,
Joint secondJoint,
SkeletonNodeOffset offsetKey
BoneType offsetKey
) {
this.parent = firstJoint;
this.child = secondJoint;

View File

@@ -1,31 +0,0 @@
package dev.slimevr.vr.processor.skeleton;
public enum SkeletonNodeOffset {
HEAD,
NECK,
CHEST,
CHEST_TRACKER,
WAIST,
WAIST_TRACKER,
HIP,
HIP_TRACKER,
LEFT_HIP,
RIGHT_HIP,
KNEE,
KNEE_TRACKER,
ANKLE,
FOOT,
FOOT_TRACKER,
CONTROLLER,
FOREARM,
FOREARM_HMD,
ELBOW_TRACKER,
UPPER_ARM,
LEFT_SHOULDER,
RIGHT_SHOULDER,
HAND,
HAND_TRACKER;
public static final SkeletonNodeOffset[] values = values();
}

View File

@@ -46,7 +46,7 @@ public class ComputedTracker implements Tracker, TrackerWithTPS {
// not be
// allowed if editing is not allowed
if (userEditable()) {
bodyPosition = TrackerPosition.getByDesignation(config.designation);
bodyPosition = TrackerPosition.getByDesignation(config.designation).get();
}
}

View File

@@ -83,7 +83,7 @@ public class IMUTracker implements Tracker, TrackerWithTPS, TrackerWithBattery {
} else {
rotAdjust.loadIdentity();
}
bodyPosition = TrackerPosition.getByDesignation(config.designation);
bodyPosition = TrackerPosition.getByDesignation(config.designation).get();
setFilter(
vrserver.config.getString("filters.type"),
vrserver.config.getFloat("filters.amount", 0.3f),

View File

@@ -2,7 +2,6 @@ package dev.slimevr.vr.trackers;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import dev.slimevr.vr.trackers.udp.UDPDevice;
public class ReferenceAdjustedTracker<E extends Tracker> implements Tracker {
@@ -177,7 +176,7 @@ public class ReferenceAdjustedTracker<E extends Tracker> implements Tracker {
}
@Override
public UDPDevice getDevice() {
public Device getDevice() {
return tracker.getDevice();
}
}

View File

@@ -2,7 +2,6 @@ package dev.slimevr.vr.trackers;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import dev.slimevr.vr.trackers.udp.UDPDevice;
import java.util.concurrent.atomic.AtomicInteger;
@@ -51,7 +50,7 @@ public interface Tracker {
int getTrackerNum();
UDPDevice getDevice();
Device getDevice();
default String getDescriptiveName() {
return getName();

View File

@@ -1,87 +1,127 @@
package dev.slimevr.vr.trackers;
import java.lang.reflect.Modifier;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.logging.Level;
import solarxr_protocol.datatypes.BodyPart;
/**
* Represents a position on the body that a tracker could be placed. Any bone is
* a valid position.
*
* TrackerPosition intentionally lacks a numerical id to avoid breakage.
*/
public enum TrackerPosition {
NONE(0, "", TrackerRole.NONE),
HMD(1, "HMD", TrackerRole.HMD),
NECK(2, "body:neck", null),
CHEST(3, "body:chest", TrackerRole.CHEST),
WAIST(4, "body:waist", TrackerRole.WAIST),
HIP(5, "body:hip", null),
LEFT_KNEE(6, "body:left_knee", TrackerRole.LEFT_KNEE),
RIGHT_KNEE(7, "body:right_knee", TrackerRole.RIGHT_KNEE),
LEFT_ANKLE(8, "body:left_ankle", null),
RIGHT_ANKLE(9, "body:right_ankle", null),
LEFT_FOOT(10, "body:left_foot", TrackerRole.LEFT_FOOT),
RIGHT_FOOT(11, "body:right_foot", TrackerRole.RIGHT_FOOT),
LEFT_CONTROLLER(12, "body:left_controller", TrackerRole.LEFT_CONTROLLER),
RIGHT_CONTROLLER(13, "body:right_controller", TrackerRole.RIGHT_CONTROLLER),
LEFT_FOREARM(14, "body:left_forearm", TrackerRole.LEFT_ELBOW),
RIGHT_FOREARM(15, "body:right_forearm", TrackerRole.RIGHT_ELBOW),
LEFT_UPPER_ARM(16, "body:left_upperarm", null),
RIGHT_UPPER_ARM(17, "body:right_upperarm", null),
LEFT_HAND(18, "body:left_hand", TrackerRole.LEFT_HAND),
RIGHT_HAND(19, "body:right_hand", TrackerRole.RIGHT_HAND),;
// @formatter:off
HMD("HMD", TrackerRole.HMD, BodyPart.HMD),
NECK("body:neck", TrackerRole.NECK, BodyPart.NECK),
CHEST("body:chest", TrackerRole.CHEST, BodyPart.CHEST),
WAIST("body:waist", Optional.empty(), BodyPart.WAIST),
HIP("body:hip", TrackerRole.WAIST, BodyPart.HIP),
LEFT_KNEE("body:left_knee", TrackerRole.LEFT_KNEE, BodyPart.LEFT_KNEE),
RIGHT_KNEE("body:right_knee", TrackerRole.RIGHT_KNEE, BodyPart.RIGHT_KNEE),
LEFT_ANKLE("body:left_ankle", Optional.empty(), BodyPart.LEFT_ANKLE),
RIGHT_ANKLE("body:right_ankle", Optional.empty(), BodyPart.RIGHT_ANKLE),
LEFT_FOOT("body:left_foot", TrackerRole.LEFT_FOOT, BodyPart.LEFT_FOOT),
RIGHT_FOOT("body:right_foot", TrackerRole.RIGHT_FOOT, BodyPart.RIGHT_FOOT),
LEFT_CONTROLLER("body:left_controller", TrackerRole.LEFT_CONTROLLER, BodyPart.LEFT_CONTROLLER),
RIGHT_CONTROLLER("body:right_controller", TrackerRole.RIGHT_CONTROLLER, BodyPart.RIGHT_CONTROLLER),
LEFT_FOREARM("body:left_forearm", TrackerRole.LEFT_ELBOW, BodyPart.LEFT_FOREARM),
RIGHT_FOREARM("body:right_forearm", TrackerRole.RIGHT_ELBOW, BodyPart.RIGHT_FOREARM),
LEFT_UPPER_ARM("body:left_upperarm", TrackerRole.LEFT_SHOULDER, BodyPart.LEFT_UPPER_ARM),
RIGHT_UPPER_ARM("body:right_upperarm", TrackerRole.RIGHT_SHOULDER, BodyPart.RIGHT_UPPER_ARM),
LEFT_HAND("body:left_hand", TrackerRole.LEFT_HAND, BodyPart.LEFT_HAND),
RIGHT_HAND("body:right_hand", TrackerRole.RIGHT_HAND, BodyPart.RIGHT_HAND),;
// @formatter:on
public static final TrackerPosition[] values = values();
private static final Map<Integer, TrackerPosition> byId = new HashMap<>();
private static final Map<String, TrackerPosition> byDesignation = new HashMap<>();
private static final EnumMap<TrackerRole, TrackerPosition> byRole = new EnumMap<>(
TrackerRole.class
);
static {
for (TrackerPosition tbp : values()) {
byDesignation.put(tbp.designation.toLowerCase(), tbp);
byId.put(tbp.id, tbp);
if (tbp.trackerRole != null) {
TrackerPosition old = byRole.get(tbp.trackerRole);
if (old != null)
throw new AssertionError(
"Only one tracker position can match tracker role. "
+ tbp.trackerRole
+ " is occupied by "
+ old
+ " when adding "
+ tbp
);
byRole.put(tbp.trackerRole, tbp);
}
}
public final String designation;
public final Optional<TrackerRole> trackerRole;
/** The associated `BodyPart` */
public final int bodyPart;
private TrackerPosition(String designation, TrackerRole nullableTrackerRole, int bodyPart) {
this(designation, Optional.ofNullable(nullableTrackerRole), bodyPart);
}
public final int id;
public final String designation;
public final TrackerRole trackerRole;
TrackerPosition(int id, String designation, TrackerRole trackerRole) {
this.id = id;
private TrackerPosition(String designation, Optional<TrackerRole> trackerRole, int bodyPart) {
this.designation = designation;
this.trackerRole = trackerRole;
this.bodyPart = bodyPart;
}
public static TrackerPosition getByDesignation(String designation) {
// Support old configs. leg was renamed to knee.
if (designation != null) {
if (designation.equals("body:left_leg"))
designation = "body:left_knee";
if (designation.equals("body:right_leg"))
designation = "body:right_knee";
/** Indexed by `BodyPart` int value. EFFICIENCY FTW */
private static final TrackerPosition[] byBodyPart;
static {
// Determine maximum value of BodyPart. Kinda hacky, but this will
// prevent breakage if the max value changes.
int max = 0;
for (var field : BodyPart.class.getFields()) {
if (!Modifier.isStatic(field.getModifiers()) || !field.getType().equals(int.class)) {
continue;
}
try {
var v = field.getInt(null);
max = Math.max(max, v);
} catch (IllegalAccessException e) {
// unreachable
java.util.logging.Logger.getGlobal().log(Level.SEVERE, "Reached unreachable code");
}
}
byBodyPart = new TrackerPosition[max + 1];
for (var tp : TrackerPosition.values) {
byBodyPart[tp.bodyPart] = tp;
}
}
private static final Map<String, TrackerPosition> byDesignation = new HashMap<>();
private static final EnumMap<TrackerRole, TrackerPosition> byTrackerRole = new EnumMap<>(
TrackerRole.class
);
static {
for (TrackerPosition tp : values()) {
byDesignation.put(tp.designation.toLowerCase(), tp);
tp.trackerRole.ifPresent((tr) -> byTrackerRole.put(tr, tp));
}
}
/**
* Gets the `TrackerPosition` by its string designation.
*
* @return Returns an optional as not all strings are valid designators.
*/
public static Optional<TrackerPosition> getByDesignation(String designation) {
if (designation == null) {
return Optional.empty();
}
return designation == null ? null : byDesignation.get(designation.toLowerCase());
// Support old configs. leg was renamed to knee.
if (designation.equalsIgnoreCase("body:left_leg"))
designation = "body:left_knee";
if (designation.equalsIgnoreCase("body:right_leg"))
designation = "body:right_knee";
return Optional.ofNullable(byDesignation.get(designation.toLowerCase()));
}
public static TrackerPosition getByRole(TrackerRole role) {
return byRole.get(role);
public static Optional<TrackerPosition> getByTrackerRole(TrackerRole role) {
return Optional.ofNullable(byTrackerRole.get(role));
}
public static TrackerPosition getById(int id) {
return byId.get(id);
public static Optional<TrackerPosition> getByBodyPart(int bodyPart) {
if (bodyPart < 0 || bodyPart >= TrackerPosition.byBodyPart.length) {
return Optional.empty();
}
return Optional.of(TrackerPosition.byBodyPart[bodyPart]);
}
}

View File

@@ -1,7 +1,17 @@
package dev.slimevr.vr.trackers;
/**
* The SteamVR tracker role.
*
* The tracker role classifies the position and the role of a tracker on user's
* body or playspace (like CAMERA or BEACON), using SteamVR naming. Tracker
* roles are hints for interacting programs what the tracker means, and they do
* not correspond to body's bones on purpose. Example: virtual vive trackers for
* SteamVR vs actual SlimeVR trackers.
*/
public enum TrackerRole {
// @formatter:off
NONE(0, "", "", null),
WAIST(1, "vive_tracker_waist", "TrackerRole_Waist", DeviceType.TRACKER),
LEFT_FOOT(2, "vive_tracker_left_foot", "TrackerRole_LeftFoot", DeviceType.TRACKER),
@@ -12,12 +22,7 @@ public enum TrackerRole {
LEFT_ELBOW(7, "vive_tracker_left_elbow", "TrackerRole_LeftElbow", DeviceType.TRACKER),
RIGHT_ELBOW(8, "vive_tracker_right_elbow", "TrackerRole_RightElbow", DeviceType.TRACKER),
LEFT_SHOULDER(9, "vive_tracker_left_shoulder", "TrackerRole_LeftShoulder", DeviceType.TRACKER),
RIGHT_SHOULDER(
10,
"vive_tracker_right_shoulder",
"TrackerRole_RightShoulder",
DeviceType.TRACKER
),
RIGHT_SHOULDER(10, "vive_tracker_right_shoulder", "TrackerRole_RightShoulder", DeviceType.TRACKER),
LEFT_HAND(11, "vive_tracker_handed", "TrackerRole_Handed", DeviceType.TRACKER),
RIGHT_HAND(12, "vive_tracker_handed", "TrackerRole_Handed", DeviceType.TRACKER),
LEFT_CONTROLLER(13, "vive_tracker_handed", "TrackerRole_Handed", DeviceType.CONTROLLER),
@@ -29,6 +34,7 @@ public enum TrackerRole {
HMD(19, "", "", DeviceType.HMD),
BEACON(20, "", "", DeviceType.TRACKING_REFERENCE),
GENERIC_CONTROLLER(21, "vive_tracker_handed", "TrackerRole_Handed", DeviceType.CONTROLLER);
// @formatter:on
public static final TrackerRole[] values = values();
private static final TrackerRole[] byId = new TrackerRole[22];
@@ -53,7 +59,7 @@ public enum TrackerRole {
public final String viveRole;
public final DeviceType deviceType;
TrackerRole(int id, String roleHint, String viveRole, DeviceType deviceType) {
private TrackerRole(int id, String roleHint, String viveRole, DeviceType deviceType) {
this.id = id;
this.roleHint = roleHint;
this.viveRole = viveRole;