Skip to content

Commit

Permalink
Merge branch 'computed-head-reset' of https://github.com/SlimeVR/Slim…
Browse files Browse the repository at this point in the history
…eVR-Server into computed-head-reset
  • Loading branch information
Erimelowo committed Jul 31, 2024
2 parents f511283 + 347c454 commit ceea157
Show file tree
Hide file tree
Showing 13 changed files with 488 additions and 273 deletions.
13 changes: 8 additions & 5 deletions gui/public/i18n/en/translation.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -510,22 +510,25 @@ settings-osc-router-network-address-placeholder = IPV4 address
## OSC VRChat settings
settings-osc-vrchat = VRChat OSC Trackers
# This cares about multilines
settings-osc-vrchat-description =
Change VRChat-specific settings to receive headset (HMD) data and send
tracker data for FBT without SteamVR (ex. Quest standalone).
settings-osc-vrchat-description-v1 =
Change settings specific to the OSC Trackers standard used for sending
tracking data to applications without SteamVR (ex. Quest standalone).
Make sure to enable OSC in VRChat via the Action Menu under OSC > Enabled.
To allow receiving HMD and controller data from VRChat, go in your main menu's
settings under Tracking & IK > Allow Sending Head and Wrist VR Tracking OSC Data.
settings-osc-vrchat-enable = Enable
settings-osc-vrchat-enable-description = Toggle the sending and receiving of data.
settings-osc-vrchat-enable-label = Enable
settings-osc-vrchat-network = Network ports
settings-osc-vrchat-network-description = Set the ports for listening and sending data to VRChat.
settings-osc-vrchat-network-description-v1 = Set the ports for listening and sending data. Can be left untouched for VRChat.
settings-osc-vrchat-network-port_in =
.label = Port In
.placeholder = Port in (default: 9001)
settings-osc-vrchat-network-port_out =
.label = Port Out
.placeholder = Port out (default: 9000)
settings-osc-vrchat-network-address = Network address
settings-osc-vrchat-network-address-description = Choose which address to send out data to VRChat (check your Wi-Fi settings on your device).
settings-osc-vrchat-network-address-description-v1 = Choose which address to send out data to. Can be left untouched for VRChat.
settings-osc-vrchat-network-address-placeholder = VRChat ip address
settings-osc-vrchat-network-trackers = Trackers
settings-osc-vrchat-network-trackers-description = Toggle the sending of specific trackers via OSC.
Expand Down
6 changes: 3 additions & 3 deletions gui/src/components/settings/pages/VRCOSCSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export function VRCOSCSettings() {
<div className="flex flex-col pt-2 pb-4">
<>
{l10n
.getString('settings-osc-vrchat-description')
.getString('settings-osc-vrchat-description-v1')
.split('\n')
.map((line, i) => (
<Typography color="secondary" key={i}>
Expand Down Expand Up @@ -162,7 +162,7 @@ export function VRCOSCSettings() {
</Typography>
<div className="flex flex-col pb-2">
<Typography color="secondary">
{l10n.getString('settings-osc-vrchat-network-description')}
{l10n.getString('settings-osc-vrchat-network-description-v1')}
</Typography>
</div>
<div className="grid grid-cols-2 gap-3 pb-5">
Expand Down Expand Up @@ -199,7 +199,7 @@ export function VRCOSCSettings() {
<div className="flex flex-col pb-2">
<Typography color="secondary">
{l10n.getString(
'settings-osc-vrchat-network-address-description'
'settings-osc-vrchat-network-address-description-v1'
)}
</Typography>
</div>
Expand Down
2 changes: 2 additions & 0 deletions server/android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />

<application
android:allowBackup="true"
Expand Down
8 changes: 8 additions & 0 deletions server/android/src/main/java/dev/slimevr/android/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

package dev.slimevr.android

import android.content.Context
import android.net.wifi.WifiManager
import androidx.appcompat.app.AppCompatActivity
import dev.slimevr.Keybinding
import dev.slimevr.VRServer
Expand Down Expand Up @@ -50,6 +52,12 @@ fun main(activity: AppCompatActivity) {
vrServer = VRServer(
configPath = File(activity.filesDir, "vrconfig.yml").absolutePath,
serialHandlerProvider = { _ -> AndroidSerialHandler(activity) },
acquireMulticastLock = {
val wifi = activity.getSystemService(Context.WIFI_SERVICE) as WifiManager
val lock = wifi.createMulticastLock("slimevr-jmdns-multicast-lock")
lock.setReferenceCounted(true)
lock.acquire()
},
)
vrServer.start()
Keybinding(vrServer)
Expand Down
3 changes: 2 additions & 1 deletion server/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ configure<com.diffplug.gradle.spotless.SpotlessExtension> {
"ij_kotlin_packages_to_use_import_on_demand" to
"java.util.*,kotlin.math.*,dev.slimevr.autobone.errors.*" +
",io.github.axisangles.ktmath.*,kotlinx.atomicfu.*" +
",dev.slimevr.tracking.trackers.*,dev.slimevr.desktop.platform.ProtobufMessages.*",
",dev.slimevr.tracking.trackers.*,dev.slimevr.desktop.platform.ProtobufMessages.*" +
",com.illposed.osc.*",
"ij_kotlin_allow_trailing_comma" to true,
)
val ktlintVersion = "1.2.1"
Expand Down
6 changes: 5 additions & 1 deletion server/core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ allprojects {
// Use jcenter for resolving dependencies.
// You can declare any Maven/Ivy/file repository here.
mavenCentral()
maven(url = "https://jitpack.io")
}
}

Expand All @@ -68,12 +69,15 @@ dependencies {
implementation("org.apache.commons:commons-lang3:3.12.0")
implementation("org.apache.commons:commons-collections4:4.4")

implementation("com.illposed.osc:javaosc-core:0.8")
implementation("com.illposed.osc:javaosc-core:0.9")
implementation("org.java-websocket:Java-WebSocket:1.+")
implementation("com.melloware:jintellitype:1.+")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
implementation("it.unimi.dsi:fastutil:8.5.12")

// Jitpack
implementation("com.github.SlimeVR:oscquery-kt:566a0cba58")

testImplementation(kotlin("test"))
// Use JUnit test framework
testImplementation(platform("org.junit:junit-bom:5.9.0"))
Expand Down
8 changes: 3 additions & 5 deletions server/core/src/main/java/dev/slimevr/VRServer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class VRServer @JvmOverloads constructor(
driverBridgeProvider: SteamBridgeProvider = { _, _ -> null },
feederBridgeProvider: (VRServer) -> ISteamVRBridge? = { _ -> null },
serialHandlerProvider: (VRServer) -> SerialHandler = { _ -> SerialHandlerStub() },
acquireMulticastLock: () -> Any? = { null },
configPath: String,
) : Thread("VRServer") {
@JvmField
Expand All @@ -60,6 +61,7 @@ class VRServer @JvmOverloads constructor(
private val tasks: Queue<Runnable> = LinkedBlockingQueue()
private val newTrackersConsumers: MutableList<Consumer<Tracker>> = FastList()
private val onTick: MutableList<Runnable> = FastList()
private val lock = acquireMulticastLock()
val oSCRouter: OSCRouter

@JvmField
Expand Down Expand Up @@ -143,8 +145,6 @@ class VRServer @JvmOverloads constructor(
// Initialize OSC handlers
vrcOSCHandler = VRCOSCHandler(
this,
humanPoseManager,
driverBridge,
configManager.vrConfig.vrcOSC,
computedTrackers,
)
Expand Down Expand Up @@ -283,13 +283,11 @@ class VRServer @JvmOverloads constructor(
fun updateSkeletonModel() {
queueTask {
humanPoseManager.updateSkeletonModelFromServer()
vrcOSCHandler.setHeadTracker(TrackerUtils.getTrackerForSkeleton(trackers, TrackerPosition.HEAD))
if (this.getVRBridge(ISteamVRBridge::class.java)?.updateShareSettingsAutomatically() == true) {
RPCSettingsHandler.sendSteamVRUpdatedSettings(protocolAPI, protocolAPI.rpcHandler)
}
}
vrcOSCHandler.setHeadTracker(
TrackerUtils.getTrackerForSkeleton(trackers, TrackerPosition.HEAD),
)
}

fun resetTrackersFull(resetSourceName: String?) {
Expand Down
4 changes: 4 additions & 0 deletions server/core/src/main/java/dev/slimevr/osc/OSCHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ public interface OSCHandler {

public void refreshSettings(boolean refreshRouterSettings);

public void updateOscReceiver(int portIn, String[] args);

public void updateOscSender(int portOut, String address);

public void update();

public OSCPortOut getOscSender();
Expand Down
135 changes: 74 additions & 61 deletions server/core/src/main/java/dev/slimevr/osc/VMCHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -70,110 +70,123 @@ class VMCHandler(
anchorHip = config.anchorHip
mirrorTracking = config.mirrorTracking

updateOscReceiver(
config.portIn,
arrayOf(
"/VMC/Ext/Bone/Pos",
"/VMC/Ext/Hmd/Pos",
"/VMC/Ext/Con/Pos",
"/VMC/Ext/Tra/Pos",
"/VMC/Ext/Root/Pos",
),
)
updateOscSender(config.portOut, config.address)

if (config.enabled) {
// Load VRM data
if (outputUnityArmature != null && config.vrmJson != null) {
val vrmReader = VRMReader(config.vrmJson!!)
for (unityBone in UnityBone.entries) {
val node = outputUnityArmature!!.getHeadNodeOfBone(unityBone)
if (node != null) {
val offset = if (unityBone == UnityBone.HIPS) {
// For the hip bone, add average of upper leg offsets (which are negative). The hip bone's offset is global.
vrmReader.getOffsetForBone(UnityBone.HIPS) +
((vrmReader.getOffsetForBone(UnityBone.LEFT_UPPER_LEG) + vrmReader.getOffsetForBone(UnityBone.RIGHT_UPPER_LEG)) / 2f)
} else {
vrmReader.getOffsetForBone(unityBone)
}

node.localTransform.translation = offset
}
}
// Make sure to account for the upper legs because of the hip.
vrmHeight = (
vrmReader.getOffsetForBone(UnityBone.HIPS) +
((vrmReader.getOffsetForBone(UnityBone.LEFT_UPPER_LEG) + vrmReader.getOffsetForBone(UnityBone.RIGHT_UPPER_LEG))) +
vrmReader.getOffsetForBone(UnityBone.SPINE) +
vrmReader.getOffsetForBone(UnityBone.CHEST) +
vrmReader.getOffsetForBone(UnityBone.UPPER_CHEST) +
vrmReader.getOffsetForBone(UnityBone.NECK) +
vrmReader.getOffsetForBone(UnityBone.HEAD)
).y
}
}

if (refreshRouterSettings) server.oSCRouter.refreshSettings(false)
}

override fun updateOscReceiver(portIn: Int, args: Array<String>) {
// Stops listening and closes OSC port
val wasListening = oscReceiver != null && oscReceiver!!.isListening
if (wasListening) {
oscReceiver!!.stopListening()
}
val wasConnected = oscSender != null && oscSender!!.isConnected
if (wasConnected) {
try {
oscSender!!.close()
} catch (e: IOException) {
LogManager.severe("[VMCHandler] Error closing the OSC sender: $e")
}
}

if (config.enabled) {
// Instantiates the OSC receiver
try {
val port = config.portIn
oscReceiver = OSCPortIn(port)
if (lastPortIn != port || !wasListening) {
LogManager.info("[VMCHandler] Listening to port $port")
oscReceiver = OSCPortIn(portIn)
if (lastPortIn != portIn || !wasListening) {
LogManager.info("[VMCHandler] Listening to port $portIn")
}
lastPortIn = port
lastPortIn = portIn
} catch (e: IOException) {
LogManager
.severe(
"[VMCHandler] Error listening to the port ${config.portIn}: $e",
"[VMCHandler] Error listening to the port $portIn: $e",
)
}

// Starts listening for VMC messages
if (oscReceiver != null) {
val listener = OSCMessageListener { event: OSCMessageEvent -> this.handleReceivedMessage(event) }
val listenAddresses = arrayOf(
"/VMC/Ext/Bone/Pos",
"/VMC/Ext/Hmd/Pos",
"/VMC/Ext/Con/Pos",
"/VMC/Ext/Tra/Pos",
"/VMC/Ext/Root/Pos",
)

for (address in listenAddresses) {
for (address in args) {
oscReceiver!!
.dispatcher
.addListener(OSCPatternAddressMessageSelector(address), listener)
}

oscReceiver!!.startListening()
}
}
}

override fun updateOscSender(portOut: Int, ip: String) {
// Stop sending
val wasConnected = oscSender != null && oscSender!!.isConnected
if (wasConnected) {
try {
oscSender!!.close()
} catch (e: IOException) {
LogManager.severe("[VMCHandler] Error closing the OSC sender: $e")
}
}

if (config.enabled) {
// Instantiate the OSC sender
try {
val address = InetAddress.getByName(config.address)
val port = config.portOut
oscSender = OSCPortOut(InetSocketAddress(address, port))
if ((lastPortOut != port && lastAddress !== address) || !wasConnected) {
val addr = InetAddress.getByName(ip)
oscSender = OSCPortOut(InetSocketAddress(addr, portOut))
if ((lastPortOut != portOut && lastAddress !== addr) || !wasConnected) {
LogManager
.info(
"[VMCHandler] Sending to port $port at address $address",
"[VMCHandler] Sending to port $portOut at address $ip",
)
}
lastPortOut = port
lastAddress = address
lastPortOut = portOut
lastAddress = addr

oscSender!!.connect()
outputUnityArmature = UnityArmature(false)
} catch (e: IOException) {
LogManager
.severe(
"[VMCHandler] Error connecting to port ${config.portOut} at the address ${config.address}: $e",
"[VMCHandler] Error connecting to port $portOut at the address $ip: $e",
)
}

// Load VRM data
if (outputUnityArmature != null && config.vrmJson != null) {
val vrmReader = VRMReader(config.vrmJson!!)
for (unityBone in UnityBone.entries) {
val node = outputUnityArmature!!.getHeadNodeOfBone(unityBone)
if (node != null) {
val offset = if (unityBone == UnityBone.HIPS) {
// For the hip bone, add average of upper leg offsets (which are negative). The hip bone's offset is global.
vrmReader.getOffsetForBone(UnityBone.HIPS) +
((vrmReader.getOffsetForBone(UnityBone.LEFT_UPPER_LEG) + vrmReader.getOffsetForBone(UnityBone.RIGHT_UPPER_LEG)) / 2f)
} else {
vrmReader.getOffsetForBone(unityBone)
}

node.localTransform.translation = offset
}
}
// Make sure to account for the upper legs because of the hip.
vrmHeight = (
vrmReader.getOffsetForBone(UnityBone.HIPS) +
((vrmReader.getOffsetForBone(UnityBone.LEFT_UPPER_LEG) + vrmReader.getOffsetForBone(UnityBone.RIGHT_UPPER_LEG))) +
vrmReader.getOffsetForBone(UnityBone.SPINE) +
vrmReader.getOffsetForBone(UnityBone.CHEST) +
vrmReader.getOffsetForBone(UnityBone.UPPER_CHEST) +
vrmReader.getOffsetForBone(UnityBone.NECK) +
vrmReader.getOffsetForBone(UnityBone.HEAD)
).y
}
}

if (refreshRouterSettings) server.oSCRouter.refreshSettings(false)
}

private fun handleReceivedMessage(event: OSCMessageEvent) {
Expand Down
Loading

0 comments on commit ceea157

Please sign in to comment.