Compare commits

...

16 Commits

Author SHA1 Message Date
Eiren Rain
755ab592f1 Rework legs proportions to legs length and knee height 2021-07-08 06:36:23 +03:00
Eiren Rain
9e0147ed27 Implemented body proportions reset buttons to reset things to their defaults based on user's height 2021-07-08 06:16:02 +03:00
Eiren Rain
0ab92322a8 Minor GUI fix 2021-07-08 06:15:21 +03:00
Eiren Rain
4e58df76fb Merge pull request #6 from ButterscotchVanilla/main
Add Slime Java Commons to Gradle build and create a GitHub Actions workflow
2021-07-08 02:50:33 +03:00
Butterscotch!
d3f81625ce Add testing to workflow 2021-07-07 19:39:08 -04:00
Butterscotch!
b97a92b682 Create GitHub Actions build script (#1)
* Create gradle.yml
2021-07-07 19:29:54 -04:00
ButterscotchVanilla
5b2918acb2 Build dependencies when packaging a server jar 2021-07-07 19:19:46 -04:00
ButterscotchVanilla
cbf37a7c9c Add Slime Java Commons as a dependency and add runnable jar task 2021-07-07 18:48:24 -04:00
Eiren Rain
f169cfd0c7 Merge pull request #5 from ButterscotchVanilla/main
Retain initial font size while zooming and display zoom level
2021-07-08 00:39:33 +03:00
ButterscotchVanilla
0e51b79775 Fix formatting 2021-07-07 10:27:42 -04:00
ButterscotchVanilla
1ee13c02d9 Retain initial font size while zooming and display zoom level 2021-07-07 10:19:55 -04:00
Eiren Rain
94829060a3 Added GUI zoom button, made default zoom level 150% instead of 200%
Minor gui refactoring
2021-07-06 23:00:10 +03:00
Eiren Rain
b790cfbd7d Fix updating waist doesn't move tracker right away 2021-07-06 22:59:15 +03:00
Eiren Rain
43bc140d42 Fix hips switching place when changing hips width 2021-07-05 17:00:33 +03:00
Eiren Rain
143cf49ce7 Added a button to switch between 3 and 1 vive trackers mode 2021-07-05 02:32:30 +03:00
Eiren Rain
a7aec873e6 Added scroll bar to the main window 2021-07-05 02:32:10 +03:00
13 changed files with 519 additions and 92 deletions

48
.github/workflows/gradle.yml vendored Normal file
View File

@@ -0,0 +1,48 @@
# This workflow will build a Java project with Gradle
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
name: Build SlimeVR Server with Gradle
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.3.4
- name: Clone Slime Java Commons
uses: actions/checkout@v2.3.4
with:
repository: Eirenliel/slime-java-commons
# Relative path under $GITHUB_WORKSPACE to place the repository
path: Slime Java Commons
- name: Set up JDK 11
uses: actions/setup-java@v2.1.0
with:
java-version: '11'
distribution: 'adopt'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Test with Gradle
run: ./gradlew clean test
- name: Build with Gradle
run: ./gradlew clean serverJar
- name: Upload the Server JAR as a Build Artifact
uses: actions/upload-artifact@v2.2.4
with:
# Artifact name
name: "SlimeVR-Server" # optional, default is artifact
# A file, directory or wildcard pattern that describes what to upload
path: build/libs/*

View File

@@ -19,6 +19,8 @@ repositories {
}
dependencies {
compile project(':Slime Java Commons')
// This dependency is exported to consumers, that is to say found on their compile classpath.
api 'org.apache.commons:commons-math3:3.6.1'
api 'org.yaml:snakeyaml:1.25'
@@ -33,3 +35,15 @@ dependencies {
// Use JUnit test framework
testImplementation 'junit:junit:4.12'
}
subprojects.each { subproject -> evaluationDependsOn(subproject.path) }
task serverJar (type: Jar, dependsOn: subprojects.tasks['build']) {
manifest {
attributes 'Main-Class': 'io.eiren.vr.Main'
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
with jar
}

View File

@@ -0,0 +1,42 @@
package io.eiren.gui;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.AbstractButton;
public class ButtonTimer {
private static Timer timer = new Timer();
public static void runTimer(AbstractButton button, int seconds, String defaultText, Runnable runnable) {
if(seconds <= 0) {
button.setText(defaultText);
runnable.run();
} else {
button.setText(String.valueOf(seconds));
timer.schedule(new ButtonTimerTask(button, seconds - 1, defaultText, runnable), 1000);
}
}
private static class ButtonTimerTask extends TimerTask {
private final AbstractButton button;
private final int seconds;
private final String defaultText;
private final Runnable runnable;
private ButtonTimerTask(AbstractButton button, int seconds, String defaultText, Runnable runnable) {
this.button = button;
this.seconds = seconds;
this.defaultText = defaultText;
this.runnable = runnable;
}
@Override
public void run() {
runTimer(button, seconds, defaultText, runnable);
}
}
}

View File

@@ -0,0 +1,91 @@
package io.eiren.gui;
import java.awt.Font;
import java.text.AttributedCharacterIterator.Attribute;
import java.util.Map;
public class ScalableFont extends Font {
protected float scale = 1.0f;
protected int initSize;
protected float initPointSize;
public ScalableFont(Map<? extends Attribute, ?> attributes) {
super(attributes);
this.initSize = this.size;
this.initPointSize = this.pointSize;
}
public ScalableFont(Font font) {
super(font);
if(font instanceof ScalableFont) {
ScalableFont sourceFont = (ScalableFont)font;
this.initSize = sourceFont.getInitSize();
this.initPointSize = sourceFont.getInitSize2D();
this.size = this.initSize;
this.pointSize = this.initPointSize;
} else {
this.initSize = this.size;
this.initPointSize = this.pointSize;
}
}
public ScalableFont(Font font, float scale) {
super(font);
if(font instanceof ScalableFont) {
ScalableFont sourceFont = (ScalableFont)font;
this.initSize = sourceFont.getInitSize();
this.initPointSize = sourceFont.getInitSize2D();
} else {
this.initSize = this.size;
this.initPointSize = this.pointSize;
}
setScale(scale);
}
public ScalableFont(String name, int style, int size) {
super(name, style, size);
this.initSize = this.size;
this.initPointSize = this.pointSize;
}
public ScalableFont(String name, int style, int size, float scale) {
super(name, style, size);
this.initSize = this.size;
this.initPointSize = this.pointSize;
setScale(scale);
}
public int getInitSize() {
return initSize;
}
public float getInitSize2D() {
return initPointSize;
}
public float getScale() {
return scale;
}
private void setScale(float scale) {
this.scale = scale;
float newPointSize = initPointSize * scale;
this.size = (int)(newPointSize + 0.5);
this.pointSize = newPointSize;
}
}

View File

@@ -34,53 +34,71 @@ public class SkeletonConfig extends EJBag {
removeAll();
int row = 0;
add(new TimedResetButton("Reset All", "All"), s(c(1, row, 1), 3, 1));
row++;
add(new JLabel("Chest"), c(0, row, 1));
add(new AdjButton("+", "Chest", 0.01f), c(1, row, 1));
add(new SkeletonLabel("Chest"), c(2, row, 1));
add(new AdjButton("-", "Chest", -0.01f), c(3, row, 1));
add(new ResetButton("Reset", "Chest"), c(4, row, 1));
row++;
add(new JLabel("Waist"), c(0, row, 1));
add(new AdjButton("+", "Waist", 0.01f), c(1, row, 1));
add(new SkeletonLabel("Waist"), c(2, row, 1));
add(new AdjButton("-", "Waist", -0.01f), c(3, row, 1));
row++;
add(new JLabel("Virtual waist"), c(0, row, 1));
add(new AdjButton("+", "Virtual waist", 0.01f), c(1, row, 1));
add(new SkeletonLabel("Virtual waist"), c(2, row, 1));
add(new AdjButton("-", "Virtual waist", -0.01f), c(3, row, 1));
add(new TimedResetButton("Reset", "Waist"), c(4, row, 1));
row++;
add(new JLabel("Hips width"), c(0, row, 1));
add(new AdjButton("+", "Hips width", 0.01f), c(1, row, 1));
add(new SkeletonLabel("Hips width"), c(2, row, 1));
add(new AdjButton("-", "Hips width", -0.01f), c(3, row, 1));
add(new ResetButton("Reset", "Hips width"), c(4, row, 1));
row++;
add(new JLabel("Hip length"), c(0, row, 1));
add(new AdjButton("+", "Hip length", 0.01f), c(1, row, 1));
add(new SkeletonLabel("Hip length"), c(2, row, 1));
add(new AdjButton("-", "Hip length", -0.01f), c(3, row, 1));
add(new JLabel("Legs length"), c(0, row, 1));
add(new AdjButton("+", "Legs length", 0.01f), c(1, row, 1));
add(new SkeletonLabel("Legs length"), c(2, row, 1));
add(new AdjButton("-", "Legs length", -0.01f), c(3, row, 1));
add(new TimedResetButton("Reset", "Legs length"), c(4, row, 1));
row++;
add(new JLabel("Ankle length"), c(0, row, 1));
add(new AdjButton("+", "Ankle length", 0.01f), c(1, row, 1));
add(new SkeletonLabel("Ankle length"), c(2, row, 1));
add(new AdjButton("-", "Ankle length", -0.01f), c(3, row, 1));
add(new JLabel("Knee height"), c(0, row, 1));
add(new AdjButton("+", "Knee height", 0.01f), c(1, row, 1));
add(new SkeletonLabel("Knee height"), c(2, row, 1));
add(new AdjButton("-", "Knee height", -0.01f), c(3, row, 1));
add(new TimedResetButton("Reset", "Knee height"), c(4, row, 1));
row++;
add(new JLabel("Foot length"), c(0, row, 1));
add(new AdjButton("+", "Foot length", 0.01f), c(1, row, 1));
add(new SkeletonLabel("Foot length"), c(2, row, 1));
add(new AdjButton("-", "Foot length", -0.01f), c(3, row, 1));
add(new ResetButton("Reset", "Foot length"), c(4, row, 1));
row++;
add(new JLabel("Head offset"), c(0, row, 1));
add(new AdjButton("+", "Head", 0.01f), c(1, row, 1));
add(new SkeletonLabel("Head"), c(2, row, 1));
add(new AdjButton("-", "Head", -0.01f), c(3, row, 1));
add(new ResetButton("Reset", "Head"), c(4, row, 1));
row++;
add(new JLabel("Neck length"), c(0, row, 1));
add(new AdjButton("+", "Neck", 0.01f), c(1, row, 1));
add(new SkeletonLabel("Neck"), c(2, row, 1));
add(new AdjButton("-", "Neck", -0.01f), c(3, row, 1));
add(new ResetButton("Reset", "Neck"), c(4, row, 1));
row++;
add(new JLabel("Virtual waist"), c(0, row, 1));
add(new AdjButton("+", "Virtual waist", 0.01f), c(1, row, 1));
add(new SkeletonLabel("Virtual waist"), c(2, row, 1));
add(new AdjButton("-", "Virtual waist", -0.01f), c(3, row, 1));
add(new ResetButton("Reset", "Virtual waist"), c(4, row, 1));
row++;
gui.refresh();
@@ -91,13 +109,27 @@ public class SkeletonConfig extends EJBag {
float current = server.humanPoseProcessor.getSkeletonConfig(joint);
server.humanPoseProcessor.setSkeletonConfig(joint, current + diff);
server.saveConfig();
labels.get(joint).setText(StringUtils.prettyNumber(current + diff, 2));
labels.get(joint).setText(StringUtils.prettyNumber((current + diff) * 100, 0));
}
private void reset(String joint) {
server.humanPoseProcessor.resetSkeletonConfig(joint);
server.saveConfig();
if(!"All".equals(joint)) {
float current = server.humanPoseProcessor.getSkeletonConfig(joint);
labels.get(joint).setText(StringUtils.prettyNumber((current) * 100, 0));
} else {
labels.forEach((jnt, label) -> {
float current = server.humanPoseProcessor.getSkeletonConfig(jnt);
label.setText(StringUtils.prettyNumber((current) * 100, 0));
});
}
}
private class SkeletonLabel extends JLabel {
public SkeletonLabel(String joint) {
super(StringUtils.prettyNumber(server.humanPoseProcessor.getSkeletonConfig(joint), 2));
super(StringUtils.prettyNumber(server.humanPoseProcessor.getSkeletonConfig(joint) * 100, 0));
labels.put(joint, this);
}
}
@@ -114,4 +146,30 @@ public class SkeletonConfig extends EJBag {
});
}
}
private class ResetButton extends JButton {
public ResetButton(String text, String joint) {
super(text);
addMouseListener(new MouseInputAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
reset(joint);
}
});
}
}
private class TimedResetButton extends JButton {
public TimedResetButton(String text, String joint) {
super(text);
addMouseListener(new MouseInputAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
ButtonTimer.runTimer(TimedResetButton.this, 3, text, () -> reset(joint));
}
});
}
}
}

View File

@@ -11,7 +11,6 @@ import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.border.BevelBorder;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
@@ -20,7 +19,6 @@ import com.jme3.math.Vector3f;
import io.eiren.util.StringUtils;
import io.eiren.util.ann.AWTThread;
import io.eiren.util.ann.ThreadSafe;
import io.eiren.util.ann.VRServerThread;
import io.eiren.util.collections.FastList;
import io.eiren.vr.VRServer;
import io.eiren.vr.processor.TrackerBodyPosition;

View File

@@ -4,12 +4,15 @@ import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.MouseInputAdapter;
import io.eiren.util.StringUtils;
import io.eiren.util.ann.AWTThread;
import io.eiren.vr.VRServer;
import io.eiren.vr.bridge.NamedPipeVRBridge;
import java.awt.Component;
import java.awt.Container;
import java.awt.Font;
import java.awt.event.MouseEvent;
import java.util.TimerTask;
import static javax.swing.BoxLayout.PAGE_AXIS;
import static javax.swing.BoxLayout.LINE_AXIS;
@@ -19,24 +22,40 @@ public class VRServerGUI extends JFrame {
public final VRServer server;
private final TrackersList trackersList;
private final SkeletonList skeletonList;
private java.util.Timer timer = new java.util.Timer();
private JButton resetButton;
private JScrollPane scroll;
private EJBox pane;
private float zoom = 1.5f;
private float initZoom = zoom;
@AWTThread
public VRServerGUI(VRServer server) {
super("SlimeVR Server");
increaseFontSize();
//increaseFontSize();
this.server = server;
this.zoom = server.config.getFloat("zoom", zoom);
this.initZoom = zoom;
setDefaultFontSize(zoom);
// All components should be constructed to the current zoom level by default
setDefaultCloseOperation(EXIT_ON_CLOSE);
getContentPane().setLayout(new BoxLayout(getContentPane(), PAGE_AXIS));
this.trackersList = new TrackersList(server, this);
this.skeletonList = new SkeletonList(server, this);
add(scroll = new JScrollPane(pane = new EJBox(PAGE_AXIS), ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED));
build();
}
public float getZoom() {
return this.zoom;
}
public void refresh() {
// Pack and display
pack();
@@ -52,12 +71,14 @@ public class VRServerGUI extends JFrame {
@AWTThread
private void build() {
Container pane = getContentPane();
pane.removeAll();
NamedPipeVRBridge npvb = server.getVRBridge(NamedPipeVRBridge.class);
pane.add(new EJBox(LINE_AXIS) {{
setBorder(new EmptyBorder(i(5)));
add(Box.createHorizontalGlue());
add(resetButton = new JButton("RESET") {{
addMouseListener(new MouseInputAdapter() {
@Override
@@ -66,6 +87,29 @@ public class VRServerGUI extends JFrame {
}
});
}});
add(Box.createHorizontalGlue());
if(npvb != null) {
add(new JButton(npvb.isOneTrackerMode() ? "Trackers: 1" : "Trackers: 3") {{
addMouseListener(new MouseInputAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
npvb.setSpawnOneTracker(!npvb.isOneTrackerMode());
setText(npvb.isOneTrackerMode() ? "Trackers: 1" : "Trackers: 3");
}
});
}});
add(Box.createHorizontalStrut(10));
}
add(new JButton("GUI Zoom (x" + StringUtils.prettyNumber(zoom, 2) + ")") {{
addMouseListener(new MouseInputAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
guiZoom();
setText("GUI Zoom (x" + StringUtils.prettyNumber(zoom, 2) + ")");
}
});
}});
add(Box.createHorizontalStrut(10));
}});
pane.add(new EJBox(LINE_AXIS) {{
@@ -80,10 +124,10 @@ public class VRServerGUI extends JFrame {
add(new EJBox(PAGE_AXIS) {{
setAlignmentY(TOP_ALIGNMENT);
add(new JLabel("Skeleton"));
add(skeletonList);
add(new JLabel("Skeleton config"));
add(new JLabel("Body proportions"));
add(new SkeletonConfig(server, VRServerGUI.this));
add(new JLabel("Skeleton data"));
add(skeletonList);
add(Box.createVerticalGlue());
}});
}});
@@ -95,14 +139,45 @@ public class VRServerGUI extends JFrame {
server.addOnTick(skeletonList::updateBones);
}
private static void increaseFontSize() {
// For now only changes font size, but should change fixed components size in the future too
private void guiZoom() {
if(zoom <= 1.0f) {
zoom = 1.5f;
} else if(zoom <= 1.5f) {
zoom = 1.75f;
} else if(zoom <= 1.75f) {
zoom = 2.0f;
} else if(zoom <= 2.0f) {
zoom = 2.5f;
} else {
zoom = 1.0f;
}
processNewZoom(zoom / initZoom, pane);
refresh();
server.config.setProperty("zoom", zoom);
server.saveConfig();
}
private static void processNewZoom(float zoom, Component comp) {
if(comp.isFontSet()) {
Font newFont = new ScalableFont(comp.getFont(), zoom);
comp.setFont(newFont);
}
if(comp instanceof Container) {
Container cont = (Container) comp;
for(Component child : cont.getComponents())
processNewZoom(zoom, child);
}
}
private static void setDefaultFontSize(float zoom) {
java.util.Enumeration<Object> keys = UIManager.getDefaults().keys();
while(keys.hasMoreElements()) {
Object key = keys.nextElement();
Object value = UIManager.get(key);
if(value instanceof javax.swing.plaf.FontUIResource) {
javax.swing.plaf.FontUIResource f = (javax.swing.plaf.FontUIResource) value;
javax.swing.plaf.FontUIResource f2 = new javax.swing.plaf.FontUIResource(f.deriveFont(f.getSize() * 2f));
javax.swing.plaf.FontUIResource f2 = new javax.swing.plaf.FontUIResource(f.deriveFont(f.getSize() * zoom));
UIManager.put(key, f2);
}
}
@@ -110,37 +185,6 @@ public class VRServerGUI extends JFrame {
@AWTThread
private void reset() {
resetButton.setText("5");
timer.schedule(new TimerTask() {
@Override
public void run() {
resetButton.setText("4");
timer.schedule(new TimerTask() {
@Override
public void run() {
resetButton.setText("3");
timer.schedule(new TimerTask() {
@Override
public void run() {
resetButton.setText("2");
timer.schedule(new TimerTask() {
@Override
public void run() {
resetButton.setText("1");
timer.schedule(new TimerTask() {
@Override
public void run() {
server.resetTrackers();
resetButton.setText("RESET");
}
}, 1000);
}
}, 1000);
}
}, 1000);
}
}, 1000);
}
}, 1000);
ButtonTimer.runTimer(resetButton, 3, "RESET", server::resetTrackers);
}
}

View File

@@ -46,6 +46,7 @@ public class VRServer extends Thread {
public VRServer() {
super("VRServer");
loadConfig();
hmdTracker = new HMDTracker("HMD");
hmdTracker.position.set(0, 1.8f, 0); // Set starting position for easier debugging
// TODO Multiple processors
@@ -53,7 +54,7 @@ public class VRServer extends Thread {
List<? extends Tracker> shareTrackers = humanPoseProcessor.getComputedTrackers();
// Create named pipe bridge for SteamVR driver
NamedPipeVRBridge driverBridge = new NamedPipeVRBridge(hmdTracker, shareTrackers);
NamedPipeVRBridge driverBridge = new NamedPipeVRBridge(hmdTracker, shareTrackers, this);
tasks.add(() -> driverBridge.start());
bridges.add(driverBridge);
@@ -71,6 +72,16 @@ public class VRServer extends Thread {
for(int i = 0; i < shareTrackers.size(); ++i)
registerTracker(shareTrackers.get(i));
}
@ThreadSafe
public <E extends VRBridge> E getVRBridge(Class<E> cls) {
for(int i = 0; i < bridges.size(); ++i) {
VRBridge b = bridges.get(i);
if(cls.isInstance(b))
return cls.cast(b);
}
return null;
}
@ThreadSafe
public TrackerConfig getTrackerConfig(Tracker tracker) {
@@ -169,7 +180,6 @@ public class VRServer extends Thread {
@Override
@VRServerThread
public void run() {
loadConfig();
trackersServer.start();
while(true) {
//final long start = System.currentTimeMillis();

View File

@@ -14,6 +14,7 @@ import com.sun.jna.ptr.IntByReference;
import io.eiren.util.collections.FastList;
import io.eiren.util.logging.LogManager;
import io.eiren.vr.VRServer;
import io.eiren.vr.trackers.ComputedTracker;
import io.eiren.vr.trackers.HMDTracker;
import io.eiren.vr.trackers.Tracker;
@@ -32,6 +33,7 @@ public class NamedPipeVRBridge extends Thread implements VRBridge {
private final Quaternion qBuffer = new Quaternion();
private final Quaternion qBuffer2 = new Quaternion();
private final VRServer server;
private Pipe hmdPipe;
private final HMDTracker hmd;
private final List<Pipe> trackerPipes;
@@ -41,8 +43,11 @@ public class NamedPipeVRBridge extends Thread implements VRBridge {
private final HMDTracker internalHMDTracker = new HMDTracker("itnernal://HMD");
private final AtomicBoolean newHMDData = new AtomicBoolean(false);
public NamedPipeVRBridge(HMDTracker hmd, List<? extends Tracker> shareTrackers) {
private boolean spawnOneTracker = false;
public NamedPipeVRBridge(HMDTracker hmd, List<? extends Tracker> shareTrackers, VRServer server) {
super("Named Pipe VR Bridge");
this.server = server;
this.hmd = hmd;
this.shareTrackers = new FastList<>(shareTrackers);
this.trackerPipes = new FastList<>(shareTrackers.size());
@@ -53,6 +58,26 @@ public class NamedPipeVRBridge extends Thread implements VRBridge {
ct.setStatus(TrackerStatus.OK);
this.internalTrackers.add(ct);
}
this.spawnOneTracker = server.config.getBoolean("openvr.onetracker", spawnOneTracker);
}
public boolean isOneTrackerMode() {
return this.spawnOneTracker;
}
/**
* Makes OpenVR bridge spawn only 1 tracker instead of 3, for
* use with only waist/chest tracking. Requires restart.
*/
public void setSpawnOneTracker(boolean spawnOneTracker) {
if(spawnOneTracker == this.spawnOneTracker)
return;
this.spawnOneTracker = spawnOneTracker;
if(this.spawnOneTracker)
this.server.config.setProperty("openvr.onetracker", true);
else
this.server.config.removeProperty("openvr.onetracker");
this.server.saveConfig();
}
@Override
@@ -108,6 +133,8 @@ public class NamedPipeVRBridge extends Thread implements VRBridge {
if(tryOpeningPipe(trackerPipe))
initTrackerPipe(trackerPipe, i);
}
if(spawnOneTracker)
break;
}
}
@@ -167,7 +194,7 @@ public class NamedPipeVRBridge extends Thread implements VRBridge {
}
private void initTrackerPipe(Pipe pipe, int trackerId) {
String trackerHello = this.shareTrackers.size() + " 0";
String trackerHello = (spawnOneTracker ? "1" : this.shareTrackers.size()) + " 0";
System.arraycopy(trackerHello.getBytes(ASCII), 0, buffer, 0, trackerHello.length());
buffer[trackerHello.length()] = '\0';
IntByReference lpNumberOfBytesWritten = new IntByReference(0);
@@ -225,6 +252,8 @@ public class NamedPipeVRBridge extends Thread implements VRBridge {
throw new IOException("Can't open " + pipeName + " pipe: " + Kernel32.INSTANCE.GetLastError());
LogManager.log.info("[VRBridge] Pipe " + pipeName + " created");
trackerPipes.add(new Pipe(pipeHandle, pipeName));
if(spawnOneTracker)
break;
}
LogManager.log.info("[VRBridge] Pipes are open");
} catch(IOException e) {

View File

@@ -38,6 +38,12 @@ public class HumanPoseProcessor {
if(skeleton != null)
skeleton.setSkeletonConfig(key, newLength);
}
@ThreadSafe
public void resetSkeletonConfig(String key) {
if(skeleton != null)
skeleton.resetSkeletonConfig(key);
}
@ThreadSafe
public float getSkeletonConfig(String key) {

View File

@@ -4,6 +4,7 @@ import java.util.List;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import io.eiren.util.ann.VRServerThread;
import io.eiren.vr.VRServer;
@@ -13,6 +14,10 @@ import io.eiren.vr.trackers.TrackerUtils;
public class HumanSekeletonWithLegs extends HumanSkeleonWithWaist {
public static final float HIPS_WIDTH_DEFAULT = 0.3f;
public static final float FOOT_LENGTH_DEFAULT = 0.05f;
public static final float DEFAULT_FLOOR_OFFSET = 0.05f;
protected final float[] kneeAngles = new float[3];
protected final float[] hipAngles = new float[3];
protected final Quaternion hipBuf = new Quaternion();
@@ -39,15 +44,16 @@ public class HumanSekeletonWithLegs extends HumanSkeleonWithWaist {
/**
* Distance between centers of both hips
*/
protected float hipsWidth = 0.30f;
protected float hipsWidth = HIPS_WIDTH_DEFAULT;
/**
* Length from waist to knees
*/
protected float hipsLength = 0.51f;
protected float kneeHeight = 0.42f;
/**
* Distance from waist to ankle
*/
protected float ankleLength = 0.55f;
protected float legsLength = 0.84f;
protected float footLength = FOOT_LENGTH_DEFAULT;
protected float minKneePitch = 0f * FastMath.DEG_TO_RAD;
protected float maxKneePitch = 90f * FastMath.DEG_TO_RAD;
@@ -77,8 +83,9 @@ public class HumanSekeletonWithLegs extends HumanSkeleonWithWaist {
lat.setStatus(TrackerStatus.OK);
rat.setStatus(TrackerStatus.OK);
hipsWidth = server.config.getFloat("body.hipsWidth", hipsWidth);
hipsLength = server.config.getFloat("body.hipLength", hipsLength);
ankleLength = server.config.getFloat("body.ankleLength", ankleLength);
kneeHeight = server.config.getFloat("body.kneeHeight", kneeHeight);
legsLength = server.config.getFloat("body.legsLength", legsLength);
footLength = server.config.getFloat("body.footLength", footLength);
waistNode.attachChild(leftHipNode);
leftHipNode.localTransform.setTranslation(-hipsWidth / 2, 0, 0);
@@ -87,26 +94,58 @@ public class HumanSekeletonWithLegs extends HumanSkeleonWithWaist {
rightHipNode.localTransform.setTranslation(hipsWidth / 2, 0, 0);
leftHipNode.attachChild(leftKneeNode);
leftKneeNode.localTransform.setTranslation(0, -hipsLength, 0);
leftKneeNode.localTransform.setTranslation(0, -(legsLength - kneeHeight), 0);
rightHipNode.attachChild(rightKneeNode);
rightKneeNode.localTransform.setTranslation(0, -hipsLength, 0);
rightKneeNode.localTransform.setTranslation(0, -(legsLength - kneeHeight), 0);
leftKneeNode.attachChild(leftAnkleNode);
leftAnkleNode.localTransform.setTranslation(0, -ankleLength, 0);
leftAnkleNode.localTransform.setTranslation(0, -kneeHeight, 0);
rightKneeNode.attachChild(rightAnkleNode);
rightAnkleNode.localTransform.setTranslation(0, -ankleLength, 0);
rightAnkleNode.localTransform.setTranslation(0, -kneeHeight, 0);
leftAnkleNode.attachChild(leftFootNode);
leftFootNode.localTransform.setTranslation(0, 0, -0.05f);
leftFootNode.localTransform.setTranslation(0, 0, -footLength);
rightAnkleNode.attachChild(rightFootNode);
rightFootNode.localTransform.setTranslation(0, 0, -0.05f);
rightFootNode.localTransform.setTranslation(0, 0, -footLength);
configMap.put("Hips width", hipsWidth);
configMap.put("Hip length", hipsLength);
configMap.put("Ankle length", ankleLength);
configMap.put("Legs length", legsLength);
configMap.put("Knee height", kneeHeight);
configMap.put("Foot length", footLength);
}
@Override
public void resetSkeletonConfig(String joint) {
super.resetSkeletonConfig(joint);
switch(joint) {
case "All":
// Resets from the parent already performed
resetSkeletonConfig("Hips width");
resetSkeletonConfig("Foot length");
resetSkeletonConfig("Legs length");
break;
case "Hips width":
setSkeletonConfig(joint, HIPS_WIDTH_DEFAULT);
break;
case "Foot length":
setSkeletonConfig(joint, FOOT_LENGTH_DEFAULT);
break;
case "Legs length": // Set legs length to be 5cm above floor level
Vector3f vec = new Vector3f();
hmdTracker.getPosition(vec);
float height = vec.y;
if(height > 0.5f) { // Reset only if floor level is right, todo: read floor level from SteamVR if it's not 0
setSkeletonConfig(joint, height - neckLength - waistDistance - DEFAULT_FLOOR_OFFSET);
}
resetSkeletonConfig("Knee height");
break;
case "Knee height": // Knees are at 50% of the legs by default
setSkeletonConfig(joint, legsLength / 2.0f);
break;
}
}
@Override
@@ -116,20 +155,28 @@ public class HumanSekeletonWithLegs extends HumanSkeleonWithWaist {
case "Hips width":
hipsWidth = newLength;
server.config.setProperty("body.hipsWidth", hipsWidth);
leftHipNode.localTransform.setTranslation(hipsWidth / 2, 0, 0);
rightHipNode.localTransform.setTranslation(-hipsWidth / 2, 0, 0);
leftHipNode.localTransform.setTranslation(-hipsWidth / 2, 0, 0);
rightHipNode.localTransform.setTranslation(hipsWidth / 2, 0, 0);
break;
case "Hip length":
hipsLength = newLength;
server.config.setProperty("body.hipLength", hipsLength);
leftKneeNode.localTransform.setTranslation(0, -hipsLength, 0);
rightKneeNode.localTransform.setTranslation(0, -hipsLength, 0);
case "Knee height":
kneeHeight = newLength;
server.config.setProperty("body.kneeHeight", kneeHeight);
leftAnkleNode.localTransform.setTranslation(0, -kneeHeight, 0);
rightAnkleNode.localTransform.setTranslation(0, -kneeHeight, 0);
leftKneeNode.localTransform.setTranslation(0, -(legsLength - kneeHeight), 0);
rightKneeNode.localTransform.setTranslation(0, -(legsLength - kneeHeight), 0);
break;
case "Ankle length":
ankleLength = newLength;
server.config.setProperty("body.ankleLength", ankleLength);
leftAnkleNode.localTransform.setTranslation(0, -ankleLength, 0);
rightAnkleNode.localTransform.setTranslation(0, -ankleLength, 0);
case "Legs length":
legsLength = newLength;
server.config.setProperty("body.legsLength", legsLength);
leftKneeNode.localTransform.setTranslation(0, -(legsLength - kneeHeight), 0);
rightKneeNode.localTransform.setTranslation(0, -(legsLength - kneeHeight), 0);
break;
case "Foot length":
footLength = newLength;
server.config.setProperty("body.footLength", footLength);
leftFootNode.localTransform.setTranslation(0, 0, -footLength);
rightFootNode.localTransform.setTranslation(0, 0, -footLength);
break;
}
}

View File

@@ -16,6 +16,9 @@ import io.eiren.vr.trackers.TrackerUtils;
public class HumanSkeleonWithWaist extends HumanSkeleton {
public static final float HEAD_SHIFT_DEFAULT = 0.1f;
public static final float NECK_LENGTH_DEFAULT = 0.1f;
protected final Map<String, Float> configMap = new HashMap<>();
protected final VRServer server;
@@ -48,11 +51,11 @@ public class HumanSkeleonWithWaist extends HumanSkeleton {
/**
* Distacne from eyes to the base of the neck
*/
protected float neckLength = 0.1f;
protected float neckLength = NECK_LENGTH_DEFAULT;
/**
* Distance from eyes to ear
*/
protected float headShift = 0.1f;
protected float headShift = HEAD_SHIFT_DEFAULT;
public HumanSkeleonWithWaist(VRServer server, List<ComputedHumanPoseTracker> computedTrackers) {
List<Tracker> allTracekrs = server.getAllTrackers();
@@ -96,6 +99,39 @@ public class HumanSkeleonWithWaist extends HumanSkeleton {
configMap.put("Virtual waist", trackerWaistDistance);
}
@Override
public void resetSkeletonConfig(String joint) {
switch(joint) {
case "All": // Reset all joints according to height
resetSkeletonConfig("Head");
resetSkeletonConfig("Neck");
resetSkeletonConfig("Virtual waist");
resetSkeletonConfig("Waist");
resetSkeletonConfig("Chest");
break;
case "Head":
setSkeletonConfig(joint, HEAD_SHIFT_DEFAULT);
break;
case "Neck":
setSkeletonConfig(joint, NECK_LENGTH_DEFAULT);
break;
case "Virtual waist":
setSkeletonConfig(joint, 0.0f);
break;
case "Chest":
setSkeletonConfig(joint, waistDistance / 2.0f);
break;
case "Waist": // Puts Waist in the middle of the height
Vector3f vec = new Vector3f();
hmdTracker.getPosition(vec);
float height = vec.y;
if(height > 0.5f) { // Reset only if floor level is right, todo: read floor level from SteamVR if it's not 0
setSkeletonConfig(joint, (height) / 2.0f);
}
break;
}
}
@Override
public Map<String, Float> getSkeletonConfig() {
return configMap;
@@ -119,6 +155,7 @@ public class HumanSkeleonWithWaist extends HumanSkeleton {
waistDistance = newLength;
server.config.setProperty("body.waistDistance", waistDistance);
waistNode.localTransform.setTranslation(0, -(waistDistance - chestDistance), 0);
trackerWaistNode.localTransform.setTranslation(0, -(waistDistance + trackerWaistDistance - chestDistance), 0);
break;
case "Chest":
chestDistance = newLength;

View File

@@ -18,6 +18,9 @@ public abstract class HumanSkeleton {
@ThreadSafe
public abstract void setSkeletonConfig(String key, float newLength);
@ThreadSafe
public abstract void resetSkeletonConfig(String joint);
@VRServerThread
public abstract void resetTrackersFull();