Skip to content

Commit

Permalink
Merge branch 'main' into slimevr-flashing-instruction-change
Browse files Browse the repository at this point in the history
  • Loading branch information
loucass003 authored Jan 7, 2025
2 parents 4489b7b + 2ff6e99 commit 02552a7
Show file tree
Hide file tree
Showing 16 changed files with 175 additions and 123 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/gradle.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ jobs:
./bundle_dmg.sh --volname SlimeVR --icon slimevr 180 170 --app-drop-link 480 170 \
--window-size 660 400 --hide-extension ../macos/SlimeVR.app \
--volicon ../macos/SlimeVR.app/Contents/Resources/icon.icns --skip-jenkins \
--eula ../../../../LICENSE-MIT slimevr.dmg ../macos/SlimeVR.app
--eula ../../../../../LICENSE-MIT slimevr.dmg ../macos/SlimeVR.app
- uses: actions/upload-artifact@v4
with:
Expand Down
1 change: 1 addition & 0 deletions dev.slimevr.SlimeVR.metainfo.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
</provides>

<releases>
<release version="0.13.2" date="2024-11-06"><url>https://github.com/SlimeVR/SlimeVR-Server/releases/tag/v0.13.2</url></release>
<release version="0.13.1" date="2024-11-05"><url>https://github.com/SlimeVR/SlimeVR-Server/releases/tag/v0.13.1</url></release>
<release version="0.13.1~rc.3" type="development" date="2024-10-31"><url>https://github.com/SlimeVR/SlimeVR-Server/releases/tag/v0.13.1-rc.3</url></release>
<release version="0.13.1~rc.2" type="development" date="2024-10-26"><url>https://github.com/SlimeVR/SlimeVR-Server/releases/tag/v0.13.1-rc.2</url></release>
Expand Down
2 changes: 1 addition & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
harfbuzz
libffi
libsoup_3
openssl
openssl.dev
pango
pkg-config
treefmt
Expand Down
1 change: 1 addition & 0 deletions gui/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ yarn-error.log*
# vite
/dist
/stats.html
vite.config.ts.timestamp*

# eslint
.eslintcache
4 changes: 3 additions & 1 deletion gui/src/components/TopBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,9 @@ export function TopBar({
await invoke('update_tray_text');
} else if (
config?.connectedTrackersWarning &&
connectedIMUTrackers.length > 0
connectedIMUTrackers.filter(
(t) => t.tracker.status !== TrackerStatus.TIMED_OUT
).length > 0
) {
setConnectedTrackerWarning(true);
} else {
Expand Down
4 changes: 3 additions & 1 deletion gui/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ const versionTag = execSync('git --no-pager tag --sort -taggerdate --points-at H
.split('\n')[0]
.trim();
// If not empty then it's not clean
const gitClean = execSync('git status --porcelain').toString() ? false : true;
const gitCleanString = execSync('git status --porcelain').toString();
const gitClean = gitCleanString ? false : true;
if (!gitClean) console.log('Git is dirty because of:\n' + gitCleanString);

console.log(`version is ${versionTag || commitHash}${gitClean ? '' : '-dirty'}`);

Expand Down
131 changes: 88 additions & 43 deletions server/core/src/main/java/dev/slimevr/autobone/AutoBone.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import dev.slimevr.autobone.errors.*
import dev.slimevr.config.AutoBoneConfig
import dev.slimevr.poseframeformat.PoseFrameIO
import dev.slimevr.poseframeformat.PoseFrames
import dev.slimevr.tracking.processor.BoneType
import dev.slimevr.tracking.processor.HumanPoseManager
import dev.slimevr.tracking.processor.config.SkeletonConfigManager
import dev.slimevr.tracking.processor.config.SkeletonConfigOffsets
Expand Down Expand Up @@ -94,38 +95,73 @@ class AutoBone(server: VRServer) {
}
}

fun getBoneDirection(
/**
* Computes the local tail position of the bone after rotation.
*/
fun getBoneLocalTail(
skeleton: HumanPoseManager,
configOffset: SkeletonConfigOffsets,
rightSide: Boolean,
boneType: BoneType,
): Vector3 {
// IMPORTANT: This assumption for acquiring BoneType only works if
// SkeletonConfigOffsets is set up to only affect one BoneType, make sure no
// changes to SkeletonConfigOffsets goes against this assumption, please!
val boneType = when (configOffset) {
SkeletonConfigOffsets.HIPS_WIDTH, SkeletonConfigOffsets.SHOULDERS_WIDTH,
SkeletonConfigOffsets.SHOULDERS_DISTANCE, SkeletonConfigOffsets.UPPER_ARM,
SkeletonConfigOffsets.LOWER_ARM, SkeletonConfigOffsets.UPPER_LEG,
SkeletonConfigOffsets.LOWER_LEG, SkeletonConfigOffsets.FOOT_LENGTH,
->
if (rightSide) configOffset.affectedOffsets[1] else configOffset.affectedOffsets[0]

else -> configOffset.affectedOffsets[0]
}
return skeleton.getBone(boneType).getGlobalRotation().toRotationVector()
val bone = skeleton.getBone(boneType)
return bone.getTailPosition() - bone.getPosition()
}

/**
* Computes the direction of the bone tail's movement between skeletons 1 and 2.
*/
fun getBoneLocalTailDir(
skeleton1: HumanPoseManager,
skeleton2: HumanPoseManager,
boneType: BoneType,
): Vector3? {
val boneOff = getBoneLocalTail(skeleton2, boneType) - getBoneLocalTail(skeleton1, boneType)
val boneOffLen = boneOff.len()
return if (boneOffLen > MIN_SLIDE_DIST) boneOff / boneOffLen else null
}

fun getDotProductDiff(
/**
* Predicts how much the provided config should be affecting the slide offsets
* of the left and right ankles.
*/
fun getSlideDot(
skeleton1: HumanPoseManager,
skeleton2: HumanPoseManager,
configOffset: SkeletonConfigOffsets,
rightSide: Boolean,
offset: Vector3,
config: SkeletonConfigOffsets,
slideL: Vector3?,
slideR: Vector3?,
): Float {
val normalizedOffset = offset.unit()
val dot1 = normalizedOffset.dot(getBoneDirection(skeleton1, configOffset, rightSide))
val dot2 = normalizedOffset.dot(getBoneDirection(skeleton2, configOffset, rightSide))
return dot2 - dot1
var slideDot = 0f
// Used for right offset if not a symmetric bone
var boneOffL: Vector3? = null

if (slideL != null) {
boneOffL = getBoneLocalTailDir(skeleton1, skeleton2, config.affectedOffsets[0])

if (boneOffL != null) {
slideDot += slideL.dot(boneOffL)
}
}

if (slideR != null) {
// IMPORTANT: This assumption for acquiring BoneType only works if
// SkeletonConfigOffsets is set up to only affect one BoneType, make sure no
// changes to SkeletonConfigOffsets goes against this assumption, please!
val boneOffR = if (SYMM_CONFIGS.contains(config)) {
getBoneLocalTailDir(skeleton1, skeleton2, config.affectedOffsets[1])
} else if (slideL != null) {
// Use cached offset if slideL was used
boneOffL
} else {
// Compute offset if missing because of slideL
getBoneLocalTailDir(skeleton1, skeleton2, config.affectedOffsets[0])
}

if (boneOffR != null) {
slideDot += slideR.dot(boneOffR)
}
}

return slideDot / 2f
}

fun applyConfig(
Expand Down Expand Up @@ -488,13 +524,15 @@ class AutoBone(server: VRServer) {
return
}

val slideLeft = skeleton2
.getComputedTracker(TrackerRole.LEFT_FOOT).position -
val slideL = skeleton2.getComputedTracker(TrackerRole.LEFT_FOOT).position -
skeleton1.getComputedTracker(TrackerRole.LEFT_FOOT).position
val slideLLen = slideL.len()
val slideLUnit: Vector3? = if (slideLLen > MIN_SLIDE_DIST) slideL / slideLLen else null

val slideRight = skeleton2
.getComputedTracker(TrackerRole.RIGHT_FOOT).position -
val slideR = skeleton2.getComputedTracker(TrackerRole.RIGHT_FOOT).position -
skeleton1.getComputedTracker(TrackerRole.RIGHT_FOOT).position
val slideRLen = slideR.len()
val slideRUnit: Vector3? = if (slideRLen > MIN_SLIDE_DIST) slideR / slideRLen else null

val intermediateOffsets = EnumMap(offsets)
for (entry in intermediateOffsets.entries) {
Expand All @@ -505,28 +543,23 @@ class AutoBone(server: VRServer) {
}
val originalLength = entry.value

val leftDotProduct = getDotProductDiff(
skeleton1,
skeleton2,
entry.key,
false,
slideLeft,
)
val rightDotProduct = getDotProductDiff(
// Calculate the total effect of the bone based on change in rotation
val slideDot = getSlideDot(
skeleton1,
skeleton2,
entry.key,
true,
slideRight,
slideLUnit,
slideRUnit,
)

// Calculate the total effect of the bone based on change in rotation
val dotLength = originalLength * ((leftDotProduct + rightDotProduct) / 2f)
val dotLength = originalLength * slideDot

// Scale by the total effect of the bone
val curAdjustVal = adjustVal * -dotLength
val newLength = originalLength + curAdjustVal
if (curAdjustVal == 0f) {
continue
}

val newLength = originalLength + curAdjustVal
// No small or negative numbers!!! Bad algorithm!
if (newLength < 0.01f) {
continue
Expand Down Expand Up @@ -754,6 +787,7 @@ class AutoBone(server: VRServer) {

companion object {
const val MIN_HEIGHT = 0.4f
const val MIN_SLIDE_DIST = 0.002f
const val AUTOBONE_FOLDER = "AutoBone Recordings"
const val LOADAUTOBONE_FOLDER = "Load AutoBone Recordings"

Expand All @@ -773,5 +807,16 @@ class AutoBone(server: VRServer) {
private fun errorFunc(errorDeriv: Float): Float = 0.5f * (errorDeriv * errorDeriv)

private fun decayFunc(initialAdjustRate: Float, adjustRateDecay: Float, epoch: Int): Float = if (epoch >= 0) initialAdjustRate / (1 + (adjustRateDecay * epoch)) else 0.0f

private val SYMM_CONFIGS = arrayOf(
SkeletonConfigOffsets.HIPS_WIDTH,
SkeletonConfigOffsets.SHOULDERS_WIDTH,
SkeletonConfigOffsets.SHOULDERS_DISTANCE,
SkeletonConfigOffsets.UPPER_ARM,
SkeletonConfigOffsets.LOWER_ARM,
SkeletonConfigOffsets.UPPER_LEG,
SkeletonConfigOffsets.LOWER_LEG,
SkeletonConfigOffsets.FOOT_LENGTH,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,14 @@ class PositionError : IAutoBoneError {
val position = trackerFrame.tryGetPosition() ?: continue
val trackerRole = trackerFrame.tryGetTrackerPosition()?.trackerRole ?: continue

val computedTracker = skeleton.getComputedTracker(trackerRole) ?: continue
try {
val computedTracker = skeleton.getComputedTracker(trackerRole)

offset += (position - computedTracker.position).len()
offsetCount++
offset += (position - computedTracker.position).len()
offsetCount++
} catch (_: Exception) {
// Ignore unsupported positions
}
}
return if (offsetCount > 0) offset / offsetCount else 0f
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,17 @@ class PositionOffsetError : IAutoBoneError {
val position2 = trackerFrame2.tryGetPosition() ?: continue
val trackerRole2 = trackerFrame2.tryGetTrackerPosition()?.trackerRole ?: continue

val computedTracker1 = skeleton1.getComputedTracker(trackerRole1) ?: continue
val computedTracker2 = skeleton2.getComputedTracker(trackerRole2) ?: continue
try {
val computedTracker1 = skeleton1.getComputedTracker(trackerRole1)
val computedTracker2 = skeleton2.getComputedTracker(trackerRole2)

val dist1 = (position1 - computedTracker1.position).len()
val dist2 = (position2 - computedTracker2.position).len()
offset += abs(dist2 - dist1)
offsetCount++
val dist1 = (position1 - computedTracker1.position).len()
val dist2 = (position2 - computedTracker2.position).len()
offset += abs(dist2 - dist1)
offsetCount++
} catch (_: Exception) {
// Ignore unsupported positions
}
}
return if (offsetCount > 0) offset / offsetCount else 0f
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ class AutoBoneConfig {
var cursorIncrement = 2
var minDataDistance = 1
var maxDataDistance = 1
var numEpochs = 100
var numEpochs = 50
var printEveryNumEpochs = 25
var initialAdjustRate = 10.0f
var adjustRateDecay = 1.0f
var slideErrorFactor = 0.0f
var offsetSlideErrorFactor = 1.0f
var slideErrorFactor = 1.0f
var offsetSlideErrorFactor = 0.0f
var footHeightOffsetErrorFactor = 0.0f
var bodyProportionErrorFactor = 0.25f
var bodyProportionErrorFactor = 0.05f
var heightErrorFactor = 0.0f
var positionErrorFactor = 0.0f
var positionOffsetErrorFactor = 0.0f
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,32 @@ public ObjectNode convert(
}
}
}

if (version < 14) {
// Update AutoBone defaults
ObjectNode autoBoneNode = (ObjectNode) modelData.get("autoBone");
if (autoBoneNode != null) {
JsonNode offsetSlideNode = autoBoneNode.get("offsetSlideErrorFactor");
JsonNode slideNode = autoBoneNode.get("slideErrorFactor");
if (
offsetSlideNode != null
&& slideNode != null
&& offsetSlideNode.floatValue() == 1.0f
&& slideNode.floatValue() == 0.0f
) {
autoBoneNode.set("offsetSlideErrorFactor", new FloatNode(0.0f));
autoBoneNode.set("slideErrorFactor", new FloatNode(1.0f));
}
JsonNode bodyProportionsNode = autoBoneNode.get("bodyProportionErrorFactor");
if (bodyProportionsNode != null && bodyProportionsNode.floatValue() == 0.25f) {
autoBoneNode.set("bodyProportionErrorFactor", new FloatNode(0.05f));
}
JsonNode numEpochsNode = autoBoneNode.get("numEpochs");
if (numEpochsNode != null && numEpochsNode.intValue() == 100) {
autoBoneNode.set("numEpochs", new IntNode(50));
}
}
}
} catch (Exception e) {
LogManager.severe("Error during config migration: " + e);
}
Expand Down
4 changes: 2 additions & 2 deletions server/core/src/main/java/dev/slimevr/config/VRConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import dev.slimevr.tracking.trackers.Tracker
import dev.slimevr.tracking.trackers.TrackerRole

@JsonVersionedModel(
currentVersion = "13",
defaultDeserializeToVersion = "13",
currentVersion = "14",
defaultDeserializeToVersion = "14",
toCurrentConverterClass = CurrentVRConfigConverter::class,
)
class VRConfig {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ data class TrackerFrames(val name: String = "", val frames: FastList<TrackerFram
// Make sure this is false!! Otherwise HumanSkeleton ignores it
isInternal = false,
isComputed = true,
trackRotDirection = false,
)

tracker.status = TrackerStatus.OK
Expand Down
Loading

0 comments on commit 02552a7

Please sign in to comment.