mirror of
https://github.com/SlimeVR/SlimeVR-Server.git
synced 2026-04-06 02:01:58 +02:00
Compare commits
1 Commits
v0.16.3-rc
...
accessorie
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
472f6d8fc0 |
@@ -42,6 +42,7 @@ body_part-LEFT_HAND = Left hand
|
||||
body_part-LEFT_UPPER_LEG = Left thigh
|
||||
body_part-LEFT_LOWER_LEG = Left ankle
|
||||
body_part-LEFT_FOOT = Left foot
|
||||
body_part-ACCESSORY = Accessory
|
||||
|
||||
## Proportions
|
||||
skeleton_bone-NONE = None
|
||||
@@ -453,6 +454,8 @@ settings-osc-vrchat = VRChat OSC Trackers
|
||||
settings-osc-vrchat-description =
|
||||
Change VRChat-specific settings to receive HMD data and send
|
||||
tracker data for FBT without SteamVR (ex. Quest standalone).
|
||||
This also allows you to receive trackers under the OSCTrackers standard.
|
||||
This will also send accessory trackers' rotations for custom avatars.
|
||||
settings-osc-vrchat-enable = Enable
|
||||
settings-osc-vrchat-enable-description = Toggle the sending and receiving of data.
|
||||
settings-osc-vrchat-enable-label = Enable
|
||||
@@ -480,7 +483,7 @@ settings-osc-vmc = Virtual Motion Capture
|
||||
# This cares about multilines
|
||||
settings-osc-vmc-description =
|
||||
Change settings specific to the VMC (Virtual Motion Capture) protocol
|
||||
to send SlimeVR's bone data and receive bone data from other apps.
|
||||
to send and received bone and tracker data from and to other apps.
|
||||
settings-osc-vmc-enable = Enable
|
||||
settings-osc-vmc-enable-description = Toggle the sending and receiving of data.
|
||||
settings-osc-vmc-enable-label = Enable
|
||||
|
||||
@@ -84,6 +84,9 @@ export const mapPart: Record<
|
||||
<UpperLegIcon width={width} flipped></UpperLegIcon>
|
||||
),
|
||||
[BodyPart.WAIST]: ({ width }) => <WaistIcon width={width}></WaistIcon>,
|
||||
[BodyPart.ACCESSORY]: ({ width }) => (
|
||||
<SlimeVRIcon width={width}></SlimeVRIcon>
|
||||
),
|
||||
};
|
||||
|
||||
export function BodyPartIcon({
|
||||
|
||||
@@ -293,6 +293,19 @@ export function BodyAssignment({
|
||||
direction="left"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{advanced && (
|
||||
<div className="flex flex-col gap-2">
|
||||
<TrackerPartCard
|
||||
onlyAssigned={onlyAssigned}
|
||||
roleError={rolesWithErrors[BodyPart.ACCESSORY]?.label}
|
||||
td={trackerPartGrouped[BodyPart.ACCESSORY]}
|
||||
role={BodyPart.ACCESSORY}
|
||||
onClick={() => onRoleSelected(BodyPart.ACCESSORY)}
|
||||
direction="left"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
></BodyInteractions>
|
||||
|
||||
@@ -53,6 +53,7 @@ export function ManualMountingPage() {
|
||||
assignreq.trackerId = td.tracker.trackerId;
|
||||
assignreq.allowDriftCompensation =
|
||||
td.tracker.info?.allowDriftCompensation ?? true;
|
||||
assignreq.accessoryId = td.tracker.info?.accessoryId || 0;
|
||||
|
||||
sendRPCPacket(RpcMessage.AssignTrackerRequest, assignreq);
|
||||
});
|
||||
|
||||
@@ -96,6 +96,7 @@ export const mapPart: Record<
|
||||
<FootIcon width={width} flipped></FootIcon>
|
||||
),
|
||||
[BodyPart.WAIST]: ({ width }) => <FootIcon width={width}></FootIcon>,
|
||||
[BodyPart.ACCESSORY]: ({ width }) => <FootIcon width={width}></FootIcon>,
|
||||
};
|
||||
|
||||
export function MountingBodyPartIcon({
|
||||
|
||||
@@ -192,6 +192,7 @@ export function TrackersAssignPage() {
|
||||
assignreq.trackerId = trackerId;
|
||||
assignreq.allowDriftCompensation =
|
||||
tracker?.tracker?.info?.allowDriftCompensation ?? true;
|
||||
assignreq.accessoryId = 1; // TODO
|
||||
|
||||
sendRPCPacket(RpcMessage.AssignTrackerRequest, assignreq);
|
||||
};
|
||||
|
||||
@@ -92,6 +92,7 @@ export function TrackerSettingsPage() {
|
||||
assignreq.trackerId = tracker?.tracker.trackerId;
|
||||
if (allowDriftCompensation != null)
|
||||
assignreq.allowDriftCompensation = allowDriftCompensation;
|
||||
assignreq.accessoryId = 1; // TODO
|
||||
sendRPCPacket(RpcMessage.AssignTrackerRequest, assignreq);
|
||||
setSelectBodypart(false);
|
||||
};
|
||||
|
||||
@@ -59,7 +59,6 @@ class VRServer @JvmOverloads constructor(
|
||||
val trackersServer: TrackersUDPServer
|
||||
private val bridges: MutableList<Bridge> = FastList()
|
||||
private val tasks: Queue<Runnable> = LinkedBlockingQueue()
|
||||
private val newTrackersConsumers: MutableList<Consumer<Tracker>> = FastList()
|
||||
private val onTick: MutableList<Runnable> = FastList()
|
||||
val oSCRouter: OSCRouter
|
||||
|
||||
@@ -197,16 +196,6 @@ class VRServer @JvmOverloads constructor(
|
||||
onTick.add(runnable)
|
||||
}
|
||||
|
||||
@ThreadSafe
|
||||
fun addNewTrackerConsumer(consumer: Consumer<Tracker>) {
|
||||
queueTask {
|
||||
newTrackersConsumers.add(consumer)
|
||||
for (tracker in trackers) {
|
||||
consumer.accept(tracker)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ThreadSafe
|
||||
fun trackerUpdated(tracker: Tracker?) {
|
||||
queueTask {
|
||||
@@ -215,6 +204,11 @@ class VRServer @JvmOverloads constructor(
|
||||
refreshTrackersDriftCompensationEnabled()
|
||||
configManager.vrConfig.writeTrackerConfig(tracker)
|
||||
configManager.saveConfig()
|
||||
if (tracker != null) {
|
||||
if (tracker.trackerPosition == TrackerPosition.ACCESSORY) {
|
||||
vrcOSCHandler.addAccessoryTracker(tracker)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,6 +264,9 @@ class VRServer @JvmOverloads constructor(
|
||||
if (tracker.isComputed && tracker.name != "HMD") {
|
||||
vMCHandler.addComputedTracker(tracker)
|
||||
}
|
||||
if (tracker.trackerPosition == TrackerPosition.ACCESSORY) {
|
||||
vrcOSCHandler.addAccessoryTracker(tracker)
|
||||
}
|
||||
refreshTrackersDriftCompensationEnabled()
|
||||
}
|
||||
|
||||
@@ -279,9 +276,6 @@ class VRServer @JvmOverloads constructor(
|
||||
queueTask {
|
||||
trackers.add(tracker)
|
||||
trackerAdded(tracker)
|
||||
for (tc in newTrackersConsumers) {
|
||||
tc.accept(tracker)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
package dev.slimevr.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import dev.slimevr.tracking.trackers.Tracker;
|
||||
import io.github.axisangles.ktmath.Quaternion;
|
||||
|
||||
|
||||
public class TrackerConfig {
|
||||
|
||||
private String customName;
|
||||
private String designation;
|
||||
private boolean hide;
|
||||
private Quaternion adjustment;
|
||||
private Quaternion mountingOrientation;
|
||||
private Boolean allowDriftCompensation;
|
||||
|
||||
|
||||
public TrackerConfig() {
|
||||
}
|
||||
|
||||
public TrackerConfig(Tracker tracker) {
|
||||
this.designation = tracker.getTrackerPosition()
|
||||
!= null ? tracker.getTrackerPosition().getDesignation() : null;
|
||||
this.customName = tracker.getCustomName();
|
||||
allowDriftCompensation = tracker.isImu();
|
||||
}
|
||||
|
||||
static JsonNode toV2(JsonNode v1, JsonNodeFactory factory) {
|
||||
ObjectNode node = factory.objectNode();
|
||||
if (v1.has("customName"))
|
||||
node.set("customName", v1.get("customName"));
|
||||
if (v1.has("designation"))
|
||||
node.set("designation", v1.get("designation"));
|
||||
if (v1.has("hide"))
|
||||
node.set("hide", v1.get("hide"));
|
||||
if (v1.has("mountingRotation"))
|
||||
node.set("mountingRotation", v1.get("mountingRotation"));
|
||||
if (v1.has("adjustment"))
|
||||
node.set("adjustment", v1.get("adjustment"));
|
||||
return node;
|
||||
}
|
||||
|
||||
public String getCustomName() {
|
||||
return customName;
|
||||
}
|
||||
|
||||
public void setCustomName(String customName) {
|
||||
this.customName = customName;
|
||||
}
|
||||
|
||||
public String getDesignation() {
|
||||
return designation;
|
||||
}
|
||||
|
||||
public void setDesignation(String designation) {
|
||||
this.designation = designation;
|
||||
}
|
||||
|
||||
public boolean isHide() {
|
||||
return hide;
|
||||
}
|
||||
|
||||
public void setHide(boolean hide) {
|
||||
this.hide = hide;
|
||||
}
|
||||
|
||||
public Quaternion getAdjustment() {
|
||||
return adjustment;
|
||||
}
|
||||
|
||||
public void setAdjustment(Quaternion adjustment) {
|
||||
this.adjustment = adjustment;
|
||||
}
|
||||
|
||||
public Quaternion getMountingOrientation() {
|
||||
return mountingOrientation;
|
||||
}
|
||||
|
||||
public void setMountingOrientation(Quaternion mountingOrientation) {
|
||||
this.mountingOrientation = mountingOrientation;
|
||||
}
|
||||
|
||||
public Boolean getAllowDriftCompensation() {
|
||||
return allowDriftCompensation;
|
||||
}
|
||||
|
||||
public void setAllowDriftCompensation(Boolean allowDriftCompensation) {
|
||||
this.allowDriftCompensation = allowDriftCompensation;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package dev.slimevr.config
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
import com.fasterxml.jackson.databind.node.JsonNodeFactory
|
||||
import dev.slimevr.tracking.trackers.Tracker
|
||||
import io.github.axisangles.ktmath.Quaternion
|
||||
|
||||
class TrackerConfig {
|
||||
var customName: String? = null
|
||||
var designation: String? = null
|
||||
var isHide = false
|
||||
var adjustment: Quaternion? = null
|
||||
var mountingOrientation: Quaternion? = null
|
||||
var allowDriftCompensation: Boolean? = null
|
||||
var accessoryId: Int? = null
|
||||
|
||||
constructor()
|
||||
constructor(tracker: Tracker) {
|
||||
designation = if (tracker.trackerPosition
|
||||
!= null
|
||||
) {
|
||||
tracker.trackerPosition!!.designation
|
||||
} else {
|
||||
null
|
||||
}
|
||||
customName = tracker.customName
|
||||
allowDriftCompensation = tracker.isImu()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun toV2(v1: JsonNode, factory: JsonNodeFactory): JsonNode {
|
||||
val node = factory.objectNode()
|
||||
if (v1.has("customName")) node.set<JsonNode>("customName", v1["customName"])
|
||||
if (v1.has("designation")) node.set<JsonNode>("designation", v1["designation"])
|
||||
if (v1.has("hide")) node.set<JsonNode>("hide", v1["hide"])
|
||||
if (v1.has("mountingRotation")) node.set<JsonNode>("mountingRotation", v1["mountingRotation"])
|
||||
if (v1.has("adjustment")) node.set<JsonNode>("adjustment", v1["adjustment"])
|
||||
return node
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,8 @@ import io.github.axisangles.ktmath.Vector3
|
||||
import java.io.IOException
|
||||
import java.net.InetAddress
|
||||
import java.net.InetSocketAddress
|
||||
import java.util.*
|
||||
import java.util.Timer
|
||||
import java.util.TimerTask
|
||||
|
||||
private const val OFFSET_SLERP_FACTOR = 0.5f // Guessed from eyeing VRChat
|
||||
|
||||
@@ -63,6 +64,7 @@ class VRCOSCHandler(
|
||||
private val postReceivingOffset = EulerAngles(EulerOrder.YXZ, 0f, FastMath.PI, 0f).toQuaternion()
|
||||
private var timeAtLastReceivedRotationOffset = System.currentTimeMillis()
|
||||
private var fpsTimer: NanoTimer? = null
|
||||
private val accessoryTrackers: MutableList<Tracker> = FastList()
|
||||
|
||||
init {
|
||||
refreshSettings(false)
|
||||
@@ -329,6 +331,7 @@ class VRCOSCHandler(
|
||||
// Create new bundle
|
||||
val bundle = OSCBundle()
|
||||
|
||||
// Send computed trackers as OSC Trackers
|
||||
for (i in computedTrackers.indices) {
|
||||
if (trackersEnabled[i]) {
|
||||
// Send regular trackers' positions
|
||||
@@ -353,12 +356,7 @@ class VRCOSCHandler(
|
||||
// Y quaternion represents a rotation from z to x
|
||||
// When we negate the z direction, X and Y quaternion
|
||||
// components must be negated.
|
||||
val (_, x2, y2, z2) = Quaternion(
|
||||
w,
|
||||
-x1,
|
||||
-y1,
|
||||
z1
|
||||
).toEulerAngles(EulerOrder.YXZ)
|
||||
val (_, x2, y2, z2) = Quaternion(w, -x1, -y1, z1).toEulerAngles(EulerOrder.YXZ)
|
||||
oscArgs.clear()
|
||||
oscArgs.add(x2 * FastMath.RAD_TO_DEG)
|
||||
oscArgs.add(y2 * FastMath.RAD_TO_DEG)
|
||||
@@ -386,6 +384,27 @@ class VRCOSCHandler(
|
||||
}
|
||||
}
|
||||
|
||||
// Send accessory trackers for custom avatars
|
||||
for (i in accessoryTrackers.indices) {
|
||||
if (accessoryTrackers[i].trackerPosition == TrackerPosition.ACCESSORY && accessoryTrackers[i].status.sendData) {
|
||||
val (_, x, y, z) = accessoryTrackers[i].getRotation().toEulerAngles(EulerOrder.YZX)
|
||||
// Note: we must send the rotations as -1 to 1
|
||||
// https://creators.vrchat.com/avatars/animator-parameters/#parameter-types
|
||||
// Horizontal
|
||||
oscArgs.clear()
|
||||
oscArgs.add(x / FastMath.PI)
|
||||
bundle.addPacket(OSCMessage("/avatar/parameters/SVRAccessory${accessoryTrackers[i].accessoryId}X", oscArgs.clone()))
|
||||
// Vertical
|
||||
oscArgs.clear()
|
||||
oscArgs.add(y / FastMath.PI)
|
||||
bundle.addPacket(OSCMessage("/avatar/parameters/SVRAccessory${accessoryTrackers[i].accessoryId}Y", oscArgs.clone()))
|
||||
// Twist
|
||||
oscArgs.clear()
|
||||
oscArgs.add(z / FastMath.PI)
|
||||
bundle.addPacket(OSCMessage("/avatar/parameters/SVRAccessory${accessoryTrackers[i].accessoryId}Z", oscArgs.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
oscSender!!.send(bundle)
|
||||
} catch (e: IOException) {
|
||||
@@ -455,6 +474,17 @@ class VRCOSCHandler(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an accessory tracker to the list of trackers to send.
|
||||
*
|
||||
* @param tracker the accessory tracker
|
||||
*/
|
||||
fun addAccessoryTracker(tracker: Tracker) {
|
||||
if (!accessoryTrackers.contains(tracker)) {
|
||||
accessoryTrackers.add(tracker)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getOscSender(): OSCPortOut {
|
||||
return oscSender!!
|
||||
}
|
||||
|
||||
@@ -270,6 +270,10 @@ public class RPCHandler extends ProtocolHandler<RpcMessageHeader> {
|
||||
tracker.getResetsHandler().setAllowDriftCompensation(req.allowDriftCompensation());
|
||||
}
|
||||
|
||||
if (req.accessoryId() != 0) {
|
||||
tracker.setAccessoryId(req.accessoryId());
|
||||
}
|
||||
|
||||
this.api.server.trackerUpdated(tracker);
|
||||
}
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ class Tracker @JvmOverloads constructor(
|
||||
var signalStrength: Int? = null
|
||||
var temperature: Float? = null
|
||||
var customName: String? = null
|
||||
var accessoryId: Int? = null
|
||||
|
||||
/**
|
||||
* If the tracker has gotten disconnected after it was initialized first time
|
||||
@@ -209,6 +210,9 @@ class Tracker @JvmOverloads constructor(
|
||||
config.customName?.let {
|
||||
customName = it
|
||||
}
|
||||
config.accessoryId?.let {
|
||||
accessoryId = it
|
||||
}
|
||||
config.designation?.let { designation ->
|
||||
getByDesignation(designation)?.let { trackerPosition = it }
|
||||
} ?: run { trackerPosition = null }
|
||||
@@ -238,6 +242,7 @@ class Tracker @JvmOverloads constructor(
|
||||
fun writeConfig(config: TrackerConfig) {
|
||||
trackerPosition?.let { config.designation = it.designation } ?: run { config.designation = null }
|
||||
customName?.let { config.customName = it }
|
||||
accessoryId?.let { config.accessoryId = it }
|
||||
if (needsMounting) {
|
||||
config.mountingOrientation = resetsHandler.mountingOrientation
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ enum class TrackerPosition(
|
||||
RIGHT_HAND("body:right_hand", TrackerRole.RIGHT_HAND, BodyPart.RIGHT_HAND),
|
||||
LEFT_SHOULDER("body:left_shoulder", TrackerRole.LEFT_SHOULDER, BodyPart.LEFT_SHOULDER),
|
||||
RIGHT_SHOULDER("body:right_shoulder", TrackerRole.RIGHT_SHOULDER, BodyPart.RIGHT_SHOULDER),
|
||||
ACCESSORY("accessory", null, BodyPart.ACCESSORY),
|
||||
;
|
||||
|
||||
companion object {
|
||||
|
||||
Submodule solarxr-protocol updated: c22d4729ec...01631f99e4
Reference in New Issue
Block a user