Compare commits

...

1 Commits

Author SHA1 Message Date
Louka
472f6d8fc0 add accessories wip 2023-07-29 02:19:08 -04:00
15 changed files with 122 additions and 115 deletions

View File

@@ -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

View File

@@ -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({

View File

@@ -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>

View File

@@ -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);
});

View File

@@ -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({

View File

@@ -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);
};

View File

@@ -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);
};

View File

@@ -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)
}
}
}

View File

@@ -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;
}
}

View File

@@ -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
}
}
}

View File

@@ -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!!
}

View File

@@ -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);
}

View File

@@ -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
}

View File

@@ -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 {