mirror of
https://github.com/SlimeVR/SlimeVR-Server.git
synced 2026-04-06 02:01:58 +02:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c65129bb4 | ||
|
|
acaf6a7679 | ||
|
|
8044a8a824 | ||
|
|
1ec03b83b6 | ||
|
|
44caf24126 | ||
|
|
a125195695 | ||
|
|
d27385dfa4 | ||
|
|
68bd670d7c | ||
|
|
42eda428ae | ||
|
|
6504ebb9fb | ||
|
|
6332615b08 | ||
|
|
e054c393ea | ||
|
|
85c30b9425 | ||
|
|
5f5d96a1c7 | ||
|
|
2bce1af454 | ||
|
|
d45527f72e | ||
|
|
bd3acf1889 | ||
|
|
347fcf503f | ||
|
|
12c3379e9a | ||
|
|
77a871239d | ||
|
|
337d345ded | ||
|
|
34de9c3586 | ||
|
|
cb28221c58 | ||
|
|
64903038e0 | ||
|
|
4cbc6031f8 | ||
|
|
66f619bb4f | ||
|
|
09a0ed153b | ||
|
|
e8fa981225 | ||
|
|
54f9907238 | ||
|
|
8525f85d59 | ||
|
|
7293ddaa90 | ||
|
|
3ec6a61763 | ||
|
|
7874bbca27 | ||
|
|
b978eaf3f1 | ||
|
|
b2817cb3ba | ||
|
|
ab265d1689 | ||
|
|
527c4c7f91 | ||
|
|
7c506a3fe2 | ||
|
|
217dc875d5 | ||
|
|
489052141d |
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
@@ -14,7 +14,7 @@
|
||||
/gui/src/i18n/ @ImUrX @Erimelowo
|
||||
/l10n.toml @ImUrX @Erimelowo
|
||||
|
||||
/gui/src/components/settings/ @Erimelowo @ImUrX
|
||||
/gui/src/components/settings/ @Erimelowo @ImUrX @loucass003
|
||||
|
||||
# Rust part of the GUI
|
||||
/gui/src-tauri/ @ImUrX
|
||||
|
||||
31
.github/workflows/build-gui.yml
vendored
31
.github/workflows/build-gui.yml
vendored
@@ -42,35 +42,46 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-22.04, windows-latest, macos-latest]
|
||||
os:
|
||||
[
|
||||
ubuntu-22.04,
|
||||
windows-latest,
|
||||
macos-latest,
|
||||
ubuntu-22.04-arm,
|
||||
windows-11-arm,
|
||||
]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
env:
|
||||
# Don't mark warnings as errors
|
||||
CI: false
|
||||
BUILD_ARCH: ${{ endsWith(matrix.os, 'arm') && 'aarch64' || 'amd64' }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- if: matrix.os == 'ubuntu-22.04'
|
||||
- if: startsWith(matrix.os, 'ubuntu')
|
||||
name: Set up Linux dependencies
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.5.0
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.5.1
|
||||
with:
|
||||
packages: libgtk-3-dev webkit2gtk-4.1 libappindicator3-dev librsvg2-dev patchelf
|
||||
# Increment to invalidate the cache
|
||||
version: 1.0
|
||||
version: ${{ format('v1.0-{0}', env.BUILD_ARCH) }}
|
||||
# Enables a workaround to attempt to run pre and post install scripts
|
||||
execute_install_scripts: true
|
||||
# Disables uploading logs as a build artifact
|
||||
debug: false
|
||||
|
||||
- if: matrix.os == 'windows-11-arm'
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
cache: false
|
||||
|
||||
- name: Cache cargo dependencies
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: "${{ matrix.os }}"
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
- name: Use Node.js
|
||||
@@ -90,21 +101,21 @@ jobs:
|
||||
NODE_OPTIONS: ${{ matrix.os == 'macos-latest' && '--max-old-space-size=4096' || '' }}
|
||||
run: pnpm run skipbundler --config $( ./gui/scripts/gitversion.mjs )
|
||||
|
||||
- if: matrix.os == 'windows-latest'
|
||||
- if: startsWith(matrix.os, 'windows')
|
||||
name: Upload a Build Artifact (Windows)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
# Artifact name
|
||||
name: SlimeVR-GUI-Windows
|
||||
name: ${{ format('SlimeVR-GUI-Windows-{0}', env.BUILD_ARCH) }}
|
||||
# A file, directory or wildcard pattern that describes what to upload
|
||||
path: target/release/slimevr.exe
|
||||
|
||||
- if: matrix.os == 'ubuntu-22.04'
|
||||
- if: startsWith(matrix.os, 'ubuntu')
|
||||
name: Upload a Build Artifact (Linux)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
# Artifact name
|
||||
name: SlimeVR-GUI-Linux
|
||||
name: ${{ format('SlimeVR-GUI-Linux-{0}', env.BUILD_ARCH) }}
|
||||
# A file, directory or wildcard pattern that describes what to upload
|
||||
path: target/release/slimevr
|
||||
|
||||
|
||||
34
.github/workflows/generate-update-manifest.yml
vendored
Normal file
34
.github/workflows/generate-update-manifest.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
# This workflow will build the update manifest for the updater and update a GitHub release
|
||||
|
||||
name: Generate update manifest
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [released]
|
||||
|
||||
jobs:
|
||||
generate-manifest:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22.x'
|
||||
|
||||
- name: Generate update-manifest.json
|
||||
run: |
|
||||
npx @slimevr/update-manifest-generator@latest
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "update-manifest.json"
|
||||
path: ./update-manifest.json
|
||||
|
||||
- name: Upload update-manifest.json to release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: ${{ secrets.UPDATE_MANIFEST_RELEASE_TAG }}
|
||||
files: ./update-manifest.json
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
133
.github/workflows/gradle.yaml
vendored
133
.github/workflows/gradle.yaml
vendored
@@ -29,8 +29,8 @@ jobs:
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: "17"
|
||||
distribution: "adopt"
|
||||
java-version: '17'
|
||||
distribution: 'adopt'
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
@@ -41,9 +41,8 @@ jobs:
|
||||
- name: Check code formatting
|
||||
run: ./gradlew spotlessCheck
|
||||
|
||||
# - name: Test with Gradle
|
||||
# run: ./gradlew test
|
||||
|
||||
- name: Test with Gradle
|
||||
run: ./gradlew test
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -59,32 +58,31 @@ jobs:
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: "17"
|
||||
distribution: "adopt"
|
||||
java-version: '17'
|
||||
distribution: 'adopt'
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
|
||||
# - name: Build with Gradle
|
||||
# run: ./gradlew shadowJar
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew shadowJar
|
||||
|
||||
# - name: Upload the Server JAR as a Build Artifact
|
||||
# uses: actions/upload-artifact@v4
|
||||
# with:
|
||||
# # Artifact name
|
||||
# name: "SlimeVR-Server" # optional, default is artifact
|
||||
# # A file, directory or wildcard pattern that describes what to upload
|
||||
# path: server/desktop/build/libs/slimevr.jar
|
||||
|
||||
# - name: Upload to draft release
|
||||
# uses: softprops/action-gh-release@v2
|
||||
# if: startsWith(github.ref, 'refs/tags/')
|
||||
# with:
|
||||
# draft: true
|
||||
# generate_release_notes: true
|
||||
# files: |
|
||||
# server/desktop/build/libs/slimevr.jar
|
||||
- name: Upload the Server JAR as a Build Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
# Artifact name
|
||||
name: 'SlimeVR-Server' # optional, default is artifact
|
||||
# A file, directory or wildcard pattern that describes what to upload
|
||||
path: server/desktop/build/libs/slimevr.jar
|
||||
|
||||
- name: Upload to draft release
|
||||
uses: softprops/action-gh-release@v2
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
draft: true
|
||||
generate_release_notes: true
|
||||
files: |
|
||||
server/desktop/build/libs/slimevr.jar
|
||||
|
||||
bundle-android:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -99,8 +97,8 @@ jobs:
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: "17"
|
||||
distribution: "adopt"
|
||||
java-version: '17'
|
||||
distribution: 'adopt'
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
@@ -127,7 +125,7 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
# Artifact name
|
||||
name: "SlimeVR-Android" # optional, default is artifact
|
||||
name: 'SlimeVR-Android' # optional, default is artifact
|
||||
# A file, directory or wildcard pattern that describes what to upload
|
||||
path: server/android/build/outputs/apk/*
|
||||
|
||||
@@ -145,28 +143,33 @@ jobs:
|
||||
files: |
|
||||
./SlimeVR-android.apk
|
||||
|
||||
|
||||
bundle-linux:
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, ubuntu-24.04-arm]
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: [build, test]
|
||||
if: contains(fromJSON('["workflow_dispatch", "create"]'), github.event_name)
|
||||
|
||||
env:
|
||||
BUILD_ARCH: ${{ endsWith(matrix.os, 'arm') && 'aarch64' || 'amd64' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: carlosperate/download-file-action@v2
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
file-url: "https://imurx.org/slimevr.jar"
|
||||
location: server/desktop/build/libs/
|
||||
name: 'SlimeVR-Server'
|
||||
path: server/desktop/build/libs/
|
||||
|
||||
- name: Set up Linux dependencies
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.5.0
|
||||
uses: awalsh128/cache-apt-pkgs-action@v1.5.1
|
||||
with:
|
||||
packages: |
|
||||
build-essential curl wget file libssl-dev libgtk-3-dev libappindicator3-dev librsvg2-dev
|
||||
# Increment to invalidate the cache
|
||||
version: 2.0
|
||||
version: ${{ format('v1.0-{0}', env.BUILD_ARCH) }}
|
||||
# Enables a workaround to attempt to run pre and post install scripts
|
||||
execute_install_scripts: true
|
||||
# Disables uploading logs as a build artifact
|
||||
@@ -184,8 +187,6 @@ jobs:
|
||||
|
||||
- name: Cache cargo dependencies
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: "ubuntu-22.04"
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
- name: Use Node.js
|
||||
@@ -207,31 +208,32 @@ jobs:
|
||||
tar czf slimevr-gui-dist.tar.gz -C gui/dist/ .
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
with:
|
||||
name: SlimeVR-GUI-Dist
|
||||
path: ./slimevr-gui-dist.tar.gz
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: SlimeVR-GUI-Deb
|
||||
name: ${{ format('SlimeVR-GUI-Deb-{0}', env.BUILD_ARCH) }}
|
||||
path: target/release/bundle/deb/slimevr*.deb
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: SlimeVR-GUI-AppImage
|
||||
name: ${{ format('SlimeVR-GUI-AppImage-{0}', env.BUILD_ARCH) }}
|
||||
path: target/release/bundle/appimage/slimevr*.AppImage
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: SlimeVR-GUI-RPM
|
||||
path: target/release/bundle/rpm/slimevr*.rpm
|
||||
name: ${{ format('SlimeVR-GUI-RPM-{0}', env.BUILD_ARCH) }}
|
||||
path: target/release/bundle/rpm/slimevr*.rpm
|
||||
|
||||
- name: Prepare for release
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
run: |
|
||||
cp target/release/bundle/appimage/slimevr*.AppImage ./SlimeVR-amd64.appimage
|
||||
cp target/release/bundle/deb/slimevr*.deb ./SlimeVR-amd64.deb
|
||||
cp target/release/bundle/rpm/slimevr*.rpm ./SlimeVR-amd64.rpm
|
||||
cp target/release/bundle/appimage/slimevr*.AppImage "./SlimeVR-$BUILD_ARCH.appimage"
|
||||
cp target/release/bundle/deb/slimevr*.deb "./SlimeVR-$BUILD_ARCH.deb"
|
||||
cp target/release/bundle/rpm/slimevr*.rpm "./SlimeVR-$BUILD_ARCH.rpm"
|
||||
|
||||
- name: Upload to draft release
|
||||
uses: softprops/action-gh-release@v2
|
||||
@@ -241,10 +243,9 @@ jobs:
|
||||
generate_release_notes: true
|
||||
files: |
|
||||
./slimevr-gui-dist.tar.gz
|
||||
./SlimeVR-amd64.appimage
|
||||
./SlimeVR-amd64.deb
|
||||
./SlimeVR-amd64.rpm
|
||||
|
||||
./SlimeVR-*.appimage
|
||||
./SlimeVR-*.deb
|
||||
./SlimeVR-*.rpm
|
||||
|
||||
bundle-mac:
|
||||
runs-on: macos-latest
|
||||
@@ -255,15 +256,13 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: carlosperate/download-file-action@v2
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
file-url: "https://imurx.org/slimevr.jar"
|
||||
location: server/desktop/build/libs/
|
||||
name: 'SlimeVR-Server'
|
||||
path: server/desktop/build/libs/
|
||||
|
||||
- name: Cache cargo dependencies
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: "macos-latest"
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
- name: Use Node.js
|
||||
@@ -322,25 +321,33 @@ jobs:
|
||||
files: |
|
||||
./SlimeVR-mac.dmg
|
||||
|
||||
|
||||
bundle-windows:
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-latest, windows-11-arm]
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: [build, test]
|
||||
if: contains(fromJSON('["workflow_dispatch", "create"]'), github.event_name)
|
||||
|
||||
env:
|
||||
BUILD_ARCH: ${{ endsWith(matrix.os, 'arm') && 'win-aarch64' || 'win64' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: carlosperate/download-file-action@v2
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
file-url: "https://imurx.org/slimevr.jar"
|
||||
location: server/desktop/build/libs/
|
||||
name: 'SlimeVR-Server'
|
||||
path: server/desktop/build/libs/
|
||||
|
||||
- if: matrix.os == 'windows-11-arm'
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
cache: false
|
||||
|
||||
- name: Cache cargo dependencies
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: "windows-latest"
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
- name: Use Node.js
|
||||
@@ -367,11 +374,11 @@ jobs:
|
||||
cp server/desktop/build/libs/slimevr.jar ./SlimeVR/slimevr.jar
|
||||
cp server/core/resources/* ./SlimeVR/
|
||||
cp target/release/slimevr.exe ./SlimeVR/
|
||||
7z a -tzip SlimeVR-win64.zip ./SlimeVR/
|
||||
7z a -tzip "SlimeVR-$BUILD_ARCH.zip" ./SlimeVR/
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: SlimeVR-GUI-Windows
|
||||
name: ${{ format('SlimeVR-GUI-Windows-{0}', env.BUILD_ARCH) }}
|
||||
path: ./SlimeVR*.zip
|
||||
|
||||
- name: Upload to draft release
|
||||
@@ -380,4 +387,4 @@ jobs:
|
||||
with:
|
||||
draft: true
|
||||
generate_release_notes: true
|
||||
files: ./SlimeVR-win64.zip
|
||||
files: ./SlimeVR-*.zip
|
||||
|
||||
@@ -1 +1 @@
|
||||
18.12.1
|
||||
22.17.0
|
||||
|
||||
1482
Cargo.lock
generated
1482
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
683
flake.lock
generated
683
flake.lock
generated
@@ -2,14 +2,15 @@
|
||||
"nodes": {
|
||||
"cachix": {
|
||||
"inputs": {
|
||||
"devenv": "devenv_2",
|
||||
"devenv": [
|
||||
"devenv"
|
||||
],
|
||||
"flake-compat": [
|
||||
"devenv",
|
||||
"flake-compat"
|
||||
"devenv"
|
||||
],
|
||||
"git-hooks": [
|
||||
"devenv",
|
||||
"pre-commit-hooks"
|
||||
"git-hooks"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
@@ -17,51 +18,16 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1726520618,
|
||||
"narHash": "sha256-jOsaBmJ/EtX5t/vbylCdS7pWYcKGmWOKg4QKUzKr6dA=",
|
||||
"lastModified": 1748883665,
|
||||
"narHash": "sha256-R0W7uAg+BLoHjMRMQ8+oiSbTq8nkGz5RDpQ+ZfxxP3A=",
|
||||
"owner": "cachix",
|
||||
"repo": "cachix",
|
||||
"rev": "695525f9086542dfb09fde0871dbf4174abbf634",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "cachix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"cachix_2": {
|
||||
"inputs": {
|
||||
"devenv": "devenv_3",
|
||||
"flake-compat": [
|
||||
"devenv",
|
||||
"cachix",
|
||||
"devenv",
|
||||
"flake-compat"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"cachix",
|
||||
"devenv",
|
||||
"nixpkgs"
|
||||
],
|
||||
"pre-commit-hooks": [
|
||||
"devenv",
|
||||
"cachix",
|
||||
"devenv",
|
||||
"pre-commit-hooks"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1712055811,
|
||||
"narHash": "sha256-7FcfMm5A/f02yyzuavJe06zLa9hcMHsagE28ADcmQvk=",
|
||||
"owner": "cachix",
|
||||
"repo": "cachix",
|
||||
"rev": "02e38da89851ec7fec3356a5c04bc8349cae0e30",
|
||||
"rev": "f707778d902af4d62d8dd92c269f8e70de09acbe",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"ref": "latest",
|
||||
"repo": "cachix",
|
||||
"type": "github"
|
||||
}
|
||||
@@ -69,92 +35,23 @@
|
||||
"devenv": {
|
||||
"inputs": {
|
||||
"cachix": "cachix",
|
||||
"flake-compat": "flake-compat_2",
|
||||
"nix": "nix_3",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"pre-commit-hooks": "pre-commit-hooks_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1730213537,
|
||||
"narHash": "sha256-bWoeNdFISbGK8M0Xw4edmManGCkJ1oNqbfNY0Hlv9Vc=",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"rev": "5c046eeafd13f7a2b9fc733f70ea17571b24410f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"devenv_2": {
|
||||
"inputs": {
|
||||
"cachix": "cachix_2",
|
||||
"flake-compat": [
|
||||
"devenv",
|
||||
"cachix",
|
||||
"flake-compat"
|
||||
],
|
||||
"nix": "nix_2",
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"cachix",
|
||||
"nixpkgs"
|
||||
],
|
||||
"pre-commit-hooks": [
|
||||
"devenv",
|
||||
"cachix",
|
||||
"git-hooks"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1723156315,
|
||||
"narHash": "sha256-0JrfahRMJ37Rf1i0iOOn+8Z4CLvbcGNwa2ChOAVrp/8=",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"rev": "ff5eb4f2accbcda963af67f1a1159e3f6c7f5f91",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"devenv_3": {
|
||||
"inputs": {
|
||||
"flake-compat": [
|
||||
"devenv",
|
||||
"cachix",
|
||||
"devenv",
|
||||
"cachix",
|
||||
"flake-compat"
|
||||
],
|
||||
"flake-compat": "flake-compat",
|
||||
"git-hooks": "git-hooks",
|
||||
"nix": "nix",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"poetry2nix": "poetry2nix",
|
||||
"pre-commit-hooks": [
|
||||
"devenv",
|
||||
"cachix",
|
||||
"devenv",
|
||||
"cachix",
|
||||
"pre-commit-hooks"
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1708704632,
|
||||
"narHash": "sha256-w+dOIW60FKMaHI1q5714CSibk99JfYxm0CzTinYWr+Q=",
|
||||
"lastModified": 1750800495,
|
||||
"narHash": "sha256-wBTGFNCx3Gr3BkNkEoFrKx9+d7otSdQesCDCPGDKZHk=",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"rev": "2ee4450b0f4b95a1b90f2eb5ffea98b90e48c196",
|
||||
"rev": "b33ab3610c084a7e3fabc5eefaeb437449f1efe7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"ref": "python-rewrite",
|
||||
"repo": "devenv",
|
||||
"type": "github"
|
||||
}
|
||||
@@ -167,11 +64,11 @@
|
||||
"rust-analyzer-src": "rust-analyzer-src"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1730270567,
|
||||
"narHash": "sha256-ZTBMwsY0i5zTT6rejotc9wqcSGkEgAeejXktJBo9Z5M=",
|
||||
"lastModified": 1750833544,
|
||||
"narHash": "sha256-e5W27mfPGiM35qr0DjTUzLHP4ET2MbvRc4HJHScw/ko=",
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"rev": "6535bb2a77a3bec73cc5b2d2ff63da8a479e32bd",
|
||||
"rev": "c3940d9ff4d37e965e5841149367234c2aad1ab6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -183,27 +80,11 @@
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1673956053,
|
||||
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
|
||||
"lastModified": 1747046372,
|
||||
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat_2": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1696426674,
|
||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -221,11 +102,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1712014858,
|
||||
"narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=",
|
||||
"lastModified": 1733312601,
|
||||
"narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "9126214d0a59633752a136528f5f3b9aa8565b7d",
|
||||
"rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -239,11 +120,11 @@
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1727826117,
|
||||
"narHash": "sha256-K5ZLCyfO/Zj9mPFldf3iwS6oZStJcU4tSpiXTMYaaL0=",
|
||||
"lastModified": 1749398372,
|
||||
"narHash": "sha256-tYBdgS56eXYaWVW3fsnPQ/nFlgWi/Z2Ymhyu21zVM98=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "3d04084d54bedc3d6b8b736c70ef449225c361b1",
|
||||
"rev": "9305fe4e5c2a6fcf5ba6a3ff155720fbe4076569",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -252,57 +133,6 @@
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1689068808,
|
||||
"narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils_2": {
|
||||
"locked": {
|
||||
"lastModified": 1667395993,
|
||||
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils_3": {
|
||||
"inputs": {
|
||||
"systems": "systems_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1710146030,
|
||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils_4": {
|
||||
"locked": {
|
||||
"lastModified": 1659877975,
|
||||
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
|
||||
@@ -317,17 +147,41 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"git-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": [
|
||||
"devenv",
|
||||
"flake-compat"
|
||||
],
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1749636823,
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"rev": "623c56286de5a3193aa38891a6991b28f9bab056",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"pre-commit-hooks",
|
||||
"git-hooks",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1709087332,
|
||||
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
|
||||
@@ -339,22 +193,6 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"libgit2": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1697646580,
|
||||
"narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=",
|
||||
"owner": "libgit2",
|
||||
"repo": "libgit2",
|
||||
"rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "libgit2",
|
||||
"repo": "libgit2",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"mk-shell-bin": {
|
||||
"locked": {
|
||||
"lastModified": 1677004959,
|
||||
@@ -371,142 +209,62 @@
|
||||
}
|
||||
},
|
||||
"nix": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"cachix",
|
||||
"devenv",
|
||||
"cachix",
|
||||
"devenv",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-regression": "nixpkgs-regression"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1712911606,
|
||||
"narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=",
|
||||
"owner": "domenkozar",
|
||||
"repo": "nix",
|
||||
"rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "domenkozar",
|
||||
"ref": "devenv-2.21",
|
||||
"repo": "nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-github-actions": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"cachix",
|
||||
"devenv",
|
||||
"cachix",
|
||||
"devenv",
|
||||
"poetry2nix",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1688870561,
|
||||
"narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nix-github-actions",
|
||||
"rev": "165b1650b753316aa7f1787f3005a8d2da0f5301",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "nix-github-actions",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix2container": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils_3",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1729339656,
|
||||
"narHash": "sha256-smV7HQ/OqZeRguQxNjsb3uQDwm0p6zKDbSDbPCav/oY=",
|
||||
"owner": "nlewo",
|
||||
"repo": "nix2container",
|
||||
"rev": "cc96df7c3747c61c584d757cfc083922b4f4b33e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nlewo",
|
||||
"repo": "nix2container",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix_2": {
|
||||
"inputs": {
|
||||
"flake-compat": [
|
||||
"devenv",
|
||||
"cachix",
|
||||
"devenv",
|
||||
"flake-compat"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"cachix",
|
||||
"devenv",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-regression": "nixpkgs-regression_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1712911606,
|
||||
"narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=",
|
||||
"owner": "domenkozar",
|
||||
"repo": "nix",
|
||||
"rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "domenkozar",
|
||||
"ref": "devenv-2.21",
|
||||
"repo": "nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix_3": {
|
||||
"inputs": {
|
||||
"flake-compat": [
|
||||
"devenv",
|
||||
"flake-compat"
|
||||
],
|
||||
"flake-parts": "flake-parts",
|
||||
"libgit2": "libgit2",
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"nixpkgs-23-11": "nixpkgs-23-11",
|
||||
"nixpkgs-regression": "nixpkgs-regression_3",
|
||||
"pre-commit-hooks": "pre-commit-hooks"
|
||||
"git-hooks-nix": [
|
||||
"devenv",
|
||||
"git-hooks"
|
||||
],
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs-23-11": [
|
||||
"devenv"
|
||||
],
|
||||
"nixpkgs-regression": [
|
||||
"devenv"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1727438425,
|
||||
"narHash": "sha256-X8ES7I1cfNhR9oKp06F6ir4Np70WGZU5sfCOuNBEwMg=",
|
||||
"owner": "domenkozar",
|
||||
"lastModified": 1750117611,
|
||||
"narHash": "sha256-LTwASICtyN3AjzlF9l2ZNAIVZqclio3yRcwwZy3QSJA=",
|
||||
"owner": "cachix",
|
||||
"repo": "nix",
|
||||
"rev": "f6c5ae4c1b2e411e6b1e6a8181cc84363d6a7546",
|
||||
"rev": "9e4fc95c388e2223d47da865503dee20d179776a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "domenkozar",
|
||||
"ref": "devenv-2.24",
|
||||
"owner": "cachix",
|
||||
"ref": "devenv-2.30",
|
||||
"repo": "nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix2container": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1749158376,
|
||||
"narHash": "sha256-uirStFNxauh0lxzBowcp28X+Sq7JgsBIDnbwbAfZwf8=",
|
||||
"owner": "nlewo",
|
||||
"repo": "nix2container",
|
||||
"rev": "0f8974c58755dba441df03598eefd1e1cd50e341",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nlewo",
|
||||
"repo": "nix2container",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixgl": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils_4",
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
@@ -527,135 +285,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1692808169,
|
||||
"narHash": "sha256-x9Opq06rIiwdwGeK2Ykj69dNc2IvUH1fY55Wm7atwrE=",
|
||||
"lastModified": 1747179050,
|
||||
"narHash": "sha256-qhFMmDkeJX9KJwr5H32f1r7Prs7XbQWtO0h3V0a0rFY=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "9201b5ff357e781bf014d0330d18555695df7ba8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-23-11": {
|
||||
"locked": {
|
||||
"lastModified": 1717159533,
|
||||
"narHash": "sha256-oamiKNfr2MS6yH64rUn99mIZjc45nGJlj9eGth/3Xuw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"lastModified": 1727825735,
|
||||
"narHash": "sha256-0xHYkMkeLVQAMa7gvkddbPqpxph+hDzdu1XdGPJR+Os=",
|
||||
"type": "tarball",
|
||||
"url": "https://github.com/NixOS/nixpkgs/archive/fb192fec7cc7a4c26d51779e9bab07ce6fa5597a.tar.gz"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
"url": "https://github.com/NixOS/nixpkgs/archive/fb192fec7cc7a4c26d51779e9bab07ce6fa5597a.tar.gz"
|
||||
}
|
||||
},
|
||||
"nixpkgs-regression": {
|
||||
"locked": {
|
||||
"lastModified": 1643052045,
|
||||
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-regression_2": {
|
||||
"locked": {
|
||||
"lastModified": 1643052045,
|
||||
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-regression_3": {
|
||||
"locked": {
|
||||
"lastModified": 1643052045,
|
||||
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1720386169,
|
||||
"narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "194846768975b7ad2c4988bdb82572c00222c0d7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-24.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1717432640,
|
||||
"narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "88269ab3044128b7c2f4c7d68448b2fb50456870",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "release-24.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1729880355,
|
||||
"narHash": "sha256-RP+OQ6koQQLX5nw0NmcDrzvGL8HDLnyXt/jHhL1jwjM=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "18536bf04cd71abd345f9579158841376fdd0c5a",
|
||||
"rev": "adaa24fbf46737f3f1b5497bf64bae750f82942e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -665,93 +299,34 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"poetry2nix": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nix-github-actions": "nix-github-actions",
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"cachix",
|
||||
"devenv",
|
||||
"cachix",
|
||||
"devenv",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"lastModified": 1692876271,
|
||||
"narHash": "sha256-IXfZEkI0Mal5y1jr6IRWMqK8GW2/f28xJenZIPQqkY0=",
|
||||
"lastModified": 1748740939,
|
||||
"narHash": "sha256-rQaysilft1aVMwF14xIdGS3sj1yHlI6oKQNBRTF40cc=",
|
||||
"owner": "nix-community",
|
||||
"repo": "poetry2nix",
|
||||
"rev": "d5006be9c2c2417dafb2e2e5034d83fabd207ee3",
|
||||
"repo": "nixpkgs.lib",
|
||||
"rev": "656a64127e9d791a334452c6b6606d17539476e2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "poetry2nix",
|
||||
"repo": "nixpkgs.lib",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pre-commit-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": [
|
||||
"devenv",
|
||||
"nix"
|
||||
],
|
||||
"flake-utils": "flake-utils_2",
|
||||
"gitignore": [
|
||||
"devenv",
|
||||
"nix"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"nix",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-stable": [
|
||||
"devenv",
|
||||
"nix",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1712897695,
|
||||
"narHash": "sha256-nMirxrGteNAl9sWiOhoN5tIHyjBbVi5e2tgZUgZlK3Y=",
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"rev": "40e6053ecb65fcbf12863338a6dcefb3f55f1bf8",
|
||||
"lastModified": 1750741721,
|
||||
"narHash": "sha256-Z0djmTa1YmnGMfE9jEe05oO4zggjDmxOGKwt844bUhE=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "4b1164c3215f018c4442463a27689d973cffd750",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pre-commit-hooks_2": {
|
||||
"inputs": {
|
||||
"flake-compat": [
|
||||
"devenv",
|
||||
"flake-compat"
|
||||
],
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-stable": "nixpkgs-stable"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1726745158,
|
||||
"narHash": "sha256-D5AegvGoEjt4rkKedmxlSEmC+nNLMBPWFxvmYnVLhjk=",
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"rev": "4e743a6920eab45e8ba0fbe49dc459f1423a4b74",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
@@ -763,17 +338,17 @@
|
||||
"mk-shell-bin": "mk-shell-bin",
|
||||
"nix2container": "nix2container",
|
||||
"nixgl": "nixgl",
|
||||
"nixpkgs": "nixpkgs_3"
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
}
|
||||
},
|
||||
"rust-analyzer-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1730197931,
|
||||
"narHash": "sha256-MjYc80pHGrD6TYMHHpXniCW0egVyHiDR23xAh7MN7Ww=",
|
||||
"lastModified": 1750788942,
|
||||
"narHash": "sha256-bBSdUlEw/7xh66rtMEDg39xQUNN2VaDJIbRPIwhpFYk=",
|
||||
"owner": "rust-lang",
|
||||
"repo": "rust-analyzer",
|
||||
"rev": "468b5cd43023d9771996b06ab7215997895a6059",
|
||||
"rev": "c68c8a81a7d2979778ae1e8ba024beacb4fff6b6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -782,36 +357,6 @@
|
||||
"repo": "rust-analyzer",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_2": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
|
||||
@@ -78,7 +78,6 @@
|
||||
cacert
|
||||
])
|
||||
++ lib.optionals pkgs.stdenv.isLinux (with pkgs; [
|
||||
appimagekit
|
||||
atk
|
||||
cairo
|
||||
dbus
|
||||
@@ -122,13 +121,15 @@
|
||||
languages.javascript = {
|
||||
enable = true;
|
||||
corepack.enable = true;
|
||||
pnpm.enable = true;
|
||||
npm.enable = true;
|
||||
};
|
||||
|
||||
languages.rust = {
|
||||
enable = true;
|
||||
toolchain = fenixpkgs.fromToolchainName {
|
||||
name = rust_toolchain.toolchain.channel;
|
||||
sha256 = "sha256-VZZnlyP69+Y3crrLHQyJirqlHrTtGTsyiSnZB8jEvVo=";
|
||||
sha256 = "sha256-yMuSb5eQPO/bHv+Bcf/US8LVMbf/G/0MSfiPwBhiPpk=";
|
||||
};
|
||||
components = rust_toolchain.toolchain.components;
|
||||
};
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"@tauri-apps/api": "^2.0.2",
|
||||
"@tauri-apps/plugin-dialog": "^2.0.0",
|
||||
"@tauri-apps/plugin-fs": "^2.0.0",
|
||||
"@tauri-apps/plugin-http": "^2.5.0",
|
||||
"@tauri-apps/plugin-os": "^2.0.0",
|
||||
"@tauri-apps/plugin-shell": "^2.0.0",
|
||||
"@tauri-apps/plugin-store": "^2.0.0",
|
||||
|
||||
@@ -7,8 +7,13 @@
|
||||
|
||||
## Websocket (server) status
|
||||
|
||||
websocket-connecting = يتم التوصيل بالسيرفر
|
||||
websocket-connection_lost = انقطع الاتصال بالسيرفر. يتم إعادة التوصيل...
|
||||
websocket-connecting = جاري التحميل
|
||||
websocket-connection_lost = تعطل الخادم!
|
||||
websocket-connection_lost-desc = يبدو أن خادم SlimeVR تعطل. تحقق من السجلات وأعد تشغيل البرنامج
|
||||
websocket-timedout = تعذر الاتصال بالخادم
|
||||
websocket-timedout-desc = يبدو أن خادم SlimeVR قد تعطل أو انتهت مهلته. تحقق من السجلات وأعد تشغيل البرنامج
|
||||
websocket-error-close = الخروج من SlimeVR
|
||||
websocket-error-logs = افتح مجلد السجلات
|
||||
|
||||
## Update notification
|
||||
|
||||
@@ -49,12 +54,72 @@ body_part-LEFT_HAND = اليد اليسرى
|
||||
body_part-LEFT_UPPER_LEG = الفخذ الأيسر
|
||||
body_part-LEFT_LOWER_LEG = الكاحل الأيسر
|
||||
body_part-LEFT_FOOT = القدم اليسرى
|
||||
body_part-LEFT_THUMB_METACARPAL = عظم مشط الإبهام الأيسر
|
||||
body_part-LEFT_THUMB_PROXIMAL = الإبهام الأيسر القريب
|
||||
body_part-LEFT_THUMB_DISTAL = الإبهام الأيسر البعيد
|
||||
body_part-LEFT_INDEX_PROXIMAL = السبابة اليسرى القريبة
|
||||
body_part-LEFT_INDEX_INTERMEDIATE = السبابة اليسرى المتوسطة
|
||||
body_part-LEFT_INDEX_DISTAL = السبابة اليسرى البعيدة
|
||||
body_part-LEFT_MIDDLE_PROXIMAL = الجزء الأوسط الأيسر القريب
|
||||
body_part-LEFT_MIDDLE_INTERMEDIATE = الوسط الأيسر المتوسط
|
||||
body_part-LEFT_MIDDLE_DISTAL = الجزء الأوسط الأيسر البعيد
|
||||
body_part-LEFT_RING_PROXIMAL = الحلقة اليسرى القريبة
|
||||
body_part-LEFT_RING_INTERMEDIATE = الحلقة اليسرى المتوسطة
|
||||
body_part-LEFT_RING_DISTAL = الحلقة اليسرى البعيدة
|
||||
body_part-LEFT_LITTLE_PROXIMAL = الجزء الأيسر الصغير القريب
|
||||
body_part-LEFT_LITTLE_INTERMEDIATE = اليسار الصغير المتوسط
|
||||
body_part-LEFT_LITTLE_DISTAL = الجزء البعيد الأيسر الصغير
|
||||
body_part-RIGHT_THUMB_METACARPAL = مشط الإبهام الأيمن
|
||||
body_part-RIGHT_THUMB_PROXIMAL = الإبهام الأيمن القريب
|
||||
body_part-RIGHT_THUMB_DISTAL = الإبهام الأيمن البعيد
|
||||
body_part-RIGHT_INDEX_PROXIMAL = السبابة اليمنى القريبة
|
||||
body_part-RIGHT_INDEX_INTERMEDIATE = السبابة اليمنى المتوسطة
|
||||
body_part-RIGHT_INDEX_DISTAL = السبابة اليمنى البعيدة
|
||||
body_part-RIGHT_MIDDLE_PROXIMAL = منتصف اليمين القريب
|
||||
body_part-RIGHT_MIDDLE_INTERMEDIATE = الوسط الأيمن المتوسط
|
||||
body_part-RIGHT_MIDDLE_DISTAL = منتصف اليمين البعيد
|
||||
body_part-RIGHT_RING_PROXIMAL = الحلقة اليمنى القريبة
|
||||
body_part-RIGHT_RING_INTERMEDIATE = الحلقة اليمنى المتوسطة
|
||||
body_part-RIGHT_RING_DISTAL = الحلقة اليمنى البعيدة
|
||||
body_part-RIGHT_LITTLE_PROXIMAL = الجزء القريب الأيمن الصغير
|
||||
body_part-RIGHT_LITTLE_INTERMEDIATE = المتوسط الصغير الأيمن
|
||||
body_part-RIGHT_LITTLE_DISTAL = الجزء البعيد الصغير الأيمن
|
||||
|
||||
## BoardType
|
||||
|
||||
board_type-UNKNOWN = مجهول
|
||||
board_type-NODEMCU = NodeMCU
|
||||
board_type-CUSTOM = لوحة مخصصة
|
||||
board_type-WROOM32 = WROOM32
|
||||
board_type-WEMOSD1MINI = Wemos D1 Mini
|
||||
board_type-TTGO_TBASE = TTGO T-Base
|
||||
board_type-ESP01 = ESP-01
|
||||
board_type-SLIMEVR = سلايم في آر
|
||||
board_type-LOLIN_C3_MINI = Lolin C3 Mini
|
||||
board_type-BEETLE32C3 = Beetle ESP32-C3
|
||||
board_type-ESP32C3DEVKITM1 = Espressif ESP32-C3 DevKitM-1
|
||||
board_type-OWOTRACK = owoTrack
|
||||
board_type-WRANGLER = رانجلر جويكونز
|
||||
board_type-MOCOPI = سوني موكوبي (Mocopi)
|
||||
board_type-WEMOSWROOM02 = Wemos Wroom-02 D1 Mini
|
||||
board_type-XIAO_ESP32C3 = Seeed Studio XIAO ESP32C3
|
||||
board_type-HARITORA = Haritora
|
||||
board_type-ESP32C6DEVKITC1 = Espressif ESP32-C6 DevKitC-1
|
||||
board_type-GLOVE_IMU_SLIMEVR_DEV = قفاز SlimeVR تطوير IMU
|
||||
|
||||
## Proportions
|
||||
|
||||
skeleton_bone-NONE = غير محدد
|
||||
skeleton_bone-HEAD = إمالة الرأس
|
||||
skeleton_bone-HEAD-desc =
|
||||
هذه هي المسافة من سماعة الرأس إلى منتصف رأسك.
|
||||
لضبط المسافة ، هز رأسك من اليسار إلى اليمين كما لو كنت لا توافق وقم بتعديله
|
||||
حتى أي حركة في أجهزة التتبع الأخرى لا تكاد تذكر.
|
||||
skeleton_bone-NECK = طول العنق
|
||||
skeleton_bone-NECK-desc =
|
||||
هذه هي المسافة من منتصف رأسك إلى قاعدة رقبتك.
|
||||
لضبطها ، حرك رأسك لأعلى ولأسفل كما لو كنت تومئ رأسك أو تميل رأسك
|
||||
إلى اليسار واليمين وقم بتعديله حتى تصبح أي حركة في أجهزة التتبع الأخرى ضئيلة.
|
||||
skeleton_bone-torso_group = طول الجذع
|
||||
skeleton_bone-UPPER_CHEST = طول أعلى الصدر
|
||||
skeleton_bone-CHEST_OFFSET = درجة تشريد الصدر
|
||||
@@ -81,6 +146,14 @@ skeleton_bone-ELBOW_OFFSET = درجة تشريد الكوع
|
||||
## Tracker reset buttons
|
||||
|
||||
reset-reset_all = إعادة تعيين جميع النسب
|
||||
reset-reset_all_warning-v2 =
|
||||
<b>تحذير:</b> ستتم إعادة تعيين النسب إلى الإعدادات الافتراضية التي تم تحجيمها إلى الارتفاع الذي تم تكوينه.
|
||||
هل أنت متأكد من أنك تريد القيام بذلك؟
|
||||
reset-reset_all_warning-reset = إعادة تعيين النسب
|
||||
reset-reset_all_warning-cancel = إلغاء
|
||||
reset-reset_all_warning_default-v2 =
|
||||
<b>تحذير:</b> لم يتم ضبط طولك، وسيتم إعادة تعيين نسبك إلى الإعدادات الافتراضية مع الارتفاع الافتراضي.
|
||||
هل أنت متأكد من أنك تريد القيام بذلك؟
|
||||
reset-full = اعاده تعيين
|
||||
reset-mounting = إعادة تعيين التركيب
|
||||
reset-yaw = إعادة تعيين الانعراج
|
||||
@@ -142,9 +215,12 @@ widget-developer_mode-more_info = المزيد
|
||||
## Widget: IMU Visualizer
|
||||
|
||||
widget-imu_visualizer = دوران
|
||||
widget-imu_visualizer-preview = عرض
|
||||
widget-imu_visualizer-hide = إخفاء
|
||||
widget-imu_visualizer-rotation_raw = صافي
|
||||
widget-imu_visualizer-rotation_preview = عرض مسبق
|
||||
widget-imu_visualizer-rotation_hide = إخفاء
|
||||
widget-imu_visualizer-acceleration = التسارع
|
||||
widget-imu_visualizer-position = الموضع
|
||||
|
||||
## Widget: Skeleton Visualizer
|
||||
|
||||
@@ -196,9 +272,17 @@ tracker-infos-url = عنوان URL لجهاز التعقب
|
||||
tracker-infos-version = إصدار البرنامج الثابت
|
||||
tracker-infos-hardware_rev = مراجعة الأجهزة
|
||||
tracker-infos-hardware_identifier = معرف الجهاز
|
||||
tracker-infos-data_support = دعم البيانات
|
||||
tracker-infos-imu = مستشعر IMU
|
||||
tracker-infos-board_type = اللوحة الرئيسية
|
||||
tracker-infos-network_version = نسخة البروتوكول
|
||||
tracker-infos-magnetometer = المقياس المغناطيسي
|
||||
tracker-infos-magnetometer-status-v1 =
|
||||
{ $status ->
|
||||
[DISABLED] ملغي
|
||||
[ENABLED] فعال
|
||||
*[NOT_SUPPORTED] غير مدعوم
|
||||
}
|
||||
|
||||
## Tracker settings
|
||||
|
||||
@@ -213,14 +297,27 @@ tracker-settings-mounting_section-edit = تعديل التركيب
|
||||
tracker-settings-drift_compensation_section = السماح بتعويض الانجراف
|
||||
tracker-settings-drift_compensation_section-description = هل يجب أن يعوض جهاز التعقب عن انحرافه عند تمكين تعويض الانجراف؟
|
||||
tracker-settings-drift_compensation_section-edit = السماح بتعويض الانجراف
|
||||
tracker-settings-use_mag = اسمح بالمقياس المغناطيسي على هذا المتتبع
|
||||
# Multiline!
|
||||
tracker-settings-use_mag-description =
|
||||
هل يجب أن يستخدم هذا المتتبع مقياس المغناطيسية لتقليل الانجراف عند السماح باستخدام المقياس المغناطيسي؟ <b>من فضلا لا تغلق جهاز التتبع الخاص بك أثناء تبديل هذا!</b>
|
||||
|
||||
تحتاج إلى السماح باستخدام مقياس المغناطيسية أولا ، <magSetting>انقر هنا للذهاب إلى الإعداد</magSetting>.
|
||||
tracker-settings-use_mag-label = السماح بالمقياس المغناطيسي
|
||||
# The .<name> means it's an attribute and it's related to the top key.
|
||||
# In this case that is the settings for the assignment section.
|
||||
tracker-settings-name_section = اسم جهاز التعقب
|
||||
tracker-settings-name_section-description = أعطها لقب لطيف :)
|
||||
tracker-settings-name_section-placeholder = ساق نايتي بيست اليسرى
|
||||
tracker-settings-name_section-label = اسم جهاز التعقب
|
||||
tracker-settings-forget = انسي جهاز التعقب
|
||||
tracker-settings-forget-description = يزيل جهاز التعقب من خادم SlimeVR ويمنعه من الاتصال به حتى يتم إعادة تشغيل الخادم. لن تضيع تكوين جهاز التعقب.
|
||||
tracker-settings-forget-label = ننسى جهاز التعقب
|
||||
tracker-settings-update-unavailable = لا يمكن تحديثه (اصنعها بنفسك)
|
||||
tracker-settings-update-up_to_date = حديث
|
||||
tracker-settings-update-available = { $versionName } متاح الآن
|
||||
tracker-settings-update = التحديث الآن
|
||||
tracker-settings-update-title = إصدار البرنامج الثابت
|
||||
|
||||
## Tracker part card info
|
||||
|
||||
@@ -296,6 +393,9 @@ settings-sidebar-utils = الأدوات المساعدة
|
||||
settings-sidebar-serial = وحدة التحكم التسلسلية
|
||||
settings-sidebar-appearance = مظهر
|
||||
settings-sidebar-notifications = إشعارات
|
||||
settings-sidebar-behavior = سلوك
|
||||
settings-sidebar-firmware-tool = أداة برامج الجهاز المصنوع بنفسك
|
||||
settings-sidebar-advanced = متقدم
|
||||
|
||||
## SteamVR settings
|
||||
|
||||
@@ -310,10 +410,14 @@ settings-general-steamvr-description =
|
||||
مفيد فقط للألعاب أو التطبيقات التي تدعم أجهزة تعقب معينة.
|
||||
settings-general-steamvr-trackers-waist = الخصر
|
||||
settings-general-steamvr-trackers-chest = الصدر
|
||||
settings-general-steamvr-trackers-feet = القدمين
|
||||
settings-general-steamvr-trackers-knees = الركبتين
|
||||
settings-general-steamvr-trackers-elbows = الكوعين
|
||||
settings-general-steamvr-trackers-hands = اليدين
|
||||
settings-general-steamvr-trackers-left_foot = القدم اليسرى
|
||||
settings-general-steamvr-trackers-right_foot = القدم اليمنى
|
||||
settings-general-steamvr-trackers-left_knee = الركبة اليسرى
|
||||
settings-general-steamvr-trackers-right_knee = الركبة اليمنى
|
||||
settings-general-steamvr-trackers-left_elbow = الكوع الأيسر
|
||||
settings-general-steamvr-trackers-right_elbow = الكوع الأيمن
|
||||
settings-general-steamvr-trackers-left_hand = اليد اليسرى
|
||||
settings-general-steamvr-trackers-right_hand = اليد اليمنى
|
||||
settings-general-steamvr-trackers-tracker_toggling = تعيين جهاز التعقب تلقائي
|
||||
settings-general-steamvr-trackers-tracker_toggling-description = يتعامل تلقائيا مع تبديل أجهزة تعقب SteamVR أو إيقاف تشغيلها اعتمادا على مهام التعقب الحالية
|
||||
settings-general-steamvr-trackers-tracker_toggling-label = تعيين جهاز التعقب التلقائي
|
||||
@@ -339,14 +443,39 @@ settings-general-tracker_mechanics-filtering-type-smoothing-description = ينع
|
||||
settings-general-tracker_mechanics-filtering-type-prediction = التنبؤ
|
||||
settings-general-tracker_mechanics-filtering-type-prediction-description = يقلل من وقت الإستجابة ويجعل الحركات أكثر سرعة ، ولكنه قد يزيد من التوتر.
|
||||
settings-general-tracker_mechanics-filtering-amount = المبلغ
|
||||
settings-general-tracker_mechanics-yaw-reset-smooth-time = إعادة ضبط الانعراج على نحو سلس (يتعطل التنعيم 0 ثوان)
|
||||
settings-general-tracker_mechanics-drift_compensation = تعويض الانجراف
|
||||
# This cares about multilines
|
||||
settings-general-tracker_mechanics-drift_compensation-description =
|
||||
يعوض عن انجراف انعراج وحدة IMU بتطبيق دوران عكسي.
|
||||
قم بتغيير كمية التعويض وعدد عمليات إعادة التعيين التي يتم أخذها في الاعتبار.
|
||||
settings-general-tracker_mechanics-drift_compensation-enabled-label = تعويض الانجراف
|
||||
settings-general-tracker_mechanics-drift_compensation-prediction = التنبؤ بتعويض الانحراف
|
||||
# This cares about multilines
|
||||
settings-general-tracker_mechanics-drift_compensation-prediction-description =
|
||||
يتنبأ بتعويض انحراف الانعراج خارج النطاق المقاس مسبقا.
|
||||
قم بتمكين هذا إذا كانت أجهزة التتبع تدور باستمرار على محور الانعراج.
|
||||
settings-general-tracker_mechanics-drift_compensation-prediction-label = التنبؤ بتعويض الانحراف
|
||||
settings-general-tracker_mechanics-drift_compensation_warning =
|
||||
<b>تحذير:</b> استخدم تعويض الانحراف فقط إذا كنت بحاجة إلى إعادة الضبط
|
||||
في كثير من الأحيان (كل ~ 5-10 دقائق).
|
||||
|
||||
تتضمن بعض وحدات IMU المعرضة لإعادة الضبط المتكرر ما يلي:
|
||||
Joy-Cons و owoTrack و MPUs (بدون برامج ثابتة حديثة).
|
||||
settings-general-tracker_mechanics-drift_compensation_warning-cancel = إلغاء
|
||||
settings-general-tracker_mechanics-drift_compensation_warning-done = أتفهم
|
||||
settings-general-tracker_mechanics-drift_compensation-amount-label = مبلغ التعويض
|
||||
settings-general-tracker_mechanics-drift_compensation-max_resets-label = استخدام ما يصل إلى x عمليات إعادة التعيين الأخيرة
|
||||
settings-general-tracker_mechanics-save_mounting_reset = حفظ معايرة إعادة ضبط التركيب التلقائي
|
||||
settings-general-tracker_mechanics-save_mounting_reset-description =
|
||||
يحفظ معايرة إعادة تعيين التثبيت التلقائي لأجهزة التتبع بين عمليات إعادة التشغيل. مفيد
|
||||
عند ارتداء بدلة حيث لا تتحرك المتتبعات بين الجلسات. <b>لا ينصح به للمستخدمين العاديين!</b>
|
||||
settings-general-tracker_mechanics-save_mounting_reset-enabled-label = حفظ إعادة تعيين التركيب
|
||||
settings-general-tracker_mechanics-use_mag_on_all_trackers = استخدم المقياس المغناطيسي على جميع أجهزة تتبع IMU التي تدعمه
|
||||
settings-general-tracker_mechanics-use_mag_on_all_trackers-description =
|
||||
يستخدم المقياس المغناطيسي على جميع أجهزة التعقب التي تحتوي على برامج ثابتة متوافقة له ، مما يقلل من الانحراف في البيئات المغناطيسية المستقرة.
|
||||
يمكن تعطيله لكل جهاز تعقب في إعدادات التعقب. <b>من فضلك لا تغلق أيا من أجهزة التعقب أثناء تبديل هذا!</b>
|
||||
settings-general-tracker_mechanics-use_mag_on_all_trackers-label = استخدم المقياس المغناطيسي على أجهزة التعقب
|
||||
|
||||
## FK/Tracking settings
|
||||
|
||||
@@ -370,9 +499,17 @@ settings-general-fk_settings-leg_tweak-foot_plant-description = تثبيت اص
|
||||
settings-general-fk_settings-leg_fk = تعقب الساق
|
||||
settings-general-fk_settings-leg_fk-reset_mounting_feet-description = تمكين إعادة ضبط تركيب القدمين عن طريق المشي على رؤوس الأصابع.
|
||||
settings-general-fk_settings-leg_fk-reset_mounting_feet = إعادة تعيين تركيب القدمين
|
||||
settings-general-fk_settings-enforce_joint_constraints = حدود الهيكل العظمي
|
||||
settings-general-fk_settings-enforce_joint_constraints-enforce_constraints = فرض القيود
|
||||
settings-general-fk_settings-enforce_joint_constraints-enforce_constraints-description = منع المفاصل من الدوران إلى ما بعد الحد الأقصى
|
||||
settings-general-fk_settings-enforce_joint_constraints-correct_constraints = التصحيح مع قيود
|
||||
settings-general-fk_settings-enforce_joint_constraints-correct_constraints-description = تصحيح دوران المفاصل عندما تتجاوز حدودها
|
||||
settings-general-fk_settings-arm_fk = تعقب الذراع
|
||||
settings-general-fk_settings-arm_fk-description = تغيير طريقة تعقب الذراعين.
|
||||
settings-general-fk_settings-arm_fk-force_arms = إجبار الذراعين من ايتش أم دي
|
||||
settings-general-fk_settings-reset_settings = إعادة تعيين الإعدادات
|
||||
settings-general-fk_settings-reset_settings-reset_hmd_pitch-description = أعد تعيين درجة جهاز الرأس (الدوران الرأسي) عند إجراء إعادة تعيين كاملة. مفيد في حالة ارتداء جهاز الرأس على الجبهة ل VTubing أو mocap. لا تقم بتمكين لVR.
|
||||
settings-general-fk_settings-reset_settings-reset_hmd_pitch = إعادة تعيين درجة جهاز الرأس
|
||||
settings-general-fk_settings-arm_fk-reset_mode-description = قم بتغيير وضع الذراع المتوقع لإعادة ضبط المتصاعد.
|
||||
settings-general-fk_settings-arm_fk-back = العودة
|
||||
settings-general-fk_settings-arm_fk-back-description = الوضع الافتراضي، مع وضع الذراعين العلويين إلى الخلف والساعدين للأمام.
|
||||
@@ -398,9 +535,6 @@ settings-general-fk_settings-skeleton_settings-interp_knee_tracker_ankle = مت
|
||||
settings-general-fk_settings-skeleton_settings-interp_knee_ankle = متوسط انحراف الركبتين ولفة مع الكاحلين
|
||||
settings-general-fk_settings-self_localization-title = وضع Mocap
|
||||
settings-general-fk_settings-self_localization-description = يسمح وضع Mocap للهيكل العظمي بتعقب موضعه تقريبا بدون سماعة رأس أو أجهزة تعقب أخرى. لاحظ أن هذا يتطلب أجهزة تعقب القدمين والرأس للعمل ولا تزال تجريبية.
|
||||
settings-general-fk_settings-vive_emulation-title = محاكاة فايف
|
||||
settings-general-fk_settings-vive_emulation-description = محاكاة مشاكل تعقب الخصر التي تعاني منها أجهزة تعقب فايف. هذه مزحة وتجعل التتبع أسوأ.
|
||||
settings-general-fk_settings-vive_emulation-label = تمكين محاكاة فايف
|
||||
|
||||
## Gesture control settings (tracker tapping)
|
||||
|
||||
@@ -449,6 +583,9 @@ settings-general-interface-dev_mode = وضع المطوّر
|
||||
settings-general-interface-dev_mode-description = يمكن أن يكون هذا الوضع مفيدًا إذا كنت بحاجة إلى بيانات متعمقة أو للتفاعل مع أجهزة التعقب المتصلة على مستوى أكثر تقدمًا.
|
||||
settings-general-interface-dev_mode-label = وضع المطوّر
|
||||
settings-general-interface-theme = موضوع اللون
|
||||
settings-general-interface-show-navbar-onboarding = إظهار "{ navbar-onboarding }" على قائمة التنقل
|
||||
settings-general-interface-show-navbar-onboarding-description = يغير هذا إذا ظهر الزر "{ navbar-onboarding }" على قائمة التنقل.
|
||||
settings-general-interface-show-navbar-onboarding-label = إظهار "{ navbar-onboarding }"
|
||||
settings-general-interface-lang = اختر اللغة
|
||||
settings-general-interface-lang-description = قم بتغيير اللغة الافتراضية التي تريد استخدامها.
|
||||
settings-general-interface-lang-placeholder = اختر اللغة التي تريد استخدامها
|
||||
@@ -460,6 +597,9 @@ settings-interface-appearance-font-os_font = خط نظام التشغيل
|
||||
settings-interface-appearance-font-slime_font = الخط الافتراضي
|
||||
settings-interface-appearance-font_size = قياس الخط الأساسي
|
||||
settings-interface-appearance-font_size-description = يؤثر هذا على حجم خط الواجهة بأكملها باستثناء لوحة الإعدادات هذه.
|
||||
settings-interface-appearance-decorations = استخدم الزخارف الأصلية للنظام
|
||||
settings-interface-appearance-decorations-description = لن يؤدي هذا إلى عرض الشريط الأعلى للواجهة وسيستخدم نظام التشغيل بدلا من ذلك.
|
||||
settings-interface-appearance-decorations-label = استخدم الزخارف الأصلية
|
||||
|
||||
## Notification settings
|
||||
|
||||
@@ -474,9 +614,29 @@ settings-general-interface-feedback_sound-volume = حجم صوت ردود الف
|
||||
settings-general-interface-connected_trackers_warning = تحذير عن أجهزة التعقب المتصلة
|
||||
settings-general-interface-connected_trackers_warning-description = سيعرض هذا الخيار نافذة كل مرة تحاول فيها الخروج من SlimeVR أثناء وجود جهاز أو أكثر من أجهزة التعقب المتصلة. سيذكرك بإيقاف تشغيل أجهزة التعقب عند الانتهاء للحفاظ على عمر البطارية.
|
||||
settings-general-interface-connected_trackers_warning-label = تحذير عن أجهزة التعقب المتصلة عند الخروج
|
||||
|
||||
## Behavior settings
|
||||
|
||||
settings-interface-behavior = السلوك
|
||||
settings-general-interface-dev_mode = وضع المطوّر
|
||||
settings-general-interface-dev_mode-description = يمكن أن يكون هذا الوضع مفيدًا إذا كنت بحاجة إلى بيانات متعمقة أو للتفاعل مع أجهزة التعقب المتصلة على مستوى أكثر تقدمًا.
|
||||
settings-general-interface-dev_mode-label = وضع المطوّر
|
||||
settings-general-interface-use_tray = تصغير إلى علبة النظام
|
||||
settings-general-interface-use_tray-description = يتيح لك إغلاق النافذة دون إغلاق خادم SlimeVR حتى تتمكن من الاستمرار في استخدامه دون إزعاجك من واجهة المستخدم الرسومية.
|
||||
settings-general-interface-use_tray-label = تصغير إلى علبة النظام
|
||||
settings-general-interface-discord_presence = مشاركة النشاط على Discord
|
||||
settings-general-interface-discord_presence-description = يخبر عميل Discord الخاص بك أنك تستخدم SlimeVR جنبا إلى جنب مع عدد أجهزة تعقب IMU التي تستخدمها.
|
||||
settings-general-interface-discord_presence-label = مشاركة النشاط على Discord
|
||||
settings-general-interface-discord_presence-message =
|
||||
{ $amount ->
|
||||
[0] صفر
|
||||
[zero] صفر
|
||||
[one] واحد
|
||||
[two] اثنان
|
||||
[few] قليل
|
||||
[many] كثيرة
|
||||
*[other] أخرى
|
||||
}
|
||||
|
||||
## Serial settings
|
||||
|
||||
@@ -528,15 +688,10 @@ settings-osc-router-network-address-placeholder = عنوان آي بي في 4
|
||||
## OSC VRChat settings
|
||||
|
||||
settings-osc-vrchat = أجهزة تعقب "في ار تشات أوه أس سي"
|
||||
# This cares about multilines
|
||||
settings-osc-vrchat-description =
|
||||
قم بتغيير الإعدادات الخاصة ب في ار تشات لتلقي بيانات ايتش أم دي وإرسالها
|
||||
بيانات أجهزة تعقب لتعقب الجسم (يعمل على كوست مستقل).
|
||||
settings-osc-vrchat-enable = تمكين
|
||||
settings-osc-vrchat-enable-description = بتبديل إرسال واستقبال البيانات.
|
||||
settings-osc-vrchat-enable-label = تمكين
|
||||
settings-osc-vrchat-network = منافذ الشبكة
|
||||
settings-osc-vrchat-network-description = قم بتعيين المنافذ للاستماع وإرسال البيانات إلى في ار تشات
|
||||
settings-osc-vrchat-network-port_in =
|
||||
.label = منفذ الدخول
|
||||
.placeholder = منفذ الدخول (الإفتراضي: 9001)
|
||||
@@ -544,7 +699,6 @@ settings-osc-vrchat-network-port_out =
|
||||
.label = منفذ الخروج
|
||||
.placeholder = منفذ الخروج (الإفتراضي: 9000)
|
||||
settings-osc-vrchat-network-address = عنوان الشبكة
|
||||
settings-osc-vrchat-network-address-description = اختر العنوان الذي تريد إرسال البيانات إلى في ار تشات (تحقق من إعدادات واي فاي على جهازك)
|
||||
settings-osc-vrchat-network-address-placeholder = عنوان آي بي الخاص بفي ار تشات
|
||||
settings-osc-vrchat-network-trackers = أجهزة التعقب
|
||||
settings-osc-vrchat-network-trackers-description = تبديل إرسال أجهزة تتبع محددة عبر أوه أس سي.
|
||||
@@ -577,17 +731,14 @@ settings-osc-vmc-network-address-description = قم بتعيين العنوان
|
||||
settings-osc-vmc-network-address-placeholder = عنوان آي بي في 4
|
||||
settings-osc-vmc-vrm = نموذج في ار إم
|
||||
settings-osc-vmc-vrm-description = قم بتحميل نموذج في ار إم للسماح بتركيز الرأس وتمكين توافق أعلى مع تطبيقات الأخرى
|
||||
settings-osc-vmc-vrm-model_unloaded = لم يتم تحميل أي نموذج
|
||||
settings-osc-vmc-vrm-model_loaded =
|
||||
{ $titled ->
|
||||
[true] تحميل النموذج: { $name }
|
||||
*[other] تم تحميل نموذج بدون عنوان
|
||||
}
|
||||
settings-osc-vmc-vrm-file_select = اسحب نموذج وأفلته لاستخدامه أو <u> تصفح </ u>
|
||||
settings-osc-vmc-anchor_hip = ثبت في الوركين
|
||||
settings-osc-vmc-anchor_hip-description = ثبت التعقب في الوركين، هو مفيد إن كنت تيوبنغ جالسًا. في حالة التعطيل، قم بتحميل نموذج في ار إم.
|
||||
settings-osc-vmc-anchor_hip-label = ثبت في الوركين
|
||||
|
||||
## Advanced settings
|
||||
|
||||
|
||||
## Setup/onboarding menu
|
||||
|
||||
onboarding-skip = تخطى الإعداد
|
||||
@@ -697,7 +848,6 @@ onboarding-connect_tracker-next = لقد قمت بتوصيل جميع أجهزة
|
||||
|
||||
onboarding-calibration_tutorial = برنامج تعليم معايرة IMU
|
||||
onboarding-calibration_tutorial-subtitle = سوف يساعد هذا في تقليل الانجراف التعقب!
|
||||
onboarding-calibration_tutorial-description = كل مرة تقوم بتشغيل أجهزة التعقب، يجب أن تستريح للحظة على سطح مستوٍ للمعايرة. لنفعل الشيء نفسه بالنقر فوق الزر "{ onboarding-calibration_tutorial-calibrate }" ، <b>لا تحركها!</b>
|
||||
onboarding-calibration_tutorial-calibrate = وضعت أجهزة التعقب على الطاولة
|
||||
onboarding-calibration_tutorial-status-waiting = بانتظارك
|
||||
onboarding-calibration_tutorial-status-calibrating = جاري المعايرة
|
||||
@@ -809,12 +959,12 @@ onboarding-choose_mounting = ما طريقة معايرة التركيب الم
|
||||
# Multiline text
|
||||
onboarding-choose_mounting-description = اتجاه التركيب يصحح وضع أجهزة التعقب على جسمك.
|
||||
onboarding-choose_mounting-auto_mounting = التركيب التلقائي
|
||||
# Italized text
|
||||
onboarding-choose_mounting-auto_mounting-label = تجريبي
|
||||
# Italicized text
|
||||
onboarding-choose_mounting-auto_mounting-label-v2 = الموصى به
|
||||
onboarding-choose_mounting-auto_mounting-description = سيكتشف هذا تلقائيًا اتجاهات التركيب لجميع أجهزة التعقب من وضعين
|
||||
onboarding-choose_mounting-manual_mounting = التركيب اليدوي
|
||||
# Italized text
|
||||
onboarding-choose_mounting-manual_mounting-label = المستحسن
|
||||
# Italicized text
|
||||
onboarding-choose_mounting-manual_mounting-label-v2 = قد لا تكون الدقة كافية
|
||||
onboarding-choose_mounting-manual_mounting-description = سيسمح لك باختيار اتجاه التثبيت يدويًا لكل جهاز تعقب
|
||||
# Multiline text
|
||||
onboarding-choose_mounting-manual_modal-title =
|
||||
@@ -847,44 +997,19 @@ onboarding-automatic_mounting-mounting_reset-title = إعادة تعيين ال
|
||||
onboarding-automatic_mounting-mounting_reset-step-0 = 1. قرفص في وضع "التزلج" مع ثني ساقيك ، وإمالة الجزء العلوي من جسمك إلى الأمام ، وثني ذراعيك.
|
||||
onboarding-automatic_mounting-mounting_reset-step-1 = 2. اضغط على زر "إعادة تعيين التركيب" وانتظر لمدة 3 ثوان قبل إعادة تعيين دوران تركيب أجهزة التعقب.
|
||||
onboarding-automatic_mounting-preparation-title = التحضير
|
||||
onboarding-automatic_mounting-preparation-step-0 = 1. قف بشكل مستقيم مع ذراعيك على جانبيك.
|
||||
onboarding-automatic_mounting-preparation-step-1 = اضغط على زر "إعادة ضبط" و انتظر لمدة 3 ثوانٍ قبل إعادة تعيين أجهزة التعقب.
|
||||
onboarding-automatic_mounting-put_trackers_on-title = ارتدي أجهزة التعقب
|
||||
onboarding-automatic_mounting-put_trackers_on-description = لمعايرة دوران التركيب، سنستخدم أجهزة التعقب التي قمت بتعيينها. ارتدي جميع أجهزة التعقب، يمكنك معرفة أي منها في المستند على اليمين.
|
||||
onboarding-automatic_mounting-put_trackers_on-next = ارتديت جميع أجهزة التعقب.
|
||||
|
||||
## Tracker proportions method choose
|
||||
|
||||
onboarding-choose_proportions = ما هي طريقة معايرة النسب التي يجب استخدامها؟
|
||||
# Multiline string
|
||||
onboarding-choose_proportions-description-v1 =
|
||||
تستخدم نسب الجسم لمعرفة قياسات جسمك. إنهم مطالبون لحساب مواقع أجهزة التعقب.
|
||||
عندما لا تتطابق نسب جسمك مع تلك المحفوظة ، ستكون دقة التعقب أسوأ وستلاحظ أشياء مثل التزلج أو الانزلاق ، أو أن جسمك لا يتطابق مع صورتك الرمزية جيدا.
|
||||
<b>ما عليك سوى قياس جسمك مرة واحدة!</b> إن لم تكن خاطئة أو تغير جسمك ، فلن تحتاج إلى القيام بها مرة أخرى.
|
||||
onboarding-choose_proportions-auto_proportions = النسب التلقائية
|
||||
# Italized text
|
||||
onboarding-choose_proportions-auto_proportions-subtitle = الموصى به
|
||||
onboarding-choose_proportions-auto_proportions-descriptionv3 =
|
||||
سيؤدي ذلك إلى تخمين نسبك عن طريق تسجيل عينة من تحركاتك وتمريرها عبر خوارزمية.
|
||||
|
||||
<b>يتطلب ذلك توصيل جهاز الواقع الافتراضي (HMD) ب SlimeVR و وضعها على رأسك!</b>
|
||||
onboarding-choose_proportions-manual_proportions = النسب اليدوية
|
||||
# Italized text
|
||||
onboarding-choose_proportions-manual_proportions-subtitle = للمسات الصغيرة
|
||||
onboarding-choose_proportions-manual_proportions-description = سيسمح لك بتعديل النسب يدويًا عن طريق تعديلها مباشرة
|
||||
onboarding-choose_proportions-export = تصدير النسب
|
||||
onboarding-choose_proportions-import = استيراد النسب
|
||||
onboarding-choose_proportions-import-success = تم استيراده
|
||||
onboarding-choose_proportions-import-failed = فشل
|
||||
onboarding-choose_proportions-file_type = ملف نسب الجسم
|
||||
|
||||
## Tracker manual proportions setup
|
||||
## Tracker manual proportions setupa
|
||||
|
||||
onboarding-manual_proportions-back = العودة إلى برنامج تعليم إعادة التعيين
|
||||
onboarding-manual_proportions-title = نسب الجسم اليدوية
|
||||
onboarding-manual_proportions-precision = ضبط الدقة
|
||||
onboarding-manual_proportions-auto = المعايرة التلقائية
|
||||
onboarding-manual_proportions-ratio = اضبط حسب مجموعات النسب
|
||||
onboarding-manual_proportions-fine_tuning_button = ضبط النسب تلقائيا
|
||||
onboarding-manual_proportions-fine_tuning_button-disabled-tooltip = يرجى توصيل سماعة رأس VR لاستخدام الضبط الدقيق التلقائي
|
||||
onboarding-manual_proportions-export = تصدير النسب
|
||||
onboarding-manual_proportions-import = استيراد النسب
|
||||
onboarding-manual_proportions-file_type = ملف نسب الجسم
|
||||
|
||||
## Tracker automatic proportions setup
|
||||
|
||||
@@ -905,21 +1030,33 @@ onboarding-automatic_proportions-requirements-descriptionv2 =
|
||||
يقوم جهاز الواقع الافتراضي بالإبلاغ عن البيانات الموضعية إلى خادم SlimeVR (وهذا يعني عموما تشغيل SteamVR وتوصيله ب SlimeVR باستخدام برنامج تشغيل SteamVR الخاص ب SlimeVR).
|
||||
يعمل التتبع الخاص بك ويمثل تحركاتك بدقة (على سبيل المثال ، لقد أجريت إعادة تعيين كاملة وتتحرك في الاتجاه الصحيح عند الركل, الانحناء, الجلوس, إلخ).
|
||||
onboarding-automatic_proportions-requirements-next = لقد قرأت المتطلبات
|
||||
onboarding-automatic_proportions-check_height-title = تحقق من طولك
|
||||
onboarding-automatic_proportions-check_height-description = نستخدم طولك كأساس لقياساتنا باستخدام ارتفاع HMD كتقريب لطولك الفعلي ، ولكن من الأفضل التحقق مما إذا كانت صحيحة بنفسك!
|
||||
onboarding-automatic_proportions-check_height-title-v3 = قم بقياس ارتفاع سماعة الرأس
|
||||
onboarding-automatic_proportions-check_height-description-v2 = يجب أن يكون ارتفاع سماعة الرأس (HMD) أقل قليلا من طولك الكامل ، حيث تقيس سماعات الرأس ارتفاع عينيك. سيتم استخدام هذا القياس كخط أساس لنسب جسمك.
|
||||
# All the text is in bold!
|
||||
onboarding-automatic_proportions-check_height-calculation_warning = يرجى الضغط على الزر أثناء الوقوف <u>في وضع مستقيم</u> لحساب طولك. لديك 3 ثوان بعد الضغط على الزر!
|
||||
onboarding-automatic_proportions-check_height-calculation_warning-v3 = ابدأ في القياس أثناء الوقوف <u>في وضع مستقيم</u> لقياس طولك. احرص على عدم رفع يديك أعلى من سماعة الرأس ، لأنها قد تؤثر على القياس!
|
||||
onboarding-automatic_proportions-check_height-guardian_tip =
|
||||
إذا كنت تستخدم سماعة رأس VR مستقلة ، فتأكد من تشغيل حدود الحارس /
|
||||
لكي يكون طولك صحيحا!
|
||||
onboarding-automatic_proportions-check_height-fetch_height = أنا واقف!
|
||||
# Context is that the height is unknown
|
||||
onboarding-automatic_proportions-check_height-unknown = مجهول
|
||||
# Shows an element below it
|
||||
onboarding-automatic_proportions-check_height-hmd_height1 = طولك من خلال HMD
|
||||
# Shows an element below it
|
||||
onboarding-automatic_proportions-check_height-height1 = لذا فإن طولك الفعلي هو
|
||||
onboarding-automatic_proportions-check_height-hmd_height2 = ارتفاع سماعة الرأس هو:
|
||||
onboarding-automatic_proportions-check_height-measure-start = ابدأ القياس
|
||||
onboarding-automatic_proportions-check_height-measure-stop = توقف عن القياس
|
||||
onboarding-automatic_proportions-check_height-measure-reset = إعادة محاولة القياس
|
||||
onboarding-automatic_proportions-check_height-next_step = انهم بخير
|
||||
onboarding-automatic_proportions-check_floor_height-title = قم بقياس الارتفاع عن الأرض (اختياري)
|
||||
onboarding-automatic_proportions-check_floor_height-description = في بعض الحالات، قد لا يتم ضبط الارتفاع عن الأرض بشكل صحيح بواسطة سماعة الرأس، مما يتسبب في قياس ارتفاع سماعة الرأس على أنه أعلى مما ينبغي. يمكنك قياس "الارتفاع" عن الأرض لتصحيح ارتفاع سماعة الرأس.
|
||||
# All the text is in bold!
|
||||
onboarding-automatic_proportions-check_floor_height-calculation_warning-v2 = ابدأ بقياس و ضع وحدة التحكم على الأرض لقياس ارتفاعها. إذا كنت متأكدا من صحة الارتفاع عن الأرض ، فيمكنك تخطي هذه الخطوة.
|
||||
# Shows an element below it
|
||||
onboarding-automatic_proportions-check_floor_height-floor_height = الارتفاع عن الأرض هو:
|
||||
onboarding-automatic_proportions-check_floor_height-full_height = طولك الكامل المقدر هو:
|
||||
onboarding-automatic_proportions-check_floor_height-measure-start = ابدأ القياس
|
||||
onboarding-automatic_proportions-check_floor_height-measure-stop = توقف عن القياس
|
||||
onboarding-automatic_proportions-check_floor_height-measure-reset = إعد محاولة القياس
|
||||
onboarding-automatic_proportions-check_floor_height-skip_step = تخطي الخطوة وحفظ
|
||||
onboarding-automatic_proportions-check_floor_height-next_step = استخدم الارتفاع عن الأرض وحفظه
|
||||
onboarding-automatic_proportions-start_recording-title = استعد للتحرك
|
||||
onboarding-automatic_proportions-start_recording-description = سنقوم الآن بتسجيل بعض الوضعيات والحركات المحددة. ستتم مطالبتك بذلك في الشاشة التالية. كن مستعدا للبدء عند الضغط على الزر!
|
||||
onboarding-automatic_proportions-start_recording-next = بدء التسجيل
|
||||
@@ -953,10 +1090,33 @@ onboarding-automatic_proportions-verify_results-redo = إعادة التسجيل
|
||||
onboarding-automatic_proportions-verify_results-confirm = تبدو صحيحة
|
||||
onboarding-automatic_proportions-done-title = تم قياس الجسم و حفظه.
|
||||
onboarding-automatic_proportions-done-description = اكتملت معايرة نسب جسمك!
|
||||
onboarding-automatic_proportions-error_modal =
|
||||
<b>تحذير:</b> تم العثور على خطأ أثناء تقدير النسب!
|
||||
يرجى <docs>التحقق من المستندات</docs> أو الانضمام إلى <discord>Discord</discord> للحصول على المساعدة ^_^
|
||||
onboarding-automatic_proportions-error_modal-v2 =
|
||||
<b>تحذير:</b> حدث خطأ أثناء تقدير النسب!
|
||||
من المحتمل أن تكون هذه مشكلة معايرة التركيب. تأكد من أن التعقب يعمل بشكل صحيح قبل المحاولة مرة أخرى.
|
||||
يرجى <docs>التحقق من التعليمات</docs> أو الانضمام إلى <discord>Discord</discord> للحصول على المساعدة ^_^
|
||||
onboarding-automatic_proportions-error_modal-confirm = مفهوم!
|
||||
onboarding-automatic_proportions-smol_warning =
|
||||
الارتفاع الذي تم تكوينه هو { $height } و هو أصغر من الحد الأدنى للارتفاع المقبول البالغ { $minHeight }.
|
||||
<b>يرجى إعادة القياسات والتأكد من صحتها.</b>
|
||||
onboarding-automatic_proportions-smol_warning-cancel = الرجوع
|
||||
|
||||
## Tracker scaled proportions setup
|
||||
|
||||
onboarding-scaled_proportions-title = النسب المقاسة
|
||||
onboarding-scaled_proportions-description = لكي تعمل أجهزة التعقب SlimeVR ، نحتاج إلى معرفة طول عظامك. ستستخدم نسبة متوسطة وقياسها بناء على طولك.
|
||||
onboarding-scaled_proportions-manual_height-title = تكوين طولك
|
||||
onboarding-scaled_proportions-manual_height-description-v2 = سيتم استخدام هذا الطول كخط أساس لنسب جسمك.
|
||||
onboarding-scaled_proportions-manual_height-missing_steamvr = SteamVR غير متصل حاليا ب SlimeVR ، لذلك لا يمكن أن تستند القياسات إلى سماعة الرأس الخاصة بك. <b>تابع على مسؤوليتك الخاصة أو تحقق من المستندات!</b>
|
||||
onboarding-scaled_proportions-manual_height-height-v2 = طولك الكامل هو
|
||||
onboarding-scaled_proportions-manual_height-estimated_height = الارتفاع المقدر لسماعة الرأس هو:
|
||||
onboarding-scaled_proportions-manual_height-next_step = المتابعة والحفظ
|
||||
onboarding-scaled_proportions-manual_height-warning-no_hmd = وصّل سماعة رأس VR
|
||||
|
||||
## Tracker scaled proportions reset
|
||||
|
||||
|
||||
## Stay Aligned setup
|
||||
|
||||
|
||||
## Home
|
||||
|
||||
@@ -981,6 +1141,21 @@ status_system-StatusSteamVRDisconnected =
|
||||
}
|
||||
status_system-StatusTrackerError = يحتوي جهاز التعقب { $trackerName } على خطأ.
|
||||
|
||||
## Firmware tool globals
|
||||
|
||||
|
||||
## Firmware tool Steps
|
||||
|
||||
|
||||
## firmware tool build status
|
||||
|
||||
|
||||
## Firmware update status
|
||||
|
||||
|
||||
## Dedicated Firmware Update Page
|
||||
|
||||
|
||||
## Tray Menu
|
||||
|
||||
tray_menu-show = عرض
|
||||
@@ -1008,3 +1183,6 @@ unknown_device-modal-description =
|
||||
هل تريد توصيله ب SlimeVR؟
|
||||
unknown_device-modal-confirm = أكيد
|
||||
unknown_device-modal-forget = تجاهلها
|
||||
|
||||
## Error collection consent modal
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
## Websocket (server) status
|
||||
websocket-connecting = Loading...
|
||||
websocket-connection_lost = The server crashed!
|
||||
websocket-connection_lost-desc = It looks like the SlimeVR server crashed. Check the logs and restart the program
|
||||
websocket-connection_lost-desc = It looks like the SlimeVR server crashed. Check the logs and restart the program.
|
||||
websocket-timedout = Could not connect to the server
|
||||
websocket-timedout-desc = It looks like the SlimeVR server crashed or timed out. Check the logs and restart the program
|
||||
websocket-timedout-desc = It looks like the SlimeVR server crashed or timed out. Check the logs and restart the program.
|
||||
websocket-error-close = Exit SlimeVR
|
||||
websocket-error-logs = Open the logs Folder
|
||||
|
||||
@@ -23,8 +23,8 @@ version_update-close = Close
|
||||
tips-find_tracker = Not sure which tracker is which? Shake a tracker and it will highlight the corresponding item.
|
||||
tips-do_not_move_heels = Ensure your heels do not move during recording!
|
||||
tips-file_select = Drag & drop files to use, or <u>browse</u>.
|
||||
tips-tap_setup = You can slowly tap 2 times your tracker to choose it instead of selecting it from the menu.
|
||||
tips-turn_on_tracker = Using official SlimeVR trackers? Remember to <b><em>turn on your tracker</em></b> after connecting it to the PC!
|
||||
tips-tap_setup = You can slowly tap your tracker 2 times to choose it instead of selecting it from the menu.
|
||||
tips-turn_on_tracker = Using official SlimeVR trackers? Don't forget to <b><em>turn on your tracker</em></b> after connecting it to the PC!
|
||||
tips-failed_webgl = Failed to initialize WebGL.
|
||||
|
||||
## Body parts
|
||||
@@ -139,9 +139,9 @@ skeleton_bone-WAIST-desc =
|
||||
(sitting down, bending over, lying down, etc.) until your virtual spine matches with your real one.
|
||||
skeleton_bone-HIP = Hip Length
|
||||
skeleton_bone-HIP-desc =
|
||||
This is the distance from your belly button to your hips
|
||||
To adjust it, adjust your Torso Length properly and modify it in various positions
|
||||
(sitting down, bending over, lying down, etc.) until your virtual spine matches with your real one.
|
||||
This is the distance from your belly button to your hips.
|
||||
To adjust it, set your Torso Length properly and modify it in various positions
|
||||
(sitting down, bending over, lying down, etc.) until your virtual spine matches your real one.
|
||||
skeleton_bone-HIP_OFFSET = Hip Offset
|
||||
skeleton_bone-HIP_OFFSET-desc =
|
||||
This can be adjusted to move your virtual hip tracker up or down in order to aid
|
||||
@@ -178,8 +178,8 @@ skeleton_bone-FOOT_SHIFT-desc =
|
||||
feet line up with the middle of your ankles.
|
||||
skeleton_bone-SKELETON_OFFSET = Skeleton Offset
|
||||
skeleton_bone-SKELETON_OFFSET-desc =
|
||||
This can be adjusted to offsets all your trackers forward or backwards.
|
||||
It can be used in order to aid with calibration in certain games or applications
|
||||
This can be adjusted to offset all your trackers forward or backward.
|
||||
It can be used to help with calibration in certain games or applications
|
||||
that may expect your trackers to be more forward.
|
||||
skeleton_bone-SHOULDERS_DISTANCE = Shoulders Distance
|
||||
skeleton_bone-SHOULDERS_DISTANCE-desc =
|
||||
@@ -237,6 +237,8 @@ reset-reset_all_warning_default-v2 =
|
||||
|
||||
reset-full = Full Reset
|
||||
reset-mounting = Reset Mounting
|
||||
reset-mounting-feet = Reset Feet Mounting
|
||||
reset-mounting-fingers = Reset Fingers Mounting
|
||||
reset-yaw = Yaw Reset
|
||||
|
||||
## Serial detection stuff
|
||||
@@ -259,6 +261,7 @@ navbar-settings = Settings
|
||||
## Biovision hierarchy recording
|
||||
bvh-start_recording = Record BVH
|
||||
bvh-recording = Recording...
|
||||
bvh-save_title = Save BVH recording
|
||||
|
||||
## Tracking pause
|
||||
tracking-unpaused = Pause tracking
|
||||
@@ -331,7 +334,7 @@ tracker-rotation-back = Back
|
||||
tracker-rotation-back_left = Back-Left
|
||||
tracker-rotation-back_right = Back-Right
|
||||
tracker-rotation-custom = Custom
|
||||
tracker-rotation-overriden = (overriden by mounting reset)
|
||||
tracker-rotation-overriden = (overridden by mounting reset)
|
||||
|
||||
## Tracker information
|
||||
tracker-infos-manufacturer = Manufacturer
|
||||
@@ -378,11 +381,12 @@ tracker-settings-name_section-description = Give it a cute nickname :)
|
||||
tracker-settings-name_section-placeholder = NightyBeast's left leg
|
||||
tracker-settings-name_section-label = Tracker name
|
||||
tracker-settings-forget = Forget tracker
|
||||
tracker-settings-forget-description = Removes the tracker from the SlimeVR Server and prevent it from connecting to it until the server is restarted. The configuration of the tracker won't be lost.
|
||||
tracker-settings-forget-description = Removes the tracker from the SlimeVR Server and prevents it from connecting until the server is restarted. The configuration of the tracker won't be lost.
|
||||
tracker-settings-forget-label = Forget tracker
|
||||
tracker-settings-update-unavailable = Cannot be updated (DIY)
|
||||
tracker-settings-update-low-battery = Cannot update. Battery lower than 50%
|
||||
tracker-settings-update-up_to_date = Up to date
|
||||
tracker-settings-update-blocked = Update not available. No other releases available
|
||||
tracker-settings-update-available = { $versionName } is now available
|
||||
tracker-settings-update = Update now
|
||||
tracker-settings-update-title = Firmware version
|
||||
@@ -393,7 +397,7 @@ tracker-part_card-unassigned = Unassigned
|
||||
|
||||
## Body assignment menu
|
||||
body_assignment_menu = Where do you want this tracker to be?
|
||||
body_assignment_menu-description = Choose a location where you want this tracker to be assigned. Alternatively you can choose to manage all trackers at once instead of one by one.
|
||||
body_assignment_menu-description = Choose a location where you want this tracker to be assigned. Alternatively, you can choose to manage all trackers at once instead of one by one.
|
||||
body_assignment_menu-show_advanced_locations = Show advanced assign locations
|
||||
body_assignment_menu-manage_trackers = Manage all trackers
|
||||
body_assignment_menu-unassign_tracker = Unassign tracker
|
||||
@@ -436,8 +440,8 @@ tracker_selection_menu-dont_assign = Unassign
|
||||
# This line cares about multilines.
|
||||
# <b>text</b> means that the text should be bold.
|
||||
tracker_selection_menu-neck_warning =
|
||||
<b>Warning:</b> A neck tracker can be deadly if adjusted too tightly,
|
||||
the strap could cut the circulation to your head!
|
||||
<b>Warning:</b> A neck tracker can be deadly if adjusted too tightly;
|
||||
the strap could cut off circulation to your head!
|
||||
tracker_selection_menu-neck_warning-done = I understand the risks
|
||||
tracker_selection_menu-neck_warning-cancel = Cancel
|
||||
|
||||
@@ -485,7 +489,7 @@ settings-general-steamvr-trackers-right_elbow = Right elbow
|
||||
settings-general-steamvr-trackers-left_hand = Left hand
|
||||
settings-general-steamvr-trackers-right_hand = Right hand
|
||||
settings-general-steamvr-trackers-tracker_toggling = Automatic tracker assignment
|
||||
settings-general-steamvr-trackers-tracker_toggling-description = Automatically handles toggling SteamVR trackers on or off depending on your current tracker assignments
|
||||
settings-general-steamvr-trackers-tracker_toggling-description = Automatically handles toggling SteamVR trackers on or off depending on your current tracker assignments.
|
||||
settings-general-steamvr-trackers-tracker_toggling-label = Automatic tracker assignment
|
||||
settings-general-steamvr-trackers-hands-warning = <b>Warning:</b> hand trackers will override your controllers.
|
||||
Are you sure?
|
||||
@@ -498,7 +502,7 @@ settings-general-tracker_mechanics-filtering = Filtering
|
||||
# This also cares about multilines
|
||||
settings-general-tracker_mechanics-filtering-description =
|
||||
Choose the filtering type for your trackers.
|
||||
Prediction predicts movement while smoothing smoothens movement.
|
||||
Prediction predicts movement while smoothing smooths movement.
|
||||
settings-general-tracker_mechanics-filtering-type = Filtering type
|
||||
settings-general-tracker_mechanics-filtering-type-none = No filtering
|
||||
settings-general-tracker_mechanics-filtering-type-none-description = Use rotations as is. Will not do any filtering.
|
||||
@@ -511,8 +515,8 @@ settings-general-tracker_mechanics-yaw-reset-smooth-time = Yaw reset smooth time
|
||||
settings-general-tracker_mechanics-drift_compensation = Drift compensation
|
||||
# This cares about multilines
|
||||
settings-general-tracker_mechanics-drift_compensation-description =
|
||||
Compensates IMU yaw drift by applying an inverse rotation.
|
||||
Change amount of compensation and up to how many resets are taken into account.
|
||||
Compensates for IMU yaw drift by applying an inverse rotation.
|
||||
Change the amount of compensation and the number of resets taken into account.
|
||||
This should only be used if you need to reset very often!
|
||||
settings-general-tracker_mechanics-drift_compensation-enabled-label = Drift compensation
|
||||
settings-general-tracker_mechanics-drift_compensation-prediction = Drift compensation prediction
|
||||
@@ -577,9 +581,9 @@ settings-general-fk_settings-leg_tweak-skating_correction = Skating correction
|
||||
settings-general-fk_settings-leg_tweak-toe_snap = Toe snap
|
||||
settings-general-fk_settings-leg_tweak-foot_plant = Foot plant
|
||||
settings-general-fk_settings-leg_tweak-skating_correction-amount = Skating correction strength
|
||||
settings-general-fk_settings-leg_tweak-skating_correction-description = Skating-correction corrects for ice skating but can decrease accuracy in certain movement patterns. When enabling this make sure to full reset and recalibrate in game.
|
||||
settings-general-fk_settings-leg_tweak-floor_clip-description = Floor-clip can Reduce or even eliminates clipping through the floor. When enabling this, make sure to full reset and recalibrate in game.
|
||||
settings-general-fk_settings-leg_tweak-toe_snap-description = Toe-snap attempts to guess the rotation of your feet if feet trackers are not in use.
|
||||
settings-general-fk_settings-leg_tweak-skating_correction-description = Skating-correction corrects for ice skating, but can decrease accuracy in certain movement patterns. When enabling this, make sure to perform a full reset and recalibrate in-game.
|
||||
settings-general-fk_settings-leg_tweak-floor_clip-description = Floor-clip can reduce or eliminate clipping through the floor. When enabling this, make sure to perform a full reset and recalibrate in-game.
|
||||
settings-general-fk_settings-leg_tweak-toe_snap-description = Toe-snap attempts to guess the rotation of your feet if foot trackers are not in use.
|
||||
settings-general-fk_settings-leg_tweak-foot_plant-description = Foot-plant rotates your feet to be parallel to the ground when in contact.
|
||||
settings-general-fk_settings-leg_fk = Leg tracking
|
||||
settings-general-fk_settings-leg_fk-reset_mounting_feet-description = Enable feet Mounting Reset by tiptoeing.
|
||||
@@ -599,11 +603,11 @@ settings-general-fk_settings-arm_fk-reset_mode-description = Change which arm po
|
||||
settings-general-fk_settings-arm_fk-back = Back
|
||||
settings-general-fk_settings-arm_fk-back-description = The default mode, with the upper arms going back and lower arms going forward.
|
||||
settings-general-fk_settings-arm_fk-tpose_up = T-pose (up)
|
||||
settings-general-fk_settings-arm_fk-tpose_up-description = Expects your arms to be down on the sides during Full Reset, and 90 degrees up to the sides during Mounting Reset.
|
||||
settings-general-fk_settings-arm_fk-tpose_up-description = Expects your arms to be down at your sides during Full Reset, and 90 degrees up to the sides during Mounting Reset.
|
||||
settings-general-fk_settings-arm_fk-tpose_down = T-pose (down)
|
||||
settings-general-fk_settings-arm_fk-tpose_down-description = Expects your arms to be 90 degrees up to the sides during Full Reset, and down on the sides during Mounting Reset.
|
||||
settings-general-fk_settings-arm_fk-tpose_down-description = Expects your arms to be 90 degrees up to the sides during Full Reset, and down at your sides during Mounting Reset.
|
||||
settings-general-fk_settings-arm_fk-forward = Forward
|
||||
settings-general-fk_settings-arm_fk-forward-description = Expects your arms to be up 90 degrees forward. Useful for VTubing.
|
||||
settings-general-fk_settings-arm_fk-forward-description = Expects your arms to be raised forward at 90 degrees. Useful for VTubing.
|
||||
settings-general-fk_settings-skeleton_settings-toggles = Skeleton toggles
|
||||
settings-general-fk_settings-skeleton_settings-description = Toggle skeleton settings on or off. It is recommended to leave these on.
|
||||
settings-general-fk_settings-skeleton_settings-extended_spine_model = Extended spine model
|
||||
@@ -658,10 +662,10 @@ settings-general-interface-dev_mode-description = This mode can be useful if you
|
||||
settings-general-interface-dev_mode-label = Developer Mode
|
||||
settings-general-interface-theme = Color theme
|
||||
settings-general-interface-show-navbar-onboarding = Show "{ navbar-onboarding }" on navigation bar
|
||||
settings-general-interface-show-navbar-onboarding-description = This changes if the "{ navbar-onboarding }" button shows on the navigation bar.
|
||||
settings-general-interface-show-navbar-onboarding-description = This changes whether the "{ navbar-onboarding }" button shows on the navigation bar.
|
||||
settings-general-interface-show-navbar-onboarding-label = Show "{ navbar-onboarding }"
|
||||
settings-general-interface-lang = Select language
|
||||
settings-general-interface-lang-description = Change the default language you want to use.
|
||||
settings-general-interface-lang-description = Change the default language.
|
||||
settings-general-interface-lang-placeholder = Select the language to use
|
||||
# Keep the font name untranslated
|
||||
settings-interface-appearance-font = GUI font
|
||||
@@ -678,10 +682,10 @@ settings-interface-appearance-decorations-label = Use native decorations
|
||||
## Notification settings
|
||||
settings-interface-notifications = Notifications
|
||||
settings-general-interface-serial_detection = Serial device detection
|
||||
settings-general-interface-serial_detection-description = This option will show a pop-up every time you plug a new serial device that could be a tracker. It helps improving the setup process of a tracker.
|
||||
settings-general-interface-serial_detection-description = This option will show a pop-up every time you plug in a new serial device that could be a tracker. It helps to improve the setup process of a tracker.
|
||||
settings-general-interface-serial_detection-label = Serial device detection
|
||||
settings-general-interface-feedback_sound = Feedback sound
|
||||
settings-general-interface-feedback_sound-description = This option will play a sound when a reset is triggered.
|
||||
settings-general-interface-feedback_sound-description = This option plays a sound when a reset is triggered.
|
||||
settings-general-interface-feedback_sound-label = Feedback sound
|
||||
settings-general-interface-feedback_sound-volume = Feedback sound volume
|
||||
settings-general-interface-connected_trackers_warning = Connected trackers warning
|
||||
@@ -691,10 +695,10 @@ settings-general-interface-connected_trackers_warning-label = Connected trackers
|
||||
## Behavior settings
|
||||
settings-interface-behavior = Behavior
|
||||
settings-general-interface-dev_mode = Developer Mode
|
||||
settings-general-interface-dev_mode-description = This mode can be useful if you need in-depth data or to interact with connected trackers on a more advanced level.
|
||||
settings-general-interface-dev_mode-description = This mode can be useful if you need in-depth data or need to interact with connected trackers on a more advanced level.
|
||||
settings-general-interface-dev_mode-label = Developer Mode
|
||||
settings-general-interface-use_tray = Minimize to system tray
|
||||
settings-general-interface-use_tray-description = Lets you close the window without closing the SlimeVR Server so you can continue using it without having the GUI bothering you.
|
||||
settings-general-interface-use_tray-description = Lets you close the window without closing the SlimeVR Server so you can continue using it without having the GUI bother you.
|
||||
settings-general-interface-use_tray-label = Minimize to system tray
|
||||
settings-general-interface-discord_presence = Share activity on Discord
|
||||
settings-general-interface-discord_presence-description = Tells your Discord client that you are using SlimeVR along with the number of IMU trackers you are using.
|
||||
@@ -713,6 +717,9 @@ settings-interface-behavior-error_tracking-description_v2 =
|
||||
To provide the best user experience, we collect anonymized error reports, performance metrics, and operating system information. This helps us detect bugs and issues with SlimeVR. These metrics are collected via Sentry.io.
|
||||
|
||||
settings-interface-behavior-error_tracking-label = Send errors to developers
|
||||
settings-interface-behavior-bvh_directory = Directory to save BVH recordings
|
||||
settings-interface-behavior-bvh_directory-description = Choose a directory to save your BVH recordings instead of having to choose where to save them each time.
|
||||
settings-interface-behavior-bvh_directory-label = Directory for BVH recordings
|
||||
|
||||
## Serial settings
|
||||
settings-serial = Serial Console
|
||||
@@ -742,7 +749,7 @@ settings-osc-router = OSC router
|
||||
# This cares about multilines
|
||||
settings-osc-router-description =
|
||||
Forward OSC messages from another program.
|
||||
Useful for using another OSC program with VRChat for example.
|
||||
Useful for using another OSC program with VRChat, for example.
|
||||
settings-osc-router-enable = Enable
|
||||
settings-osc-router-enable-description = Toggle the forwarding of messages.
|
||||
settings-osc-router-enable-label = Enable
|
||||
@@ -802,7 +809,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 SlimeVR's bone data and receive bone data from 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
|
||||
@@ -888,30 +895,30 @@ onboarding-wifi_creds-password =
|
||||
## Mounting setup
|
||||
onboarding-reset_tutorial-back = Go Back to Mounting calibration
|
||||
onboarding-reset_tutorial = Reset tutorial
|
||||
onboarding-reset_tutorial-explanation = While you use your trackers they might get out of alignment because of IMU yaw drift, or because you might have moved them physically. You have several ways to fix this.
|
||||
onboarding-reset_tutorial-explanation = While you use your trackers, they might get out of alignment because of IMU yaw drift, or because you might have moved them physically. You have several ways to fix this.
|
||||
onboarding-reset_tutorial-skip = Skip step
|
||||
# Cares about multiline
|
||||
onboarding-reset_tutorial-0 = Tap { $taps } times the highlighted tracker for triggering yaw reset.
|
||||
onboarding-reset_tutorial-0 = Tap the highlighted tracker { $taps } times to trigger a yaw reset.
|
||||
|
||||
This will make the trackers face the same direction as your headset (HMD).
|
||||
# Cares about multiline
|
||||
onboarding-reset_tutorial-1 = Tap { $taps } times the highlighted tracker for triggering full reset.
|
||||
onboarding-reset_tutorial-1 = Tap the highlighted tracker { $taps } times to trigger a full reset.
|
||||
|
||||
You need to be standing for this (i-pose). There is a 3 seconds delay (configurable) before it actually happens.
|
||||
This fully resets the position and rotation of all your trackers. It should fix most issues.
|
||||
# Cares about multiline
|
||||
onboarding-reset_tutorial-2 = Tap { $taps } times the highlighted tracker for triggering mounting reset.
|
||||
onboarding-reset_tutorial-2 = Tap the highlighted tracker { $taps } times to trigger a mounting reset.
|
||||
|
||||
Mounting reset helps on how the trackers are actually put on you, so if you accidentally moved them and changed how they are oriented by a big amount, this will help.
|
||||
Mounting reset adjusts for how trackers are placed on your body. If they've moved or rotated significantly, this helps recalibrate their orientation.
|
||||
|
||||
You need to be on a pose like you are skiing like it's shown on the Automatic Mounting wizard and you have a 3 second delay (configurable) before it gets triggered.
|
||||
You need to be in a pose like you are skiing as shown in the Automatic Mounting wizard, and you have a 3 second delay (configurable) before it gets triggered.
|
||||
|
||||
## Setup start
|
||||
onboarding-home = Welcome to SlimeVR
|
||||
onboarding-home-start = Let's get set up!
|
||||
|
||||
## Enter VR part of setup
|
||||
onboarding-enter_vr-back = Go Back to Tracker assignent
|
||||
onboarding-enter_vr-back = Go Back to Tracker assignment
|
||||
onboarding-enter_vr-title = Time to enter VR!
|
||||
onboarding-enter_vr-description = Put on all your trackers and then enter VR!
|
||||
onboarding-enter_vr-ready = I'm ready
|
||||
@@ -940,13 +947,13 @@ onboarding-connect_tracker-connection_status-done = Connected to the Server
|
||||
onboarding-connect_tracker-connection_status-no_serial_log = Could not get logs from the tracker
|
||||
onboarding-connect_tracker-connection_status-no_serial_device_found = Could not find a tracker from USB
|
||||
onboarding-connect_serial-error-modal-no_serial_log = Is the tracker turned on?
|
||||
onboarding-connect_serial-error-modal-no_serial_log-desc = Make sure the tracker is turned on and connected to your computer
|
||||
onboarding-connect_serial-error-modal-no_serial_log-desc = Make sure the tracker is turned on and connected to your computer.
|
||||
onboarding-connect_serial-error-modal-no_serial_device_found = No trackers detected
|
||||
onboarding-connect_serial-error-modal-no_serial_device_found-desc =
|
||||
Please connect a tracker with the provided usb cable to your computer and turn the tracker on.
|
||||
Please connect a tracker with the provided USB cable to your computer and turn the tracker on.
|
||||
If this does not work:
|
||||
- try with another usb cable
|
||||
- try with another usb port
|
||||
- try using a different USB cable
|
||||
- try using a different USB port
|
||||
- try reinstalling the SlimeVR server and select "USB Drivers" in the components section
|
||||
# $amount (Number) - Amount of trackers connected (this is a number, but you can use CLDR plural rules for your language)
|
||||
# More info on https://www.unicode.org/cldr/cldr-aux/charts/22/supplemental/language_plural_rules.html
|
||||
@@ -963,8 +970,8 @@ onboarding-connect_tracker-next = I connected all my trackers
|
||||
## Tracker calibration tutorial
|
||||
onboarding-calibration_tutorial = IMU Calibration Tutorial
|
||||
onboarding-calibration_tutorial-subtitle = This will help reduce tracker drifting!
|
||||
onboarding-calibration_tutorial-description = Every time you turn on your trackers, they need to rest for a moment on a flat surface to calibrate. Let's do the same thing by clicking the "{ onboarding-calibration_tutorial-calibrate }" button, <b>do not move them!</b>
|
||||
onboarding-calibration_tutorial-calibrate = I placed my trackers on the table
|
||||
onboarding-calibration_tutorial-description-v1 = After turning on your trackers, place them on a stable surface for a moment to allow for calibration. Calibration can be performed at any time after the trackers are powered on—this page simply provides a tutorial. To begin, click the "{ onboarding-calibration_tutorial-calibrate }" button, then <b>do not move your trackers!</b>
|
||||
onboarding-calibration_tutorial-calibrate = I placed my trackers on a table
|
||||
onboarding-calibration_tutorial-status-waiting = Waiting for you
|
||||
onboarding-calibration_tutorial-status-calibrating = Calibrating
|
||||
onboarding-calibration_tutorial-status-success = Nice!
|
||||
@@ -1155,9 +1162,9 @@ onboarding-automatic_proportions-requirements-descriptionv2 =
|
||||
onboarding-automatic_proportions-requirements-next = I have read the requirements
|
||||
|
||||
onboarding-automatic_proportions-check_height-title-v3 = Measure your headset height
|
||||
onboarding-automatic_proportions-check_height-description-v2 = Your headset (HMD) height should be slightly less than your full height, as headsets measure your eye height. This measurement will be used as a baseline for your body proportions.
|
||||
onboarding-automatic_proportions-check_height-description-v2 = Your headset (HMD) height should be slightly less than your full height because headsets measure your eye height. This measurement will be used as a baseline for your body proportions.
|
||||
# All the text is in bold!
|
||||
onboarding-automatic_proportions-check_height-calculation_warning-v3 = Start measuring while standing <u>upright</u> to measure your height. Be careful to not raise your hands higher than your headset, as they may affect the measurement!
|
||||
onboarding-automatic_proportions-check_height-calculation_warning-v3 = Start measuring while standing <u>upright</u> to measure your height. Be careful not to raise your hands higher than your headset, as they may affect the measurement!
|
||||
onboarding-automatic_proportions-check_height-guardian_tip = If you are using a standalone VR headset, make sure to have your guardian /
|
||||
boundary turned on so that your height is correct!
|
||||
# Context is that the height is unknown
|
||||
@@ -1192,9 +1199,9 @@ onboarding-automatic_proportions-recording-description-p1 = Make the moves shown
|
||||
# Each line of text is a different list item
|
||||
onboarding-automatic_proportions-recording-steps =
|
||||
Standing up straight, roll your head in a circle.
|
||||
Bend your back forwards and squat. While squatting, look to your left, then to your right.
|
||||
Twist your upper body to the left (counter-clockwise), then reach down towards the ground.
|
||||
Twist your upper body to the right (clockwise), then reach down towards the ground.
|
||||
Bend your back forward and squat. While squatting, look to your left, then to your right.
|
||||
Twist your upper body to the left (counter-clockwise), then reach down toward the ground.
|
||||
Twist your upper body to the right (clockwise), then reach down toward the ground.
|
||||
Roll your hips in a circular motion as if you're using a hula hoop.
|
||||
If there is time left on the recording, you can repeat these steps until it's finished.
|
||||
onboarding-automatic_proportions-recording-processing = Processing the result
|
||||
@@ -1235,7 +1242,7 @@ onboarding-scaled_proportions-manual_height-estimated_height = Your estimated he
|
||||
onboarding-scaled_proportions-manual_height-next_step = Continue and save
|
||||
onboarding-scaled_proportions-manual_height-warning =
|
||||
You are currently using the manual way of setting up scaled proportions!
|
||||
<b>This mode is recommended only if you do not use a HMD with SlimeVR</b>
|
||||
<b>This mode is recommended only if you do not use an HMD with SlimeVR.</b>
|
||||
|
||||
To be able to use the automatic scaled proportions please:
|
||||
onboarding-scaled_proportions-manual_height-warning-no_hmd = Connect a VR Headset
|
||||
@@ -1258,10 +1265,10 @@ onboarding-stay_aligned-verify_mounting-title = Check your Mounting
|
||||
onboarding-stay_aligned-verify_mounting-step-0 = Stay Aligned requires good mounting. Otherwise, you won't get a good experience with Stay Aligned.
|
||||
onboarding-stay_aligned-verify_mounting-step-1 = 1. Move around while standing.
|
||||
onboarding-stay_aligned-verify_mounting-step-2 = 2. Sit down and move your legs and feet.
|
||||
onboarding-stay_aligned-verify_mounting-step-3 = 3. If your trackers aren't in the right place, press "Redo Mounting Calibration"
|
||||
onboarding-stay_aligned-verify_mounting-step-3 = 3. If your trackers aren't in the right place, press "Redo Mounting Calibration".
|
||||
onboarding-stay_aligned-verify_mounting-redo_mounting = Redo Mounting calibration
|
||||
onboarding-stay_aligned-preparation-title = Preparation
|
||||
onboarding-stay_aligned-preparation-tip = Make sure to stand upright. You must be looking forward and your arms must be down to your sides.
|
||||
onboarding-stay_aligned-preparation-tip = Make sure to stand upright. Keep looking forward with your arms down at your sides.
|
||||
onboarding-stay_aligned-relaxed_poses-standing-title = Relaxed Standing Pose
|
||||
onboarding-stay_aligned-relaxed_poses-standing-step-0 = 1. Stand in a comfortable position. Relax!
|
||||
onboarding-stay_aligned-relaxed_poses-standing-step-1-v2 = 2. Press the "Save pose" button.
|
||||
@@ -1274,7 +1281,7 @@ onboarding-stay_aligned-relaxed_poses-flat-step-1-v2 = 2. Press the "Save pose"
|
||||
onboarding-stay_aligned-relaxed_poses-skip_step = Skip
|
||||
onboarding-stay_aligned-done-title = Stay Aligned enabled!
|
||||
onboarding-stay_aligned-done-description = Your Stay Aligned setup is complete!
|
||||
onboarding-stay_aligned-done-description-2 = Setup is complete! You may restart the process if you want to re-calibrate the poses
|
||||
onboarding-stay_aligned-done-description-2 = Setup is complete! You may restart the process if you want to recalibrate the poses.
|
||||
onboarding-stay_aligned-previous_step = Previous
|
||||
onboarding-stay_aligned-next_step = Next
|
||||
onboarding-stay_aligned-restart = Restart
|
||||
@@ -1299,7 +1306,10 @@ status_system-StatusSteamVRDisconnected = { $type ->
|
||||
}
|
||||
status_system-StatusTrackerError = The { $trackerName } tracker has an error.
|
||||
status_system-StatusUnassignedHMD = The VR headset should be assigned as a head tracker.
|
||||
status_system-StatusPublicNetwork = Your network profile is currently set to Public. This is not recomended for SlimeVR to function properly. <PublicFixLink>See how to fix it here.</PublicFixLink>
|
||||
status_system-StatusPublicNetwork = {$count ->
|
||||
[one] Your network profile is currently set to Public ({$adapters}). This is not recommended for SlimeVR to function properly. <PublicFixLink>See how to fix it here.</PublicFixLink>
|
||||
*[many] Some of your network adapters are set to public: {$adapters}. This is not recommended for SlimeVR to function properly. <PublicFixLink>See how to fix it here.</PublicFixLink>
|
||||
}
|
||||
|
||||
|
||||
## Firmware tool globals
|
||||
@@ -1323,7 +1333,7 @@ firmware_tool-board_step-description = Select one of the boards listed below.
|
||||
firmware_tool-board_pins_step = Check the pins
|
||||
firmware_tool-board_pins_step-description =
|
||||
Please verify that the selected pins are correct.
|
||||
If you followed the SlimeVR documentation the defaults values should be correct
|
||||
If you followed the SlimeVR documentation, the default values should be correct.
|
||||
firmware_tool-board_pins_step-enable_led = Enable LED
|
||||
firmware_tool-board_pins_step-led_pin =
|
||||
.label = LED Pin
|
||||
@@ -1351,8 +1361,8 @@ firmware_tool-board_pins_step-battery_shield_resistor-1 =
|
||||
|
||||
firmware_tool-add_imus_step = Declare your IMUs
|
||||
firmware_tool-add_imus_step-description =
|
||||
Please add the IMUs that your tracker has
|
||||
If you followed the SlimeVR documentation the defaults values should be correct
|
||||
Please add the IMUs that your tracker has.
|
||||
If you followed the SlimeVR documentation, the default values should be correct.
|
||||
firmware_tool-add_imus_step-imu_type-label = IMU type
|
||||
firmware_tool-add_imus_step-imu_type-placeholder = Select the type of IMU
|
||||
firmware_tool-add_imus_step-imu_rotation =
|
||||
@@ -1384,23 +1394,23 @@ firmware_tool-flash_method_step-description =
|
||||
Please select the flashing method you want to use
|
||||
firmware_tool-flash_method_step-ota =
|
||||
.label = OTA
|
||||
.description = Use the over the air method. Your tracker will use the Wi-Fi to update it's firmware. Works only on already setup trackers.
|
||||
.description = Use the over-the-air method. Your tracker will use Wi-Fi to update its firmware. Only works on trackers that have been set up.
|
||||
firmware_tool-flash_method_step-serial =
|
||||
.label = Serial
|
||||
.description = Use a USB cable to update your tracker.
|
||||
|
||||
firmware_tool-flashbtn_step = Press the boot btn
|
||||
firmware_tool-flashbtn_step-description = Before going into the next step there is a few things you need to do
|
||||
firmware_tool-flashbtn_step-description = Before going to the next step, there are a few things you need to do
|
||||
|
||||
firmware_tool-flashbtn_step-board_SLIMEVR = Turn off the tracker, remove the case (if any), connect a USB cable to this computer, then do one of the following steps according to your SlimeVR board revision:
|
||||
firmware_tool-flashbtn_step-board_SLIMEVR-r11 = Turn on the tracker while shorting the second rectangular FLASH pad from the edge on the top side of the board, and the metal shield of the microcontroller
|
||||
firmware_tool-flashbtn_step-board_SLIMEVR-r12 = Turn on the tracker while shorting the circular FLASH pad on the top side of the board, and the metal shield of the microcontroller
|
||||
firmware_tool-flashbtn_step-board_SLIMEVR-r14 = Turn on the tracker while pushing in the FLASH button on the top side of the board
|
||||
firmware_tool-flashbtn_step-board_SLIMEVR = Turn off the tracker, remove the case (if any), connect the USB cable to your computer, then follow the appropriate steps for your SlimeVR board revision:
|
||||
firmware_tool-flashbtn_step-board_SLIMEVR-r11 = Turn on the tracker while shorting the second rectangular FLASH pad from the edge on the top side of the board to the metal shield of the microcontroller.
|
||||
firmware_tool-flashbtn_step-board_SLIMEVR-r12 = Turn on the tracker while shorting the circular FLASH pad on the top side of the board to the metal shield of the microcontroller.
|
||||
firmware_tool-flashbtn_step-board_SLIMEVR-r14 = Turn on the tracker while pushing in the FLASH button on the top side of the board.
|
||||
|
||||
firmware_tool-flashbtn_step-board_OTHER = Before flashing you will probably need to put the tracker into bootloader mode.
|
||||
Most of the time it means pressing the boot button on the board before the flashing process starts.
|
||||
If the flashing process timeout at the begining of the flashing it probably means that the tracker was not in bootloader mode
|
||||
Please refer to the flashing instructions of your board to know how to turn on the boatloader mode
|
||||
firmware_tool-flashbtn_step-board_OTHER = Before flashing, you will probably need to put the tracker into bootloader mode.
|
||||
Most of the time, this means pressing the boot button on the board before the flashing process starts.
|
||||
If the flashing process times out at the start, it probably means that the tracker was not in bootloader mode.
|
||||
Refer to your board's flashing instructions to learn how to enter bootloader mode.
|
||||
|
||||
|
||||
|
||||
@@ -1453,8 +1463,8 @@ firmware_update-status-ERROR_UNKNOWN = Unknown error
|
||||
## Dedicated Firmware Update Page
|
||||
firmware_update-title = Firmware update
|
||||
firmware_update-devices = Available Devices
|
||||
firmware_update-devices-description = Please select the trackers you want to update to the latest version of SlimeVR firmware
|
||||
firmware_update-no_devices = Plase make sure that the trackers you want to update are ON and connected to the Wi-Fi!
|
||||
firmware_update-devices-description = Please select the trackers you want to update to the latest version of SlimeVR firmware.
|
||||
firmware_update-no_devices = Please make sure that the trackers you want to update are ON and connected to the Wi-Fi!
|
||||
firmware_update-changelog-title = Updating to {$version}
|
||||
firmware_update-looking_for_devices = Looking for devices to update...
|
||||
firmware_update-retry = Retry
|
||||
@@ -1469,7 +1479,7 @@ tray_menu-quit = Quit
|
||||
## First exit modal
|
||||
tray_or_exit_modal-title = What should the close button do?
|
||||
# Multiline text
|
||||
tray_or_exit_modal-description = This lets you choose whether you want to exit the server or to minimize it to the tray when pressing the close button.
|
||||
tray_or_exit_modal-description = Choose whether to exit the server or minimize it to the tray when clicking the close button.
|
||||
|
||||
You can change this later in the interface settings!
|
||||
tray_or_exit_modal-radio-exit = Exit on close
|
||||
|
||||
@@ -269,6 +269,7 @@ navbar-settings = Réglages
|
||||
|
||||
bvh-start_recording = Enregistrer BVH
|
||||
bvh-recording = Enregistrement...
|
||||
bvh-save_title = Sauvegarder l’enregistrement BVH
|
||||
|
||||
## Tracking pause
|
||||
|
||||
@@ -406,6 +407,7 @@ tracker-settings-forget-label = Oublier capteur
|
||||
tracker-settings-update-unavailable = Ne peut pas être mis à jour (DIY)
|
||||
tracker-settings-update-low-battery = Mise à jour impossible. Batterie inférieure à 50 %
|
||||
tracker-settings-update-up_to_date = À jour
|
||||
tracker-settings-update-blocked = Mise à jour non disponible. Aucune autre version disponible
|
||||
tracker-settings-update-available = { $versionName } est maintenant disponible
|
||||
tracker-settings-update = Mettre à jour maintenant
|
||||
tracker-settings-update-title = Version du micrologiciel
|
||||
@@ -584,6 +586,7 @@ settings-stay_aligned-relaxed_poses-sitting = Ajuster les capteurs en position a
|
||||
settings-stay_aligned-relaxed_poses-flat = Ajuster les capteurs en position assise sur le sol ou allongée sur le dos
|
||||
settings-stay_aligned-relaxed_poses-save_pose = Enregistrer la posture
|
||||
settings-stay_aligned-relaxed_poses-reset_pose = Réinitialiser la posture
|
||||
settings-stay_aligned-relaxed_poses-close = Fermer
|
||||
settings-stay_aligned-debug-label = Débogage
|
||||
settings-stay_aligned-debug-description = Veuillez inclure vos paramètres lorsque vous signalez des problèmes concernant Garder Aligné.
|
||||
settings-stay_aligned-debug-copy-label = Copier les paramètres dans le presse-papiers
|
||||
@@ -744,6 +747,9 @@ settings-interface-behavior-error_tracking-description_v2 =
|
||||
|
||||
Pour offrir la meilleure expérience utilisateur possible, nous collectons des rapports d'erreurs anonymisés, des mesures de performance et des informations sur le système d'exploitation. Cela nous aide à détecter les bugs et les problèmes liés à SlimeVR. Ces données sont collectées via Sentry.io.
|
||||
settings-interface-behavior-error_tracking-label = Envoyer les erreurs aux développeurs
|
||||
settings-interface-behavior-bvh_directory = Répertoire pour sauvegarder les enregistrements BVH
|
||||
settings-interface-behavior-bvh_directory-description = Choisissez un répertoire où sauvegarder vos enregistrements BVH au lieu d’avoir à choisir où les sauvegarder à chaque fois.
|
||||
settings-interface-behavior-bvh_directory-label = Répertoire où sauvegarder les enregistrements BVH
|
||||
|
||||
## Serial settings
|
||||
|
||||
@@ -1014,7 +1020,7 @@ onboarding-connect_tracker-next = J'ai connecté tous mes capteurs
|
||||
|
||||
onboarding-calibration_tutorial = Tutoriel de calibration IMU
|
||||
onboarding-calibration_tutorial-subtitle = Ceci vous aidera à réduire la dérive du capteur !
|
||||
onboarding-calibration_tutorial-description = Chaque fois que vous allumez vos capteurs, ils doivent rester sur une surface plane pour se calibrer. Faisons de même en cliquant sur le bouton « { onboarding-calibration_tutorial-calibrate } ». <b>Ne les déplacez pas !</b>
|
||||
onboarding-calibration_tutorial-description-v1 = Après avoir allumé vos capteurs, placez-les sur une surface stable pendant un moment pour leur permettre de se calibrer. La calibration peut être effectué n'importe quand lors que les capteurs sont allumés - cette page sert simplement de tutoriel. Pour commencer, cliquez sur le bouton « { onboarding-calibration_tutorial-calibrate } », puis <b>ne déplacez pas vos capteurs !</b>
|
||||
onboarding-calibration_tutorial-calibrate = J'ai posé mes capteurs sur la table
|
||||
onboarding-calibration_tutorial-status-waiting = En attente de vous
|
||||
onboarding-calibration_tutorial-status-calibrating = Calibration...
|
||||
@@ -1332,13 +1338,13 @@ onboarding-stay_aligned-preparation-title = Préparation
|
||||
onboarding-stay_aligned-preparation-tip = Assurez-vous de vous tenir droit. Vous devez regarder vers l'avant et vos bras doivent être le long de votre corps.
|
||||
onboarding-stay_aligned-relaxed_poses-standing-title = Posture debout détendu
|
||||
onboarding-stay_aligned-relaxed_poses-standing-step-0 = 1. Tenez-vous dans une position confortable. Détendez-vous !
|
||||
onboarding-stay_aligned-relaxed_poses-standing-step-2 = 3. Appuyez sur le bouton « Enregistrer la posture ».
|
||||
onboarding-stay_aligned-relaxed_poses-standing-step-1-v2 = 2. Appuyez sur le bouton « Enregistrer la posture ».
|
||||
onboarding-stay_aligned-relaxed_poses-sitting-title = Posture assis détendu dans une chaise
|
||||
onboarding-stay_aligned-relaxed_poses-sitting-step-0 = 1. Asseyez-vous dans une position confortable. Détendez-vous !
|
||||
onboarding-stay_aligned-relaxed_poses-sitting-step-2 = 3. Appuyez sur le bouton « Enregistrer la posture ».
|
||||
onboarding-stay_aligned-relaxed_poses-sitting-step-1-v2 = 2. Appuyez sur le bouton « Enregistrer la posture ».
|
||||
onboarding-stay_aligned-relaxed_poses-flat-title = Posture assis détendu sur le sol
|
||||
onboarding-stay_aligned-relaxed_poses-flat-step-0 = 1. Asseyez-vous sur le sol, les jambes devant. Détendez-vous !
|
||||
onboarding-stay_aligned-relaxed_poses-flat-step-2 = 3. Appuyez sur le bouton « Enregistrer la posture ».
|
||||
onboarding-stay_aligned-relaxed_poses-flat-step-1-v2 = 2. Appuyez sur le bouton « Enregistrer la posture ».
|
||||
onboarding-stay_aligned-relaxed_poses-skip_step = Sauter
|
||||
onboarding-stay_aligned-done-title = Garder Aligné activé !
|
||||
onboarding-stay_aligned-done-description = La configuration de Garder Aligné est terminée !
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
|
||||
websocket-connecting = Verbinding maken met de server
|
||||
websocket-connection_lost = Verbinding met de server verbroken. Opniew verbinding maken...
|
||||
websocket-connection_lost-desc = Het ziet er naar uit dat de verbinding met de SlimeVR server is verbroken. Check het logboek en start het programma opnieuw.
|
||||
websocket-timedout = Kan niet verbinden met de server.
|
||||
websocket-timedout-desc = Het ziet er naar uit dat de SlimeVR server is gestopt. Check het logboek en start het programma opnieuw.
|
||||
websocket-error-close = SlimeVR afsluiten
|
||||
websocket-error-logs = Open het logboek.
|
||||
|
||||
## Update notification
|
||||
|
||||
@@ -49,10 +54,42 @@ body_part-LEFT_HAND = Linkerhand
|
||||
body_part-LEFT_UPPER_LEG = Linkerdij
|
||||
body_part-LEFT_LOWER_LEG = Linkerenkel
|
||||
body_part-LEFT_FOOT = Linkervoet
|
||||
body_part-LEFT_THUMB_METACARPAL = Linkerduim middenhandsbeentje
|
||||
body_part-LEFT_THUMB_PROXIMAL = Linkerduim proximaal
|
||||
body_part-LEFT_THUMB_DISTAL = Linkerduim distaal
|
||||
body_part-LEFT_INDEX_PROXIMAL = Linker wijsvinger proximaal
|
||||
body_part-LEFT_INDEX_INTERMEDIATE = Linker middelste kootje van de wijsvinger
|
||||
body_part-LEFT_INDEX_DISTAL = Linker wijsvinger distaal
|
||||
body_part-LEFT_MIDDLE_PROXIMAL = Linker middelvinger proximaal
|
||||
body_part-LEFT_MIDDLE_INTERMEDIATE = Linker middelste kootje van de middelvinger
|
||||
body_part-LEFT_MIDDLE_DISTAL = Linker middelvinger distaal
|
||||
body_part-LEFT_RING_PROXIMAL = Linker ringvinger proximaal
|
||||
body_part-LEFT_RING_INTERMEDIATE = Linker middelste kootje van de ringvinger
|
||||
body_part-LEFT_RING_DISTAL = Linker ringvinger distaal
|
||||
body_part-LEFT_LITTLE_PROXIMAL = Linker kleine vinger proximaal
|
||||
body_part-LEFT_LITTLE_INTERMEDIATE = Linker middelste kootje van de kleine vinger
|
||||
body_part-LEFT_LITTLE_DISTAL = Linker kleine vinger distaal
|
||||
body_part-RIGHT_THUMB_METACARPAL = Rechterduim middenhandsbeentje
|
||||
body_part-RIGHT_THUMB_PROXIMAL = Rechterduim proximaal
|
||||
body_part-RIGHT_THUMB_DISTAL = Rechterduim distaal
|
||||
body_part-RIGHT_INDEX_PROXIMAL = Rechter wijsvinger proximaal
|
||||
body_part-RIGHT_INDEX_INTERMEDIATE = Rechter middelste kootje van de wijsvinger
|
||||
body_part-RIGHT_INDEX_DISTAL = Rechter wijsvinger distaal
|
||||
body_part-RIGHT_MIDDLE_PROXIMAL = Rechts middelvinger proximaal
|
||||
body_part-RIGHT_MIDDLE_INTERMEDIATE = Rechter middelste kootje van de middelvinger
|
||||
body_part-RIGHT_MIDDLE_DISTAL = Rechter middelvinger distaal
|
||||
body_part-RIGHT_RING_PROXIMAL = Rechter ringvinger proximaal
|
||||
body_part-RIGHT_RING_INTERMEDIATE = Rechter middelste kootje van de ringvinger
|
||||
body_part-RIGHT_RING_DISTAL = Rechter ringvinger distaal
|
||||
body_part-RIGHT_LITTLE_PROXIMAL = Rechter kleine vinger proximaal
|
||||
body_part-RIGHT_LITTLE_INTERMEDIATE = Rechter middelste kootje van de kleine vinger
|
||||
body_part-RIGHT_LITTLE_DISTAL = Rechter kleine vinger distaal
|
||||
|
||||
## BoardType
|
||||
|
||||
board_type-UNKNOWN = Onbekend
|
||||
board_type-NODEMCU = NodeMCU
|
||||
board_type-CUSTOM = Custom bord
|
||||
board_type-WROOM32 = WROOM32
|
||||
board_type-WEMOSD1MINI = Wemos D1 Mini
|
||||
board_type-TTGO_TBASE = TTGO T-Base
|
||||
@@ -60,43 +97,152 @@ board_type-ESP01 = ESP-01
|
||||
board_type-SLIMEVR = SlimeVR
|
||||
board_type-LOLIN_C3_MINI = Lolin C3 Mini
|
||||
board_type-BEETLE32C3 = Beetle ESP32-C3
|
||||
board_type-ES32C3DEVKITM1 = Espressif ESP32-C3 DevKitM-1
|
||||
board_type-ESP32C3DEVKITM1 = Espressif ESP32-C3 DevKitM-1
|
||||
board_type-OWOTRACK = owoTrack
|
||||
board_type-WRANGLER = Wrangler Joycons
|
||||
board_type-MOCOPI = Sony Mocopi
|
||||
board_type-WEMOSWROOM02 = Wemos Wroom-02 D1 Mini
|
||||
board_type-XIAO_ESP32C3 = Seeed Studio XIAO ESP32C3
|
||||
board_type-HARITORA = Haritora
|
||||
board_type-ESP32C6DEVKITC1 = Espressif ESP32-C6 DevKitC-1
|
||||
board_type-GLOVE_IMU_SLIMEVR_DEV = SlimeVR Dev IMU Handschoen
|
||||
|
||||
## Proportions
|
||||
|
||||
skeleton_bone-NONE = Geen
|
||||
skeleton_bone-HEAD = Hoofdverschuiving
|
||||
skeleton_bone-HEAD-desc =
|
||||
Dit is de afstand tussen je headset en het midden van je hoofd.
|
||||
Om dit aan te passen, schud je je hoofd naar links en rechts alsof je 'nee' zegt,
|
||||
pas het aan totdat beweging van de andere trackers te verwaarlozen is.
|
||||
skeleton_bone-NECK = Neklengte
|
||||
skeleton_bone-NECK-desc =
|
||||
Dit is de afstand tussen het midden van je hoofd en de basis van je nek.
|
||||
Om dit aan te passen, beweeg je je hoofd op en neer alsof je knikt, of kantel je je hoofd
|
||||
naar links en rechts. Wijzig de positie totdat beweging in andere trackers verwaarloosbaar is.
|
||||
skeleton_bone-torso_group = Romp lengte
|
||||
skeleton_bone-torso_group-desc =
|
||||
Dit is de afstand van je nek tot je heupen.
|
||||
Om dit aan te passen, ga rechtop staan en pas het aan totdat je virtuele heupen
|
||||
in lijn zijn met je echte heupen.
|
||||
skeleton_bone-UPPER_CHEST = Bovenborst Lengte
|
||||
skeleton_bone-UPPER_CHEST-desc =
|
||||
Dit is de afstand tussen de basis van je nek en het midden van je borst.
|
||||
Om dit aan te passen, stel je de torso-lengte correct af en pas je deze aan in verschillende houdingen
|
||||
(zitten, bukken, liggen, enz.) totdat je virtuele ruggengraat overeenkomt met je echte.
|
||||
skeleton_bone-CHEST_OFFSET = Borstoffset
|
||||
skeleton_bone-CHEST_OFFSET-desc =
|
||||
Dit kan worden aangepast om je virtuele borsttracker omhoog of omlaag te verplaatsen,
|
||||
om te helpen bij de kalibratie in bepaalde spellen of applicaties die verwachten dat deze hoger of lager staat.
|
||||
skeleton_bone-CHEST = Borstafstand
|
||||
skeleton_bone-CHEST-desc =
|
||||
Dit is de afstand van het midden van je borst tot het midden van je ruggengraat.
|
||||
Om dit aan te passen, stel je de torso-lengte correct af en pas je deze aan in verschillende houdingen
|
||||
(zitten, bukken, liggen, enz.) totdat je virtuele ruggengraat overeenkomt met je echte.
|
||||
skeleton_bone-WAIST = Taille lengte
|
||||
skeleton_bone-WAIST-desc =
|
||||
Dit is de afstand van het midden van je ruggengraat tot je navel.
|
||||
Om dit aan te passen, stel je de torso-lengte correct af en pas je deze aan in verschillende houdingen
|
||||
(zitten, bukken, liggen, enz.) totdat je virtuele ruggengraat overeenkomt met je echte.
|
||||
skeleton_bone-HIP = Heuplengte
|
||||
skeleton_bone-HIP-desc =
|
||||
Dit is de afstand van je navel tot je heupen.
|
||||
Om dit aan te passen, stel je de torso-lengte correct in en pas je deze aan in verschillende houdingen
|
||||
(zitten, bukken, liggen, enz.) totdat je virtuele ruggengraat overeenkomt met je echte.
|
||||
skeleton_bone-HIP_OFFSET = Heupoffset
|
||||
skeleton_bone-HIP_OFFSET-desc =
|
||||
Dit kan worden aangepast om je virtuele heuptracker omhoog of omlaag te verplaatsen,
|
||||
om te helpen bij de kalibratie in bepaalde spellen of applicaties die mogelijk verwachten dat deze zich rond je middel bevindt.
|
||||
skeleton_bone-HIPS_WIDTH = Heupbreedte
|
||||
skeleton_bone-HIPS_WIDTH-desc =
|
||||
Dit is de afstand tussen het begin van je benen.
|
||||
Om dit aan te passen, voer je een volledige reset uit met je benen gestrekt en pas je het aan totdat je virtuele benen horizontaal overeenkomen met je echte.
|
||||
skeleton_bone-leg_group = Beenlengte
|
||||
skeleton_bone-leg_group-desc =
|
||||
Dit is de afstand van je heupen tot je voeten.
|
||||
Om dit aan te passen, pas je je torso-lengte op de juiste manier aan
|
||||
totdat je virtuele voeten op hetzelfde niveau staan als je echte.
|
||||
skeleton_bone-UPPER_LEG = Bovenbeenlengte
|
||||
skeleton_bone-UPPER_LEG-desc =
|
||||
Dit is de afstand van je heupen tot je knieën.
|
||||
Om dit aan te passen, pas je je beenlengte op de juiste manier aan
|
||||
totdat je virtuele knieën op dezelfde hoogte zijn als je echte.
|
||||
skeleton_bone-LOWER_LEG = Onderbeenlengte
|
||||
skeleton_bone-LOWER_LEG-desc =
|
||||
Dit is de afstand van je knieën tot je enkels.
|
||||
Om dit aan te passen, pas je je beenlengte op de juiste manier aan
|
||||
totdat je virtuele knieën op dezelfde hoogte zijn als je echte knieën.
|
||||
skeleton_bone-FOOT_LENGTH = Voetlengte
|
||||
skeleton_bone-FOOT_LENGTH-desc =
|
||||
Dit is de afstand van je enkels tot je tenen.
|
||||
Om dit aan te passen, ga op je tenen staan en pas het aan totdat je virtuele voeten op hun plaats blijven.
|
||||
skeleton_bone-FOOT_SHIFT = Voetverschuiving
|
||||
skeleton_bone-FOOT_SHIFT-desc =
|
||||
Deze waarde is de horizontale afstand van je knie tot je enkel.
|
||||
Dit houdt rekening met het feit dat je onderbenen naar achteren staan wanneer je rechtop staat.
|
||||
Om dit aan te passen, stel je de voetlengte in op 0, voer je een volledige reset uit,
|
||||
en pas je het aan totdat je virtuele voeten op één lijn liggen met het midden van je enkels.
|
||||
skeleton_bone-SKELETON_OFFSET = Skelet offset
|
||||
skeleton_bone-SKELETON_OFFSET-desc =
|
||||
Dit kan worden aangepast om al je trackers naar voren of naar achteren te verschuiven.
|
||||
Het kan worden gebruikt om te helpen bij de kalibratie in bepaalde spellen of toepassingen
|
||||
die mogelijk verwachten dat je trackers verder naar voren staan.
|
||||
skeleton_bone-SHOULDERS_DISTANCE = Schoudersafstand
|
||||
skeleton_bone-SHOULDERS_DISTANCE-desc =
|
||||
Dit is de verticale afstand van de basis van je nek tot je schouders.
|
||||
Om dit aan te passen, stel je de lengte van je bovenarm in op 0 en
|
||||
pas je deze aan totdat je virtuele elleboogtrackers verticaal uitlijnen met je echte schouders.
|
||||
skeleton_bone-SHOULDERS_WIDTH = Schouderbreedte
|
||||
skeleton_bone-SHOULDERS_WIDTH-desc =
|
||||
Dit is de horizontale afstand van de basis van je nek tot je schouders.
|
||||
Om dit aan te passen, stel je de lengte van je bovenarm in op 0 en pas je deze aan
|
||||
totdat je virtuele elleboogtrackers horizontaal uitlijnen met je echte schouders.
|
||||
skeleton_bone-arm_group = Armlengte
|
||||
skeleton_bone-arm_group-desc =
|
||||
Dit is de afstand van je schouders tot je polsen.
|
||||
Om dit aan te passen, pas je de schouderafstand correct aan, stel je Handafstand Y in op 0,
|
||||
en pas je deze aan totdat je handtrackers op één lijn liggen met je polsen.
|
||||
skeleton_bone-UPPER_ARM = Bovenarmlengte
|
||||
skeleton_bone-UPPER_ARM-desc =
|
||||
Dit is de afstand van je schouders tot je ellebogen.
|
||||
Om dit aan te passen, pas je de armlengte correct aan en pas je deze aan
|
||||
totdat je elleboogtrackers overeenkomen met je echte ellebogen.
|
||||
skeleton_bone-LOWER_ARM = Onderarmlengte
|
||||
skeleton_bone-LOWER_ARM-desc =
|
||||
Dit is de afstand van je ellebogen tot je polsen.
|
||||
Om dit aan te passen, pas je de armlengte correct aan en pas je deze aan
|
||||
totdat je elleboogtrackers overeenkomen met je echte ellebogen.
|
||||
skeleton_bone-HAND_Y = Afstand hand Y
|
||||
skeleton_bone-HAND_Y-desc =
|
||||
Dit is de verticale afstand van je polsen tot het midden van je hand.
|
||||
Om dit aan te passen voor motion capture, pas je de armlengte correct aan
|
||||
en pas je deze aan totdat je handtrackers verticaal uitgelijnd zijn met het midden van je handen.
|
||||
Wil je het aanpassen voor elleboogtracking vanaf je controllers,
|
||||
stel dan de armlengte in op 0 en pas je deze aan totdat je elleboogtrackers verticaal op één lijn liggen met je polsen.
|
||||
skeleton_bone-HAND_Z = Afstand hand Z
|
||||
skeleton_bone-HAND_Z-desc =
|
||||
Dit is de horizontale afstand van je polsen tot het midden van je hand.
|
||||
Als je dit wilt aanpassen voor motion capture, stel je deze in op 0.
|
||||
Wil je het aanpassen voor elleboogtracking vanaf je controllers, stel dan de armlengte in op 0
|
||||
en pas je deze aan totdat je elleboogtrackers horizontaal op één lijn liggen met je polsen.
|
||||
skeleton_bone-ELBOW_OFFSET = Elleboogoffset
|
||||
skeleton_bone-ELBOW_OFFSET-desc = Dit kan worden aangepast om je virtuele elleboogtrackers omhoog of omlaag te verplaatsen, zodat wordt voorkomen dat VRChat per ongeluk een elleboogtracker aan de borst koppelt.
|
||||
|
||||
## Tracker reset buttons
|
||||
|
||||
reset-reset_all = Alle afmetingen resetten
|
||||
reset-reset_all_warning-v2 =
|
||||
<b>Waarschuwing:</b> Je verhoudingen worden teruggezet naar de standaardinstellingen, geschaald op basis van je ingestelde lengte.
|
||||
Weet je zeker dat je dit wilt doen?
|
||||
reset-reset_all_warning-reset = Verhoudingen resetten
|
||||
reset-reset_all_warning-cancel = Annuleren
|
||||
reset-reset_all_warning_default-v2 =
|
||||
<b>Waarschuwing:</b> Uw lengte is nog niet ingesteld, je verhoudingen worden teruggezet naar de standaardinstellingen met de standaard lengte.
|
||||
Weet je zeker dat je dit wilt doen?
|
||||
reset-full = Volledige reset
|
||||
reset-mounting = Reset montage
|
||||
reset-mounting-feet = Reset voetmontage
|
||||
reset-mounting-fingers = Reset vingermontage
|
||||
reset-yaw = Yaw Reset
|
||||
|
||||
## Serial detection stuff
|
||||
@@ -122,6 +268,7 @@ navbar-settings = Instellingen
|
||||
|
||||
bvh-start_recording = BVH opnemen
|
||||
bvh-recording = Opname bezig...
|
||||
bvh-save_title = Sla BVH-opname op
|
||||
|
||||
## Tracking pause
|
||||
|
||||
@@ -162,6 +309,7 @@ widget-imu_visualizer-rotation_raw = Rauw
|
||||
widget-imu_visualizer-rotation_preview = Preview
|
||||
widget-imu_visualizer-acceleration = Versnelling
|
||||
widget-imu_visualizer-position = Positie
|
||||
widget-imu_visualizer-stay_aligned = Blijf in lijn
|
||||
|
||||
## Widget: Skeleton Visualizer
|
||||
|
||||
@@ -189,6 +337,7 @@ tracker-table-column-temperature = Temp. °C
|
||||
tracker-table-column-linear-acceleration = Accel. X/Y/Z
|
||||
tracker-table-column-rotation = Rotatie X/Y/Z
|
||||
tracker-table-column-position = Positie X/Y/Z
|
||||
tracker-table-column-stay_aligned = Blijf in lijn
|
||||
tracker-table-column-url = URL
|
||||
|
||||
## Tracker rotation
|
||||
@@ -238,15 +387,28 @@ tracker-settings-mounting_section-edit = Montage bewerken
|
||||
tracker-settings-drift_compensation_section = Laat drift compensatie toe
|
||||
tracker-settings-drift_compensation_section-description = Moet deze tracker compenseren voor drift wanneer drift compensatie is ingeschakeld?
|
||||
tracker-settings-drift_compensation_section-edit = Laat drift compensatie toe
|
||||
tracker-settings-use_mag = Sta de magnetometer toe op deze tracker.
|
||||
# Multiline!
|
||||
tracker-settings-use_mag-description =
|
||||
Wilt u dat deze tracker de magnetometer gebruikt om drift te verminderen wanneer de magnetometer is toegestaan? <b>Zet de tracker niet uit terwijl u dit aan of uit zet.</b>
|
||||
U moet eerst de magnetometer toestemming geven,<magSetting>click hier om naar de instellingen te gaan</magSetting>.
|
||||
tracker-settings-use_mag-label = Laat magnetometer toe
|
||||
# The .<name> means it's an attribute and it's related to the top key.
|
||||
# In this case that is the settings for the assignment section.
|
||||
tracker-settings-name_section = Trackernaam
|
||||
tracker-settings-name_section-description = Geef een schattige bijnaam :)
|
||||
tracker-settings-name_section-placeholder = NightyBeast's linkerbeen
|
||||
tracker-settings-name_section-label = Trackernaam
|
||||
tracker-settings-forget = Vergeet tracker
|
||||
tracker-settings-forget-description = Verwijdert de tracker van de SlimeVR Server en voorkomt dat deze verbinding kan maken totdat de server opnieuw wordt opgestart. De configuratie van de tracker blijft behouden.
|
||||
tracker-settings-forget-label = Vergeet tracker
|
||||
tracker-settings-update-unavailable = Kan niet worden bijgewerkt (DIY)
|
||||
tracker-settings-update-low-battery = Kan niet worden bijgewerkt. Batterij lager dan 50%
|
||||
tracker-settings-update-up_to_date = Up to date.
|
||||
tracker-settings-update-blocked = Update is niet beschikbaar. Er zijn geen andere versies beschikbaar.
|
||||
tracker-settings-update-available = { $versionName } is nu beschikbaar
|
||||
tracker-settings-update = Werk nu bij.
|
||||
tracker-settings-update-title = Firmware versie
|
||||
|
||||
## Tracker part card info
|
||||
|
||||
@@ -313,6 +475,7 @@ mounting_selection_menu-close = Sluiten
|
||||
settings-sidebar-title = Instellingen
|
||||
settings-sidebar-general = Algemeen
|
||||
settings-sidebar-tracker_mechanics = Trackersinstellingen
|
||||
settings-sidebar-stay_aligned = Blijf in lijn
|
||||
settings-sidebar-fk_settings = FK-instellingen
|
||||
settings-sidebar-gesture_control = Tikbediening
|
||||
settings-sidebar-interface = Interface
|
||||
@@ -322,6 +485,9 @@ settings-sidebar-utils = Hulpmiddelen
|
||||
settings-sidebar-serial = Serieel console
|
||||
settings-sidebar-appearance = Uiterlijk
|
||||
settings-sidebar-notifications = Notificaties
|
||||
settings-sidebar-behavior = Gedrag
|
||||
settings-sidebar-firmware-tool = DIY Firmware Tool
|
||||
settings-sidebar-vrc_warnings = VRChat Configuratie-waarschuwingen
|
||||
settings-sidebar-advanced = Geavanceerd
|
||||
|
||||
## SteamVR settings
|
||||
@@ -376,8 +542,19 @@ settings-general-tracker_mechanics-drift_compensation-description =
|
||||
Veranderd de sterkte van de compensatie en hoeveel resets worden gebruikt.
|
||||
settings-general-tracker_mechanics-drift_compensation-enabled-label = Drift compensate
|
||||
settings-general-tracker_mechanics-drift_compensation-prediction = Voorspelling van driftcompensatie
|
||||
# This cares about multilines
|
||||
settings-general-tracker_mechanics-drift_compensation-prediction-description =
|
||||
Voorspelt compensatie van gierdrift buiten het eerder gemeten bereik.
|
||||
Schakel dit in als jouw trackers continu om de gier-as draaien.
|
||||
settings-general-tracker_mechanics-drift_compensation-prediction-label = Voorspelling van driftcompensatie
|
||||
settings-general-tracker_mechanics-drift_compensation_warning =
|
||||
<b>Waarschuwing:</b> Gebruik alleen driftcompensatie als je heel vaak
|
||||
moet resetten (elke ~5-10 minuten).
|
||||
|
||||
IMU's die vaak worden gereset, zijn onder ander:
|
||||
Joy-Cons, owoTrack en MPU's (zonder recente firmware).
|
||||
settings-general-tracker_mechanics-drift_compensation_warning-cancel = Annuleren
|
||||
settings-general-tracker_mechanics-drift_compensation_warning-done = Ik begrijp het
|
||||
settings-general-tracker_mechanics-drift_compensation-amount-label = Compensatiesterkte
|
||||
settings-general-tracker_mechanics-drift_compensation-max_resets-label = Gebruik de laatste x resets
|
||||
settings-general-tracker_mechanics-save_mounting_reset = Sla de automatische montage reset kalibratie op
|
||||
@@ -388,6 +565,25 @@ settings-general-tracker_mechanics-use_mag_on_all_trackers-description =
|
||||
Gebruikt magnetometer op alle trackers die er een compatibele firmware voor hebben, waardoor drift in stabiele magnetische omgevingen wordt verminderd.
|
||||
Je kan dit per individuele tracker uit zetten in de instellingen van de tracker. <b>Sluit geen van de trackers af terwijl u dit in- en uitschakelt!</b>
|
||||
settings-general-tracker_mechanics-use_mag_on_all_trackers-label = Gebruik magnetometer op de trackers
|
||||
settings-stay_aligned = Blijf in lijn
|
||||
settings-stay_aligned-description = ijf in lijn vermindert drift door je trackers geleidelijk aan te passen zodat ze overeenkomen met je ontspannen houdingen.
|
||||
settings-stay_aligned-setup-label = Blijf in lijn instellen
|
||||
settings-stay_aligned-setup-description = Je moet "Blijf in lijn instellen" voltooien om Blijf in lijn te activeren.
|
||||
settings-stay_aligned-warnings-drift_compensation = ⚠ Schakel Drift Compensation uit! Drift Compensation conflicteert met Blijf in lijn.
|
||||
settings-stay_aligned-enabled-label = Trackers aanpassen
|
||||
settings-stay_aligned-hide_yaw_correction-label = Aanpassing verbergen (om te vergelijken zonder Blijf in lijn)
|
||||
settings-stay_aligned-general-label = Algemeen
|
||||
settings-stay_aligned-relaxed_poses-label = Ontspannen houdingen
|
||||
settings-stay_aligned-relaxed_poses-description = Blijf in lijn gebruikt je ontspannen houdingen om je trackers in lijn te houden. Gebruik "Stel Blijf in lijn in" om deze houdingen bij te werken.
|
||||
settings-stay_aligned-relaxed_poses-standing = Pas trackers aan terwijl je staat
|
||||
settings-stay_aligned-relaxed_poses-sitting = Pas trackers aan terwijl je op een stoel zit
|
||||
settings-stay_aligned-relaxed_poses-flat = Pas trackers aan terwijl je op de grond zit of op je rug ligt.
|
||||
settings-stay_aligned-relaxed_poses-save_pose = Sla houding op
|
||||
settings-stay_aligned-relaxed_poses-reset_pose = Reset houding
|
||||
settings-stay_aligned-relaxed_poses-close = Sluiten
|
||||
settings-stay_aligned-debug-label = Foutopsporing
|
||||
settings-stay_aligned-debug-description = Voeg je instellingen toe wanneer je problemen met Blijf in lijn rapporteert.
|
||||
settings-stay_aligned-debug-copy-label = Instellingen naar klembord kopiëren
|
||||
|
||||
## FK/Tracking settings
|
||||
|
||||
@@ -413,6 +609,11 @@ settings-general-fk_settings-leg_tweak-foot_plant-description = Foot-plant rotee
|
||||
settings-general-fk_settings-leg_fk = Been tracking
|
||||
settings-general-fk_settings-leg_fk-reset_mounting_feet-description = Schakel Montage Reset voor de voeten in door op je tenen te staan.
|
||||
settings-general-fk_settings-leg_fk-reset_mounting_feet = Voeten montage reset.
|
||||
settings-general-fk_settings-enforce_joint_constraints = Bewegingslimieten van het skelet
|
||||
settings-general-fk_settings-enforce_joint_constraints-enforce_constraints = Beperkingen toepassen
|
||||
settings-general-fk_settings-enforce_joint_constraints-enforce_constraints-description = Voorkomt dat gewrichten over hun limiet draaien
|
||||
settings-general-fk_settings-enforce_joint_constraints-correct_constraints = Corrigeren met beperkingen
|
||||
settings-general-fk_settings-enforce_joint_constraints-correct_constraints-description = Corrigeer gewrichtsrotaties wanneer ze hun limiet overschrijden
|
||||
settings-general-fk_settings-arm_fk = Arm tracking
|
||||
settings-general-fk_settings-arm_fk-description = Verander de manier waarop de armen worden getrackt.
|
||||
settings-general-fk_settings-arm_fk-force_arms = Dwing armen vanuit HMD
|
||||
@@ -444,9 +645,6 @@ settings-general-fk_settings-skeleton_settings-interp_knee_tracker_ankle = Berek
|
||||
settings-general-fk_settings-skeleton_settings-interp_knee_ankle = Bereken het gemiddelde van de 'yaw en roll van de knie trackers met die van de enkels'
|
||||
settings-general-fk_settings-self_localization-title = Mocap modus
|
||||
settings-general-fk_settings-self_localization-description = Mocap modus staat het skelet model toe om zijn eigen positie te bepalen zonder het gebruik van een headset of andere trackers. Dit vergt wel het gebruik van voet en hoofd trackers, dit is momenteel nog expirimenteel.
|
||||
settings-general-fk_settings-vive_emulation-title = Vive-emulatie
|
||||
settings-general-fk_settings-vive_emulation-description = Emuleer de problemen met de taille van Vive trackers. Dit is een mop en maakt tracking slechter.
|
||||
settings-general-fk_settings-vive_emulation-label = Vive-emulatie inschakelen
|
||||
|
||||
## Gesture control settings (tracker tapping)
|
||||
|
||||
@@ -518,6 +716,13 @@ settings-general-interface-feedback_sound-volume = Feedback geluid volume
|
||||
settings-general-interface-connected_trackers_warning = Waarschuwing voor verbonden trackers
|
||||
settings-general-interface-connected_trackers_warning-description = Deze optie toont een pop-up bericht telkens wanneer je SlimeVR probeert af te sluiten terwijl er nog trackers verbonden zijn. Dit bericht herinnert je eraan om je trackers uit te schakelen wanneer je klaar bent om de batterijduur te sparen.
|
||||
settings-general-interface-connected_trackers_warning-label = Waarschuwing voor verbonden trackers bij het afsluiten
|
||||
|
||||
## Behavior settings
|
||||
|
||||
settings-interface-behavior = Gedrag
|
||||
settings-general-interface-dev_mode = Ontwikkelaarsmodus
|
||||
settings-general-interface-dev_mode-description = Deze modus kan nuttig zijn als je diepgaande gegevens nodig hebt of op een geavanceerd niveau wilt communiceren met aangesloten trackers.
|
||||
settings-general-interface-dev_mode-label = Ontwikkelaarsmodus
|
||||
settings-general-interface-use_tray = Minimaliseren naar systeem vak
|
||||
settings-general-interface-use_tray-description = Hiermee kun je het venster sluiten zonder de SlimeVR server te beëindigen, zodat je deze op de achtergrond kunt blijven gebruiken zonder dat de GUI in de weg zit.
|
||||
settings-general-interface-use_tray-label = Minimaliseren naar systeem vak
|
||||
@@ -530,6 +735,17 @@ settings-general-interface-discord_presence-message =
|
||||
[one] Gebruikt 1 tracker
|
||||
*[other] Gebruikt { $amount } trackers
|
||||
}
|
||||
settings-interface-behavior-error_tracking = Foutverzameling via Sentry.io
|
||||
settings-interface-behavior-error_tracking-description_v2 =
|
||||
<h1>Geeft u toestemming voor het verzamelen van geanonimiseerde foutgegevens?</h1>
|
||||
|
||||
<b>We verzamelen geen persoonlijke informatie</b> zoals uw IP-adres of draadloze inloggegevens. SlimeVR hecht veel waarde aan uw privacy!
|
||||
|
||||
Om de beste gebruikerservaring te bieden, verzamelen we geanonimiseerde foutrapporten, prestatiestatistieken en informatie over het besturingssysteem. Dit helpt ons bij het detecteren van fouten en problemen met SlimeVR. Deze statistieken worden verzameld via Sentry.io.
|
||||
settings-interface-behavior-error_tracking-label = Stuur fouten naar de ontwikkelaars
|
||||
settings-interface-behavior-bvh_directory = Map om BVH-opnames op te slaan
|
||||
settings-interface-behavior-bvh_directory-description = Kies een map om je BVH-opnames op te slaan, zodat je niet elke keer hoeft te kiezen waar je ze opslaat.
|
||||
settings-interface-behavior-bvh_directory-label = Map voor BVH-opnames
|
||||
|
||||
## Serial settings
|
||||
|
||||
@@ -588,6 +804,13 @@ settings-osc-vrchat-description-v1 = Wijzig instellingen die specifiek zijn voor
|
||||
settings-osc-vrchat-enable = Inschakelen
|
||||
settings-osc-vrchat-enable-description = Schakel het verzenden en ontvangen van gegevens in en uit.
|
||||
settings-osc-vrchat-enable-label = Inschakelen
|
||||
settings-osc-vrchat-oscqueryEnabled = OSCQuery inschakelen
|
||||
settings-osc-vrchat-oscqueryEnabled-description =
|
||||
OSCQuery detecteert automatisch actieve instanties van VRChat en stuurt hen data.
|
||||
Het kan zichzelf ook aan hen bekendmaken om HMD- en controllerdata te ontvangen.
|
||||
Om HMD- en controllerdata van VRChat te kunnen ontvangen, ga je in het hoofdmenu naar Instellingen,
|
||||
onder "Tracking & IK", en schakel je "Allow Sending Head and Wrist VR Tracking OSC Data" in.
|
||||
settings-osc-vrchat-oscqueryEnabled-label = Schakel OSCQuery in
|
||||
settings-osc-vrchat-network = Netwerkpoorten
|
||||
settings-osc-vrchat-network-description-v1 = Stel de poorten in voor het ontvangen en verzenden van tracking data. Kan op standaardinstellingen blijven voor VRChat.
|
||||
settings-osc-vrchat-network-port_in =
|
||||
@@ -630,6 +853,7 @@ settings-osc-vmc-network-address-description = Stel het adres in waarnaar gegeve
|
||||
settings-osc-vmc-network-address-placeholder = IPV4-adres
|
||||
settings-osc-vmc-vrm = VRM Model
|
||||
settings-osc-vmc-vrm-description = Laad een VRM-model om hoofdverankering mogelijk te maken en zorg voor een hogere compatibiliteit met andere applicaties.
|
||||
settings-osc-vmc-vrm-untitled_model = Naamloos model
|
||||
settings-osc-vmc-vrm-file_select = Sleep een modelbestand naar hier om ze te gebruiken of <u>blader</u>.
|
||||
settings-osc-vmc-anchor_hip = Heupverankering
|
||||
settings-osc-vmc-anchor_hip-description = Veranker de tracking aan de heupen, handig voor zittende VTubing. Als u deze uitschakelt, laadt u een VRM-model.
|
||||
@@ -650,11 +874,26 @@ settings-utils-advanced-reset-server-label = Tracking resetten
|
||||
settings-utils-advanced-reset-all = Alle instellingen resetten
|
||||
settings-utils-advanced-reset-all-description = Herstel de standaardwaarden voor instellingen van beide de interface en de tracking.
|
||||
settings-utils-advanced-reset-all-label = Alles resetten
|
||||
settings-utils-advanced-reset_warning =
|
||||
{ $type ->
|
||||
[gui]
|
||||
<b>Waarschuwing</b>Hiermee worden al uw GUI instellingen teruggezet naar de standaardinstellingen.
|
||||
Weet u zeker dat u dit wilt doen?
|
||||
[server]
|
||||
<b>Waarschuwing</b>Hiermee worden al uw tracking instellingen teruggezet naar de standaardinstellingen.
|
||||
Weet u zeker dat u dit wilt doen?
|
||||
*[all]
|
||||
<b>Waarschuwing:</b> Hiermee worden al uw instellingen teruggezet naar de standaardinstellingen.
|
||||
Weet u zeker dat u dit wilt doen?
|
||||
}
|
||||
settings-utils-advanced-reset_warning-reset = Instellingen resetten
|
||||
settings-utils-advanced-reset_warning-cancel = Annuleren
|
||||
settings-utils-advanced-open_data = Gegevensmap
|
||||
settings-utils-advanced-open_data-description = Open de gegevensmap van SlimeVR in de bestandsverkenner, met configuratie- en logbestanden.
|
||||
settings-utils-advanced-open_data-v1 = Configuratiemap
|
||||
settings-utils-advanced-open_data-description-v1 = Open de configuratiemap van SlimeVR in de bestandsverkenner, met configuratiebestanden.
|
||||
settings-utils-advanced-open_data-label = Map openen
|
||||
settings-utils-advanced-open_logs = logboeken
|
||||
settings-utils-advanced-open_logs-description = Open de logmap van SlimeVR in de bestandsverkenner, met de logboeken van de app
|
||||
settings-utils-advanced-open_logs-label = Map openen
|
||||
|
||||
## Setup/onboarding menu
|
||||
|
||||
@@ -746,6 +985,17 @@ onboarding-connect_tracker-connection_status-looking_for_server = Op zoek naar s
|
||||
onboarding-connect_tracker-connection_status-connection_error = Kan geen verbinding maken met Wi-Fi
|
||||
onboarding-connect_tracker-connection_status-could_not_find_server = Kan de server niet vinden
|
||||
onboarding-connect_tracker-connection_status-done = Verbonden met de server
|
||||
onboarding-connect_tracker-connection_status-no_serial_log = Kon geen logbestanden van de tracker ophalen.
|
||||
onboarding-connect_tracker-connection_status-no_serial_device_found = Kon geen tracker via USB vinden.
|
||||
onboarding-connect_serial-error-modal-no_serial_log = Staat de tracker aan?
|
||||
onboarding-connect_serial-error-modal-no_serial_log-desc = Zorg dat de tracker aan staat en verbonden is met je computer.
|
||||
onboarding-connect_serial-error-modal-no_serial_device_found = Geen trackers gedetecteerd
|
||||
onboarding-connect_serial-error-modal-no_serial_device_found-desc =
|
||||
Sluit een tracker met de meegeleverde USB-kabel aan op je computer en zet de tracker aan.
|
||||
Als dit niet werkt:
|
||||
-probeer een andere USB-kabel
|
||||
-probeer een andere USB-poort
|
||||
-probeer de SlimeVR-server opnieuw te installeren en selecteer "USB Drivers" in het onderdeelkeuze-menu
|
||||
# $amount (Number) - Amount of trackers connected (this is a number, but you can use CLDR plural rules for your language)
|
||||
# More info on https://www.unicode.org/cldr/cldr-aux/charts/22/supplemental/language_plural_rules.html
|
||||
# English in this case only has 2 plural rules, which are "one" and "other",
|
||||
@@ -763,7 +1013,7 @@ onboarding-connect_tracker-next = Ik heb al mijn trackers verbonden
|
||||
|
||||
onboarding-calibration_tutorial = Handleiding voor IMU-kalibratie
|
||||
onboarding-calibration_tutorial-subtitle = Helpt met het verminderen van het driften van de trackers!
|
||||
onboarding-calibration_tutorial-description = Elke keer dat je jouw trackers inschakelt, moeten ze even op een plat oppervlak rusten om te kalibreren. Leg al je trackers op een vlak oppervlak en <b>verplaats ze niet!</b>
|
||||
onboarding-calibration_tutorial-description-v1 = Zet je trackers aan en leg ze even op een stabiele ondergrond om te kalibreren. Kalibratie kan op elk moment na het inschakelen van de trackers worden uitgevoerd — deze pagina biedt alleen een handleiding. Klik op de knop "{ onboarding-calibration_tutorial-calibrate }" om te beginnen en <b>beweeg je trackers daarna niet meer!</b>
|
||||
onboarding-calibration_tutorial-calibrate = Al mijn trackers liggen neer
|
||||
onboarding-calibration_tutorial-status-waiting = Ik wacht op jou
|
||||
onboarding-calibration_tutorial-status-calibrating = Kalibreren
|
||||
@@ -891,7 +1141,7 @@ onboarding-assign_trackers-warning-WAIST =
|
||||
|
||||
onboarding-choose_mounting = Welke montagekalibratiemethode moet worden gebruikt?
|
||||
# Multiline text
|
||||
onboarding-choose_mounting-description = De oriëntatie van de montage corrigeert de plaatsing van trackers op uw lichaam.
|
||||
onboarding-choose_mounting-description = De oriëntatie van de montage corrigeert de plaatsing van trackers op je lichaam.
|
||||
onboarding-choose_mounting-auto_mounting = Automatische bevestiging
|
||||
# Italicized text
|
||||
onboarding-choose_mounting-auto_mounting-label-v2 = Aanbevolen
|
||||
@@ -929,43 +1179,27 @@ onboarding-automatic_mounting-mounting_reset-title = Montage-reset
|
||||
onboarding-automatic_mounting-mounting_reset-step-0 = 1. Ga staan in een "skie"-houding met gebogen benen, je bovenlichaam naar voren gekanteld en armen gebogen.
|
||||
onboarding-automatic_mounting-mounting_reset-step-1 = 2. Druk op de knop "Reset montage" en wacht 3 seconden voordat de montagerichtingen van de trackers opnieuw worden ingesteld.
|
||||
onboarding-automatic_mounting-preparation-title = Voorbereiding
|
||||
onboarding-automatic_mounting-preparation-step-0 = 1. Sta rechtop met je armen langs je zij.
|
||||
onboarding-automatic_mounting-preparation-step-1 = 2. Druk op de knop "Resetten" en wacht 3 seconden voordat de trackers opnieuw worden ingesteld.
|
||||
onboarding-automatic_mounting-preparation-v2-step-0 = 1. Druk op de knop "Volledige reset".
|
||||
onboarding-automatic_mounting-preparation-v2-step-1 = 2. Ga rechtop staan met je armen langs je zij. Zorg dat je recht vooruit kijkt.
|
||||
onboarding-automatic_mounting-preparation-v2-step-2 = 3. Houd deze houding aan totdat de timer van 3 seconden is afgelopen.
|
||||
onboarding-automatic_mounting-put_trackers_on-title = Doe je trackers aan
|
||||
onboarding-automatic_mounting-put_trackers_on-description = Om montagerichtingen te kalibreren gaan we gebruik maken van de trackers die je net hebt toegewezen. Doe al je trackers aan, je kunt zien welke trackers welke zijn in de figuur rechts.
|
||||
onboarding-automatic_mounting-put_trackers_on-next = Ik heb al mijn trackers aan
|
||||
|
||||
## Tracker proportions method choose
|
||||
|
||||
onboarding-choose_proportions = Welke verhoudingskalibratiemethode moet worden gebruikt?
|
||||
# Multiline string
|
||||
onboarding-choose_proportions-description-v1 =
|
||||
Lichaamsverhoudingen worden gebruikt om de afmetingen van je lichaam te bepalen. Deze informatie is nodig om de posities van de trackers te berekenen.
|
||||
Als de verhoudingen van je lichaam niet overeenkomen met de opgeslagen waarden, zal de tracking-precisie slechter zijn. Je kunt dan last krijgen van ongemakkelijke effecten zoals schuiven of glijden, of kan je lichaam niet goed overeenkomen met je avatar in VR.
|
||||
<b>Je hoeft je lichaam maar één keer te meten!</b> Tenzij de metingen onjuist zijn of je lichaam is veranderd, hoef je dit niet opnieuw te doen.
|
||||
onboarding-choose_proportions-auto_proportions = Automatische verhoudingen
|
||||
# Italicized text
|
||||
onboarding-choose_proportions-auto_proportions-subtitle = Aanbevolen
|
||||
onboarding-choose_proportions-auto_proportions-descriptionv3 =
|
||||
Deze functie zal je lichaamsverhoudingen schatten door een sample van je bewegingen op te nemen en deze door een algoritme te laten analyseren.
|
||||
<b>Hiervoor moet je headset (HMD) verbonden zijn met SlimeVR en op je hoofd zitten!</b>
|
||||
onboarding-choose_proportions-manual_proportions = Handmatige lichaamsverhoudingen
|
||||
# Italicized text
|
||||
onboarding-choose_proportions-manual_proportions-subtitle = Voor kleine details
|
||||
onboarding-choose_proportions-manual_proportions-description = Hier kan je jouw verhoudingen handmatig aanpassen
|
||||
onboarding-choose_proportions-export = Export proporties
|
||||
onboarding-choose_proportions-import = Importeer proporties
|
||||
onboarding-choose_proportions-import-success = geïmporteerd
|
||||
onboarding-choose_proportions-import-failed = Mislukt
|
||||
onboarding-choose_proportions-file_type = Lichaamsproporties bestand
|
||||
|
||||
## Tracker manual proportions setup
|
||||
## Tracker manual proportions setupa
|
||||
|
||||
onboarding-manual_proportions-back = Ga terug naar de reset tutorial
|
||||
onboarding-manual_proportions-title = Handmatige lichaamsverhoudingen
|
||||
onboarding-manual_proportions-precision = Precisie-aanpassing
|
||||
onboarding-manual_proportions-auto = Automatische kalibratie
|
||||
onboarding-manual_proportions-ratio = Aanpassen via verhoudingen
|
||||
onboarding-manual_proportions-fine_tuning_button = Automatisch afstemmen van verhoudingen
|
||||
onboarding-manual_proportions-fine_tuning_button-disabled-tooltip = Sluit een VR-headset aan om automatische fijnafstelling te gebruiken
|
||||
onboarding-manual_proportions-export = Export proporties
|
||||
onboarding-manual_proportions-import = Importeer proporties
|
||||
onboarding-manual_proportions-file_type = Lichaamsproporties bestand
|
||||
onboarding-manual_proportions-normal_increment = Normale verhoging
|
||||
onboarding-manual_proportions-precise_increment = Nauwkeurige verhoging
|
||||
onboarding-manual_proportions-grouped_proportions = Gegroepeerde verhoudingen
|
||||
onboarding-manual_proportions-all_proportions = Alle verhoudingen
|
||||
onboarding-manual_proportions-estimated_height = Geschatte gebruikerslengte
|
||||
|
||||
## Tracker automatic proportions setup
|
||||
|
||||
@@ -981,21 +1215,31 @@ onboarding-automatic_proportions-requirements-title = Vereisten
|
||||
# Each line of text is a different list item
|
||||
onboarding-automatic_proportions-requirements-descriptionv2 = Je hebt voldaan aan de minimale vereisten om je voeten te tracken (over het algemeen 5 trackers). Je hebt je trackers en headset aan en draagt ze. Je trackers en headset zijn verbonden met de SlimeVR server en werken naar behoren (zonder haperingen, loskoppelingen etc.). Je headset stuurt positiedata naar de SlimeVR server (dit vereist doorgaans dat SteamVR draait en verbonden is met SlimeVR via de SlimeVR SteamVR-driver). De tracking werkt en registreert je bewegingen nauwkeurig (je hebt bijvoorbeeld een volledige reset uitgevoerd en de trackers bewegen in de juiste richting bij schoppen, bukken, zitten etc.).
|
||||
onboarding-automatic_proportions-requirements-next = Ik heb de vereisten gelezen
|
||||
onboarding-automatic_proportions-check_height-title = Controleer je lengte
|
||||
onboarding-automatic_proportions-check_height-description =
|
||||
We gebruiken je lengte als een basis voor onze metingen middels de HMD's hoogte, hiermee bepalen we je echte lengte.
|
||||
Maar het is beter om zelf te controleren of dit klopt.
|
||||
onboarding-automatic_proportions-check_height-title-v3 = Meet de hoogte van uw headset
|
||||
onboarding-automatic_proportions-check_height-description-v2 = De hoogte van uw headset (HMD) moet iets minder zijn dan uw volledige lengte, aangezien headsets uw ooghoogte meten. Deze meting wordt gebruikt als basis voor uw lichaamsverhoudingen.
|
||||
# All the text is in bold!
|
||||
onboarding-automatic_proportions-check_height-calculation_warning = Druk op de knop terwijl je <u>rechtop</u> staat om je lengte te berekenen. Je hebt 3 seconden na dat je op de knop drukt!
|
||||
onboarding-automatic_proportions-check_height-calculation_warning-v3 = Begin met meten terwijl je <u>rechtop</u> staat om je lengte te meten. Let erop dat je je handen niet hoger dan je headset tilt, want dat kan de meting beïnvloeden!
|
||||
onboarding-automatic_proportions-check_height-guardian_tip = Als je een losse VR-bril gebruikt, zorg er dan voor dat je guardian/veilige zone is ingeschakeld zodat je lengte correct is gekalibreerd!
|
||||
onboarding-automatic_proportions-check_height-fetch_height = Ik sta!
|
||||
# Context is that the height is unknown
|
||||
onboarding-automatic_proportions-check_height-unknown = Onbekend
|
||||
# Shows an element below it
|
||||
onboarding-automatic_proportions-check_height-hmd_height1 = Je HMD lengte is
|
||||
# Shows an element below it
|
||||
onboarding-automatic_proportions-check_height-height1 = Je echte lengte is
|
||||
onboarding-automatic_proportions-check_height-hmd_height2 = De hoogte van uw headset is:
|
||||
onboarding-automatic_proportions-check_height-measure-start = Begin met meten
|
||||
onboarding-automatic_proportions-check_height-measure-stop = Stoppen met meten
|
||||
onboarding-automatic_proportions-check_height-measure-reset = Probeer opnieuw te meten
|
||||
onboarding-automatic_proportions-check_height-next_step = Ze zijn goed
|
||||
onboarding-automatic_proportions-check_floor_height-title = Meet uw vloerhoogte (optioneel)
|
||||
onboarding-automatic_proportions-check_floor_height-description = In sommige gevallen wordt uw vloerhoogte mogelijk niet correct ingesteld door uw headset, waardoor de hoogte van de headset hoger wordt gemeten dan zou moeten. U kunt de "hoogte" van uw vloer meten om de hoogte van uw headset te corrigeren.
|
||||
# All the text is in bold!
|
||||
onboarding-automatic_proportions-check_floor_height-calculation_warning-v2 = Begin met meten en zet een controller op je vloer om de hoogte te meten. Als je zeker weet dat je vloerhoogte klopt, kun je deze stap overslaan.
|
||||
# Shows an element below it
|
||||
onboarding-automatic_proportions-check_floor_height-floor_height = Uw vloerhoogte is:
|
||||
onboarding-automatic_proportions-check_floor_height-full_height = Uw geschatte volledige lengte is:
|
||||
onboarding-automatic_proportions-check_floor_height-measure-start = Begin met meten
|
||||
onboarding-automatic_proportions-check_floor_height-measure-stop = Stoppen met meten
|
||||
onboarding-automatic_proportions-check_floor_height-measure-reset = Probeer opnieuw te meten
|
||||
onboarding-automatic_proportions-check_floor_height-skip_step = Sla deze stap over en sla op.
|
||||
onboarding-automatic_proportions-check_floor_height-next_step = Gebruik vloerhoogte en bespaar
|
||||
onboarding-automatic_proportions-start_recording-title = Zorg dat je klaar bent om te bewegen
|
||||
onboarding-automatic_proportions-start_recording-description = We gaan nu enkele specifieke houdingen en bewegingen opnemen. Deze worden in het volgende scherm geprompt. Zorg dat je klaar bent om te beginnen als de knop wordt ingedrukt!
|
||||
onboarding-automatic_proportions-start_recording-next = Start opname
|
||||
@@ -1030,6 +1274,73 @@ onboarding-automatic_proportions-error_modal-v2 =
|
||||
Dit is waarschijnlijk een probleem met de montagekalibratie. Zorg ervoor dat je tracking goed werkt voordat je het opnieuw probeert.
|
||||
<docs>Bekijk de documentatie</docs> of word lid van onze <discord>Discord</discord> voor hulp ^_^
|
||||
onboarding-automatic_proportions-error_modal-confirm = Begrepen!
|
||||
onboarding-automatic_proportions-smol_warning =
|
||||
Uw ingestelde lengte van { $height } is lager dan de toegestane minimumlengte van { $minHeight }.
|
||||
<b>Voer de metingen opnieuw uit en controleer of ze correct zijn.</b>
|
||||
onboarding-automatic_proportions-smol_warning-cancel = Ga terug
|
||||
|
||||
## Tracker scaled proportions setup
|
||||
|
||||
onboarding-scaled_proportions-title = Geschaalde proporties
|
||||
onboarding-scaled_proportions-description =
|
||||
Voor een correcte werking van de SlimeVR-trackers hebben we de lengte van uw botten nodig.
|
||||
We gebruiken hiervoor een gemiddelde lichaamsverhouding, geschaald op basis van uw lengte.
|
||||
onboarding-scaled_proportions-manual_height-title = Configureer uw lengte
|
||||
onboarding-scaled_proportions-manual_height-description-v2 = Deze lengte wordt gebruikt als basis voor je lichaamsverhoudingen.
|
||||
onboarding-scaled_proportions-manual_height-missing_steamvr =
|
||||
SteamVR is momenteel niet verbonden met SlimeVR, dus metingen kunnen niet worden gebaseerd op je headset.
|
||||
<b>Ga verder op eigen risico of raadpleeg de documentatie!</b>
|
||||
onboarding-scaled_proportions-manual_height-height-v2 = Uw volledige lengte is
|
||||
onboarding-scaled_proportions-manual_height-estimated_height = De geschatte hoogte van uw headset is:
|
||||
onboarding-scaled_proportions-manual_height-next_step = Opslaan en doorgaan
|
||||
onboarding-scaled_proportions-manual_height-warning =
|
||||
Je gebruikt momenteel de handmatige manier om geschaalde verhoudingen in te stellen!
|
||||
<b>Deze modus wordt alleen aanbevolen als je geen HMD met SlimeVR gebruikt.</b>
|
||||
|
||||
Om de automatische geschaalde verhoudingen te kunnen gebruiken, doe het volgende:
|
||||
onboarding-scaled_proportions-manual_height-warning-no_hmd = Sluit een VR-headset aan
|
||||
onboarding-scaled_proportions-manual_height-warning-no_controllers = Zorg ervoor dat je controllers zijn verbonden en correct aan je handen zijn toegewezen
|
||||
|
||||
## Tracker scaled proportions reset
|
||||
|
||||
onboarding-scaled_proportions-reset_proportion-title = Reset je lichaamsverhoudingen
|
||||
onboarding-scaled_proportions-reset_proportion-description = Om je lichaamsverhoudingen op basis van je lengte in te stellen, moet je nu al je verhoudingen resetten. Dit zal alle verhoudingen die je hebt ingesteld wissen en een basisconfiguratie bieden.
|
||||
onboarding-scaled_proportions-done-title = Lichaamsverhoudingen ingesteld
|
||||
onboarding-scaled_proportions-done-description = Je lichaamsverhoudingen zouden nu gebaseerd moeten zijn op je lengte
|
||||
|
||||
## Stay Aligned setup
|
||||
|
||||
onboarding-stay_aligned-title = Blijf in lijn
|
||||
onboarding-stay_aligned-description = Stel Blijf in lijn in om je trackers in lijn te houden.
|
||||
onboarding-stay_aligned-put_trackers_on-title = Doe je trackers aan
|
||||
onboarding-stay_aligned-put_trackers_on-description = Om je ontspannen houdingen op te slaan, gebruiken we de trackers die je zojuist hebt toegewezen. Doe al je trackers om; je kunt zien welke welke zijn op de afbeelding rechts.
|
||||
onboarding-stay_aligned-put_trackers_on-trackers_warning = Je hebt momenteel minder dan 5 trackers aangesloten en toegewezen! Dit is het minimale aantal trackers dat nodig is om Blijf in lijn goed te laten werken.
|
||||
onboarding-stay_aligned-put_trackers_on-next = Ik heb al mijn trackers aan
|
||||
onboarding-stay_aligned-verify_mounting-title = Controleer je montage
|
||||
onboarding-stay_aligned-verify_mounting-step-0 = Blijf in lijn vereist een goede montage. Anders krijg je geen goede ervaring met Blijf in lijn.
|
||||
onboarding-stay_aligned-verify_mounting-step-1 = 1. Beweeg terwijl je staat.
|
||||
onboarding-stay_aligned-verify_mounting-step-2 = 2. Ga zitten en beweeg je benen en voeten.
|
||||
onboarding-stay_aligned-verify_mounting-step-3 = 3. Als je trackers niet op de juiste plek zitten, druk dan op "Montagekalibratie opnieuw uitvoeren".
|
||||
onboarding-stay_aligned-verify_mounting-redo_mounting = Montagekalibratie opnieuw uitvoeren
|
||||
onboarding-stay_aligned-preparation-title = Voorbereiding
|
||||
onboarding-stay_aligned-preparation-tip = Zorg dat je rechtop staat. Blijf recht vooruit kijken en houd je armen langs je zij.
|
||||
onboarding-stay_aligned-relaxed_poses-standing-title = Ontspannen Staande Houding
|
||||
onboarding-stay_aligned-relaxed_poses-standing-step-0 = 1. Ga in een comfortabele houding staan. Ontspan!
|
||||
onboarding-stay_aligned-relaxed_poses-standing-step-1-v2 = 2. Druk op de knop "Houding opslaan".
|
||||
onboarding-stay_aligned-relaxed_poses-sitting-title = Ontspannen Zittende Houding op Stoel
|
||||
onboarding-stay_aligned-relaxed_poses-sitting-step-0 = 1. Ga comfortabel zitten. Ontspan!
|
||||
onboarding-stay_aligned-relaxed_poses-sitting-step-1-v2 = 2. Druk op de knop "Houding opslaan".
|
||||
onboarding-stay_aligned-relaxed_poses-flat-title = Ontspannen Zittende Houding op de Vloer
|
||||
onboarding-stay_aligned-relaxed_poses-flat-step-0 = 1. Ga op de vloer zitten met je benen voor je uit. Ontspan!
|
||||
onboarding-stay_aligned-relaxed_poses-flat-step-1-v2 = 2. Druk op de knop "Houding opslaan".
|
||||
onboarding-stay_aligned-relaxed_poses-skip_step = Overslaan
|
||||
onboarding-stay_aligned-done-title = Blijf in lijn ingeschakeld!
|
||||
onboarding-stay_aligned-done-description = Je Blijf in lijn-instelling is voltooid!
|
||||
onboarding-stay_aligned-done-description-2 = De setup is voltooid! Je kunt het proces opnieuw starten als je de houdingen opnieuw wilt kalibreren.
|
||||
onboarding-stay_aligned-previous_step = Vorige
|
||||
onboarding-stay_aligned-next_step = Volgende
|
||||
onboarding-stay_aligned-restart = Herstarten
|
||||
onboarding-stay_aligned-done = Klaar
|
||||
|
||||
## Home
|
||||
|
||||
@@ -1054,6 +1365,11 @@ status_system-StatusSteamVRDisconnected =
|
||||
}
|
||||
status_system-StatusTrackerError = De { $trackerName } tracker heeft een error.
|
||||
status_system-StatusUnassignedHMD = De VR-headset moet worden toegewezen als hoofdtracker.
|
||||
status_system-StatusPublicNetwork =
|
||||
{ $count ->
|
||||
[one] Je netwerkprofiel staat momenteel ingesteld op Openbaar ({ $adapters }). Dit wordt niet aanbevolen voor een goede werking van SlimeVR. <PublicFixLink>Hier lees je hoe je het kunt oplossen.</PublicFixLink>
|
||||
*[other] Sommige van je netwerkadapters staan ingesteld op openbaar: { $adapters }. Dit wordt niet aanbevolen voor een goede werking van SlimeVR. <PublicFixLink>Hier lees je hoe je dit kunt oplossen.</PublicFixLink>
|
||||
}
|
||||
|
||||
## Firmware tool globals
|
||||
|
||||
@@ -1066,7 +1382,7 @@ firmware_tool-loading = Laden...
|
||||
## Firmware tool Steps
|
||||
|
||||
firmware_tool = DIY firmware-tool
|
||||
firmware_tool-description = Hiermee kunt u uw DIY-trackers configureren en flashen
|
||||
firmware_tool-description = Hiermee kan je uw DIY-trackers configureren en flashen
|
||||
firmware_tool-not_available = Oeps, de firmwaretool is momenteel niet beschikbaar. Kom later terug!
|
||||
firmware_tool-not_compatible = De firmwaretool is niet compatibel met deze versie van de server. Gelieve te updaten!
|
||||
firmware_tool-board_step = Selecteer je bord
|
||||
@@ -1079,11 +1395,80 @@ firmware_tool-board_pins_step-enable_led = LED inschakelen
|
||||
firmware_tool-board_pins_step-led_pin =
|
||||
.label = LED-pin
|
||||
.placeholder = Voer het adres van de LED-pin in
|
||||
firmware_tool-board_pins_step-battery_type = Selecteer het batterijtype
|
||||
firmware_tool-board_pins_step-battery_type-BAT_EXTERNAL = Externe batterij
|
||||
firmware_tool-board_pins_step-battery_type-BAT_INTERNAL = Interne batterij
|
||||
firmware_tool-board_pins_step-battery_type-BAT_INTERNAL_MCP3021 = Interne MCP3021
|
||||
firmware_tool-board_pins_step-battery_type-BAT_MCP3021 = MCP3021
|
||||
firmware_tool-board_pins_step-battery_sensor_pin =
|
||||
.label = Batterij sensor Pin
|
||||
.placeholder = Voer het pin-adres van de batterij sensor in
|
||||
firmware_tool-board_pins_step-battery_resistor =
|
||||
.label = Batterij Weerstand (Ohm)
|
||||
.placeholder = Voer de waarde van de batterijweerstand in
|
||||
firmware_tool-board_pins_step-battery_shield_resistor-0 =
|
||||
.label = Batterij Shield R1 (Ohm)
|
||||
.placeholder = Voer de waarde in van Battery Shield R1
|
||||
firmware_tool-board_pins_step-battery_shield_resistor-1 =
|
||||
.label = Batterij Shield R2 (Ohm)
|
||||
.placeholder = Voer de waarde in van Battery Shield R2
|
||||
firmware_tool-add_imus_step = Declareer uw IMU's
|
||||
firmware_tool-add_imus_step-description =
|
||||
Voeg de IMU's toe die je tracker heeft
|
||||
Als je de SlimeVR-documentatie hebt gevolgd, zouden de standaardwaarden correct moeten zijn.
|
||||
firmware_tool-add_imus_step-imu_type-label = IMU-type
|
||||
firmware_tool-add_imus_step-imu_type-placeholder = Selecteer het type IMU
|
||||
firmware_tool-add_imus_step-imu_rotation =
|
||||
.label = IMU-rotatie (graden)
|
||||
.placeholder = Rotatie van de IMU
|
||||
firmware_tool-add_imus_step-scl_pin =
|
||||
.label = SCL-pin
|
||||
.placeholder = Pin-adres van SCL
|
||||
firmware_tool-add_imus_step-sda_pin =
|
||||
.label = SDA-pin
|
||||
.placeholder = Pin-adres van SDA
|
||||
firmware_tool-add_imus_step-int_pin =
|
||||
.label = INT-pin
|
||||
.placeholder = Pin-adres van INT
|
||||
firmware_tool-add_imus_step-optional_tracker =
|
||||
.label = Optionele tracker
|
||||
firmware_tool-add_imus_step-show_less = Toon minder
|
||||
firmware_tool-add_imus_step-show_more = Toon meer
|
||||
firmware_tool-add_imus_step-add_more = Voeg meer IMU's toe
|
||||
firmware_tool-select_firmware_step = Selecteer de firmwareversie
|
||||
firmware_tool-select_firmware_step-description = Kies de versie van de firmware die je wilt gebruiken
|
||||
firmware_tool-select_firmware_step-show-third-party =
|
||||
.label = Firmware van derden weergeven
|
||||
firmware_tool-flash_method_step = Flashing methode
|
||||
firmware_tool-flash_method_step-description = Kies de flashingsmethode die je wilt gebruiken
|
||||
firmware_tool-flash_method_step-ota =
|
||||
.label = OTA
|
||||
.description = Gebruik de draadloze methode. Je tracker zal de Wi-Fi gebruiken om de firmware bij te werken. Werkt alleen op reeds geconfigureerde trackers.
|
||||
firmware_tool-flash_method_step-serial =
|
||||
.label = Serial
|
||||
.description = Gebruik een USB-kabel om je tracker bij te werken.
|
||||
firmware_tool-flashbtn_step = Druk op de bootknop
|
||||
firmware_tool-flashbtn_step-description = Voordat u naar de volgende stap gaat, zijn er een paar dingen die u moet doen.
|
||||
firmware_tool-flashbtn_step-board_SLIMEVR = Zet de tracker uit, verwijder de behuizing (indien aanwezig), verbind een USB-kabel met deze computer en voer vervolgens een van de volgende stappen uit, afhankelijk van de revisie van uw SlimeVR-board:
|
||||
firmware_tool-flashbtn_step-board_SLIMEVR-r11 = Zet de tracker aan terwijl u het tweede rechthoekige FLASH-pad vanaf de rand aan de bovenkant van het board kortsluit, en het metalen schild van de microcontroller.
|
||||
firmware_tool-flashbtn_step-board_SLIMEVR-r12 = Zet de tracker aan terwijl u het ronde FLASH-pad aan de bovenkant van het board kortsluit, en het metalen schild van de microcontroller.
|
||||
firmware_tool-flashbtn_step-board_SLIMEVR-r14 = Zet de tracker aan terwijl u de FLASH-knop aan de bovenkant van het board indrukt.
|
||||
firmware_tool-flashbtn_step-board_OTHER =
|
||||
Voordat u gaat flashen, moet de tracker waarschijnlijk in de bootloader-modus worden gezet.
|
||||
Meestal betekent dit het indrukken van de bootknop op het board voordat het flashproces begint.
|
||||
Als het flashproces time-out bij het begin van het flashen, betekent dit waarschijnlijk dat de tracker niet in de bootloader-modus stond.
|
||||
Raadpleeg de flitsinstructies van uw board om te weten hoe u de bootloader-modus inschakelt.
|
||||
firmware_tool-flash_method_ota-devices = Gedetecteerde OTA-apparaten:
|
||||
firmware_tool-flash_method_ota-no_devices = Er zijn geen boards die via OTA bijgewerkt kunnen worden, zorg ervoor dat u het juiste boardtype heeft geselecteerd.
|
||||
firmware_tool-flash_method_serial-wifi = Wi-Fi-gegevens:
|
||||
firmware_tool-flash_method_serial-devices-label = Gedetecteerde serial apparaten:
|
||||
firmware_tool-flash_method_serial-devices-placeholder = Selecteer een serieel apparaat
|
||||
firmware_tool-flash_method_serial-no_devices = Er zijn geen compatibele seriële apparaten gedetecteerd, zorg ervoor dat de tracker is aangesloten.
|
||||
firmware_tool-build_step = Aan het bouwen
|
||||
firmware_tool-build_step-description = De firmware wordt gebouwd, even geduld a.u.b.
|
||||
firmware_tool-flashing_step = Firmware aan het uploaden
|
||||
firmware_tool-flashing_step-description = Je trackers worden geflashed, volg de instructies op het scherm
|
||||
firmware_tool-flashing_step-warning = Trek de tracker niet los en start hem niet opnieuw op tijdens het uploadproces, tenzij dit wordt verteld, hierdoor kan je bord onbruikbaar worden
|
||||
firmware_tool-flashing_step-warning-v2 = Koppel de tracker niet los en zet hem niet uit tijdens het uploadproces, tenzij dit wordt aangegeven. Dit kan je apparaat onbruikbaar maken.
|
||||
firmware_tool-flashing_step-flash_more = Flash meer trackers
|
||||
firmware_tool-flashing_step-exit = Sluit
|
||||
|
||||
@@ -1094,17 +1479,24 @@ firmware_tool-build-DOWNLOADING_FIRMWARE = Firmware wordt gedownload
|
||||
firmware_tool-build-EXTRACTING_FIRMWARE = Firmware wordt uitgepakt
|
||||
firmware_tool-build-SETTING_UP_DEFINES = Configureren van de definities
|
||||
firmware_tool-build-BUILDING = Firmware wordt gebouwd
|
||||
firmware_tool-build-SAVING = De build opslaan
|
||||
firmware_tool-build-DONE = Build voltooid
|
||||
firmware_tool-build-ERROR = Kan de firmware niet bouwen
|
||||
|
||||
## Firmware update status
|
||||
|
||||
firmware_update-status-DOWNLOADING = Firmware wordt gedownload
|
||||
firmware_update-status-NEED_MANUAL_REBOOT-v2 = Zet je tracker uit en daarna weer aan.
|
||||
firmware_update-status-AUTHENTICATING = Authenticatie met de mcu
|
||||
firmware_update-status-UPLOADING = Firmware wordt geüpload
|
||||
firmware_update-status-SYNCING_WITH_MCU = Synchroniseren met de mcu
|
||||
firmware_update-status-REBOOTING = De update toepassen
|
||||
firmware_update-status-PROVISIONING = Wi-Fi-inloggegevens instellen
|
||||
firmware_update-status-DONE = Update voltooid!
|
||||
firmware_update-status-ERROR_DEVICE_NOT_FOUND = Kan het apparaat niet vinden
|
||||
firmware_update-status-ERROR_TIMEOUT = Er is een time-out opgetreden voor het updateproces
|
||||
firmware_update-status-ERROR_DOWNLOAD_FAILED = Kan de firmware niet downloaden
|
||||
firmware_update-status-ERROR_AUTHENTICATION_FAILED = Kan niet verifiëren met de mcu
|
||||
firmware_update-status-ERROR_UPLOAD_FAILED = Kan de firmware niet uploaden
|
||||
firmware_update-status-ERROR_PROVISIONING_FAILED = Kan de Wi-Fi-inloggegevens niet instellen
|
||||
firmware_update-status-ERROR_UNSUPPORTED_METHOD = De updatemethode wordt niet ondersteund
|
||||
@@ -1120,6 +1512,7 @@ firmware_update-changelog-title = Bijwerken naar { $version }
|
||||
firmware_update-looking_for_devices = Op zoek naar apparaten om bij te werken...
|
||||
firmware_update-retry = Opnieuw
|
||||
firmware_update-update = Geselecteerde trackers bijwerken
|
||||
firmware_update-exit = Sluit
|
||||
|
||||
## Tray Menu
|
||||
|
||||
@@ -1146,3 +1539,53 @@ unknown_device-modal-title = Er is een nieuwe tracker gevonden!
|
||||
unknown_device-modal-description = Er is een nieuwe tracker gevonden met MAC-adres <b>{ $deviceId }</b>. Wil je deze verbinden met SlimeVR?
|
||||
unknown_device-modal-confirm = Tuurlijk!
|
||||
unknown_device-modal-forget = Negeer het
|
||||
# VRChat config warnings
|
||||
vrc_config-page-title = VRChat-configuratie waarschuwingen
|
||||
vrc_config-page-desc = Deze pagina toont de status van je VRChat-instellingen en welke instellingen niet compatibel zijn met SlimeVR. Het wordt sterk aanbevolen om eventuele waarschuwingen hier te verhelpen voor de beste gebruikservaring met SlimeVR.
|
||||
vrc_config-page-help = Kan je de instellingen niet finden?
|
||||
vrc_config-page-help-desc = Bekijk onze <a>documentatie over dit onderwerp!</a>
|
||||
vrc_config-page-big_menu = Tracking & IK (Hoofdmenu)
|
||||
vrc_config-page-big_menu-desc = IK-instellingen in het hoofdmenu
|
||||
vrc_config-page-wrist_menu = Tracking & IK (Polsmenu)
|
||||
vrc_config-page-wrist_menu-desc = IK-instellingen in het kleine polsmenu
|
||||
vrc_config-on = Aan
|
||||
vrc_config-off = Uit
|
||||
vrc_config-invalid = Je VRChat-instellingen zijn verkeerd geconfigureerd!
|
||||
vrc_config-show_more = Toon meer
|
||||
vrc_config-setting_name = Naam van de VRChat-instelling
|
||||
vrc_config-recommended_value = Aanbevolen waarde
|
||||
vrc_config-current_value = Huidige waarde
|
||||
vrc_config-mute = Waarschuwing dempen
|
||||
vrc_config-mute-btn = Dempen
|
||||
vrc_config-unmute-btn = Dempen opheffen
|
||||
vrc_config-legacy_mode = Use Legacy IK Solving
|
||||
vrc_config-disable_shoulder_tracking = Disable Shoulder Tracking
|
||||
vrc_config-shoulder_width_compensation = Shoulder Width Compensation
|
||||
vrc_config-spine_mode = FBT Spine Mode
|
||||
vrc_config-tracker_model = FBT Tracker Model
|
||||
vrc_config-avatar_measurement_type = Avatar Measurement
|
||||
vrc_config-calibration_range = Calibration Range
|
||||
vrc_config-calibration_visuals = Display Calibration Visuals
|
||||
vrc_config-user_height = User Real Height
|
||||
vrc_config-spine_mode-UNKNOWN = Onbekend
|
||||
vrc_config-spine_mode-LOCK_BOTH = Lock Both
|
||||
vrc_config-spine_mode-LOCK_HEAD = Lock Head
|
||||
vrc_config-spine_mode-LOCK_HIP = Lock Hip
|
||||
vrc_config-tracker_model-UNKNOWN = Onbekend
|
||||
vrc_config-tracker_model-AXIS = Axis
|
||||
vrc_config-tracker_model-BOX = Box
|
||||
vrc_config-tracker_model-SPHERE = Sphere
|
||||
vrc_config-tracker_model-SYSTEM = Systeem
|
||||
vrc_config-avatar_measurement_type-UNKNOWN = Onbekend
|
||||
vrc_config-avatar_measurement_type-HEIGHT = Height
|
||||
vrc_config-avatar_measurement_type-ARM_SPAN = Arm Span
|
||||
|
||||
## Error collection consent modal
|
||||
|
||||
error_collection_modal-title = Kunnen we fouten verzamelen?
|
||||
error_collection_modal-description_v2 =
|
||||
{ settings-interface-behavior-error_tracking-description_v2 }
|
||||
|
||||
U kunt deze instelling later wijzigen in de sectie Gedrag van de instellingenpagina.
|
||||
error_collection_modal-confirm = Ik ben akkoord
|
||||
error_collection_modal-cancel = Ik wil het niet
|
||||
|
||||
@@ -268,6 +268,7 @@ navbar-settings = Ustawienia
|
||||
|
||||
bvh-start_recording = Nagraj BVH
|
||||
bvh-recording = Nagrywanie...
|
||||
bvh-save_title = Zapisz nagranie BVH
|
||||
|
||||
## Tracking pause
|
||||
|
||||
@@ -308,6 +309,7 @@ widget-imu_visualizer-rotation_raw = Raw
|
||||
widget-imu_visualizer-rotation_preview = Podgląd
|
||||
widget-imu_visualizer-acceleration = Akceleracja
|
||||
widget-imu_visualizer-position = Pozycja
|
||||
widget-imu_visualizer-stay_aligned = Wyrównywanie
|
||||
|
||||
## Widget: Skeleton Visualizer
|
||||
|
||||
@@ -335,6 +337,7 @@ tracker-table-column-temperature = Temp. °C
|
||||
tracker-table-column-linear-acceleration = Akceleracja X/Y/Z
|
||||
tracker-table-column-rotation = Rotacja X/Y/Z
|
||||
tracker-table-column-position = Pozycja X/Y/Z
|
||||
tracker-table-column-stay_aligned = Wyrównywanie
|
||||
tracker-table-column-url = URL
|
||||
|
||||
## Tracker rotation
|
||||
@@ -403,6 +406,7 @@ tracker-settings-forget-label = Zapomnij o trackerze
|
||||
tracker-settings-update-unavailable = Nie można zaktualizować (zrób to sam)
|
||||
tracker-settings-update-low-battery = Nie można zaktualizować. Bateria poniżej 50%
|
||||
tracker-settings-update-up_to_date = Aktualny
|
||||
tracker-settings-update-blocked = Aktualizacja niedostępna. Brak innych wersji
|
||||
tracker-settings-update-available = Wersja { $versionName } jest już dostępna
|
||||
tracker-settings-update = Zaktualizuj teraz
|
||||
tracker-settings-update-title = Wersja oprogramowania
|
||||
@@ -472,6 +476,7 @@ mounting_selection_menu-close = Zamknij
|
||||
settings-sidebar-title = Ustawienia
|
||||
settings-sidebar-general = Ogólne
|
||||
settings-sidebar-tracker_mechanics = Mechanika trackerów
|
||||
settings-sidebar-stay_aligned = Wyrównywanie
|
||||
settings-sidebar-fk_settings = Ustawienia śledzenia
|
||||
settings-sidebar-gesture_control = Sterowanie gestami
|
||||
settings-sidebar-interface = Interfejs
|
||||
@@ -481,7 +486,9 @@ settings-sidebar-utils = Narzędzia
|
||||
settings-sidebar-serial = Konsola szeregowa
|
||||
settings-sidebar-appearance = Wygląd
|
||||
settings-sidebar-notifications = Powiadomienia
|
||||
settings-sidebar-behavior = Zachowanie
|
||||
settings-sidebar-firmware-tool = Narzędzie do oprogramowania sprzętowego DIY
|
||||
settings-sidebar-vrc_warnings = Ostrzeżenia dotyczące konfiguracji VRChat
|
||||
settings-sidebar-advanced = Zaawansowany
|
||||
|
||||
## SteamVR settings
|
||||
@@ -563,6 +570,25 @@ settings-general-tracker_mechanics-use_mag_on_all_trackers-description =
|
||||
Wykorzystuje magnetometr we wszystkich trackerach, które mają kompatybilne oprogramowanie sprzętowe, redukując dryf w stabilnych środowiskach magnetycznych.¶
|
||||
Można wyłączyć dla każdego modułu śledzącego w ustawieniach modułu śledzącego. <b>Proszę nie wyłączać żadnego modułu śledzącego podczas przełączania!</b>
|
||||
settings-general-tracker_mechanics-use_mag_on_all_trackers-label = Użyj magnetometru na trackerach
|
||||
settings-stay_aligned = Wyrównywanie
|
||||
settings-stay_aligned-description = Wyrównywanie zmniejsza efekt driftu, stopniowo dostosowując trackery do twoich zrelaksowanych póz.
|
||||
settings-stay_aligned-setup-label = Konfiguracja Opcji Wyrównywania
|
||||
settings-stay_aligned-setup-description = Musisz ukończyć konfigurację, aby włączyć opcję Wyrównywania.
|
||||
settings-stay_aligned-warnings-drift_compensation = ⚠ Wyłącz kompensację Driftu, będzie ona kolidować z opcją Wyrównywania!
|
||||
settings-stay_aligned-enabled-label = Dostosuj trackery
|
||||
settings-stay_aligned-hide_yaw_correction-label = Ukryj dopasowanie (do porównania bez opcji Wyrównywania)
|
||||
settings-stay_aligned-general-label = Ogólne
|
||||
settings-stay_aligned-relaxed_poses-label = Zrelaksowane pozy
|
||||
settings-stay_aligned-relaxed_poses-description = Opcja Wyrównywania wykorzystuje Twoje zrelaksowane pozy, aby utrzymać trackery w jednej linii. Użyj opcji "Konfiguracja Opcji Wyrównywania", aby zaktualizować te pozy.
|
||||
settings-stay_aligned-relaxed_poses-standing = Dostosuj trackery w pozycji stojącej
|
||||
settings-stay_aligned-relaxed_poses-sitting = Dostosuj trackery, siedząc na krześle
|
||||
settings-stay_aligned-relaxed_poses-flat = Dostosuj trackery, siedząc na podłodze lub leżąc na plecach
|
||||
settings-stay_aligned-relaxed_poses-save_pose = Zapisz pozę
|
||||
settings-stay_aligned-relaxed_poses-reset_pose = Zresetuj Pozycję
|
||||
settings-stay_aligned-relaxed_poses-close = Zamknij
|
||||
settings-stay_aligned-debug-label = Debugowanie
|
||||
settings-stay_aligned-debug-description = Proszę dołączać ustawienia, podczas zgłaszania problemów z opcją Wyrównywania.
|
||||
settings-stay_aligned-debug-copy-label = Skopiuj ustawienia do schowka
|
||||
|
||||
## FK/Tracking settings
|
||||
|
||||
@@ -725,6 +751,9 @@ settings-interface-behavior-error_tracking-description_v2 =
|
||||
|
||||
Aby zapewnić jak najlepsze wrażenia użytkownika, gromadzimy anonimowe raporty o błędach, wskaźniki wydajności i informacje o systemie operacyjnym. Pomaga nam to wykrywać błędy i problemy ze SlimeVR. Dane te są zbierane za pomocą Sentry.io.
|
||||
settings-interface-behavior-error_tracking-label = Wysyłanie błędów do deweloperów
|
||||
settings-interface-behavior-bvh_directory = Ścieżka do zapisywania nagrań BVH
|
||||
settings-interface-behavior-bvh_directory-description = Wybierz ścieżkę domyślną, w której chcesz zapisywać nagrania BVH.
|
||||
settings-interface-behavior-bvh_directory-label = Ścieżka do nagrań BVH
|
||||
|
||||
## Serial settings
|
||||
|
||||
@@ -788,6 +817,7 @@ settings-osc-vrchat-description-v1 =
|
||||
settings-osc-vrchat-enable = Zezwól
|
||||
settings-osc-vrchat-enable-description = Zezwól na wysyłanie i odbieranie danych.
|
||||
settings-osc-vrchat-enable-label = Zezwól
|
||||
settings-osc-vrchat-oscqueryEnabled = Włącz OSCQuery
|
||||
settings-osc-vrchat-oscqueryEnabled-description =
|
||||
OSCQuery automatycznie wykrywa uruchomione instancje VRChat i wysyła im dane.
|
||||
Może również reklamować się do nich w celu otrzymania danych HMD i administratora.
|
||||
@@ -994,7 +1024,7 @@ onboarding-connect_tracker-next = Połączyłem już wszystkie trackery
|
||||
|
||||
onboarding-calibration_tutorial = Samouczek kalibracji IMU
|
||||
onboarding-calibration_tutorial-subtitle = Pomoże to ograniczyć dryf trackera!
|
||||
onboarding-calibration_tutorial-description = Za każdym razem, gdy włączasz trackery, muszą one chwilę polerzeć na płaskiej powierzchni, aby się skalibrować. Zróbmy to samo, klikając przycisk „Kalibruj”, <b>nie ruszaj ich!</b>
|
||||
onboarding-calibration_tutorial-description-v1 = Po włączeniu trackerów umieść je na chwilę na stabilnej powierzchni, aby umożliwić kalibrację. Kalibrację można przeprowadzić w dowolnym momencie po włączeniu trackerów - ta strona zawiera po prostu samouczek. Aby rozpocząć, kliknij przycisk "{ onboarding-calibration_tutorial-calibrate }", a następnie <b>nie ruszaj swoich trackerów!</b>
|
||||
onboarding-calibration_tutorial-calibrate = Położyłem trackery na stole
|
||||
onboarding-calibration_tutorial-status-waiting = Czekam na Ciebie
|
||||
onboarding-calibration_tutorial-status-calibrating = Kalibracja
|
||||
@@ -1165,6 +1195,9 @@ onboarding-automatic_mounting-mounting_reset-title = Kalibracja Pozycji
|
||||
onboarding-automatic_mounting-mounting_reset-step-0 = 1. Zrób pozycje "na Małysza" z wygiętymi nogami, tułowiem pochylonym do przodu z wygiętymi rękami.
|
||||
onboarding-automatic_mounting-mounting_reset-step-1 = 2. Naciśnij "Zresetuj Położenie" i poczekaj 3 sekundy zanim trackery się zresetują.
|
||||
onboarding-automatic_mounting-preparation-title = Przygotowania
|
||||
onboarding-automatic_mounting-preparation-v2-step-0 = 1. Naciśnij przycisk "Pełny reset".
|
||||
onboarding-automatic_mounting-preparation-v2-step-1 = 2. Stań prosto z rękami po bokach. Upewnij się, że patrzysz przed siebie.
|
||||
onboarding-automatic_mounting-preparation-v2-step-2 = 3. Utrzymaj pozycję, aż skończy się 3-sekundowy timer.
|
||||
onboarding-automatic_mounting-put_trackers_on-title = Załóż trackery
|
||||
onboarding-automatic_mounting-put_trackers_on-description = Aby skalibrować rotacje, użyjemy trackerów które przypisano przed chwilą. Załóż wszystkie trackery, możesz je odróznić na postaci po prawej.
|
||||
onboarding-automatic_mounting-put_trackers_on-next = Wszystkie trackery założone
|
||||
@@ -1298,6 +1331,37 @@ onboarding-scaled_proportions-done-description = Proporcje Twojego ciała powinn
|
||||
|
||||
## Stay Aligned setup
|
||||
|
||||
onboarding-stay_aligned-title = Wyrównywanie
|
||||
onboarding-stay_aligned-description = Skonfiguruj opcję Wyrównywania, aby Twoje trackery były wyrównane.
|
||||
onboarding-stay_aligned-put_trackers_on-title = Załóż trackery
|
||||
onboarding-stay_aligned-put_trackers_on-description = Aby skalibrować proporcje, użyjemy trackerów które przed chwilą przypisałeś. Załóż wszystkie trackery, będziesz widział który to który na postaci po prawej.
|
||||
onboarding-stay_aligned-put_trackers_on-trackers_warning = Masz mniej niż 5 trackerów aktualnie podłączonych i przypisanych! Jest to minimalna liczba elementów śledzących wymaganych do prawidłowego działania opcji Wyrównywania.
|
||||
onboarding-stay_aligned-put_trackers_on-next = Mam wszystkie trackery założone
|
||||
onboarding-stay_aligned-verify_mounting-title = Sprawdź swój montaż
|
||||
onboarding-stay_aligned-verify_mounting-step-0 = Opcja Wyrównywania wymaga stabilnego mocowania trackera. W innym przypadku będziesz miał złe rezultaty.
|
||||
onboarding-stay_aligned-verify_mounting-step-1 = 1. Poruszaj się podczas stania.
|
||||
onboarding-stay_aligned-verify_mounting-step-2 = 2. Usiądź i poruszaj nogami i stopami.
|
||||
onboarding-stay_aligned-verify_mounting-step-3 = 3. Jeśli Twoje trackery nie znajdują się we właściwym miejscu, naciśnij "Ponów kalibrację montażu".
|
||||
onboarding-stay_aligned-verify_mounting-redo_mounting = Ponów kalibrację montażu
|
||||
onboarding-stay_aligned-preparation-title = Przygotowania
|
||||
onboarding-stay_aligned-preparation-tip = Upewnij się, że stoisz prosto. Patrz przed siebie z rękami opuszczonymi po bokach.
|
||||
onboarding-stay_aligned-relaxed_poses-standing-title = Zrelaksowana pozycja stojąca
|
||||
onboarding-stay_aligned-relaxed_poses-standing-step-0 = 1. Stań w wygodnej pozycji. Zrelaksuj się!
|
||||
onboarding-stay_aligned-relaxed_poses-standing-step-1-v2 = 2. Naciśnij przycisk "Zapisz pozę".
|
||||
onboarding-stay_aligned-relaxed_poses-sitting-title = Zrelaksowana pozycja siedząca na krześle
|
||||
onboarding-stay_aligned-relaxed_poses-sitting-step-0 = 1. Usiądź w wygodnej pozycji. Zrelaksuj się!
|
||||
onboarding-stay_aligned-relaxed_poses-sitting-step-1-v2 = 2. Naciśnij przycisk "Zapisz pozę".
|
||||
onboarding-stay_aligned-relaxed_poses-flat-title = Zrelaksowana pozycja siedząca na podłodze
|
||||
onboarding-stay_aligned-relaxed_poses-flat-step-0 = 1. Usiądź na podłodze z nogami wysuniętymi do przodu. Zrelaksuj się!
|
||||
onboarding-stay_aligned-relaxed_poses-flat-step-1-v2 = 2. Naciśnij przycisk "Zapisz pozę".
|
||||
onboarding-stay_aligned-relaxed_poses-skip_step = Pomiń
|
||||
onboarding-stay_aligned-done-title = Wyrównywanie Włączone!
|
||||
onboarding-stay_aligned-done-description = Konfiguracja Wyrównywania jest zakończona!
|
||||
onboarding-stay_aligned-done-description-2 = Konfiguracja została zakończona! Możesz ponownie uruchomić proces, jeśli chcesz ponownie skalibrować pozy.
|
||||
onboarding-stay_aligned-previous_step = Poprzednie
|
||||
onboarding-stay_aligned-next_step = Następne
|
||||
onboarding-stay_aligned-restart = Restart
|
||||
onboarding-stay_aligned-done = Gotowy
|
||||
|
||||
## Home
|
||||
|
||||
@@ -1322,6 +1386,12 @@ status_system-StatusSteamVRDisconnected =
|
||||
}
|
||||
status_system-StatusTrackerError = Tracker { $trackerName } ma błąd.
|
||||
status_system-StatusUnassignedHMD = Headset powinien być przypisany do śledzenia głowy.
|
||||
status_system-StatusPublicNetwork =
|
||||
{ $count ->
|
||||
[one] Twoja karta sieciowa jest ustawiona jako publiczna: { $adapters }. Nie jest to zalecane, aby SlimeVR działał poprawnie. <PublicFixLink>Zobacz, jak to naprawić tutaj.</PublicFixLink>
|
||||
[few] Niektóre karty sieciowe są ustawione jako publiczne: { $adapters }. Nie jest to zalecane, aby SlimeVR działał poprawnie. <PublicFixLink>Zobacz, jak to naprawić tutaj.</PublicFixLink>
|
||||
*[many] Dużo twoich karty sieciowych jest ustawionych jako publiczne: { $adapters }. Nie jest to zalecane, aby SlimeVR działał poprawnie. <PublicFixLink>Zobacz, jak to naprawić tutaj.</PublicFixLink>
|
||||
}
|
||||
|
||||
## Firmware tool globals
|
||||
|
||||
@@ -1423,6 +1493,7 @@ firmware_tool-build_step = Building
|
||||
firmware_tool-build_step-description = Trwa tworzenie oprogramowania sprzętowego. Proszę czekać
|
||||
firmware_tool-flashing_step = Flashing
|
||||
firmware_tool-flashing_step-description = Twoje trackery migają. Postępuj zgodnie z instrukcjami wyświetlanymi na ekranie
|
||||
firmware_tool-flashing_step-warning-v2 = Nie odłączaj ani nie wyłączaj trackera podczas procesu przesyłania, chyba że zostaniesz o to poproszony, może to spowodować, że twoje urządzenie stanie się bezużyteczne.
|
||||
firmware_tool-flashing_step-flash_more = Flashuj więcej trackerów
|
||||
firmware_tool-flashing_step-exit = Wyjście
|
||||
|
||||
@@ -1440,6 +1511,7 @@ firmware_tool-build-ERROR = Nie można zbudować oprogramowania sprzętowego
|
||||
## Firmware update status
|
||||
|
||||
firmware_update-status-DOWNLOADING = Pobieranie oprogramowania sprzętowego
|
||||
firmware_update-status-NEED_MANUAL_REBOOT-v2 = Wyłącz i ponownie włącz swój tracker
|
||||
firmware_update-status-AUTHENTICATING = Uwierzytelnianie za pomocą MCU
|
||||
firmware_update-status-UPLOADING = Przesyłanie oprogramowania sprzętowego
|
||||
firmware_update-status-SYNCING_WITH_MCU = Synchronizacja z MCU
|
||||
@@ -1510,6 +1582,9 @@ vrc_config-show_more = Pokaż więcej
|
||||
vrc_config-setting_name = Nazwa ustawienia VRChat
|
||||
vrc_config-recommended_value = Zalecana wartość
|
||||
vrc_config-current_value = Bieżąca wartość
|
||||
vrc_config-mute = Wycisz Ostrzeżenie
|
||||
vrc_config-mute-btn = Wycisz
|
||||
vrc_config-unmute-btn = Odcisz
|
||||
vrc_config-legacy_mode = Korzystanie ze starszego rozwiązywania kinematyki odwrotnej
|
||||
vrc_config-disable_shoulder_tracking = Wyłącz śledzenie ramienia
|
||||
vrc_config-shoulder_width_compensation = Kompensacja szerokości barku
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,11 @@
|
||||
|
||||
websocket-connecting = Đang kết nối với máy chủ
|
||||
websocket-connection_lost = Kết nối với máy chủ đã mất. Đang kết nối lại...
|
||||
websocket-connection_lost-desc = Máy chủ SlimeVR bị dừng. Hãy kiểm tra nhật ký logs và khởi động lại chương trình.
|
||||
websocket-timedout = Không thể tìm thấy máy chủ
|
||||
websocket-timedout-desc = Có vẻ như máy chủ SlimeVR đã bị sập hoặc hết thời gian chờ. Vui lòng kiểm tra nhật ký logs và khởi động lại chương trình.
|
||||
websocket-error-close = Thoát SlimeVR
|
||||
websocket-error-logs = Mở thư mục nhật ký logs
|
||||
|
||||
## Update notification
|
||||
|
||||
@@ -50,6 +55,11 @@ body_part-LEFT_UPPER_LEG = Bắp chân trái
|
||||
body_part-LEFT_LOWER_LEG = Cẳng chân trái
|
||||
body_part-LEFT_FOOT = Bàn chân trái
|
||||
|
||||
## BoardType
|
||||
|
||||
board_type-MOCOPI = Sony Mocopi
|
||||
board_type-WEMOSWROOM02 = Wemos Wroom-02 D1 Mini
|
||||
|
||||
## Proportions
|
||||
|
||||
skeleton_bone-NONE = Chưa được gán
|
||||
@@ -421,9 +431,6 @@ settings-general-fk_settings-skeleton_settings-interp_knee_tracker_ankle = Tính
|
||||
settings-general-fk_settings-skeleton_settings-interp_knee_ankle = Tính trung bình của chiều quay đầu gối ngáp và lăn bằng mắt cá chân '
|
||||
settings-general-fk_settings-self_localization-title = Chế độ Mocap
|
||||
settings-general-fk_settings-self_localization-description = Chế độ Mocap cho phép bộ xương theo dõi đại khái vị trí của chính nó mà không cần kính VR hoặc các thiết bị theo dõi khác. Lưu ý rằng điều này yêu cầu bộ theo dõi chân và đầu để hoạt động và chức năng này vẫn đang trong quá trình thử nghiệm.
|
||||
settings-general-fk_settings-vive_emulation-title = Giả lập tracker Vive
|
||||
settings-general-fk_settings-vive_emulation-description = Giả lập cách tracker của Vive gặp vấn đề với việc theo dõi eo, đây là 1 tính năng được làm cho vui và sẽ làm giảm độ chính xác
|
||||
settings-general-fk_settings-vive_emulation-label = Giả lập tracker Vive
|
||||
|
||||
## Gesture control settings (tracker tapping)
|
||||
|
||||
@@ -485,6 +492,12 @@ settings-general-interface-feedback_sound-volume = Âm lượng phản hồi
|
||||
settings-general-interface-connected_trackers_warning = Cảnh báo với thiết bị đã kết nối
|
||||
settings-general-interface-connected_trackers_warning-description = Tùy chọn này sẽ hiển thị cửa sổ bật lên mỗi khi bạn thử thoát khỏi SlimeVR trong khi có một hoặc nhiều thiết bị theo dõi được kết nối. Nó nhắc nhở bạn tắt trình theo dõi khi bạn hoàn tất để duy trì tuổi thọ pin.
|
||||
settings-general-interface-connected_trackers_warning-label = Cảnh báo thiết bị đã kết nối khi thoát chương trình
|
||||
|
||||
## Behavior settings
|
||||
|
||||
settings-general-interface-dev_mode = Chế độ nhà phát triển
|
||||
settings-general-interface-dev_mode-description = Hữu dụng nếu cần thêm thông tin chi tiết của tracker hay can thiệp sâu hơn vào tracker
|
||||
settings-general-interface-dev_mode-label = Chế độ nhà phát triển
|
||||
settings-general-interface-use_tray = Thu nhỏ vào khay hệ thống
|
||||
settings-general-interface-use_tray-description = Cho phép bạn đóng cửa sổ mà không cần đóng máy chủ SlimeVR để bạn có thể tiếp tục sử dụng nó mà không bị GUI làm phiền.
|
||||
settings-general-interface-use_tray-label = Thu nhỏ vào khay hệ thống
|
||||
@@ -601,12 +614,6 @@ settings-osc-vmc-network-address-description = Chọn địa chỉ để gửi d
|
||||
settings-osc-vmc-network-address-placeholder = Địa chỉ IPV4
|
||||
settings-osc-vmc-vrm = Model VRM
|
||||
settings-osc-vmc-vrm-description = Tải mô hình VRM để cho phép neo đầu và cho phép khả năng tương thích cao hơn với các ứng dụng khác.
|
||||
settings-osc-vmc-vrm-model_unloaded = Chưa có mô hình tải lên
|
||||
settings-osc-vmc-vrm-model_loaded =
|
||||
{ $titled ->
|
||||
[true] Mô hình đã được tải: { $name }
|
||||
*[other] Mô hình chưa có tiêu đề đã được tải
|
||||
}
|
||||
settings-osc-vmc-vrm-file_select = Kéo và thả mô hình để sử dụng hoặc <u>duyệt file</u>
|
||||
settings-osc-vmc-anchor_hip = Cố định ở hông
|
||||
settings-osc-vmc-anchor_hip-description = Cố định theo dõi ở hông, hữu ích cho VTubing ngồi. Nếu tắt, hãy tải mô hình VRM.
|
||||
@@ -622,8 +629,6 @@ settings-utils-advanced-reset-gui = Đặt lại cài đặt GUI
|
||||
settings-utils-advanced-reset-all-label = Đặt lại tất cả
|
||||
settings-utils-advanced-reset_warning-reset = Đặt lại cài đặt
|
||||
settings-utils-advanced-reset_warning-cancel = Hủy
|
||||
settings-utils-advanced-open_data = Thư mục dữ liệu
|
||||
settings-utils-advanced-open_data-description = Mở thư mục dữ liệu của SlimeVR trong tệp, chứa các tệp cấu hình và logs.
|
||||
settings-utils-advanced-open_data-label = Mở thư mục
|
||||
|
||||
## Setup/onboarding menu
|
||||
@@ -731,7 +736,6 @@ onboarding-connect_tracker-next = Đã kết nối với tất cả tracker
|
||||
|
||||
onboarding-calibration_tutorial = Hướng dẫn hiệu chuẩn IMU
|
||||
onboarding-calibration_tutorial-subtitle = Cái này sẽ giúp giảm trôi trượt theo dõi!
|
||||
onboarding-calibration_tutorial-description = Mỗi khi bạn bật thiết bị theo dõi, chúng cần nghỉ ngơi một lúc trên bề mặt phẳng để hiệu chỉnh. Hãy làm điều tương tự bằng cách nhấp vào nút "{ onboarding-calibration_tutorial-calibrate }", <b>và không di chuyển chúng!</b>
|
||||
onboarding-calibration_tutorial-calibrate = Tôi đã đặt thiết bị theo dõi của mình lên bàn
|
||||
onboarding-calibration_tutorial-status-waiting = Đang chờ bạn hoàn thành
|
||||
onboarding-calibration_tutorial-status-calibrating = Đang hiệu chuẩn
|
||||
@@ -895,44 +899,14 @@ onboarding-automatic_mounting-mounting_reset-title = Đặt lại hướng gắn
|
||||
onboarding-automatic_mounting-mounting_reset-step-0 = 1. Đứng khom người như tư thế trượt tuyết với đầu gối khom lại, thân trên hướng tới trước và hai tay co lại để giữ thăng bằng như hình bên
|
||||
onboarding-automatic_mounting-mounting_reset-step-1 = 2. Nhấn nút đặt lại và chờ 3 giây trước khi hệ thống cân chỉnh hướng gắn tracker
|
||||
onboarding-automatic_mounting-preparation-title = Chuẩn bị tư thế
|
||||
onboarding-automatic_mounting-preparation-step-0 = 1. Đứng thẳng với hai tay duỗi thẳng
|
||||
onboarding-automatic_mounting-preparation-step-1 = 2. Nhấn nút đặt lại và chờ 3 giây trước khi tracker được đặt lại.
|
||||
onboarding-automatic_mounting-put_trackers_on-title = Đeo tracker lên người
|
||||
onboarding-automatic_mounting-put_trackers_on-description = Để cân chỉnh hướng gắn của tracker, SlimeVR sẽ tiến hành đo góc nghiêng của tracker khi đang đeo để cân chỉnh hướng gắn, hãy đeo tracker theo đúng vị trí đã thiết lập
|
||||
onboarding-automatic_mounting-put_trackers_on-next = Tiếp tục
|
||||
|
||||
## Tracker proportions method choose
|
||||
|
||||
onboarding-choose_proportions = Phương pháp hiệu chuẩn tỷ lệ nào để sử dụng?
|
||||
# Multiline string
|
||||
onboarding-choose_proportions-description-v1 =
|
||||
Tỷ lệ cơ thể được sử dụng để biết các số đo của cơ thể bạn. Họ được yêu cầu tính toán vị trí của trình theo dõi.
|
||||
Khi tỷ lệ cơ thể của bạn không khớp với tỷ lệ được lưu, độ chính xác theo dõi của bạn sẽ kém hơn và bạn sẽ nhận thấy những thứ như trượt băng hoặc trượt, hoặc cơ thể của bạn không khớp với hình đại diện của bạn.
|
||||
<b>Bạn chỉ cần đo cơ thể của bạn một lần!</b> Trừ khi chúng sai hoặc cơ thể bạn đã thay đổi, thì bạn không cần phải làm lại.
|
||||
onboarding-choose_proportions-auto_proportions = Đo kích thước cơ thể tự động
|
||||
# Italicized text
|
||||
onboarding-choose_proportions-auto_proportions-subtitle = Khuyến khích dùng
|
||||
onboarding-choose_proportions-auto_proportions-descriptionv3 =
|
||||
Tính năng này sẽ đoán tỷ lệ cơ thể của bạn bằng cách ghi lại một mẫu chuyển động của bạn và chuyển nó qua một thuật toán.
|
||||
|
||||
<b>Tính năng này sẽ yêu cầu headset của bạn (HMD) được kết nối với SlimeVR và đang nằm ở trên đầu của bạn!</b>
|
||||
onboarding-choose_proportions-manual_proportions = Đo kích thước cơ thể thủ công
|
||||
# Italicized text
|
||||
onboarding-choose_proportions-manual_proportions-subtitle = Cho chính xác
|
||||
onboarding-choose_proportions-manual_proportions-description = Tính năng này sẽ cho phép bạn điều chỉnh tỉ lệ cơ thể của mình theo cách thủ công bằng cách chỉnh sửa các con số một cách trực tiếp
|
||||
onboarding-choose_proportions-export = Xuất tỉ lệ cơ thể
|
||||
onboarding-choose_proportions-import = Nhập tỉ lệ cơ thể
|
||||
onboarding-choose_proportions-import-success = Đã được nhập
|
||||
onboarding-choose_proportions-import-failed = Thất bại
|
||||
onboarding-choose_proportions-file_type = File tỉ lệ cơ thể
|
||||
|
||||
## Tracker manual proportions setup
|
||||
## Tracker manual proportions setupa
|
||||
|
||||
onboarding-manual_proportions-back = Quay lại cân chỉnh hướng gắn
|
||||
onboarding-manual_proportions-title = Đo kích thước cơ thể thủ công
|
||||
onboarding-manual_proportions-precision = Cân chỉnh cụ thể (giảm hệ số chỉnh)
|
||||
onboarding-manual_proportions-auto = Đo kích thước cơ thể tự động
|
||||
onboarding-manual_proportions-ratio = Điều chỉnh theo nhóm tỷ lệ
|
||||
|
||||
## Tracker automatic proportions setup
|
||||
|
||||
@@ -953,20 +927,11 @@ onboarding-automatic_proportions-requirements-descriptionv2 =
|
||||
Headset của bạn đang báo cáo dữ liệu vị trí cho máy chủ SlimeVR (điều này thường có nghĩa là SteamVR đang chạy và kết nối với SlimeVR bằng driver SteamVR của SlimeVR).
|
||||
Tracking của bạn đang hoạt động và thể hiện chính xác các chuyển động của bạn (ví dụ: bạn đã thực hiện thiết đặt lại hoàn toàn và chúng di chuyển đúng hướng khi đá, cúi xuống, ngồi, v.v.).
|
||||
onboarding-automatic_proportions-requirements-next = Tôi đã đọc các yêu cầu
|
||||
onboarding-automatic_proportions-check_height-title = Kiểm tra chiều cao của bạn
|
||||
onboarding-automatic_proportions-check_height-description = Chúng tôi sử dụng chiều cao của bạn làm cơ sở cho các phép đo của chúng tôi bằng cách sử dụng chiều cao của headset (HMD) làm chiều cao ước tính thực tế của bạn, nhưng tốt hơn hết bạn nên tự kiểm tra xem chúng có đúng không!
|
||||
# All the text is in bold!
|
||||
onboarding-automatic_proportions-check_height-calculation_warning = Vui lòng nhấn nút trong khi đứng <u>thẳng</u> để tính chiều cao của bạn. Bạn có 3 giây sau khi nhấn nút!
|
||||
onboarding-automatic_proportions-check_height-guardian_tip =
|
||||
Nếu bạn đang sử dụng Kính VR Standalone, hãy đảm bảo có guardian /
|
||||
Ranh giới được bật để chiều cao của bạn là chính xác!
|
||||
onboarding-automatic_proportions-check_height-fetch_height = Tôi đang đứng!
|
||||
# Context is that the height is unknown
|
||||
onboarding-automatic_proportions-check_height-unknown = Không rõ
|
||||
# Shows an element below it
|
||||
onboarding-automatic_proportions-check_height-hmd_height1 = Chiều cao của HMD là
|
||||
# Shows an element below it
|
||||
onboarding-automatic_proportions-check_height-height1 = vậy chiều cao thật của bạn là
|
||||
onboarding-automatic_proportions-check_height-next_step = Những chỉ số này là đúng
|
||||
onboarding-automatic_proportions-start_recording-title = Chuẩn bị đo
|
||||
onboarding-automatic_proportions-start_recording-description = Phần mềm sẽ đo một số chuyển động, cử chỉ cụ thể, hãy chuẩn bị cho việc di chuyển theo yêu cầu trong phần tiếp theo
|
||||
@@ -997,11 +962,17 @@ onboarding-automatic_proportions-verify_results-redo = Thử lại
|
||||
onboarding-automatic_proportions-verify_results-confirm = Kết quả tương đối chính xác
|
||||
onboarding-automatic_proportions-done-title = Đã lưu chỉ số đo
|
||||
onboarding-automatic_proportions-done-description = Quá trình đo đã hoàn tất
|
||||
onboarding-automatic_proportions-error_modal =
|
||||
<b>Lưu ý:</b> Một lỗi đã được tìm thấy trong khi ước tính tỷ lệ cơ thể!
|
||||
Vui lòng <docs>kiểm tra hướng dẫn</docs> hoặc tham gia <discord>Discord</discord> của chúng tôi để được trợ giúp ^_^
|
||||
onboarding-automatic_proportions-error_modal-confirm = Đã hiểu!
|
||||
|
||||
## Tracker scaled proportions setup
|
||||
|
||||
|
||||
## Tracker scaled proportions reset
|
||||
|
||||
|
||||
## Stay Aligned setup
|
||||
|
||||
|
||||
## Home
|
||||
|
||||
home-no_trackers = Chưa có thiết bị nào được phát hiện hoặc điều ra
|
||||
@@ -1026,6 +997,21 @@ status_system-StatusSteamVRDisconnected =
|
||||
status_system-StatusTrackerError = Tracker { $trackerName } có lỗi.
|
||||
status_system-StatusUnassignedHMD = Kính thực tế ảo VR này nên được giao là bộ theo dõi đầu.
|
||||
|
||||
## Firmware tool globals
|
||||
|
||||
|
||||
## Firmware tool Steps
|
||||
|
||||
|
||||
## firmware tool build status
|
||||
|
||||
|
||||
## Firmware update status
|
||||
|
||||
|
||||
## Dedicated Firmware Update Page
|
||||
|
||||
|
||||
## Tray Menu
|
||||
|
||||
tray_menu-show = Xem
|
||||
@@ -1053,3 +1039,6 @@ unknown_device-modal-description =
|
||||
Bạn có muốn kết nối nó với SlimeVR không?
|
||||
unknown_device-modal-confirm = Chắc!
|
||||
unknown_device-modal-forget = Bỏ qua
|
||||
|
||||
## Error collection consent modal
|
||||
|
||||
|
||||
@@ -233,6 +233,8 @@ reset-reset_all_warning_default-v2 =
|
||||
您确定要执行此操作吗?
|
||||
reset-full = 完整重置
|
||||
reset-mounting = 重置佩戴
|
||||
reset-mounting-feet = 重置脚部佩戴
|
||||
reset-mounting-fingers = 重置手指佩戴
|
||||
reset-yaw = 重置航向轴
|
||||
|
||||
## Serial detection stuff
|
||||
@@ -258,6 +260,7 @@ navbar-settings = 设置
|
||||
|
||||
bvh-start_recording = 录制 BVH 文件
|
||||
bvh-recording = 录制中...
|
||||
bvh-save_title = 保存BVH记录
|
||||
|
||||
## Tracking pause
|
||||
|
||||
@@ -395,6 +398,7 @@ tracker-settings-forget-label = 忘记追踪器
|
||||
tracker-settings-update-unavailable = 无法升级(DIY)
|
||||
tracker-settings-update-low-battery = 无法更新。当前电池电量低于 50%
|
||||
tracker-settings-update-up_to_date = 已是最新
|
||||
tracker-settings-update-blocked = 更新不可用。没有其他可用版本
|
||||
tracker-settings-update-available = { $versionName } 现在可用
|
||||
tracker-settings-update = 立即更新
|
||||
tracker-settings-update-title = 固件版本
|
||||
@@ -573,6 +577,7 @@ settings-stay_aligned-relaxed_poses-sitting = 椅子上放松姿势
|
||||
settings-stay_aligned-relaxed_poses-flat = 地面/平躺放松姿势
|
||||
settings-stay_aligned-relaxed_poses-save_pose = 保存姿势
|
||||
settings-stay_aligned-relaxed_poses-reset_pose = 重置姿势
|
||||
settings-stay_aligned-relaxed_poses-close = 关闭
|
||||
settings-stay_aligned-debug-label = 调试
|
||||
settings-stay_aligned-debug-description = 在报告持续校准相关问题时,请包含您的以下设置信息
|
||||
settings-stay_aligned-debug-copy-label = 复制设置信息到剪贴板
|
||||
@@ -727,6 +732,9 @@ settings-interface-behavior-error_tracking-description_v2 =
|
||||
|
||||
为了提供最佳用户体验,我们会收集匿名错误报告、性能指标和操作系统信息。这有助于我们检测 SlimeVR 的错误和问题。这些指标将通过 Sentry.io 收集。
|
||||
settings-interface-behavior-error_tracking-label = 向开发人员发送错误信息
|
||||
settings-interface-behavior-bvh_directory = BVH记录保存目录
|
||||
settings-interface-behavior-bvh_directory-description = 选择保存BVH记录文件的目录
|
||||
settings-interface-behavior-bvh_directory-label = BVH记录保存目录
|
||||
|
||||
## Serial settings
|
||||
|
||||
@@ -997,7 +1005,7 @@ onboarding-connect_tracker-next = 所有的追踪器都连接好了
|
||||
|
||||
onboarding-calibration_tutorial = IMU校准教程
|
||||
onboarding-calibration_tutorial-subtitle = 这将有助于减少追踪器漂移!
|
||||
onboarding-calibration_tutorial-description = 每次开启追踪器时,它们都需要在平坦的表面上放置片刻以进行自校准。你也可以通过点击“{ onboarding-calibration_tutorial-calibrate }”按钮来手动校准, <b>校准过程中不要移动追踪器!</b>
|
||||
onboarding-calibration_tutorial-description-v1 = 打开追踪器后,将它们放在稳定的平面上一段时间,以便进行校准。追踪器开机后可随时进行校准 - 本页仅是一个校准教程。首先,点击 “{ onboarding-calibration_tutorial-calibrate }” 按钮,然后 <b>不要移动您的追踪器!</b>
|
||||
onboarding-calibration_tutorial-calibrate = 我已经把追踪器放在桌子上了
|
||||
onboarding-calibration_tutorial-status-waiting = 等待你的操作
|
||||
onboarding-calibration_tutorial-status-calibrating = 校准中
|
||||
@@ -1301,13 +1309,13 @@ onboarding-stay_aligned-preparation-title = 准备
|
||||
onboarding-stay_aligned-preparation-tip = 站直并向前看,双臂放在身体两侧。
|
||||
onboarding-stay_aligned-relaxed_poses-standing-title = 站立放松姿势
|
||||
onboarding-stay_aligned-relaxed_poses-standing-step-0 = 1. 以舒适的姿势站立并放松。
|
||||
onboarding-stay_aligned-relaxed_poses-standing-step-2 = 3. 按下“检测姿势”按钮。
|
||||
onboarding-stay_aligned-relaxed_poses-standing-step-1-v2 = 2. 按下“保存姿势”按钮。
|
||||
onboarding-stay_aligned-relaxed_poses-sitting-title = 椅子上放松姿势
|
||||
onboarding-stay_aligned-relaxed_poses-sitting-step-0 = 1. 以舒适的姿势坐下并放松。
|
||||
onboarding-stay_aligned-relaxed_poses-sitting-step-2 = 3. 按下“检测姿势”按钮。
|
||||
onboarding-stay_aligned-relaxed_poses-sitting-step-1-v2 = 2. 按下“保存姿势”按钮。
|
||||
onboarding-stay_aligned-relaxed_poses-flat-title = 地面/平躺放松姿势
|
||||
onboarding-stay_aligned-relaxed_poses-flat-step-0 = 1. 以舒适的姿势坐或躺在地面上,保持腿在前方并放松。
|
||||
onboarding-stay_aligned-relaxed_poses-flat-step-2 = 3. 按下“检测姿势”按钮。
|
||||
onboarding-stay_aligned-relaxed_poses-flat-step-1-v2 = 2. 按下“保存姿势”按钮。
|
||||
onboarding-stay_aligned-relaxed_poses-skip_step = 跳过
|
||||
onboarding-stay_aligned-done-title = 持续校准已开启!
|
||||
onboarding-stay_aligned-done-description = 持续校准已设定完成!
|
||||
|
||||
@@ -54,6 +54,7 @@ dirs-next = "2.0.0"
|
||||
discord-sdk = "0.3.6"
|
||||
tokio = { version = "1.37.0", features = ["time"] }
|
||||
itertools = "0.13.0"
|
||||
tauri-plugin-http = "2.5.0"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
win32job = "1"
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
"identifier": "migrated",
|
||||
"description": "permissions that were migrated from v1",
|
||||
"local": true,
|
||||
"windows": [
|
||||
"main"
|
||||
],
|
||||
"windows": ["main"],
|
||||
"permissions": [
|
||||
"core:default",
|
||||
"core:window:allow-close",
|
||||
@@ -19,6 +17,9 @@
|
||||
"core:window:allow-set-decorations",
|
||||
"store:default",
|
||||
"os:allow-os-type",
|
||||
"os:allow-hostname",
|
||||
"os:allow-locale",
|
||||
"dialog:allow-open",
|
||||
"dialog:allow-save",
|
||||
"shell:allow-open",
|
||||
"store:allow-get",
|
||||
@@ -30,6 +31,14 @@
|
||||
{
|
||||
"identifier": "fs:scope",
|
||||
"allow": [{ "path": "$APPDATA" }, { "path": "$APPDATA/**" }]
|
||||
},
|
||||
{
|
||||
"identifier": "http:default",
|
||||
"allow": [
|
||||
{
|
||||
"url": "https://github.com/SlimeVR/SlimeVR-Tracker-ESP/releases/download/*"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[Desktop Entry]
|
||||
Version=1.5
|
||||
Categories=Game;Development;GTK;
|
||||
Categories=Game;GTK;
|
||||
Exec={{exec}}
|
||||
Icon={{icon}}
|
||||
|
||||
|
||||
@@ -65,6 +65,8 @@ work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
</provides>
|
||||
|
||||
<releases>
|
||||
<release version="0.16.0" date="2025-07-01"><url>https://github.com/SlimeVR/SlimeVR-Server/releases/tag/v0.16.0</url></release>
|
||||
<release version="0.16.0~rc.2" type="development" date="2025-06-20"><url>https://github.com/SlimeVR/SlimeVR-Server/releases/tag/v0.16.0-rc.2</url></release>
|
||||
<release version="0.16.0~rc.1" type="development" date="2025-05-27"><url>https://github.com/SlimeVR/SlimeVR-Server/releases/tag/v0.16.0-rc.1</url></release>
|
||||
<release version="0.15.0" date="2025-05-19"><url>https://github.com/SlimeVR/SlimeVR-Server/releases/tag/v0.15.0</url></release>
|
||||
<release version="0.15.0~rc.4" type="development" date="2025-05-12"><url>https://github.com/SlimeVR/SlimeVR-Server/releases/tag/v0.15.0-rc.4</url></release>
|
||||
|
||||
@@ -254,6 +254,7 @@ fn setup_tauri(
|
||||
.plugin(tauri_plugin_os::init())
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.plugin(tauri_plugin_store::Builder::default().build())
|
||||
.plugin(tauri_plugin_http::init())
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
update_window_state,
|
||||
logging,
|
||||
|
||||
@@ -80,17 +80,36 @@ impl WindowState {
|
||||
window.unmaximize()?;
|
||||
}
|
||||
|
||||
if self.width > util::MIN_WINDOW_SIZE_WIDTH
|
||||
&& self.height > util::MIN_WINDOW_SIZE_HEIGHT
|
||||
let size = if self.width >= util::MIN_WINDOW_SIZE_WIDTH
|
||||
&& self.height >= util::MIN_WINDOW_SIZE_HEIGHT
|
||||
{
|
||||
window.set_size(LogicalSize::new(self.width, self.height))?;
|
||||
}
|
||||
Some(LogicalSize::new(self.width, self.height))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let pos = PhysicalPosition::new(self.x, self.y);
|
||||
for monitor in window.available_monitors()? {
|
||||
if monitor.contains(pos) {
|
||||
let monitor = window
|
||||
.available_monitors()?
|
||||
.into_iter()
|
||||
.find(|x| x.contains(pos))
|
||||
.map(|m| (m, true))
|
||||
.or(window.current_monitor()?.map(|m| (m, false)));
|
||||
|
||||
// Don't surpass the monitor's size
|
||||
if let Some((monitor, is_old)) = monitor {
|
||||
let monitor_size = *monitor.size();
|
||||
let window_size = size
|
||||
.map(|s| s.to_physical(monitor.scale_factor()))
|
||||
.unwrap_or(window.outer_size()?);
|
||||
window.set_size(PhysicalSize::new(
|
||||
u32::min(monitor_size.width, window_size.width),
|
||||
u32::min(monitor_size.height, window_size.height),
|
||||
))?;
|
||||
|
||||
// If the position of the window was previously in the config
|
||||
if is_old {
|
||||
window.set_position(pos)?;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ pub fn create_tray<R: Runtime>(app: &tauri::AppHandle<R>) -> tauri::Result<()> {
|
||||
.title("SlimeVR")
|
||||
.tooltip("SlimeVR")
|
||||
.icon_as_template(true)
|
||||
.menu_on_left_click(false)
|
||||
.show_menu_on_left_click(false)
|
||||
.icon(if cfg!(target_os = "macos") {
|
||||
include_image!("icons/appleTrayIcon.png")
|
||||
} else {
|
||||
@@ -146,7 +146,7 @@ pub fn create_tray<R: Runtime>(app: &tauri::AppHandle<R>) -> tauri::Result<()> {
|
||||
_ => {}
|
||||
})
|
||||
// We don't want this as we open the window on left click
|
||||
.menu_on_left_click(false)
|
||||
.show_menu_on_left_click(false)
|
||||
.build(app)?;
|
||||
|
||||
app.manage(TrayAvailable(true));
|
||||
|
||||
@@ -10,12 +10,15 @@
|
||||
"linux": {
|
||||
"deb": {
|
||||
"depends": [
|
||||
"openjdk-17-jre-headless"
|
||||
"openjdk-17-jre-headless",
|
||||
"udev"
|
||||
],
|
||||
"files": {
|
||||
"/usr/share/slimevr/slimevr.jar": "../../server/desktop/build/libs/slimevr.jar"
|
||||
"/usr/share/slimevr/slimevr.jar": "../../server/desktop/build/libs/slimevr.jar",
|
||||
"/lib/udev/rules.d/69-slimevr.rules": "./69-slimevr-devices.rules"
|
||||
},
|
||||
"desktopTemplate": "./dev.slimevr.SlimeVR.desktop"
|
||||
"desktopTemplate": "./dev.slimevr.SlimeVR.desktop",
|
||||
"section": "contrib/games"
|
||||
},
|
||||
"appimage": {
|
||||
"bundleMediaFramework": true,
|
||||
@@ -25,10 +28,12 @@
|
||||
},
|
||||
"rpm": {
|
||||
"depends": [
|
||||
"java-17-openjdk"
|
||||
"java-latest-openjdk",
|
||||
"udev"
|
||||
],
|
||||
"files": {
|
||||
"/usr/share/slimevr/slimevr.jar": "../../server/desktop/build/libs/slimevr.jar"
|
||||
"/usr/share/slimevr/slimevr.jar": "../../server/desktop/build/libs/slimevr.jar",
|
||||
"/usr/lib/udev/rules.d/69-slimevr.rules": "./69-slimevr-devices.rules"
|
||||
},
|
||||
"desktopTemplate": "./dev.slimevr.SlimeVR.desktop"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Localized } from '@fluent/react';
|
||||
import { Localized, useLocalization } from '@fluent/react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import {
|
||||
RecordBVHRequestT,
|
||||
@@ -9,18 +9,43 @@ import { useWebsocketAPI } from '@/hooks/websocket-api';
|
||||
import { BigButton } from './commons/BigButton';
|
||||
import { RecordIcon } from './commons/icon/RecordIcon';
|
||||
import classNames from 'classnames';
|
||||
import { isTauri } from '@tauri-apps/api/core';
|
||||
import { save } from '@tauri-apps/plugin-dialog';
|
||||
import { useConfig } from '@/hooks/config';
|
||||
|
||||
export function BVHButton(props: React.HTMLAttributes<HTMLButtonElement>) {
|
||||
const { config } = useConfig();
|
||||
const { useRPCPacket, sendRPCPacket } = useWebsocketAPI();
|
||||
const [recording, setRecording] = useState(false);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const { l10n } = useLocalization();
|
||||
|
||||
useEffect(() => {
|
||||
sendRPCPacket(RpcMessage.RecordBVHStatusRequest, new RecordBVHRequestT());
|
||||
}, []);
|
||||
|
||||
const toggleBVH = () => {
|
||||
const record = new RecordBVHRequestT();
|
||||
record.stop = recording;
|
||||
const toggleBVH = async () => {
|
||||
const record = new RecordBVHRequestT(recording);
|
||||
|
||||
if (isTauri() && !recording) {
|
||||
if (config?.bvhDirectory) {
|
||||
record.path = config.bvhDirectory;
|
||||
} else {
|
||||
setSaving(true);
|
||||
record.path = await save({
|
||||
title: l10n.getString('bvh-save_title'),
|
||||
filters: [
|
||||
{
|
||||
name: 'BVH',
|
||||
extensions: ['bvh'],
|
||||
},
|
||||
],
|
||||
defaultPath: 'bvh-recording.bvh',
|
||||
});
|
||||
setSaving(false);
|
||||
}
|
||||
}
|
||||
|
||||
sendRPCPacket(RpcMessage.RecordBVHRequest, record);
|
||||
};
|
||||
|
||||
@@ -33,6 +58,7 @@ export function BVHButton(props: React.HTMLAttributes<HTMLButtonElement>) {
|
||||
<BigButton
|
||||
icon={<RecordIcon width={20} />}
|
||||
onClick={toggleBVH}
|
||||
disabled={saving}
|
||||
className={classNames(
|
||||
props.className,
|
||||
'border',
|
||||
|
||||
@@ -56,7 +56,17 @@ export function WidgetsComponent() {
|
||||
<ResetButton type={ResetType.Full} size="big"></ResetButton>
|
||||
<ResetButton type={ResetType.Mounting} size="big"></ResetButton>
|
||||
<ClearMountingButton></ClearMountingButton>
|
||||
{typeof __ANDROID__ !== 'undefined' && !__ANDROID__?.isThere() && (
|
||||
<ResetButton
|
||||
type={ResetType.Mounting}
|
||||
size="big"
|
||||
bodyPartsToReset="feet"
|
||||
></ResetButton>
|
||||
<ResetButton
|
||||
type={ResetType.Mounting}
|
||||
size="big"
|
||||
bodyPartsToReset="fingers"
|
||||
></ResetButton>
|
||||
{(typeof __ANDROID__ === 'undefined' || !__ANDROID__?.isThere()) && (
|
||||
<BVHButton></BVHButton>
|
||||
)}
|
||||
<TrackingPauseButton></TrackingPauseButton>
|
||||
|
||||
@@ -19,7 +19,7 @@ interface InputProps {
|
||||
name: string;
|
||||
}
|
||||
|
||||
const FileInputContentBlank = ({
|
||||
export const FileInputContentBlank = ({
|
||||
isDragging,
|
||||
label,
|
||||
}: {
|
||||
@@ -55,7 +55,7 @@ const FileInputContentBlank = ({
|
||||
);
|
||||
};
|
||||
|
||||
const FileInputContentFile = ({
|
||||
export const FileInputContentFile = ({
|
||||
importedFileName,
|
||||
onClearPicker,
|
||||
}: {
|
||||
@@ -79,7 +79,8 @@ const FileInputContentFile = ({
|
||||
<a
|
||||
href="#"
|
||||
className="h-12 w-12 hover:bg-accent-background-20 cursor-pointer"
|
||||
onClick={() => {
|
||||
onClick={(ev) => {
|
||||
ev.stopPropagation();
|
||||
onClearPicker();
|
||||
}}
|
||||
>
|
||||
|
||||
68
gui/src/components/commons/TauriFileInput.tsx
Normal file
68
gui/src/components/commons/TauriFileInput.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
import {
|
||||
Control,
|
||||
Controller,
|
||||
RefCallBack,
|
||||
UseControllerProps,
|
||||
} from 'react-hook-form';
|
||||
import { FileInputContentBlank, FileInputContentFile } from './FileInput';
|
||||
import { open } from '@tauri-apps/plugin-dialog';
|
||||
|
||||
export function InnerTauriFileInput({
|
||||
label,
|
||||
value,
|
||||
onChange,
|
||||
directory,
|
||||
ref,
|
||||
}: {
|
||||
label: string;
|
||||
value: string | null;
|
||||
onChange: (...event: any[]) => void;
|
||||
directory: boolean;
|
||||
ref: RefCallBack;
|
||||
}) {
|
||||
return (
|
||||
<div ref={ref} onClick={async () => onChange(await open({ directory }))}>
|
||||
{value !== null
|
||||
? FileInputContentFile({
|
||||
importedFileName: value,
|
||||
onClearPicker: () => onChange(null),
|
||||
})
|
||||
: FileInputContentBlank({ isDragging: false, label })}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function TauriFileInput({
|
||||
control,
|
||||
rules,
|
||||
name,
|
||||
label,
|
||||
directory = false,
|
||||
}: {
|
||||
rules: UseControllerProps<any>['rules'];
|
||||
control: Control<any>;
|
||||
/**
|
||||
* Use a translation key!
|
||||
**/
|
||||
label: string;
|
||||
name: string;
|
||||
directory?: boolean;
|
||||
disabled?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<Controller
|
||||
rules={rules}
|
||||
name={name}
|
||||
control={control}
|
||||
render={({ field: { onChange, value, ref } }) => (
|
||||
<InnerTauriFileInput
|
||||
label={label}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
ref={ref}
|
||||
directory={directory}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -241,7 +241,7 @@ export function AddImusStep({
|
||||
<div className="flex flex-col w-full">
|
||||
<div className="flex flex-col gap-4">
|
||||
<Typography color="secondary">
|
||||
{l10n.getString('firmware_tool-board_pins_step-description')}
|
||||
{l10n.getString('firmware_tool-add_imus_step-description')}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className="my-4 flex flex-col gap-4">
|
||||
|
||||
@@ -3,7 +3,6 @@ import { Typography } from '@/components/commons/Typography';
|
||||
import { getTrackerName } from '@/hooks/tracker';
|
||||
import { ComponentProps, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
BoardType,
|
||||
DeviceDataT,
|
||||
DeviceIdTableT,
|
||||
FirmwareUpdateMethod,
|
||||
@@ -13,13 +12,12 @@ import {
|
||||
RpcMessage,
|
||||
TrackerStatus,
|
||||
} from 'solarxr-protocol';
|
||||
import semver from 'semver';
|
||||
import classNames from 'classnames';
|
||||
import { Button } from '@/components/commons/Button';
|
||||
import Markdown from 'react-markdown';
|
||||
import remark from 'remark-gfm';
|
||||
import { WarningBox } from '@/components/commons/TipBox';
|
||||
import { FirmwareRelease, useAppContext } from '@/hooks/app';
|
||||
import { useAppContext } from '@/hooks/app';
|
||||
import { DeviceCardControl } from '@/components/firmware-tool/DeviceCard';
|
||||
import { Control, useForm } from 'react-hook-form';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
@@ -35,34 +33,7 @@ import { LoaderIcon, SlimeState } from '@/components/commons/icon/LoaderIcon';
|
||||
import { A } from '@/components/commons/A';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { devicesAtom } from '@/store/app-store';
|
||||
|
||||
export function checkForUpdate(
|
||||
currentFirmwareRelease: FirmwareRelease,
|
||||
device: DeviceDataT
|
||||
): 'need-update' | 'low-battery' | 'updated' | 'unavailable' {
|
||||
if (
|
||||
device.hardwareInfo?.officialBoardType !== BoardType.SLIMEVR ||
|
||||
!semver.valid(currentFirmwareRelease.version) ||
|
||||
!semver.valid(device.hardwareInfo.firmwareVersion?.toString() ?? 'none')
|
||||
) {
|
||||
return 'unavailable';
|
||||
}
|
||||
|
||||
const canUpdate = semver.lt(
|
||||
device.hardwareInfo.firmwareVersion?.toString() ?? 'none',
|
||||
currentFirmwareRelease.version
|
||||
);
|
||||
|
||||
if (
|
||||
canUpdate &&
|
||||
device.hardwareStatus?.batteryPctEstimate != null &&
|
||||
device.hardwareStatus.batteryPctEstimate < 50
|
||||
) {
|
||||
return 'low-battery';
|
||||
}
|
||||
|
||||
return canUpdate ? 'need-update' : 'updated';
|
||||
}
|
||||
import { checkForUpdate } from '@/hooks/firmware-update';
|
||||
|
||||
interface FirmwareUpdateForm {
|
||||
selectedDevices: { [key: string]: boolean };
|
||||
@@ -144,7 +115,7 @@ export function FirmwareUpdate() {
|
||||
device.trackers.length > 0 &&
|
||||
currentFirmwareRelease &&
|
||||
device.hardwareInfo &&
|
||||
checkForUpdate(currentFirmwareRelease, device) === 'need-update' &&
|
||||
checkForUpdate(currentFirmwareRelease, device) === 'can-update' &&
|
||||
device.trackers.every(({ status }) => status === TrackerStatus.OK)
|
||||
) || [];
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useLocalization } from '@fluent/react';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
BodyPart,
|
||||
ResetRequestT,
|
||||
ResetType,
|
||||
RpcMessage,
|
||||
@@ -26,12 +27,14 @@ import classNames from 'classnames';
|
||||
export function ResetButton({
|
||||
type,
|
||||
size = 'big',
|
||||
bodyPartsToReset = 'default',
|
||||
className,
|
||||
onReseted,
|
||||
}: {
|
||||
className?: string;
|
||||
type: ResetType;
|
||||
size: 'big' | 'small';
|
||||
bodyPartsToReset?: 'default' | 'feet' | 'fingers';
|
||||
onReseted?: () => void;
|
||||
}) {
|
||||
const { l10n } = useLocalization();
|
||||
@@ -50,9 +53,53 @@ export function ResetButton({
|
||||
[statuses]
|
||||
);
|
||||
|
||||
const feetBodyParts = [BodyPart.LEFT_FOOT, BodyPart.RIGHT_FOOT];
|
||||
const fingerBodyParts = [
|
||||
BodyPart.LEFT_THUMB_METACARPAL,
|
||||
BodyPart.LEFT_THUMB_PROXIMAL,
|
||||
BodyPart.LEFT_THUMB_DISTAL,
|
||||
BodyPart.LEFT_INDEX_PROXIMAL,
|
||||
BodyPart.LEFT_INDEX_INTERMEDIATE,
|
||||
BodyPart.LEFT_INDEX_DISTAL,
|
||||
BodyPart.LEFT_MIDDLE_PROXIMAL,
|
||||
BodyPart.LEFT_MIDDLE_INTERMEDIATE,
|
||||
BodyPart.LEFT_MIDDLE_DISTAL,
|
||||
BodyPart.LEFT_RING_PROXIMAL,
|
||||
BodyPart.LEFT_RING_INTERMEDIATE,
|
||||
BodyPart.LEFT_RING_DISTAL,
|
||||
BodyPart.LEFT_LITTLE_PROXIMAL,
|
||||
BodyPart.LEFT_LITTLE_INTERMEDIATE,
|
||||
BodyPart.LEFT_LITTLE_DISTAL,
|
||||
BodyPart.RIGHT_THUMB_METACARPAL,
|
||||
BodyPart.RIGHT_THUMB_PROXIMAL,
|
||||
BodyPart.RIGHT_THUMB_DISTAL,
|
||||
BodyPart.RIGHT_INDEX_PROXIMAL,
|
||||
BodyPart.RIGHT_INDEX_INTERMEDIATE,
|
||||
BodyPart.RIGHT_INDEX_DISTAL,
|
||||
BodyPart.RIGHT_MIDDLE_PROXIMAL,
|
||||
BodyPart.RIGHT_MIDDLE_INTERMEDIATE,
|
||||
BodyPart.RIGHT_MIDDLE_DISTAL,
|
||||
BodyPart.RIGHT_RING_PROXIMAL,
|
||||
BodyPart.RIGHT_RING_INTERMEDIATE,
|
||||
BodyPart.RIGHT_RING_DISTAL,
|
||||
BodyPart.RIGHT_LITTLE_PROXIMAL,
|
||||
BodyPart.RIGHT_LITTLE_INTERMEDIATE,
|
||||
BodyPart.RIGHT_LITTLE_DISTAL,
|
||||
];
|
||||
|
||||
const reset = () => {
|
||||
const req = new ResetRequestT();
|
||||
req.resetType = type;
|
||||
if (bodyPartsToReset === 'default') {
|
||||
// Default (server handles it)
|
||||
req.bodyParts = [];
|
||||
} else if (bodyPartsToReset === 'feet') {
|
||||
// Feet
|
||||
req.bodyParts = feetBodyParts;
|
||||
} else if (bodyPartsToReset === 'fingers') {
|
||||
// Fingers
|
||||
req.bodyParts = fingerBodyParts;
|
||||
}
|
||||
sendRPCPacket(RpcMessage.ResetRequest, req);
|
||||
};
|
||||
|
||||
@@ -75,13 +122,22 @@ export function ResetButton({
|
||||
const text = useMemo(() => {
|
||||
switch (type) {
|
||||
case ResetType.Yaw:
|
||||
return l10n.getString('reset-yaw');
|
||||
return l10n.getString(
|
||||
'reset-yaw' +
|
||||
(bodyPartsToReset !== 'default' ? '-' + bodyPartsToReset : '')
|
||||
);
|
||||
case ResetType.Mounting:
|
||||
return l10n.getString('reset-mounting');
|
||||
return l10n.getString(
|
||||
'reset-mounting' +
|
||||
(bodyPartsToReset !== 'default' ? '-' + bodyPartsToReset : '')
|
||||
);
|
||||
case ResetType.Full:
|
||||
return l10n.getString('reset-full');
|
||||
return l10n.getString(
|
||||
'reset-full' +
|
||||
(bodyPartsToReset !== 'default' ? '-' + bodyPartsToReset : '')
|
||||
);
|
||||
}
|
||||
}, [type]);
|
||||
}, [type, bodyPartsToReset]);
|
||||
|
||||
const getIcon = () => {
|
||||
switch (type) {
|
||||
|
||||
@@ -129,7 +129,7 @@ export function CalibrationTutorialPage() {
|
||||
</Typography>
|
||||
</div>
|
||||
<Localized
|
||||
id="onboarding-calibration_tutorial-description"
|
||||
id="onboarding-calibration_tutorial-description-v1"
|
||||
elems={{ b: <b></b> }}
|
||||
>
|
||||
<Typography color="secondary">
|
||||
|
||||
@@ -35,6 +35,7 @@ import { Typography } from '@/components/commons/Typography';
|
||||
import { useLocaleConfig } from '@/i18n/config';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { ResetButton } from '@/components/home/ResetButton';
|
||||
import { Vector3 } from 'three';
|
||||
|
||||
function IconButton({
|
||||
onClick,
|
||||
@@ -454,9 +455,25 @@ export function ManualProportionsPage() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="rounded-md overflow-clip w-1/3 bg-background-60 hidden mobile:hidden sm:flex relative">
|
||||
<SkeletonVisualizerWidget />
|
||||
<SkeletonVisualizerWidget
|
||||
onInit={(context) => {
|
||||
context.addView({
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
width: 1,
|
||||
height: 1,
|
||||
position: new Vector3(3, 2.5, -3),
|
||||
onHeightChange(v, newHeight) {
|
||||
// retouch the target and scale settings so the height element doesnt hide the head
|
||||
v.controls.target.set(0, newHeight / 1.7, 0);
|
||||
const scale = Math.max(1, newHeight) / 1.2;
|
||||
v.camera.zoom = 1 / scale;
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="top-4 w-full px-4 absolute flex gap-2 flex-col md:flex-row">
|
||||
<div className="top-4 w-full px-4 absolute flex gap-2 flex-col lg:flex-row md:flex-wrap">
|
||||
<div className="h-14 flex flex-grow items-center">
|
||||
<ResetButton
|
||||
type={ResetType.Full}
|
||||
|
||||
@@ -65,7 +65,7 @@ export function ScaledProportionsPage() {
|
||||
elems={{ b: <b></b> }}
|
||||
>
|
||||
<Typography
|
||||
whitespace="whitespace-pre"
|
||||
whitespace="whitespace-pre-line"
|
||||
color="text-background-60"
|
||||
></Typography>
|
||||
</Localized>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { RpcMessage, SkeletonResetAllRequestT } from 'solarxr-protocol';
|
||||
import { Button } from '@/components/commons/Button';
|
||||
import { Typography } from '@/components/commons/Typography';
|
||||
import { useLocalization } from '@fluent/react';
|
||||
import { useWebsocketAPI } from '@/hooks/websocket-api';
|
||||
import { useLocalization } from '@fluent/react';
|
||||
import { RpcMessage, SkeletonResetAllRequestT } from 'solarxr-protocol';
|
||||
|
||||
export function ResetProportionsStep({
|
||||
nextStep,
|
||||
@@ -43,7 +43,7 @@ export function ResetProportionsStep({
|
||||
{l10n.getString('onboarding-automatic_proportions-prev_step')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
sendRPCPacket(
|
||||
RpcMessage.SkeletonResetAllRequest,
|
||||
|
||||
@@ -96,7 +96,6 @@ export type SettingsForm = {
|
||||
correctionStrength: number;
|
||||
};
|
||||
resetsSettings: {
|
||||
resetMountingFeet: boolean;
|
||||
armsMountingResetMode: number;
|
||||
yawResetSmoothTime: number;
|
||||
saveMountingReset: boolean;
|
||||
@@ -157,7 +156,6 @@ const defaultValues: SettingsForm = {
|
||||
},
|
||||
legTweaks: { correctionStrength: 0.3 },
|
||||
resetsSettings: {
|
||||
resetMountingFeet: false,
|
||||
armsMountingResetMode: 0,
|
||||
yawResetSmoothTime: 0.0,
|
||||
saveMountingReset: false,
|
||||
@@ -289,8 +287,6 @@ export function GeneralSettings() {
|
||||
|
||||
if (values.resetsSettings) {
|
||||
const resetsSettings = new ResetsSettingsT();
|
||||
resetsSettings.resetMountingFeet =
|
||||
values.resetsSettings.resetMountingFeet;
|
||||
resetsSettings.armsMountingResetMode =
|
||||
values.resetsSettings.armsMountingResetMode;
|
||||
resetsSettings.yawResetSmoothTime =
|
||||
@@ -865,24 +861,6 @@ export function GeneralSettings() {
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col pt-2 pb-3">
|
||||
<Typography color="secondary">
|
||||
{l10n.getString(
|
||||
'settings-general-fk_settings-leg_fk-reset_mounting_feet-description'
|
||||
)}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className="grid sm:grid-cols-1 gap-3 pb-3">
|
||||
<CheckBox
|
||||
variant="toggle"
|
||||
outlined
|
||||
control={control}
|
||||
name="resetsSettings.resetMountingFeet"
|
||||
label={l10n.getString(
|
||||
'settings-general-fk_settings-leg_fk-reset_mounting_feet'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Typography color="secondary">
|
||||
{l10n.getString(
|
||||
|
||||
@@ -18,6 +18,8 @@ import { Range } from '@/components/commons/Range';
|
||||
import { Dropdown } from '@/components/commons/Dropdown';
|
||||
import { ArrowRightLeftIcon } from '@/components/commons/icon/ArrowIcons';
|
||||
import { isTrayAvailable } from '@/utils/tauri';
|
||||
import { isTauri } from '@tauri-apps/api/core';
|
||||
import { TauriFileInput } from '@/components/commons/TauriFileInput';
|
||||
|
||||
interface InterfaceSettingsForm {
|
||||
appearance: {
|
||||
@@ -32,6 +34,7 @@ interface InterfaceSettingsForm {
|
||||
useTray: boolean;
|
||||
discordPresence: boolean;
|
||||
errorTracking: boolean;
|
||||
bvhDirectory: string | null;
|
||||
};
|
||||
notifications: {
|
||||
watchNewDevices: boolean;
|
||||
@@ -71,6 +74,7 @@ export function InterfaceSettings() {
|
||||
discordPresence:
|
||||
config?.discordPresence ?? defaultConfig.discordPresence,
|
||||
errorTracking: config?.errorTracking ?? false,
|
||||
bvhDirectory: config?.bvhDirectory ?? defaultConfig.bvhDirectory,
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -92,6 +96,7 @@ export function InterfaceSettings() {
|
||||
discordPresence: values.behavior.discordPresence,
|
||||
debug: values.behavior.devmode,
|
||||
errorTracking: values.behavior.errorTracking,
|
||||
bvhDirectory: values.behavior.bvhDirectory,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -314,6 +319,32 @@ export function InterfaceSettings() {
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{isTauri() && (
|
||||
<>
|
||||
<Typography bold>
|
||||
{l10n.getString('settings-interface-behavior-bvh_directory')}
|
||||
</Typography>
|
||||
<div className="flex flex-col pt-1 pb-2">
|
||||
<Localized
|
||||
id={'settings-interface-behavior-bvh_directory-description'}
|
||||
>
|
||||
<Typography color="secondary"></Typography>
|
||||
</Localized>
|
||||
</div>
|
||||
<div className="grid gap-3 pb-5">
|
||||
<TauriFileInput
|
||||
name="behavior.bvhDirectory"
|
||||
rules={{
|
||||
required: false,
|
||||
}}
|
||||
control={control}
|
||||
label="settings-interface-behavior-bvh_directory-label"
|
||||
directory
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
</SettingsPagePaneLayout>
|
||||
|
||||
|
||||
@@ -15,21 +15,26 @@ import { BodyPartIcon } from '@/components/commons/BodyPartIcon';
|
||||
import { DownloadIcon } from '@/components/commons/icon/DownloadIcon';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useAppContext } from '@/hooks/app';
|
||||
import { checkForUpdate } from '@/components/firmware-update/FirmwareUpdate';
|
||||
import { Tooltip } from '@/components/commons/Tooltip';
|
||||
import { Localized } from '@fluent/react';
|
||||
import { checkForUpdate } from '@/hooks/firmware-update';
|
||||
|
||||
function UpdateIcon({
|
||||
showUpdate,
|
||||
}: {
|
||||
showUpdate: 'need-update' | 'low-battery' | 'updated' | 'unavailable';
|
||||
showUpdate:
|
||||
| 'can-update'
|
||||
| 'low-battery'
|
||||
| 'updated'
|
||||
| 'unavailable'
|
||||
| 'blocked';
|
||||
}) {
|
||||
const content = (
|
||||
<div className="relative">
|
||||
<div
|
||||
className={classNames(
|
||||
'absolute rounded-full h-6 w-6 left-1 top-1 bg-accent-background-10 animate-[ping_2s_linear_infinite]',
|
||||
showUpdate !== 'need-update' && 'hidden'
|
||||
showUpdate !== 'can-update' && 'hidden'
|
||||
)}
|
||||
></div>
|
||||
<div
|
||||
@@ -45,7 +50,7 @@ function UpdateIcon({
|
||||
</div>
|
||||
);
|
||||
|
||||
return showUpdate !== 'need-update' ? (
|
||||
return showUpdate !== 'can-update' ? (
|
||||
<Tooltip
|
||||
preferedDirection="top"
|
||||
content={
|
||||
|
||||
@@ -37,9 +37,9 @@ import { Quaternion } from 'three';
|
||||
import { useAppContext } from '@/hooks/app';
|
||||
import { MagnetometerToggleSetting } from '@/components/settings/pages/MagnetometerToggleSetting';
|
||||
import semver from 'semver';
|
||||
import { checkForUpdate } from '@/components/firmware-update/FirmwareUpdate';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { ignoredTrackersAtom } from '@/store/app-store';
|
||||
import { checkForUpdate } from '@/hooks/firmware-update';
|
||||
|
||||
const rotationsLabels: [Quaternion, string][] = [
|
||||
[rotationToQuatMap.BACK, 'tracker-rotation-back'],
|
||||
@@ -222,12 +222,20 @@ export function TrackerSettingsPage() {
|
||||
)}
|
||||
{!updateUnavailable && (
|
||||
<>
|
||||
{needUpdate === 'blocked' && (
|
||||
// This happens only if no update is available and or the user is not in the current stagged
|
||||
<Localized id="tracker-settings-update-blocked">
|
||||
<Typography>
|
||||
Update not available. No other releases available
|
||||
</Typography>
|
||||
</Localized>
|
||||
)}
|
||||
{needUpdate === 'updated' && (
|
||||
<Localized id="tracker-settings-update-up_to_date">
|
||||
<Typography>Up to date</Typography>
|
||||
</Localized>
|
||||
)}
|
||||
{needUpdate === 'need-update' && currentFirmwareRelease && (
|
||||
{needUpdate === 'can-update' && currentFirmwareRelease && (
|
||||
<Localized
|
||||
id="tracker-settings-update-available"
|
||||
vars={{ versionName: currentFirmwareRelease.name }}
|
||||
@@ -251,9 +259,9 @@ export function TrackerSettingsPage() {
|
||||
<Localized id="tracker-settings-update">
|
||||
<Button
|
||||
variant={
|
||||
needUpdate === 'need-update' ? 'primary' : 'secondary'
|
||||
needUpdate === 'can-update' ? 'primary' : 'secondary'
|
||||
}
|
||||
disabled={needUpdate !== 'need-update'}
|
||||
disabled={needUpdate !== 'can-update'}
|
||||
to="/firmware-update"
|
||||
>
|
||||
Update now
|
||||
|
||||
@@ -13,17 +13,10 @@ import { useConfig } from './config';
|
||||
import { useBonesDataFeedConfig, useDataFeedConfig } from './datafeed-config';
|
||||
import { useWebsocketAPI } from './websocket-api';
|
||||
import { error } from '@/utils/logging';
|
||||
import { cacheWrap } from './cache';
|
||||
import { useAtomValue, useSetAtom } from 'jotai';
|
||||
import { bonesAtom, datafeedAtom, devicesAtom } from '@/store/app-store';
|
||||
import { updateSentryContext } from '@/utils/sentry';
|
||||
|
||||
export interface FirmwareRelease {
|
||||
name: string;
|
||||
version: string;
|
||||
changelog: string;
|
||||
firmwareFile: string;
|
||||
}
|
||||
import { fetchCurrentFirmwareRelease, FirmwareRelease } from './firmware-update';
|
||||
|
||||
export interface AppContext {
|
||||
currentFirmwareRelease: FirmwareRelease | null;
|
||||
@@ -82,49 +75,6 @@ export function useProvideAppContext(): AppContext {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const fetchCurrentFirmwareRelease = async () => {
|
||||
const releases: any[] | null = JSON.parse(
|
||||
(await cacheWrap(
|
||||
'firmware-releases',
|
||||
() =>
|
||||
fetch('https://api.github.com/repos/SlimeVR/SlimeVR-Tracker-ESP/releases')
|
||||
.then((res) => res.text())
|
||||
.catch(() => null),
|
||||
60 * 60 * 1000
|
||||
)) ?? 'null'
|
||||
);
|
||||
if (!releases) return null;
|
||||
|
||||
const firstRelease = releases.find(
|
||||
(release) =>
|
||||
release.prerelease === false &&
|
||||
release.assets &&
|
||||
release.assets.find(
|
||||
(asset: any) =>
|
||||
asset.name === 'BOARD_SLIMEVR-firmware.bin' && asset.browser_download_url
|
||||
)
|
||||
);
|
||||
|
||||
let version = firstRelease.tag_name;
|
||||
if (version.charAt(0) === 'v') {
|
||||
version = version.substring(1);
|
||||
}
|
||||
|
||||
if (firstRelease) {
|
||||
return {
|
||||
name: firstRelease.name,
|
||||
version,
|
||||
changelog: firstRelease.body,
|
||||
firmwareFile: firstRelease.assets.find(
|
||||
(asset: any) =>
|
||||
asset.name === 'BOARD_SLIMEVR-firmware.bin' && asset.browser_download_url
|
||||
).browser_download_url,
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
fetchCurrentFirmwareRelease().then((res) => setCurrentFirmwareRelease(res));
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ export interface Config {
|
||||
decorations: boolean;
|
||||
showNavbarOnboarding: boolean;
|
||||
vrcMutedWarnings: string[];
|
||||
bvhDirectory: string | null;
|
||||
}
|
||||
|
||||
export interface ConfigContext {
|
||||
@@ -73,6 +74,7 @@ export const defaultConfig: Config = {
|
||||
showNavbarOnboarding: true,
|
||||
vrcMutedWarnings: [],
|
||||
devSettings: defaultDevSettings,
|
||||
bvhDirectory: null,
|
||||
};
|
||||
|
||||
interface CrossStorage {
|
||||
|
||||
143
gui/src/hooks/firmware-update.ts
Normal file
143
gui/src/hooks/firmware-update.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
import { BoardType, DeviceDataT } from 'solarxr-protocol';
|
||||
import { fetch as tauriFetch } from '@tauri-apps/plugin-http';
|
||||
import { cacheWrap } from './cache';
|
||||
import semver from 'semver';
|
||||
import { hostname, locale, platform, version } from '@tauri-apps/plugin-os';
|
||||
|
||||
export interface FirmwareRelease {
|
||||
name: string;
|
||||
version: string;
|
||||
changelog: string;
|
||||
firmwareFile: string;
|
||||
userCanUpdate: boolean;
|
||||
}
|
||||
|
||||
// implemetation of https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
|
||||
const hash = (str: string) => {
|
||||
let hash = 2166136261;
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
hash ^= str.charCodeAt(i);
|
||||
hash = Math.imul(hash, 16777619); // FNV prime
|
||||
}
|
||||
|
||||
// Convert to unsigned 32-bit integer and normalize (0, 1)
|
||||
return (hash >>> 0) / 2 ** 32;
|
||||
};
|
||||
|
||||
const firstAsset = (assets: any[], name: string) =>
|
||||
assets.find((asset: any) => asset.name === name && asset.browser_download_url);
|
||||
|
||||
const checkUserCanUpdate = async (url: string, fwVersion: string) => {
|
||||
if (!url) return true;
|
||||
const deployDataJson = JSON.parse(
|
||||
(await cacheWrap(
|
||||
`firmware-${fwVersion}-deploy`,
|
||||
() =>
|
||||
tauriFetch(url)
|
||||
.then((res) => res.text())
|
||||
.catch(() => null),
|
||||
60 * 60 * 1000
|
||||
)) || 'null'
|
||||
);
|
||||
if (!deployDataJson) return true;
|
||||
|
||||
const deployData = (
|
||||
Object.entries(deployDataJson).map(([key, val]) => {
|
||||
return [parseFloat(key), new Date(val as string)];
|
||||
}) as [number, Date][]
|
||||
).sort(([a], [b]) => a - b);
|
||||
|
||||
if (deployData.find(([key]) => key > 1 || key <= 0)) return false; // values outside boundaries / cancel
|
||||
|
||||
if (
|
||||
deployData.find(
|
||||
([, date], index) =>
|
||||
index > 0 && date.getTime() < deployData[index - 1][1].getTime()
|
||||
)
|
||||
)
|
||||
return false; // Dates in the wrong order / cancel
|
||||
|
||||
const todayUpdateRange = deployData.find(([, date], index) => {
|
||||
if (index === 0 && Date.now() < date.getTime()) return true;
|
||||
return Date.now() >= date.getTime();
|
||||
})?.[0];
|
||||
|
||||
if (!todayUpdateRange) return false;
|
||||
|
||||
const uniqueUserKey = `${await hostname()}-${await locale()}-${platform()}-${version()}`;
|
||||
// Make it so the hash change every version. Prevent the same user from getting the same delay
|
||||
return hash(`${uniqueUserKey}-${fwVersion}`) <= todayUpdateRange;
|
||||
};
|
||||
|
||||
export async function fetchCurrentFirmwareRelease(): Promise<FirmwareRelease | null> {
|
||||
const releases: any[] | null = JSON.parse(
|
||||
(await cacheWrap(
|
||||
'firmware-releases',
|
||||
() =>
|
||||
fetch('https://api.github.com/repos/SlimeVR/SlimeVR-Tracker-ESP/releases')
|
||||
.then((res) => res.text())
|
||||
.catch(() => null),
|
||||
60 * 60 * 1000
|
||||
)) || 'null'
|
||||
);
|
||||
if (!releases) return null;
|
||||
|
||||
const processedReleses = [];
|
||||
for (const release of releases) {
|
||||
const fwAsset = firstAsset(release.assets, 'BOARD_SLIMEVR-firmware.bin');
|
||||
if (!release.assets || !fwAsset /* || release.prerelease */) continue;
|
||||
|
||||
let version = release.tag_name;
|
||||
if (version.charAt(0) === 'v') {
|
||||
version = version.substring(1);
|
||||
}
|
||||
|
||||
const deployAsset = firstAsset(release.assets, 'deploy.json');
|
||||
const userCanUpdate = await checkUserCanUpdate(
|
||||
deployAsset?.browser_download_url,
|
||||
version
|
||||
);
|
||||
processedReleses.push({
|
||||
name: release.name,
|
||||
version,
|
||||
changelog: release.body,
|
||||
firmwareFile: fwAsset.browser_download_url,
|
||||
userCanUpdate,
|
||||
});
|
||||
|
||||
if (userCanUpdate) break; // Stop early if we found one valid update. No need to download more
|
||||
}
|
||||
return (
|
||||
processedReleses.find(({ userCanUpdate }) => userCanUpdate) ?? processedReleses[0]
|
||||
);
|
||||
}
|
||||
|
||||
export function checkForUpdate(
|
||||
currentFirmwareRelease: FirmwareRelease,
|
||||
device: DeviceDataT
|
||||
): 'can-update' | 'low-battery' | 'updated' | 'unavailable' | 'blocked' {
|
||||
if (!currentFirmwareRelease.userCanUpdate) return 'blocked';
|
||||
|
||||
if (
|
||||
device.hardwareInfo?.officialBoardType !== BoardType.SLIMEVR ||
|
||||
!semver.valid(currentFirmwareRelease.version) ||
|
||||
!semver.valid(device.hardwareInfo.firmwareVersion?.toString() ?? 'none')
|
||||
) {
|
||||
return 'unavailable';
|
||||
}
|
||||
|
||||
const canUpdate = semver.lt(
|
||||
device.hardwareInfo.firmwareVersion?.toString() ?? 'none',
|
||||
currentFirmwareRelease.version
|
||||
);
|
||||
|
||||
if (
|
||||
canUpdate &&
|
||||
device.hardwareStatus?.batteryPctEstimate != null &&
|
||||
device.hardwareStatus.batteryPctEstimate < 50
|
||||
) {
|
||||
return 'low-battery';
|
||||
}
|
||||
|
||||
return canUpdate ? 'can-update' : 'updated';
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
RpcMessage,
|
||||
StatusData,
|
||||
StatusMessageT,
|
||||
StatusPublicNetworkT,
|
||||
StatusSteamVRDisconnectedT,
|
||||
StatusSystemFixedT,
|
||||
StatusSystemRequestT,
|
||||
@@ -124,8 +125,14 @@ export function parseStatusToLocale(
|
||||
case StatusData.NONE:
|
||||
case StatusData.StatusTrackerReset:
|
||||
case StatusData.StatusUnassignedHMD:
|
||||
case StatusData.StatusPublicNetwork:
|
||||
return {};
|
||||
case StatusData.StatusPublicNetwork: {
|
||||
const data = status.data as StatusPublicNetworkT;
|
||||
return {
|
||||
adapters: data.adapters.join(', '),
|
||||
count: data.adapters.length,
|
||||
};
|
||||
}
|
||||
case StatusData.StatusSteamVRDisconnected: {
|
||||
const data = status.data as StatusSteamVRDisconnectedT;
|
||||
if (typeof data.bridgeSettingsName === 'string') {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2022",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"lib": ["dom", "dom.iterable", "ES2023"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
|
||||
16
pnpm-lock.yaml
generated
16
pnpm-lock.yaml
generated
@@ -62,6 +62,9 @@ importers:
|
||||
'@tauri-apps/plugin-fs':
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
'@tauri-apps/plugin-http':
|
||||
specifier: ^2.5.0
|
||||
version: 2.5.0
|
||||
'@tauri-apps/plugin-os':
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
@@ -485,6 +488,7 @@ packages:
|
||||
'@dword-design/functions@5.0.27':
|
||||
resolution: {integrity: sha512-16A97RKtn21xLWI7Y7VhFz4WCWui+TLit/J5/l77BDpWmPi2LzgjQzDbafDDw7OUeY6GEAi/M4jdbuQhsIbSEg==}
|
||||
engines: {node: '>=14'}
|
||||
deprecated: Use lodash and endent
|
||||
|
||||
'@esbuild/aix-ppc64@0.21.5':
|
||||
resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
|
||||
@@ -1179,6 +1183,9 @@ packages:
|
||||
'@tauri-apps/api@2.0.2':
|
||||
resolution: {integrity: sha512-3wSwmG+1kr6WrgAFKK5ijkNFPp8TT3FLj3YHUb5EwMO+3FxX4uWlfSWkeeBy+Kc1RsKzugtYLuuya+98Flj+3w==}
|
||||
|
||||
'@tauri-apps/api@2.6.0':
|
||||
resolution: {integrity: sha512-hRNcdercfgpzgFrMXWwNDBN0B7vNzOzRepy6ZAmhxi5mDLVPNrTpo9MGg2tN/F7JRugj4d2aF7E1rtPXAHaetg==}
|
||||
|
||||
'@tauri-apps/cli-darwin-arm64@2.0.3':
|
||||
resolution: {integrity: sha512-jIsbxGWS+As1ZN7umo90nkql/ZAbrDK0GBT6UsgHSz5zSwwArICsZFFwE1pLZip5yoiV5mn3TGG2c1+v+0puzQ==}
|
||||
engines: {node: '>= 10'}
|
||||
@@ -1250,6 +1257,9 @@ packages:
|
||||
'@tauri-apps/plugin-fs@2.0.0':
|
||||
resolution: {integrity: sha512-BNEeQQ5aH8J5SwYuWgRszVyItsmquRuzK2QRkVj8Z0sCsLnSvJFYI3JHRzzr3ltZGq1nMPtblrlZzuKqVzRawA==}
|
||||
|
||||
'@tauri-apps/plugin-http@2.5.0':
|
||||
resolution: {integrity: sha512-l4M2DUIsOBIMrbj4dJZwrB4mJiB7OA/2Tj3gEbX2fjq5MOpETklJPKfDvzUTDwuq4lIKCKKykz8E8tpOgvi0EQ==}
|
||||
|
||||
'@tauri-apps/plugin-os@2.0.0':
|
||||
resolution: {integrity: sha512-M7hG/nNyQYTJxVG/UhTKhp9mpXriwWzrs9mqDreB8mIgqA3ek5nHLdwRZJWhkKjZrnDT4v9CpA9BhYeplTlAiA==}
|
||||
|
||||
@@ -5405,6 +5415,8 @@ snapshots:
|
||||
|
||||
'@tauri-apps/api@2.0.2': {}
|
||||
|
||||
'@tauri-apps/api@2.6.0': {}
|
||||
|
||||
'@tauri-apps/cli-darwin-arm64@2.0.3':
|
||||
optional: true
|
||||
|
||||
@@ -5456,6 +5468,10 @@ snapshots:
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.0.2
|
||||
|
||||
'@tauri-apps/plugin-http@2.5.0':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.6.0
|
||||
|
||||
'@tauri-apps/plugin-os@2.0.0':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.0.2
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[toolchain]
|
||||
channel = "1.81"
|
||||
channel = "1.82"
|
||||
profile = "default"
|
||||
components = ["rustc", "cargo", "clippy", "rustfmt", "rust-analyzer", "rust-src"]
|
||||
|
||||
@@ -40,6 +40,7 @@ import java.util.*
|
||||
import java.util.concurrent.LinkedBlockingQueue
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.function.Consumer
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.concurrent.schedule
|
||||
|
||||
typealias BridgeProvider = (
|
||||
@@ -241,7 +242,7 @@ class VRServer @JvmOverloads constructor(
|
||||
bridge.dataRead()
|
||||
}
|
||||
for (tracker in trackers) {
|
||||
tracker.tick()
|
||||
tracker.tick(fpsTimer.timePerFrame)
|
||||
}
|
||||
humanPoseManager.update()
|
||||
for (bridge in bridges) {
|
||||
@@ -297,16 +298,16 @@ class VRServer @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun resetTrackersFull(resetSourceName: String?) {
|
||||
queueTask { humanPoseManager.resetTrackersFull(resetSourceName) }
|
||||
fun resetTrackersFull(resetSourceName: String?, bodyParts: List<Int> = ArrayList()) {
|
||||
queueTask { humanPoseManager.resetTrackersFull(resetSourceName, bodyParts) }
|
||||
}
|
||||
|
||||
fun resetTrackersYaw(resetSourceName: String?) {
|
||||
queueTask { humanPoseManager.resetTrackersYaw(resetSourceName) }
|
||||
fun resetTrackersYaw(resetSourceName: String?, bodyParts: List<Int> = TrackerUtils.allBodyPartsButFingers) {
|
||||
queueTask { humanPoseManager.resetTrackersYaw(resetSourceName, bodyParts) }
|
||||
}
|
||||
|
||||
fun resetTrackersMounting(resetSourceName: String?) {
|
||||
queueTask { humanPoseManager.resetTrackersMounting(resetSourceName) }
|
||||
fun resetTrackersMounting(resetSourceName: String?, bodyParts: List<Int> = TrackerUtils.allBodyPartsButFeetAndFingers) {
|
||||
queueTask { humanPoseManager.resetTrackersMounting(resetSourceName, bodyParts) }
|
||||
}
|
||||
|
||||
fun clearTrackersMounting(resetSourceName: String?) {
|
||||
|
||||
@@ -102,9 +102,9 @@ class AutoBoneHandler(private val server: VRServer) {
|
||||
|
||||
// ex. 1000 samples at 20 ms per sample is 20 seconds
|
||||
val sampleCount = autoBone.globalConfig.sampleCount
|
||||
val sampleRate = autoBone.globalConfig.sampleRateMs
|
||||
val sampleRate = autoBone.globalConfig.sampleRateMs / 1000f
|
||||
// Calculate total time in seconds
|
||||
val totalTime: Float = (sampleCount * sampleRate) / 1000f
|
||||
val totalTime: Float = sampleCount * sampleRate
|
||||
|
||||
val framesFuture = poseRecorder
|
||||
.startFrameRecording(
|
||||
|
||||
@@ -58,7 +58,7 @@ public class ConfigManager {
|
||||
}
|
||||
}
|
||||
|
||||
public void atomicMove(Path from, Path to) throws IOException {
|
||||
static public void atomicMove(Path from, Path to) throws IOException {
|
||||
try {
|
||||
// Atomic move to overwrite
|
||||
Files.move(from, to, StandardCopyOption.ATOMIC_MOVE);
|
||||
|
||||
@@ -31,9 +31,6 @@ enum class ArmsResetModes(val id: Int) {
|
||||
|
||||
class ResetsConfig {
|
||||
|
||||
// Enable mounting reset for feet?
|
||||
var resetMountingFeet = false
|
||||
|
||||
// Reset mode used for the arms
|
||||
var mode = ArmsResetModes.BACK
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package dev.slimevr.poseframeformat
|
||||
import dev.slimevr.VRServer
|
||||
import dev.slimevr.poseframeformat.trackerdata.TrackerFrames
|
||||
import dev.slimevr.tracking.trackers.Tracker
|
||||
import dev.slimevr.util.TickReducer
|
||||
import dev.slimevr.util.ann.VRServerThread
|
||||
import io.eiren.util.collections.FastList
|
||||
import io.eiren.util.logging.LogManager
|
||||
@@ -18,77 +19,62 @@ class PoseRecorder(private val server: VRServer) {
|
||||
private var poseFrame: PoseFrames? = null
|
||||
private var numFrames = -1
|
||||
private var frameCursor = 0
|
||||
private var frameRecordingInterval = 60L
|
||||
private var nextFrameTimeMs = -1L
|
||||
private var currentRecording: CompletableFuture<PoseFrames>? = null
|
||||
private var currentFrameCallback: Consumer<RecordingProgress>? = null
|
||||
|
||||
// Default 50 TPS
|
||||
private val ticker = TickReducer({ onTick() }, 0.02f)
|
||||
|
||||
private var recordingFuture: CompletableFuture<PoseFrames>? = null
|
||||
private var frameCallback: Consumer<RecordingProgress>? = null
|
||||
var trackers = FastList<Pair<Tracker, TrackerFrames>>()
|
||||
|
||||
init {
|
||||
server.addOnTick { onTick() }
|
||||
server.addOnTick {
|
||||
if (numFrames > 0) {
|
||||
ticker.tick(server.fpsTimer.timePerFrame)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure it's synchronized since this is the server thread interacting with
|
||||
// an unknown outside thread controlling this class
|
||||
@Synchronized
|
||||
@VRServerThread
|
||||
fun onTick() {
|
||||
if (numFrames <= 0) {
|
||||
return
|
||||
}
|
||||
val poseFrame = poseFrame
|
||||
val trackers: List<Pair<Tracker, TrackerFrames>> = trackers
|
||||
if (poseFrame == null) {
|
||||
return
|
||||
}
|
||||
if (frameCursor >= numFrames) {
|
||||
// If done and hasn't yet, send finished recording
|
||||
stopFrameRecording()
|
||||
return
|
||||
}
|
||||
val curTime = System.currentTimeMillis()
|
||||
if (curTime < nextFrameTimeMs) {
|
||||
return
|
||||
}
|
||||
nextFrameTimeMs += frameRecordingInterval
|
||||
|
||||
// To prevent duplicate frames, make sure the frame time is always in
|
||||
// the future
|
||||
if (nextFrameTimeMs <= curTime) {
|
||||
nextFrameTimeMs = curTime + frameRecordingInterval
|
||||
// A stopped recording will be accounted for by an empty "trackers" list
|
||||
val cursor = frameCursor++
|
||||
for (tracker in trackers) {
|
||||
// Add a frame for each tracker
|
||||
tracker.right.addFrameFromTracker(cursor, tracker.left)
|
||||
}
|
||||
|
||||
// Make sure it's synchronized since this is the server thread
|
||||
// interacting with
|
||||
// an unknown outside thread controlling this class
|
||||
synchronized(this) {
|
||||
// A stopped recording will be accounted for by an empty "trackers"
|
||||
// list
|
||||
val cursor = frameCursor++
|
||||
for (tracker in trackers) {
|
||||
// Add a frame for each tracker
|
||||
tracker.right.addFrameFromTracker(cursor, tracker.left)
|
||||
}
|
||||
|
||||
currentFrameCallback?.accept(RecordingProgress(frameCursor, numFrames))
|
||||
|
||||
// If done, send finished recording
|
||||
if (frameCursor >= numFrames) {
|
||||
stopFrameRecording()
|
||||
}
|
||||
// Send the number of finished frames
|
||||
frameCallback?.accept(RecordingProgress(frameCursor, numFrames))
|
||||
// If done, send finished recording
|
||||
if (frameCursor >= numFrames) {
|
||||
stopFrameRecording()
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun startFrameRecording(
|
||||
numFrames: Int,
|
||||
intervalMs: Long,
|
||||
interval: Float,
|
||||
trackers: List<Tracker?> = server.allTrackers,
|
||||
frameCallback: Consumer<RecordingProgress>? = null,
|
||||
): Future<PoseFrames> {
|
||||
require(numFrames >= 1) { "numFrames must at least have a value of 1." }
|
||||
require(intervalMs >= 1) { "intervalMs must at least have a value of 1." }
|
||||
require(interval > 0) { "interval must be greater than 0." }
|
||||
require(trackers.isNotEmpty()) { "trackers must have at least one entry." }
|
||||
|
||||
cancelFrameRecording()
|
||||
val poseFrame = PoseFrames(trackers.size)
|
||||
poseFrame.frameInterval = intervalMs / 1000f
|
||||
poseFrame.frameInterval = interval
|
||||
|
||||
// Update tracker list
|
||||
this.trackers.ensureCapacity(trackers.size)
|
||||
@@ -107,26 +93,29 @@ class PoseRecorder(private val server: VRServer) {
|
||||
}
|
||||
require(this.trackers.isNotEmpty()) { "trackers must have at least one valid tracker." }
|
||||
|
||||
// Ticking setup
|
||||
ticker.interval = interval
|
||||
ticker.reset()
|
||||
|
||||
val recordingFuture = CompletableFuture<PoseFrames>()
|
||||
this.recordingFuture = recordingFuture
|
||||
this.frameCallback = frameCallback
|
||||
|
||||
// Recording setup
|
||||
this.poseFrame = poseFrame
|
||||
frameCursor = 0
|
||||
this.numFrames = numFrames
|
||||
frameRecordingInterval = intervalMs
|
||||
nextFrameTimeMs = -1L
|
||||
|
||||
LogManager
|
||||
.info(
|
||||
"[PoseRecorder] Recording $numFrames samples at a $intervalMs ms frame interval",
|
||||
)
|
||||
LogManager.info(
|
||||
"[PoseRecorder] Recording $numFrames samples at a $interval s frame interval",
|
||||
)
|
||||
|
||||
currentFrameCallback = frameCallback
|
||||
val internalCurrentRecording = CompletableFuture<PoseFrames>()
|
||||
currentRecording = internalCurrentRecording
|
||||
return internalCurrentRecording
|
||||
return recordingFuture
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun internalStopFrameRecording(cancel: Boolean) {
|
||||
val currentRecording = currentRecording
|
||||
val currentRecording = recordingFuture
|
||||
if (currentRecording != null && !currentRecording.isDone) {
|
||||
val currentFrames = poseFrame
|
||||
if (cancel || currentFrames == null) {
|
||||
@@ -154,25 +143,20 @@ class PoseRecorder(private val server: VRServer) {
|
||||
internalStopFrameRecording(true)
|
||||
}
|
||||
|
||||
@get:Synchronized
|
||||
val isReadyToRecord: Boolean
|
||||
get() = server.trackersCount > 0
|
||||
|
||||
@get:Synchronized
|
||||
val isRecording: Boolean
|
||||
get() = numFrames > frameCursor
|
||||
|
||||
@Synchronized
|
||||
fun hasRecording(): Boolean = currentRecording != null
|
||||
fun hasRecording(): Boolean = recordingFuture != null
|
||||
|
||||
@get:Synchronized
|
||||
val framesAsync: Future<PoseFrames>?
|
||||
get() = currentRecording
|
||||
get() = recordingFuture
|
||||
|
||||
@get:Throws(ExecutionException::class, InterruptedException::class)
|
||||
@get:Synchronized
|
||||
val frames: PoseFrames?
|
||||
get() {
|
||||
return currentRecording?.get()
|
||||
return recordingFuture?.get()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ class BVHFileStream : PoseDataStream {
|
||||
writer.write(getBufferedFrameCount(frameCount) + "\n")
|
||||
|
||||
// Frame time in seconds
|
||||
writer.write("Frame Time: ${streamer.frameInterval / 1000.0}\n")
|
||||
writer.write("Frame Time: ${streamer.frameInterval}\n")
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
@@ -178,7 +178,7 @@ class BVHFileStream : PoseDataStream {
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
public override fun writeFrame(skeleton: HumanSkeleton) {
|
||||
override fun writeFrame(skeleton: HumanSkeleton) {
|
||||
val rootBone = skeleton.headBone
|
||||
|
||||
val rootPos = rootBone.getPosition()
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
package dev.slimevr.posestreamer;
|
||||
|
||||
import dev.slimevr.VRServer;
|
||||
import io.eiren.util.logging.LogManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
public class BVHRecorder {
|
||||
|
||||
private static final File bvhSaveDir = new File("BVH Recordings");
|
||||
private final ServerPoseStreamer poseStreamer;
|
||||
private PoseDataStream poseDataStream = null;
|
||||
|
||||
public BVHRecorder(VRServer server) {
|
||||
this.poseStreamer = new ServerPoseStreamer(server);
|
||||
}
|
||||
|
||||
public void startRecording() {
|
||||
File bvhFile = getBvhFile();
|
||||
if (bvhFile != null) {
|
||||
try {
|
||||
poseDataStream = new BVHFileStream(bvhFile);
|
||||
poseStreamer.setOutput(poseDataStream, 1000L / 100L);
|
||||
} catch (IOException e1) {
|
||||
LogManager
|
||||
.severe(
|
||||
"[BVH] Failed to create the recording file \"" + bvhFile.getPath() + "\"."
|
||||
);
|
||||
}
|
||||
} else {
|
||||
LogManager.severe("[BVH] Unable to get file to save to");
|
||||
}
|
||||
}
|
||||
|
||||
public void endRecording() {
|
||||
|
||||
try {
|
||||
poseStreamer.closeOutput(poseDataStream);
|
||||
} catch (Exception e1) {
|
||||
LogManager.severe("[BVH] Exception while closing poseDataStream", e1);
|
||||
} finally {
|
||||
poseDataStream = null;
|
||||
}
|
||||
}
|
||||
|
||||
private File getBvhFile() {
|
||||
if (bvhSaveDir.isDirectory() || bvhSaveDir.mkdirs()) {
|
||||
File saveRecording;
|
||||
int recordingIndex = 1;
|
||||
do {
|
||||
saveRecording = new File(bvhSaveDir, "BVH-Recording" + recordingIndex++ + ".bvh");
|
||||
} while (saveRecording.exists());
|
||||
|
||||
return saveRecording;
|
||||
} else {
|
||||
LogManager
|
||||
.severe(
|
||||
"[BVH] Failed to create the recording directory \""
|
||||
+ bvhSaveDir.getPath()
|
||||
+ "\"."
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isRecording() {
|
||||
return poseDataStream != null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package dev.slimevr.posestreamer
|
||||
|
||||
import dev.slimevr.VRServer
|
||||
import io.eiren.util.logging.LogManager
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.nio.file.Path
|
||||
|
||||
class BVHRecorder(server: VRServer) {
|
||||
private val poseStreamer: ServerPoseStreamer = ServerPoseStreamer(server)
|
||||
private var poseDataStream: PoseDataStream? = null
|
||||
|
||||
val isRecording: Boolean
|
||||
get() = poseDataStream != null
|
||||
|
||||
fun startRecording(path: Path) {
|
||||
val filePath = path.toFile()
|
||||
|
||||
val file = if (filePath.isDirectory()) {
|
||||
getBvhFile(filePath) ?: return
|
||||
} else {
|
||||
filePath
|
||||
}
|
||||
|
||||
try {
|
||||
val stream = BVHFileStream(file)
|
||||
poseDataStream = stream
|
||||
// 100 FPS
|
||||
poseStreamer.setOutput(stream, 1f / 100f)
|
||||
} catch (_: IOException) {
|
||||
LogManager.severe("[BVH] Failed to create the recording file \"${file.path}\".")
|
||||
}
|
||||
}
|
||||
|
||||
fun endRecording() {
|
||||
try {
|
||||
val stream = poseDataStream
|
||||
if (stream != null) {
|
||||
poseStreamer.closeOutput(stream)
|
||||
}
|
||||
} catch (e1: Exception) {
|
||||
LogManager.severe("[BVH] Exception while closing poseDataStream", e1)
|
||||
} finally {
|
||||
poseDataStream = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getBvhFile(bvhSaveDir: File): File? {
|
||||
if (bvhSaveDir.isDirectory() || bvhSaveDir.mkdirs()) {
|
||||
var saveRecording: File?
|
||||
var recordingIndex = 1
|
||||
do {
|
||||
saveRecording =
|
||||
File(bvhSaveDir, "BVH-Recording${recordingIndex++}.bvh")
|
||||
} while (saveRecording.exists())
|
||||
|
||||
return saveRecording
|
||||
} else {
|
||||
LogManager
|
||||
.severe(
|
||||
"[BVH] Failed to create the recording directory \"${bvhSaveDir.path}\".",
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package dev.slimevr.posestreamer;
|
||||
|
||||
public class BVHSettings {
|
||||
private float offsetScale = 100f;
|
||||
private float positionScale = 100f;
|
||||
private boolean writeEndNodes = false;
|
||||
|
||||
public static final BVHSettings DEFAULT = new BVHSettings();
|
||||
public static final BVHSettings BLENDER = new BVHSettings(DEFAULT)
|
||||
.setOffsetScale(1f)
|
||||
.setPositionScale(1f);
|
||||
|
||||
public BVHSettings() {
|
||||
}
|
||||
|
||||
public BVHSettings(BVHSettings source) {
|
||||
this.offsetScale = source.offsetScale;
|
||||
this.positionScale = source.positionScale;
|
||||
this.writeEndNodes = source.writeEndNodes;
|
||||
}
|
||||
|
||||
public float getOffsetScale() {
|
||||
return offsetScale;
|
||||
}
|
||||
|
||||
public BVHSettings setOffsetScale(float offsetScale) {
|
||||
this.offsetScale = offsetScale;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getPositionScale() {
|
||||
return positionScale;
|
||||
}
|
||||
|
||||
public BVHSettings setPositionScale(float positionScale) {
|
||||
this.positionScale = positionScale;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean shouldWriteEndNodes() {
|
||||
return writeEndNodes;
|
||||
}
|
||||
|
||||
public BVHSettings setWriteEndNodes(boolean writeEndNodes) {
|
||||
this.writeEndNodes = writeEndNodes;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package dev.slimevr.posestreamer
|
||||
|
||||
class BVHSettings {
|
||||
var offsetScale: Float = 100f
|
||||
private set
|
||||
var positionScale: Float = 100f
|
||||
private set
|
||||
private var writeEndNodes = false
|
||||
|
||||
constructor()
|
||||
|
||||
constructor(source: BVHSettings) {
|
||||
this.offsetScale = source.offsetScale
|
||||
this.positionScale = source.positionScale
|
||||
this.writeEndNodes = source.writeEndNodes
|
||||
}
|
||||
|
||||
fun setOffsetScale(offsetScale: Float): BVHSettings {
|
||||
this.offsetScale = offsetScale
|
||||
return this
|
||||
}
|
||||
|
||||
fun setPositionScale(positionScale: Float): BVHSettings {
|
||||
this.positionScale = positionScale
|
||||
return this
|
||||
}
|
||||
|
||||
fun shouldWriteEndNodes(): Boolean = writeEndNodes
|
||||
|
||||
fun setWriteEndNodes(writeEndNodes: Boolean): BVHSettings {
|
||||
this.writeEndNodes = writeEndNodes
|
||||
return this
|
||||
}
|
||||
|
||||
companion object {
|
||||
val DEFAULT: BVHSettings = BVHSettings()
|
||||
val BLENDER: BVHSettings = BVHSettings(DEFAULT)
|
||||
.setOffsetScale(1f)
|
||||
.setPositionScale(1f)
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package dev.slimevr.posestreamer;
|
||||
|
||||
import dev.slimevr.tracking.processor.skeleton.HumanSkeleton;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
|
||||
public abstract class PoseDataStream implements AutoCloseable {
|
||||
|
||||
protected final OutputStream outputStream;
|
||||
protected boolean closed = false;
|
||||
|
||||
protected PoseDataStream(OutputStream outputStream) {
|
||||
this.outputStream = outputStream;
|
||||
}
|
||||
|
||||
protected PoseDataStream(File file) throws FileNotFoundException {
|
||||
this(new FileOutputStream(file));
|
||||
}
|
||||
|
||||
protected PoseDataStream(String file) throws FileNotFoundException {
|
||||
this(new FileOutputStream(file));
|
||||
}
|
||||
|
||||
public void writeHeader(HumanSkeleton skeleton, PoseStreamer streamer) throws IOException {
|
||||
}
|
||||
|
||||
abstract void writeFrame(HumanSkeleton skeleton) throws IOException;
|
||||
|
||||
public void writeFooter(HumanSkeleton skeleton) throws IOException {
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
outputStream.close();
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package dev.slimevr.posestreamer
|
||||
|
||||
import dev.slimevr.tracking.processor.skeleton.HumanSkeleton
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.OutputStream
|
||||
import java.lang.AutoCloseable
|
||||
|
||||
abstract class PoseDataStream protected constructor(protected val outputStream: OutputStream) : AutoCloseable {
|
||||
var isClosed: Boolean = false
|
||||
protected set
|
||||
|
||||
protected constructor(file: File) : this(FileOutputStream(file))
|
||||
protected constructor(file: String) : this(FileOutputStream(file))
|
||||
|
||||
@Throws(IOException::class)
|
||||
open fun writeHeader(skeleton: HumanSkeleton, streamer: PoseStreamer) {
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
abstract fun writeFrame(skeleton: HumanSkeleton)
|
||||
|
||||
@Throws(IOException::class)
|
||||
open fun writeFooter(skeleton: HumanSkeleton) {
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun close() {
|
||||
outputStream.close()
|
||||
this.isClosed = true
|
||||
}
|
||||
}
|
||||
@@ -6,22 +6,29 @@ import dev.slimevr.poseframeformat.player.TrackerFramesPlayer
|
||||
import dev.slimevr.tracking.processor.HumanPoseManager
|
||||
import java.io.File
|
||||
|
||||
class PoseFrameStreamer(poseFrames: PoseFrames) : PoseStreamer() {
|
||||
val trackerFramesPlayer: TrackerFramesPlayer = TrackerFramesPlayer(poseFrames)
|
||||
val humanPoseManager: HumanPoseManager = HumanPoseManager(trackerFramesPlayer.trackers.toList())
|
||||
class PoseFrameStreamer : PoseStreamer {
|
||||
|
||||
constructor(path: String) : this(File(path))
|
||||
constructor(file: File) : this(readFromFile(file))
|
||||
val player: TrackerFramesPlayer
|
||||
val hpm: HumanPoseManager
|
||||
|
||||
init {
|
||||
skeleton = humanPoseManager.skeleton
|
||||
private constructor(
|
||||
player: TrackerFramesPlayer,
|
||||
hpm: HumanPoseManager,
|
||||
) : super(hpm.skeleton) {
|
||||
this.player = player
|
||||
this.hpm = hpm
|
||||
}
|
||||
|
||||
constructor(player: TrackerFramesPlayer) : this(player, HumanPoseManager(player.trackers.toList()))
|
||||
constructor(poseFrames: PoseFrames) : this(TrackerFramesPlayer(poseFrames))
|
||||
constructor(file: File) : this(readFromFile(file))
|
||||
constructor(path: String) : this(File(path))
|
||||
|
||||
@Synchronized
|
||||
fun streamAllFrames() {
|
||||
for (i in 0 until trackerFramesPlayer.maxFrameCount) {
|
||||
trackerFramesPlayer.setCursors(i)
|
||||
humanPoseManager.update()
|
||||
for (i in 0 until player.maxFrameCount) {
|
||||
player.setCursors(i)
|
||||
hpm.update()
|
||||
captureFrame()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
package dev.slimevr.posestreamer;
|
||||
|
||||
import dev.slimevr.tracking.processor.skeleton.HumanSkeleton;
|
||||
import io.eiren.util.logging.LogManager;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
public class PoseStreamer {
|
||||
|
||||
protected long frameRecordingInterval = 60L;
|
||||
|
||||
protected HumanSkeleton skeleton;
|
||||
protected PoseDataStream poseFileStream;
|
||||
|
||||
protected PoseStreamer() {
|
||||
}
|
||||
|
||||
public PoseStreamer(HumanSkeleton skeleton) {
|
||||
this.skeleton = skeleton;
|
||||
}
|
||||
|
||||
public synchronized void captureFrame() {
|
||||
// Make sure the stream is open before trying to write
|
||||
if (poseFileStream.isClosed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
poseFileStream.writeFrame(skeleton);
|
||||
} catch (Exception e) {
|
||||
// Handle any exceptions without crashing the program
|
||||
LogManager.severe("[PoseStreamer] Exception while saving frame", e);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized long getFrameInterval() {
|
||||
return frameRecordingInterval;
|
||||
}
|
||||
|
||||
public synchronized void setFrameInterval(long intervalMs) {
|
||||
if (intervalMs < 1) {
|
||||
throw new IllegalArgumentException("intervalMs must at least have a value of 1");
|
||||
}
|
||||
|
||||
this.frameRecordingInterval = intervalMs;
|
||||
}
|
||||
|
||||
public synchronized HumanSkeleton getSkeleton() {
|
||||
return skeleton;
|
||||
}
|
||||
|
||||
public synchronized void setOutput(PoseDataStream poseFileStream, long intervalMs)
|
||||
throws IOException {
|
||||
setFrameInterval(intervalMs);
|
||||
setOutput(poseFileStream);
|
||||
}
|
||||
|
||||
public synchronized PoseDataStream getOutput() {
|
||||
return poseFileStream;
|
||||
}
|
||||
|
||||
public synchronized void setOutput(PoseDataStream poseFileStream) throws IOException {
|
||||
poseFileStream.writeHeader(skeleton, this);
|
||||
this.poseFileStream = poseFileStream;
|
||||
}
|
||||
|
||||
public synchronized void closeOutput() throws IOException {
|
||||
PoseDataStream poseFileStream = this.poseFileStream;
|
||||
|
||||
if (poseFileStream != null) {
|
||||
closeOutput(poseFileStream);
|
||||
this.poseFileStream = null;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void closeOutput(PoseDataStream poseFileStream) throws IOException {
|
||||
if (poseFileStream != null) {
|
||||
poseFileStream.writeFooter(skeleton);
|
||||
poseFileStream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package dev.slimevr.posestreamer
|
||||
|
||||
import dev.slimevr.tracking.processor.skeleton.HumanSkeleton
|
||||
import io.eiren.util.logging.LogManager
|
||||
import java.io.IOException
|
||||
|
||||
open class PoseStreamer(skeleton: HumanSkeleton) {
|
||||
// 60 FPS
|
||||
private var intervalInternal: Float = 1f / 60f
|
||||
private var stream: PoseDataStream? = null
|
||||
|
||||
var skeleton: HumanSkeleton = skeleton
|
||||
protected set
|
||||
|
||||
@Synchronized
|
||||
fun captureFrame() {
|
||||
// Make sure the stream is open before trying to write
|
||||
val stream = stream
|
||||
if (stream == null || stream.isClosed) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
stream.writeFrame(skeleton)
|
||||
} catch (e: Exception) {
|
||||
// Handle any exceptions without crashing the program
|
||||
LogManager.severe("[PoseStreamer] Exception while saving frame", e)
|
||||
}
|
||||
}
|
||||
|
||||
open var frameInterval: Float
|
||||
get() = intervalInternal
|
||||
set(interval) {
|
||||
require(interval > 0f) { "interval must be a value greater than 0" }
|
||||
this.intervalInternal = interval
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
@Throws(IOException::class)
|
||||
fun setOutput(poseFileStream: PoseDataStream, interval: Float) {
|
||||
this.frameInterval = interval
|
||||
this.output = poseFileStream
|
||||
}
|
||||
|
||||
@set:Throws(IOException::class)
|
||||
@set:Synchronized
|
||||
open var output: PoseDataStream?
|
||||
get() = stream
|
||||
set(stream) {
|
||||
requireNotNull(stream) { "stream must not be null" }
|
||||
stream.writeHeader(skeleton, this)
|
||||
this.stream = stream
|
||||
}
|
||||
|
||||
val hasOutput
|
||||
get() = output?.isClosed == false
|
||||
|
||||
@Synchronized
|
||||
@Throws(IOException::class)
|
||||
fun closeOutput() {
|
||||
val stream = this.stream
|
||||
if (stream != null) {
|
||||
closeOutput(stream)
|
||||
this.stream = null
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
@Throws(IOException::class)
|
||||
fun closeOutput(stream: PoseDataStream) {
|
||||
stream.writeFooter(skeleton)
|
||||
stream.close()
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package dev.slimevr.posestreamer;
|
||||
|
||||
import dev.slimevr.VRServer;
|
||||
import dev.slimevr.util.ann.VRServerThread;
|
||||
import dev.slimevr.tracking.processor.skeleton.HumanSkeleton;
|
||||
|
||||
|
||||
public class ServerPoseStreamer extends TickPoseStreamer {
|
||||
|
||||
protected final VRServer server;
|
||||
|
||||
public ServerPoseStreamer(VRServer server) {
|
||||
super(null); // Skeleton is registered later
|
||||
this.server = server;
|
||||
|
||||
// Register callbacks/events
|
||||
server.addSkeletonUpdatedCallback(this::onSkeletonUpdated);
|
||||
server.addOnTick(this::onTick);
|
||||
}
|
||||
|
||||
@VRServerThread
|
||||
public void onSkeletonUpdated(HumanSkeleton skeleton) {
|
||||
this.skeleton = skeleton;
|
||||
}
|
||||
|
||||
@VRServerThread
|
||||
public void onTick() {
|
||||
super.doTick();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package dev.slimevr.posestreamer
|
||||
|
||||
import dev.slimevr.VRServer
|
||||
import dev.slimevr.tracking.processor.skeleton.HumanSkeleton
|
||||
import dev.slimevr.util.ann.VRServerThread
|
||||
|
||||
class ServerPoseStreamer(val server: VRServer) : TickPoseStreamer(server.humanPoseManager.skeleton) {
|
||||
|
||||
init {
|
||||
// Register callbacks/events
|
||||
server.addSkeletonUpdatedCallback { skeleton: HumanSkeleton? ->
|
||||
this.onSkeletonUpdated(
|
||||
skeleton,
|
||||
)
|
||||
}
|
||||
server.addOnTick { this.tick() }
|
||||
}
|
||||
|
||||
@VRServerThread
|
||||
fun onSkeletonUpdated(skeleton: HumanSkeleton?) {
|
||||
if (skeleton != null) {
|
||||
this.skeleton = skeleton
|
||||
}
|
||||
}
|
||||
|
||||
@VRServerThread
|
||||
fun tick() {
|
||||
super.tick(server.fpsTimer.timePerFrame)
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package dev.slimevr.posestreamer;
|
||||
|
||||
import dev.slimevr.tracking.processor.skeleton.HumanSkeleton;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
public class TickPoseStreamer extends PoseStreamer {
|
||||
|
||||
protected long nextFrameTimeMs = -1L;
|
||||
|
||||
public TickPoseStreamer(HumanSkeleton skeleton) {
|
||||
super(skeleton);
|
||||
}
|
||||
|
||||
public void doTick() {
|
||||
PoseDataStream poseFileStream = this.poseFileStream;
|
||||
if (poseFileStream == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
HumanSkeleton skeleton = this.skeleton;
|
||||
if (skeleton == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
long curTime = System.currentTimeMillis();
|
||||
if (curTime < nextFrameTimeMs) {
|
||||
return;
|
||||
}
|
||||
|
||||
nextFrameTimeMs += frameRecordingInterval;
|
||||
|
||||
// To prevent duplicate frames, make sure the frame time is always in
|
||||
// the future
|
||||
if (nextFrameTimeMs <= curTime) {
|
||||
nextFrameTimeMs = curTime + frameRecordingInterval;
|
||||
}
|
||||
|
||||
captureFrame();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setOutput(PoseDataStream poseFileStream) throws IOException {
|
||||
super.setOutput(poseFileStream);
|
||||
nextFrameTimeMs = -1L; // Reset the frame timing
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package dev.slimevr.posestreamer
|
||||
|
||||
import dev.slimevr.tracking.processor.skeleton.HumanSkeleton
|
||||
import dev.slimevr.util.TickReducer
|
||||
import java.io.IOException
|
||||
|
||||
open class TickPoseStreamer(skeleton: HumanSkeleton) : PoseStreamer(skeleton) {
|
||||
private val ticker = TickReducer({ captureFrame() }, frameInterval)
|
||||
|
||||
@set:Throws(IOException::class)
|
||||
@set:Synchronized
|
||||
override var output: PoseDataStream?
|
||||
get() = super.output
|
||||
set(value) {
|
||||
super.output = value
|
||||
ticker.reset()
|
||||
}
|
||||
|
||||
@set:Synchronized
|
||||
override var frameInterval: Float
|
||||
get() = super.frameInterval
|
||||
set(value) {
|
||||
super.frameInterval = value
|
||||
ticker.interval = value
|
||||
}
|
||||
|
||||
fun tick(tickDelta: Float) {
|
||||
// Only tick if there is an output
|
||||
if (hasOutput) {
|
||||
ticker.tick(tickDelta)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import kotlinx.coroutines.*
|
||||
import solarxr_protocol.MessageBundle
|
||||
import solarxr_protocol.datatypes.TransactionId
|
||||
import solarxr_protocol.rpc.*
|
||||
import kotlin.io.path.Path
|
||||
|
||||
class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeader>() {
|
||||
private var currTransactionId: Long = 0
|
||||
@@ -319,9 +320,13 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
|
||||
val req = messageHeader.message(RecordBVHRequest()) as? RecordBVHRequest ?: return
|
||||
|
||||
if (req.stop()) {
|
||||
if (api.server.bvhRecorder.isRecording) api.server.bvhRecorder.endRecording()
|
||||
if (api.server.bvhRecorder.isRecording) {
|
||||
api.server.bvhRecorder.endRecording()
|
||||
}
|
||||
} else {
|
||||
if (!api.server.bvhRecorder.isRecording) api.server.bvhRecorder.startRecording()
|
||||
if (!api.server.bvhRecorder.isRecording) {
|
||||
api.server.bvhRecorder.startRecording(Path(req.path()))
|
||||
}
|
||||
}
|
||||
|
||||
val fbb = FlatBufferBuilder(40)
|
||||
@@ -346,9 +351,37 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
|
||||
fun onResetRequest(conn: GenericConnection, messageHeader: RpcMessageHeader) {
|
||||
val req = messageHeader.message(ResetRequest()) as? ResetRequest ?: return
|
||||
|
||||
if (req.resetType() == ResetType.Yaw) api.server.resetTrackersYaw(RESET_SOURCE_NAME)
|
||||
if (req.resetType() == ResetType.Full) api.server.resetTrackersFull(RESET_SOURCE_NAME)
|
||||
if (req.resetType() == ResetType.Mounting) api.server.resetTrackersMounting(RESET_SOURCE_NAME)
|
||||
// Get the list of bodyparts we want to reset
|
||||
// If empty, check in HumanSkeleton will reset all
|
||||
val bodyParts = mutableListOf<Int>()
|
||||
if (req.bodyPartsLength() > 0) {
|
||||
val buffer = req.bodyPartsAsByteBuffer()
|
||||
while (buffer.hasRemaining()) {
|
||||
bodyParts.add(buffer.get().toInt())
|
||||
}
|
||||
}
|
||||
|
||||
if (req.resetType() == ResetType.Yaw) {
|
||||
if (bodyParts.isEmpty()) {
|
||||
api.server.resetTrackersYaw(RESET_SOURCE_NAME)
|
||||
} else {
|
||||
api.server.resetTrackersYaw(RESET_SOURCE_NAME, bodyParts.toList())
|
||||
}
|
||||
}
|
||||
if (req.resetType() == ResetType.Full) {
|
||||
if (bodyParts.isEmpty()) {
|
||||
api.server.resetTrackersFull(RESET_SOURCE_NAME)
|
||||
} else {
|
||||
api.server.resetTrackersFull(RESET_SOURCE_NAME, bodyParts.toList())
|
||||
}
|
||||
}
|
||||
if (req.resetType() == ResetType.Mounting) {
|
||||
if (bodyParts.isEmpty()) {
|
||||
api.server.resetTrackersMounting(RESET_SOURCE_NAME)
|
||||
} else {
|
||||
api.server.resetTrackersMounting(RESET_SOURCE_NAME, bodyParts.toList())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onClearMountingResetRequest(
|
||||
|
||||
@@ -346,7 +346,6 @@ public class RPCSettingsBuilder {
|
||||
return ResetsSettings
|
||||
.createResetsSettings(
|
||||
fbb,
|
||||
resetsConfig.getResetMountingFeet(),
|
||||
resetsConfig.getMode().getId(),
|
||||
resetsConfig.getYawResetSmoothTime(),
|
||||
resetsConfig.getSaveMountingReset(),
|
||||
|
||||
@@ -342,7 +342,6 @@ class RPCSettingsHandler(var rpcHandler: RPCHandler, var api: ProtocolAPI) {
|
||||
if (mode != null) {
|
||||
resetsConfig.mode = mode
|
||||
}
|
||||
resetsConfig.resetMountingFeet = req.resetsSettings().resetMountingFeet()
|
||||
resetsConfig.saveMountingReset = req.resetsSettings().saveMountingReset()
|
||||
resetsConfig.yawResetSmoothTime = req.resetsSettings().yawResetSmoothTime()
|
||||
resetsConfig.resetHmdPitch = req.resetsSettings().resetHmdPitch()
|
||||
|
||||
@@ -2,13 +2,14 @@ package dev.slimevr.status
|
||||
|
||||
import solarxr_protocol.rpc.StatusDataUnion
|
||||
import solarxr_protocol.rpc.StatusMessageT
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class StatusSystem {
|
||||
private val listeners: MutableList<StatusListener> = CopyOnWriteArrayList()
|
||||
private val statuses: MutableMap<Int, StatusDataUnion> = HashMap()
|
||||
private val prioritizedStatuses: MutableSet<Int> = HashSet()
|
||||
private val statuses: MutableMap<Int, StatusDataUnion> = ConcurrentHashMap()
|
||||
private val prioritizedStatuses: MutableSet<Int> = ConcurrentHashMap.newKeySet()
|
||||
private val idCounter = AtomicInteger(1)
|
||||
|
||||
fun addListener(listener: StatusListener) {
|
||||
|
||||
@@ -10,10 +10,7 @@ import dev.slimevr.tracking.processor.config.SkeletonConfigOffsets
|
||||
import dev.slimevr.tracking.processor.config.SkeletonConfigToggles
|
||||
import dev.slimevr.tracking.processor.config.SkeletonConfigValues
|
||||
import dev.slimevr.tracking.processor.skeleton.HumanSkeleton
|
||||
import dev.slimevr.tracking.trackers.Tracker
|
||||
import dev.slimevr.tracking.trackers.TrackerPosition
|
||||
import dev.slimevr.tracking.trackers.TrackerRole
|
||||
import dev.slimevr.tracking.trackers.TrackerStatus
|
||||
import dev.slimevr.tracking.trackers.*
|
||||
import dev.slimevr.trackingpause.TrackingPauseHandler
|
||||
import dev.slimevr.util.ann.VRServerThread
|
||||
import io.eiren.util.ann.ThreadSafe
|
||||
@@ -482,8 +479,9 @@ class HumanPoseManager(val server: VRServer?) {
|
||||
skeletonConfigManager.computeNodeOffset(node)
|
||||
}
|
||||
|
||||
fun resetTrackersFull(resetSourceName: String?) {
|
||||
skeleton.resetTrackersFull(resetSourceName)
|
||||
@JvmOverloads
|
||||
fun resetTrackersFull(resetSourceName: String?, bodyParts: List<Int> = ArrayList()) {
|
||||
skeleton.resetTrackersFull(resetSourceName, bodyParts)
|
||||
if (server != null) {
|
||||
if (skeleton.headTracker == null && skeleton.neckTracker == null) {
|
||||
server.vrcOSCHandler.yawAlign(IDENTITY)
|
||||
@@ -498,8 +496,9 @@ class HumanPoseManager(val server: VRServer?) {
|
||||
}
|
||||
}
|
||||
|
||||
fun resetTrackersYaw(resetSourceName: String?) {
|
||||
skeleton.resetTrackersYaw(resetSourceName)
|
||||
@JvmOverloads
|
||||
fun resetTrackersYaw(resetSourceName: String?, bodyParts: List<Int> = TrackerUtils.allBodyPartsButFingers) {
|
||||
skeleton.resetTrackersYaw(resetSourceName, bodyParts)
|
||||
if (server != null) {
|
||||
if (skeleton.headTracker == null && skeleton.neckTracker == null) {
|
||||
server.vrcOSCHandler.yawAlign(IDENTITY)
|
||||
@@ -571,8 +570,9 @@ class HumanPoseManager(val server: VRServer?) {
|
||||
}
|
||||
}
|
||||
|
||||
fun resetTrackersMounting(resetSourceName: String?) {
|
||||
skeleton.resetTrackersMounting(resetSourceName)
|
||||
@JvmOverloads
|
||||
fun resetTrackersMounting(resetSourceName: String?, bodyParts: List<Int> = TrackerUtils.allBodyPartsButFeetAndFingers) {
|
||||
skeleton.resetTrackersMounting(resetSourceName, bodyParts)
|
||||
}
|
||||
|
||||
fun clearTrackersMounting(resetSourceName: String?) {
|
||||
|
||||
@@ -14,6 +14,7 @@ import dev.slimevr.tracking.processor.stayaligned.trackers.TrackerSkeleton
|
||||
import dev.slimevr.tracking.trackers.Tracker
|
||||
import dev.slimevr.tracking.trackers.TrackerPosition
|
||||
import dev.slimevr.tracking.trackers.TrackerRole
|
||||
import dev.slimevr.tracking.trackers.TrackerUtils
|
||||
import dev.slimevr.tracking.trackers.TrackerUtils.getFirstAvailableTracker
|
||||
import dev.slimevr.tracking.trackers.TrackerUtils.getTrackerForSkeleton
|
||||
import dev.slimevr.tracking.trackers.udp.TrackerDataType
|
||||
@@ -29,6 +30,7 @@ import io.github.axisangles.ktmath.Vector3
|
||||
import io.github.axisangles.ktmath.Vector3.Companion.NEG_Y
|
||||
import io.github.axisangles.ktmath.Vector3.Companion.NULL
|
||||
import io.github.axisangles.ktmath.Vector3.Companion.POS_Y
|
||||
import solarxr_protocol.datatypes.BodyPart
|
||||
import java.lang.IllegalArgumentException
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
@@ -1515,17 +1517,21 @@ class HumanSkeleton(
|
||||
rightLittleDistalTracker,
|
||||
)
|
||||
|
||||
fun resetTrackersFull(resetSourceName: String?) {
|
||||
@JvmOverloads
|
||||
fun resetTrackersFull(resetSourceName: String?, bodyParts: List<Int> = ArrayList()) {
|
||||
var referenceRotation = IDENTITY
|
||||
headTracker?.let {
|
||||
// Always reset the head (ifs in resetsHandler)
|
||||
it.resetsHandler.resetFull(referenceRotation)
|
||||
referenceRotation = it.getRotation()
|
||||
if (bodyParts.isEmpty() || bodyParts.contains(BodyPart.HEAD)) {
|
||||
headTracker?.let {
|
||||
// Always reset the head (ifs in resetsHandler)
|
||||
it.resetsHandler.resetFull(referenceRotation)
|
||||
referenceRotation = it.getRotation()
|
||||
}
|
||||
}
|
||||
|
||||
// Resets all axes of the trackers with the HMD as reference.
|
||||
for (tracker in trackersToReset) {
|
||||
// Only reset if tracker needsReset
|
||||
if (tracker != null && (tracker.needsReset || tracker.isHmd)) {
|
||||
if (tracker != null && (tracker.needsReset || tracker.isHmd) && (bodyParts.isEmpty() || bodyParts.contains(tracker.trackerPosition?.bodyPart))) {
|
||||
tracker.resetsHandler.resetFull(referenceRotation)
|
||||
}
|
||||
}
|
||||
@@ -1541,19 +1547,22 @@ class HumanSkeleton(
|
||||
}
|
||||
|
||||
@VRServerThread
|
||||
fun resetTrackersYaw(resetSourceName: String?) {
|
||||
@JvmOverloads
|
||||
fun resetTrackersYaw(resetSourceName: String?, bodyParts: List<Int> = TrackerUtils.allBodyPartsButFingers) {
|
||||
// Resets the yaw of the trackers with the head as reference.
|
||||
var referenceRotation = IDENTITY
|
||||
headTracker?.let {
|
||||
// Only reset if head needsReset and isn't computed
|
||||
if (it.needsReset && !it.isComputed) {
|
||||
it.resetsHandler.resetYaw(referenceRotation)
|
||||
if (bodyParts.isEmpty() || bodyParts.contains(BodyPart.HEAD)) {
|
||||
headTracker?.let {
|
||||
// Only reset if head needsReset and isn't computed
|
||||
if (it.needsReset && !it.isComputed) {
|
||||
it.resetsHandler.resetYaw(referenceRotation)
|
||||
}
|
||||
referenceRotation = it.getRotation()
|
||||
}
|
||||
referenceRotation = it.getRotation()
|
||||
}
|
||||
for (tracker in trackersToReset) {
|
||||
// Only reset if tracker needsReset
|
||||
if (tracker != null && tracker.needsReset) {
|
||||
if (tracker != null && tracker.needsReset && (bodyParts.isEmpty() || bodyParts.contains(tracker.trackerPosition?.bodyPart))) {
|
||||
tracker.resetsHandler.resetYaw(referenceRotation)
|
||||
}
|
||||
}
|
||||
@@ -1562,7 +1571,8 @@ class HumanSkeleton(
|
||||
}
|
||||
|
||||
@VRServerThread
|
||||
fun resetTrackersMounting(resetSourceName: String?) {
|
||||
@JvmOverloads
|
||||
fun resetTrackersMounting(resetSourceName: String?, bodyParts: List<Int> = ArrayList()) {
|
||||
val trackersToReset = trackersToReset
|
||||
|
||||
// TODO: PLEASE rewrite this handling at some point in the future... This is so
|
||||
@@ -1577,16 +1587,18 @@ class HumanSkeleton(
|
||||
|
||||
// Resets the mounting orientation of the trackers with the HMD as reference.
|
||||
var referenceRotation = IDENTITY
|
||||
headTracker?.let {
|
||||
// Only reset if head needsMounting or is computed but not HMD
|
||||
if (it.needsMounting || (it.isComputed && !it.isHmd)) {
|
||||
it.resetsHandler.resetMounting(referenceRotation)
|
||||
if (bodyParts.isEmpty() || bodyParts.contains(BodyPart.HEAD)) {
|
||||
headTracker?.let {
|
||||
// Only reset if head needsMounting or is computed but not HMD
|
||||
if (it.needsMounting || (it.isComputed && !it.isHmd)) {
|
||||
it.resetsHandler.resetMounting(referenceRotation)
|
||||
}
|
||||
referenceRotation = it.getRotation()
|
||||
}
|
||||
referenceRotation = it.getRotation()
|
||||
}
|
||||
for (tracker in trackersToReset) {
|
||||
// Only reset if tracker needsMounting
|
||||
if (tracker != null && tracker.needsMounting) {
|
||||
if (tracker != null && tracker.needsMounting && (bodyParts.isEmpty() || bodyParts.contains(tracker.trackerPosition?.bodyPart))) {
|
||||
tracker.resetsHandler.resetMounting(referenceRotation)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import dev.slimevr.tracking.trackers.TrackerPosition.Companion.getByDesignation
|
||||
import dev.slimevr.tracking.trackers.udp.IMUType
|
||||
import dev.slimevr.tracking.trackers.udp.MagnetometerStatus
|
||||
import dev.slimevr.tracking.trackers.udp.TrackerDataType
|
||||
import dev.slimevr.util.InterpolationHandler
|
||||
import io.eiren.util.BufferedTimer
|
||||
import io.github.axisangles.ktmath.Quaternion
|
||||
import io.github.axisangles.ktmath.Vector3
|
||||
@@ -158,6 +159,7 @@ class Tracker @JvmOverloads constructor(
|
||||
val trackerNum: Int = trackerNum ?: id
|
||||
|
||||
val stayAligned = StayAlignedTrackerState(this)
|
||||
val yawResetSmoothing = InterpolationHandler()
|
||||
|
||||
init {
|
||||
// IMPORTANT: Look here for the required states of inputs
|
||||
@@ -310,7 +312,7 @@ class Tracker @JvmOverloads constructor(
|
||||
/**
|
||||
* Synchronized with the VRServer's 1000hz while loop
|
||||
*/
|
||||
fun tick() {
|
||||
fun tick(deltaTime: Float) {
|
||||
if (usesTimeout) {
|
||||
if (System.currentTimeMillis() - timeAtLastUpdate > DISCONNECT_MS) {
|
||||
status = TrackerStatus.DISCONNECTED
|
||||
@@ -318,8 +320,9 @@ class Tracker @JvmOverloads constructor(
|
||||
status = TrackerStatus.TIMED_OUT
|
||||
}
|
||||
}
|
||||
|
||||
filteringHandler.update()
|
||||
resetsHandler.update()
|
||||
yawResetSmoothing.tick(deltaTime)
|
||||
stayAligned.update()
|
||||
}
|
||||
|
||||
@@ -410,11 +413,19 @@ class Tracker @JvmOverloads constructor(
|
||||
/**
|
||||
* Get the rotation of the tracker after the resetsHandler's corrections and filtering if applicable
|
||||
*/
|
||||
fun getRotation(): Quaternion = if (trackRotDirection) {
|
||||
filteringHandler.getFilteredRotation()
|
||||
} else {
|
||||
// Get non-filtered rotation
|
||||
getAdjustedRotation()
|
||||
fun getRotation(): Quaternion {
|
||||
var rot = if (trackRotDirection) {
|
||||
filteringHandler.getFilteredRotation()
|
||||
} else {
|
||||
// Get non-filtered rotation
|
||||
getAdjustedRotation()
|
||||
}
|
||||
|
||||
if (yawResetSmoothing.remainingTime > 0f) {
|
||||
rot = yawResetSmoothing.curRotation * rot
|
||||
}
|
||||
|
||||
return rot
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,8 +1,105 @@
|
||||
@file:Suppress("ktlint:standard:no-wildcard-imports")
|
||||
|
||||
package dev.slimevr.tracking.trackers
|
||||
|
||||
import dev.slimevr.tracking.trackers.TrackerPosition.*
|
||||
import io.github.axisangles.ktmath.Quaternion
|
||||
import solarxr_protocol.datatypes.BodyPart
|
||||
|
||||
fun TrackerPosition?.isThigh(): Boolean {
|
||||
this?.let {
|
||||
return it == LEFT_UPPER_LEG ||
|
||||
it == RIGHT_UPPER_LEG
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun TrackerPosition?.isLeftArm(): Boolean {
|
||||
this?.let {
|
||||
return it == LEFT_SHOULDER ||
|
||||
it == LEFT_UPPER_ARM ||
|
||||
it == LEFT_LOWER_ARM ||
|
||||
it == LEFT_HAND
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun TrackerPosition?.isRightArm(): Boolean {
|
||||
this?.let {
|
||||
return it == RIGHT_SHOULDER ||
|
||||
it == RIGHT_UPPER_ARM ||
|
||||
it == RIGHT_LOWER_ARM ||
|
||||
it == RIGHT_HAND
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun TrackerPosition?.isLeftLowerArm(): Boolean {
|
||||
this?.let {
|
||||
return it == LEFT_LOWER_ARM ||
|
||||
it == LEFT_HAND
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun TrackerPosition?.isRightLowerArm(): Boolean {
|
||||
this?.let {
|
||||
return it == RIGHT_LOWER_ARM ||
|
||||
it == RIGHT_HAND
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun TrackerPosition?.isFoot(): Boolean {
|
||||
this?.let {
|
||||
return it == LEFT_FOOT ||
|
||||
it == RIGHT_FOOT
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun TrackerPosition?.isLeftFinger(): Boolean {
|
||||
this?.let {
|
||||
return it == LEFT_THUMB_METACARPAL ||
|
||||
it == LEFT_THUMB_PROXIMAL ||
|
||||
it == LEFT_THUMB_DISTAL ||
|
||||
it == LEFT_INDEX_PROXIMAL ||
|
||||
it == LEFT_INDEX_INTERMEDIATE ||
|
||||
it == LEFT_INDEX_DISTAL ||
|
||||
it == LEFT_MIDDLE_PROXIMAL ||
|
||||
it == LEFT_MIDDLE_INTERMEDIATE ||
|
||||
it == LEFT_MIDDLE_DISTAL ||
|
||||
it == LEFT_RING_PROXIMAL ||
|
||||
it == LEFT_RING_INTERMEDIATE ||
|
||||
it == LEFT_RING_DISTAL ||
|
||||
it == LEFT_LITTLE_PROXIMAL ||
|
||||
it == LEFT_LITTLE_INTERMEDIATE ||
|
||||
it == LEFT_LITTLE_DISTAL
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun TrackerPosition?.isRightFinger(): Boolean {
|
||||
this?.let {
|
||||
return it == RIGHT_THUMB_METACARPAL ||
|
||||
it == RIGHT_THUMB_PROXIMAL ||
|
||||
it == RIGHT_THUMB_DISTAL ||
|
||||
it == RIGHT_INDEX_PROXIMAL ||
|
||||
it == RIGHT_INDEX_INTERMEDIATE ||
|
||||
it == RIGHT_INDEX_DISTAL ||
|
||||
it == RIGHT_MIDDLE_PROXIMAL ||
|
||||
it == RIGHT_MIDDLE_INTERMEDIATE ||
|
||||
it == RIGHT_MIDDLE_DISTAL ||
|
||||
it == RIGHT_RING_PROXIMAL ||
|
||||
it == RIGHT_RING_INTERMEDIATE ||
|
||||
it == RIGHT_RING_DISTAL ||
|
||||
it == RIGHT_LITTLE_PROXIMAL ||
|
||||
it == RIGHT_LITTLE_INTERMEDIATE ||
|
||||
it == RIGHT_LITTLE_DISTAL
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a position on the body that a tracker could be placed. Any bone is
|
||||
* a valid position.
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
package dev.slimevr.tracking.trackers
|
||||
|
||||
import com.jme3.math.FastMath
|
||||
import com.jme3.system.NanoTimer
|
||||
import dev.slimevr.VRServer
|
||||
import dev.slimevr.config.ArmsResetModes
|
||||
import dev.slimevr.config.DriftCompensationConfig
|
||||
import dev.slimevr.config.ResetsConfig
|
||||
import dev.slimevr.filtering.CircularArrayList
|
||||
import dev.slimevr.tracking.trackers.udp.TrackerDataType
|
||||
import io.eiren.math.FloatMath.animateEase
|
||||
import io.github.axisangles.ktmath.EulerAngles
|
||||
import io.github.axisangles.ktmath.EulerOrder
|
||||
import io.github.axisangles.ktmath.Quaternion
|
||||
@@ -40,10 +38,8 @@ class TrackerResetsHandler(val tracker: Tracker) {
|
||||
private var compensateDrift = false
|
||||
private var driftPrediction = false
|
||||
private var driftCompensationEnabled = false
|
||||
private var resetMountingFeet = false
|
||||
private var armsResetMode = ArmsResetModes.BACK
|
||||
private var yawResetSmoothTime = 0.0f
|
||||
private lateinit var fpsTimer: NanoTimer
|
||||
var saveMountingReset = false
|
||||
var resetHmdPitch = false
|
||||
var allowDriftCompensation = false
|
||||
@@ -110,11 +106,6 @@ class TrackerResetsHandler(val tracker: Tracker) {
|
||||
*/
|
||||
private var constraintFix = Quaternion.IDENTITY
|
||||
|
||||
// Yaw reset smoothing vars
|
||||
private var yawFixOld = Quaternion.IDENTITY
|
||||
private var yawFixSmoothIncremental = Quaternion.IDENTITY
|
||||
private var yawResetSmoothTimeRemain = 0.0f
|
||||
|
||||
// Zero-reference/identity adjustment quats for IMU debugging
|
||||
private var gyroFixNoMounting = Quaternion.IDENTITY
|
||||
private var attachmentFixNoMounting = Quaternion.IDENTITY
|
||||
@@ -172,12 +163,8 @@ class TrackerResetsHandler(val tracker: Tracker) {
|
||||
* Reads/loads reset settings from the given config
|
||||
*/
|
||||
fun readResetConfig(config: ResetsConfig) {
|
||||
resetMountingFeet = config.resetMountingFeet
|
||||
armsResetMode = config.mode
|
||||
yawResetSmoothTime = config.yawResetSmoothTime
|
||||
if (!::fpsTimer.isInitialized) {
|
||||
fpsTimer = VRServer.instance.fpsTimer
|
||||
}
|
||||
saveMountingReset = config.saveMountingReset
|
||||
resetHmdPitch = config.resetHmdPitch
|
||||
}
|
||||
@@ -192,7 +179,7 @@ class TrackerResetsHandler(val tracker: Tracker) {
|
||||
* Takes a rotation and adjusts it to resets, mounting,
|
||||
* and drift compensation, with the HMD as the reference.
|
||||
*/
|
||||
fun getReferenceAdjustedDriftRotationFrom(rotation: Quaternion): Quaternion = adjustToDrift(adjustToYawResetSmoothing(adjustToReference(rotation)))
|
||||
fun getReferenceAdjustedDriftRotationFrom(rotation: Quaternion): Quaternion = adjustToDrift(adjustToReference(rotation))
|
||||
|
||||
/**
|
||||
* Takes a rotation and adjusts it to resets and mounting,
|
||||
@@ -252,17 +239,6 @@ class TrackerResetsHandler(val tracker: Tracker) {
|
||||
return rotation
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply yaw reset smoothing to quaternion rotated to new yaw
|
||||
* fix and returns smoothed quaternion
|
||||
*/
|
||||
private fun adjustToYawResetSmoothing(rotation: Quaternion): Quaternion {
|
||||
if (yawResetSmoothTimeRemain > 0.0f) {
|
||||
return yawFixSmoothIncremental * rotation
|
||||
}
|
||||
return rotation
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the tracker so that its current rotation is counted as (0, HMD Yaw,
|
||||
* 0). This allows the tracker to be strapped to body at any pitch and roll.
|
||||
@@ -280,9 +256,9 @@ class TrackerResetsHandler(val tracker: Tracker) {
|
||||
}
|
||||
|
||||
// Adjust for T-Pose (down)
|
||||
tposeDownFix = if (((isLeftArmTracker() || isLeftFingerTracker()) && armsResetMode == ArmsResetModes.TPOSE_DOWN)) {
|
||||
tposeDownFix = if (((tracker.trackerPosition.isLeftArm() || tracker.trackerPosition.isLeftFinger()) && armsResetMode == ArmsResetModes.TPOSE_DOWN)) {
|
||||
EulerAngles(EulerOrder.YZX, 0f, 0f, -FastMath.HALF_PI).toQuaternion()
|
||||
} else if (((isRightArmTracker() || isRightFingerTracker()) && armsResetMode == ArmsResetModes.TPOSE_DOWN)) {
|
||||
} else if (((tracker.trackerPosition.isRightArm() || tracker.trackerPosition.isRightFinger()) && armsResetMode == ArmsResetModes.TPOSE_DOWN)) {
|
||||
EulerAngles(EulerOrder.YZX, 0f, 0f, FastMath.HALF_PI).toQuaternion()
|
||||
} else {
|
||||
Quaternion.IDENTITY
|
||||
@@ -337,7 +313,7 @@ class TrackerResetsHandler(val tracker: Tracker) {
|
||||
// Don't adjust yaw if head and computed
|
||||
if (tracker.trackerPosition != TrackerPosition.HEAD || !tracker.isComputed) {
|
||||
yawFix = fixYaw(mountingAdjustedRotation, reference)
|
||||
yawResetSmoothTimeRemain = 0.0f
|
||||
tracker.yawResetSmoothing.reset()
|
||||
}
|
||||
|
||||
calculateDrift(oldRot)
|
||||
@@ -380,9 +356,9 @@ class TrackerResetsHandler(val tracker: Tracker) {
|
||||
val oldRot = adjustToReference(tracker.getRawRotation())
|
||||
lastResetQuaternion = oldRot
|
||||
|
||||
yawFixOld = yawFix
|
||||
val yawFixOld = yawFix
|
||||
yawFix = fixYaw(tracker.getRawRotation() * mountingOrientation, reference)
|
||||
yawResetSmoothTimeRemain = 0.0f
|
||||
tracker.yawResetSmoothing.reset()
|
||||
|
||||
makeIdentityAdjustmentQuatsYaw()
|
||||
|
||||
@@ -390,8 +366,11 @@ class TrackerResetsHandler(val tracker: Tracker) {
|
||||
|
||||
// Start at yaw before reset if smoothing enabled
|
||||
if (yawResetSmoothTime > 0.0f) {
|
||||
yawResetSmoothTimeRemain = yawResetSmoothTime
|
||||
yawFixSmoothIncremental = yawFixOld / yawFix
|
||||
tracker.yawResetSmoothing.interpolate(
|
||||
yawFixOld / yawFix,
|
||||
Quaternion.IDENTITY,
|
||||
yawResetSmoothTime,
|
||||
)
|
||||
}
|
||||
|
||||
// Remove the status if yaw reset was performed after the tracker
|
||||
@@ -420,8 +399,6 @@ class TrackerResetsHandler(val tracker: Tracker) {
|
||||
return
|
||||
} else if (tracker.trackerDataType == TrackerDataType.FLEX_ANGLE) {
|
||||
return
|
||||
} else if (!resetMountingFeet && isFootTracker()) {
|
||||
return
|
||||
}
|
||||
|
||||
constraintFix = Quaternion.IDENTITY
|
||||
@@ -442,25 +419,25 @@ class TrackerResetsHandler(val tracker: Tracker) {
|
||||
var yawAngle = atan2(rotVector.x, rotVector.z)
|
||||
|
||||
// Adjust for T-Pose and fingers
|
||||
if ((isLeftArmTracker() && armsResetMode == ArmsResetModes.TPOSE_DOWN) ||
|
||||
(isRightArmTracker() && armsResetMode == ArmsResetModes.TPOSE_UP) ||
|
||||
isLeftFingerTracker()
|
||||
if ((tracker.trackerPosition.isLeftArm() && armsResetMode == ArmsResetModes.TPOSE_DOWN) ||
|
||||
(tracker.trackerPosition.isRightArm() && armsResetMode == ArmsResetModes.TPOSE_UP) ||
|
||||
tracker.trackerPosition.isLeftFinger()
|
||||
) {
|
||||
// Tracker goes right
|
||||
yawAngle -= FastMath.HALF_PI
|
||||
}
|
||||
if ((isLeftArmTracker() && armsResetMode == ArmsResetModes.TPOSE_UP) ||
|
||||
(isRightArmTracker() && armsResetMode == ArmsResetModes.TPOSE_DOWN) ||
|
||||
isRightFingerTracker()
|
||||
if ((tracker.trackerPosition.isLeftArm() && armsResetMode == ArmsResetModes.TPOSE_UP) ||
|
||||
(tracker.trackerPosition.isRightArm() && armsResetMode == ArmsResetModes.TPOSE_DOWN) ||
|
||||
tracker.trackerPosition.isRightFinger()
|
||||
) {
|
||||
// Tracker goes left
|
||||
yawAngle += FastMath.HALF_PI
|
||||
}
|
||||
|
||||
// Adjust for forward/back arms and thighs
|
||||
val isLowerArmBack = armsResetMode == ArmsResetModes.BACK && (isLeftLowerArmTracker() || isRightLowerArmTracker())
|
||||
val isArmForward = armsResetMode == ArmsResetModes.FORWARD && (isLeftArmTracker() || isRightArmTracker())
|
||||
if (!isThighTracker() && !isArmForward && !isLowerArmBack) {
|
||||
val isLowerArmBack = armsResetMode == ArmsResetModes.BACK && (tracker.trackerPosition.isLeftLowerArm() || tracker.trackerPosition.isRightLowerArm())
|
||||
val isArmForward = armsResetMode == ArmsResetModes.FORWARD && (tracker.trackerPosition.isLeftArm() || tracker.trackerPosition.isRightArm())
|
||||
if (!tracker.trackerPosition.isThigh() && !isArmForward && !isLowerArmBack) {
|
||||
// Tracker goes back
|
||||
yawAngle -= FastMath.PI
|
||||
}
|
||||
@@ -615,28 +592,6 @@ class TrackerResetsHandler(val tracker: Tracker) {
|
||||
return totalMatrix.toQuaternion()
|
||||
}
|
||||
|
||||
/**
|
||||
* Update yaw reset smoothing time
|
||||
*/
|
||||
@Synchronized
|
||||
fun update() {
|
||||
if (yawResetSmoothTimeRemain > 0.0f) {
|
||||
var deltaTime = 0.001f
|
||||
if (::fpsTimer.isInitialized) {
|
||||
deltaTime = fpsTimer.timePerFrame
|
||||
}
|
||||
yawResetSmoothTimeRemain = yawResetSmoothTimeRemain - deltaTime
|
||||
if (yawResetSmoothTimeRemain > 0.0f) {
|
||||
// Remaining time decreases to 0, so the interpolation is reversed
|
||||
yawFixSmoothIncremental = yawFix.inv() * yawFix
|
||||
.interpR(
|
||||
yawFixOld,
|
||||
animateEase(yawResetSmoothTimeRemain / yawResetSmoothTime),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isThighTracker(): Boolean {
|
||||
tracker.trackerPosition?.let {
|
||||
return it == TrackerPosition.LEFT_UPPER_LEG ||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package dev.slimevr.tracking.trackers
|
||||
|
||||
import solarxr_protocol.datatypes.BodyPart
|
||||
|
||||
object TrackerUtils {
|
||||
|
||||
/**
|
||||
@@ -90,4 +92,23 @@ object TrackerUtils {
|
||||
fun getFirstAvailableTracker(
|
||||
vararg trackers: Tracker?,
|
||||
): Tracker? = trackers.firstOrNull { it != null }
|
||||
|
||||
val allBodyPartsButFingers = listOf(
|
||||
BodyPart.HEAD, BodyPart.NECK, BodyPart.UPPER_CHEST,
|
||||
BodyPart.CHEST, BodyPart.WAIST, BodyPart.HIP,
|
||||
BodyPart.LEFT_UPPER_LEG, BodyPart.RIGHT_UPPER_LEG, BodyPart.LEFT_LOWER_LEG,
|
||||
BodyPart.RIGHT_LOWER_LEG, BodyPart.LEFT_LOWER_ARM, BodyPart.RIGHT_LOWER_ARM,
|
||||
BodyPart.LEFT_UPPER_ARM, BodyPart.RIGHT_UPPER_ARM, BodyPart.LEFT_HAND,
|
||||
BodyPart.RIGHT_HAND, BodyPart.LEFT_SHOULDER, BodyPart.RIGHT_SHOULDER,
|
||||
BodyPart.LEFT_FOOT, BodyPart.RIGHT_FOOT,
|
||||
)
|
||||
|
||||
val allBodyPartsButFeetAndFingers = listOf(
|
||||
BodyPart.HEAD, BodyPart.NECK, BodyPart.UPPER_CHEST,
|
||||
BodyPart.CHEST, BodyPart.WAIST, BodyPart.HIP,
|
||||
BodyPart.LEFT_UPPER_LEG, BodyPart.RIGHT_UPPER_LEG, BodyPart.LEFT_LOWER_LEG,
|
||||
BodyPart.RIGHT_LOWER_LEG, BodyPart.LEFT_LOWER_ARM, BodyPart.RIGHT_LOWER_ARM,
|
||||
BodyPart.LEFT_UPPER_ARM, BodyPart.RIGHT_UPPER_ARM, BodyPart.LEFT_HAND,
|
||||
BodyPart.RIGHT_HAND, BodyPart.LEFT_SHOULDER, BodyPart.RIGHT_SHOULDER,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
package dev.slimevr.util
|
||||
|
||||
import io.eiren.math.FloatMath.animateEase
|
||||
import io.github.axisangles.ktmath.Quaternion
|
||||
|
||||
class InterpolationHandler {
|
||||
/**
|
||||
* The starting rotation of this movement.
|
||||
*/
|
||||
var start = Quaternion.IDENTITY
|
||||
private set
|
||||
|
||||
/**
|
||||
* The ending rotation of this movement.
|
||||
*/
|
||||
var end = Quaternion.IDENTITY
|
||||
private set
|
||||
|
||||
/**
|
||||
* The time left for the current movement.
|
||||
*/
|
||||
var remainingTime = 0f
|
||||
private set
|
||||
|
||||
/**
|
||||
* The total interval of interpolation for this movement.
|
||||
*/
|
||||
var totalTime = 0f
|
||||
private set
|
||||
|
||||
/**
|
||||
* The current interpolated value for this tick.
|
||||
*/
|
||||
var curRotation = Quaternion.IDENTITY
|
||||
private set
|
||||
|
||||
/**
|
||||
* Starts an interpolation from [start] to [end] over [interval] seconds.
|
||||
* @param start The starting rotation to interpolate from.
|
||||
* @param end The ending rotation to interpolate to.
|
||||
* @param interval The amount of time that the interpolation will take.
|
||||
* @param shortestDistance Whether the interpolation will take the shortest path or
|
||||
* take the quaternion space path between [start] and [end].
|
||||
*/
|
||||
@Synchronized
|
||||
fun interpolate(
|
||||
start: Quaternion = curRotation,
|
||||
end: Quaternion,
|
||||
interval: Float,
|
||||
shortestDistance: Boolean = true,
|
||||
) {
|
||||
this.totalTime = interval
|
||||
remainingTime = interval
|
||||
|
||||
this.start = if (shortestDistance) {
|
||||
start.twinNearest(end)
|
||||
} else {
|
||||
start
|
||||
}
|
||||
this.end = end
|
||||
|
||||
// The current state is at the start
|
||||
// TODO: Maybe handle a mid-interval interpolation swap?
|
||||
curRotation = this.start
|
||||
}
|
||||
|
||||
/**
|
||||
* The main ticking function, computes [curRotation] for each tick and reduces
|
||||
* [remainingTime] by [delta].
|
||||
* @param delta The time in seconds between the last time [tick] was run and the
|
||||
* current tick.
|
||||
*/
|
||||
@Synchronized
|
||||
fun tick(delta: Float) {
|
||||
if (remainingTime > 0f) {
|
||||
remainingTime -= delta
|
||||
|
||||
// If we still need to interpolate after the delta change
|
||||
if (remainingTime > 0f) {
|
||||
// Remaining time decreases to 0, so the interpolation is reversed
|
||||
curRotation = end.interpR(
|
||||
start,
|
||||
animateEase(remainingTime / totalTime),
|
||||
)
|
||||
} else {
|
||||
remainingTime = 0f
|
||||
curRotation = end
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun reset() {
|
||||
remainingTime = 0f
|
||||
totalTime = 0f
|
||||
|
||||
start = Quaternion.IDENTITY
|
||||
end = Quaternion.IDENTITY
|
||||
|
||||
curRotation = Quaternion.IDENTITY
|
||||
}
|
||||
}
|
||||
59
server/core/src/main/java/dev/slimevr/util/TickReducer.kt
Normal file
59
server/core/src/main/java/dev/slimevr/util/TickReducer.kt
Normal file
@@ -0,0 +1,59 @@
|
||||
package dev.slimevr.util
|
||||
|
||||
class TickReducer(
|
||||
/**
|
||||
* The function to run every tick interval.
|
||||
*/
|
||||
private val onTick: (delta: Float) -> Unit,
|
||||
/**
|
||||
* The tick interval in seconds.
|
||||
*/
|
||||
var interval: Float,
|
||||
/**
|
||||
* The amount of time in seconds that a tick can fire early.
|
||||
*/
|
||||
var resolution: Float = 0f,
|
||||
) {
|
||||
/**
|
||||
* The amount of time in seconds since the last tick.
|
||||
*/
|
||||
private var tickDelta = 0f
|
||||
|
||||
/**
|
||||
* The offset in timing for the next frame to approximate a tick rate with low
|
||||
* resolution ticking.
|
||||
*/
|
||||
private var tickOffset = 0f
|
||||
|
||||
/**
|
||||
* The main ticking function to be run at a fast tick rate. Runs [onTick] at the
|
||||
* specified [interval] and [resolution].
|
||||
* @param delta The time in seconds between the last time [tick] was run and the
|
||||
* current tick.
|
||||
*/
|
||||
@Synchronized
|
||||
fun tick(delta: Float) {
|
||||
// Update tick delta from delta
|
||||
tickDelta += delta
|
||||
|
||||
// If the next tick time is not within the given resolution
|
||||
if (tickDelta + tickOffset + resolution < interval) return
|
||||
|
||||
// Run tick, providing the delta time
|
||||
onTick(tickDelta)
|
||||
|
||||
// Reset tick timing including an offset to compensate for inaccuracy
|
||||
// Define a maximum for the offset to prevent double ticking
|
||||
tickOffset = (tickDelta - interval).coerceAtMost(interval / 2f)
|
||||
tickDelta = 0f
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the tick timing.
|
||||
*/
|
||||
@Synchronized
|
||||
fun reset() {
|
||||
tickDelta = 0f
|
||||
tickOffset = 0f
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,7 @@ dependencies {
|
||||
implementation("com.google.protobuf:protobuf-java:3.21.12")
|
||||
implementation("net.java.dev.jna:jna:5.+")
|
||||
implementation("net.java.dev.jna:jna-platform:5.+")
|
||||
implementation("com.fazecast:jSerialComm:2.11.1-SNAPSHOT")
|
||||
implementation("com.fazecast:jSerialComm:2.11.2")
|
||||
implementation("org.hid4java:hid4java:0.8.0")
|
||||
}
|
||||
|
||||
|
||||
@@ -1,73 +1,344 @@
|
||||
package dev.slimevr.desktop
|
||||
|
||||
import com.sun.jna.Pointer
|
||||
import com.sun.jna.platform.win32.COM.COMException
|
||||
import com.sun.jna.platform.win32.COM.COMUtils
|
||||
import com.sun.jna.platform.win32.COM.Dispatch
|
||||
import com.sun.jna.platform.win32.Guid.CLSID
|
||||
import com.sun.jna.platform.win32.Guid.IID
|
||||
import com.sun.jna.platform.win32.OaIdl.VARIANT_BOOLByReference
|
||||
import com.sun.jna.platform.win32.Ole32
|
||||
import com.sun.jna.platform.win32.OleAuto
|
||||
import com.sun.jna.platform.win32.WTypes
|
||||
import com.sun.jna.platform.win32.WinNT.HRESULT
|
||||
import com.sun.jna.ptr.IntByReference
|
||||
import com.sun.jna.ptr.PointerByReference
|
||||
import dev.slimevr.VRServer
|
||||
import io.eiren.util.OperatingSystem
|
||||
import solarxr_protocol.rpc.StatusData
|
||||
import solarxr_protocol.rpc.StatusDataUnion
|
||||
import solarxr_protocol.rpc.StatusPublicNetworkT
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.util.*
|
||||
import kotlin.concurrent.scheduleAtFixedRate
|
||||
|
||||
enum class NetworkProfile {
|
||||
PRIVATE,
|
||||
PUBLIC,
|
||||
data class NetworkInfo(
|
||||
val name: String?,
|
||||
val description: String?,
|
||||
val category: NetworkCategory?,
|
||||
val connectivity: Set<ConnectivityFlags>?,
|
||||
val connected: Boolean?,
|
||||
)
|
||||
|
||||
/**
|
||||
* @see <a href="https://learn.microsoft.com/en-us/windows/win32/api/netlistmgr/ne-netlistmgr-nlm_network_category">NLM_NETWORK_CATEGORY enumeration (netlistmgr.h)</a>
|
||||
*/
|
||||
enum class NetworkCategory(val value: Int) {
|
||||
PUBLIC(0),
|
||||
PRIVATE(1),
|
||||
DOMAIN_AUTHENTICATED(2),
|
||||
;
|
||||
|
||||
companion object {
|
||||
fun fromInt(value: Int) = values().find { it.value == value }
|
||||
}
|
||||
}
|
||||
|
||||
fun checkNetworkProfile(): NetworkProfile? {
|
||||
/**
|
||||
* @see <a href="https://learn.microsoft.com/en-us/windows/win32/api/netlistmgr/ne-netlistmgr-nlm_connectivity">NLM_CONNECTIVITY enumeration (netlistmgr.h)</a>
|
||||
*/
|
||||
enum class ConnectivityFlags(val value: Int) {
|
||||
DISCONNECTED(0),
|
||||
IPV4_NOTRAFFIC(0x1),
|
||||
IPV6_NOTRAFFIC(0x2),
|
||||
IPV4_SUBNET(0x10),
|
||||
IPV4_LOCALNETWORK(0x20),
|
||||
IPV4_INTERNET(0x40),
|
||||
IPV6_SUBNET(0x100),
|
||||
IPV6_LOCALNETWORK(0x200),
|
||||
IPV6_INTERNET(0x400),
|
||||
;
|
||||
|
||||
companion object {
|
||||
fun fromInt(value: Int): Set<ConnectivityFlags> =
|
||||
if (value == 0) {
|
||||
setOf(DISCONNECTED)
|
||||
} else {
|
||||
values().filter { it != DISCONNECTED && (value and it.value) != 0 }.toSet()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see <a href="https://learn.microsoft.com/en-us/windows/win32/api/netlistmgr/nn-netlistmgr-inetworkconnection">INetworkConnection interface (netlistmgr.h)</a>
|
||||
*/
|
||||
@Suppress("ktlint:standard:backing-property-naming", "ktlint:standard:function-naming")
|
||||
class INetworkConnection(instance: Pointer?) :
|
||||
Dispatch(instance),
|
||||
AutoCloseable {
|
||||
override fun close() {
|
||||
Release()
|
||||
}
|
||||
companion object VTable {
|
||||
// IUnknown +3
|
||||
// IDispatch +4
|
||||
// IEnumNetworkConnections
|
||||
const val GetNetwork = 7
|
||||
const val IsConnectedToInternet = 8
|
||||
const val IsConnected = 9
|
||||
const val GetConnectivity = 10
|
||||
const val GetConnectionId = 11
|
||||
const val GetAdapterId = 12
|
||||
const val GetDomainType = 13
|
||||
}
|
||||
|
||||
@Throws(COMException::class)
|
||||
fun GetNetwork(): INetwork {
|
||||
val pNetwork = PointerByReference()
|
||||
val hr = _invokeNativeInt(VTable.GetNetwork, arrayOf(pointer, pNetwork))
|
||||
COMUtils.checkRC(HRESULT(hr))
|
||||
return INetwork(pNetwork.value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see <a href="https://learn.microsoft.com/en-us/windows/win32/api/netlistmgr/nn-netlistmgr-ienumnetworkconnections">IEnumNetworkConnections interface (netlistmgr.h)</a>
|
||||
*/
|
||||
@Suppress("ktlint:standard:backing-property-naming", "ktlint:standard:function-naming")
|
||||
class IEnumNetworkConnections(instance: Pointer?) :
|
||||
Dispatch(instance),
|
||||
AutoCloseable {
|
||||
override fun close() {
|
||||
Release()
|
||||
}
|
||||
companion object VTable {
|
||||
// IUnknown +3
|
||||
// IDispatch +4
|
||||
// IEnumNetworkConnections
|
||||
const val _NewEnum = 7
|
||||
const val Next = 8
|
||||
const val Skip = 9
|
||||
const val Reset = 10
|
||||
const val Clone = 11
|
||||
}
|
||||
|
||||
@Throws(COMException::class)
|
||||
fun Next(): INetworkConnection? {
|
||||
val ppNetworkConnections = PointerByReference()
|
||||
val nFetched = IntByReference()
|
||||
val hr = _invokeNativeInt(VTable.Next, arrayOf(pointer, 1, ppNetworkConnections, nFetched))
|
||||
COMUtils.checkRC(HRESULT(hr))
|
||||
if (nFetched.value.equals(0)) return null
|
||||
return INetworkConnection(ppNetworkConnections.value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see <a href="https://learn.microsoft.com/en-us/windows/win32/api/netlistmgr/nn-netlistmgr-inetworklistmanager">INetworkListManager interface (netlistmgr.h)</a>
|
||||
*/
|
||||
@Suppress("ktlint:standard:backing-property-naming", "ktlint:standard:function-naming")
|
||||
class INetworkListManager(instance: Pointer?) :
|
||||
Dispatch(instance),
|
||||
AutoCloseable {
|
||||
override fun close() {
|
||||
Release()
|
||||
}
|
||||
companion object VTable {
|
||||
// IUnknown +3
|
||||
// IDispatch +4
|
||||
// INetworkListManager
|
||||
const val GetNetworks = 7
|
||||
const val GetNetwork = 8
|
||||
const val GetNetworkConnections = 9
|
||||
const val GetNetworkConnection = 10
|
||||
const val IsConnectedToInternet = 11
|
||||
const val IsConnected = 12
|
||||
const val GetConnectivity = 13
|
||||
}
|
||||
|
||||
@Throws(COMException::class)
|
||||
fun GetNetworkConnections(): IEnumNetworkConnections {
|
||||
val pEnumNetworkConnections = PointerByReference()
|
||||
val hr = _invokeNativeInt(VTable.GetNetworkConnections, arrayOf(pointer, pEnumNetworkConnections))
|
||||
COMUtils.checkRC(HRESULT(hr))
|
||||
return IEnumNetworkConnections(pEnumNetworkConnections.value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see <a href="https://learn.microsoft.com/en-us/windows/win32/api/netlistmgr/nn-netlistmgr-inetwork">INetwork interface (netlistmgr.h)</a>
|
||||
*/
|
||||
@Suppress("ktlint:standard:backing-property-naming", "ktlint:standard:function-naming")
|
||||
class INetwork(instance: Pointer?) :
|
||||
Dispatch(instance),
|
||||
AutoCloseable {
|
||||
override fun close() {
|
||||
Release()
|
||||
}
|
||||
companion object VTable {
|
||||
// IUnknown +3
|
||||
// IDispatch +4
|
||||
// INetworkListManager
|
||||
const val GetName = 7
|
||||
const val SetName = 8
|
||||
const val GetDescription = 9
|
||||
const val SetDescription = 10
|
||||
const val GetNetworkId = 11
|
||||
const val GetDomainType = 12
|
||||
const val GetNetworkConnections = 13
|
||||
const val GetTimeCreatedAndConnected = 14
|
||||
const val IsConnectedToInternet = 15
|
||||
const val IsConnected = 16
|
||||
const val GetConnectivity = 17
|
||||
const val GetCategory = 18
|
||||
const val SetCategory = 19
|
||||
}
|
||||
|
||||
@Throws(COMException::class)
|
||||
private fun getNativeString(vtableId: Int): String? {
|
||||
val pStr = PointerByReference()
|
||||
val hr = _invokeNativeInt(vtableId, arrayOf(pointer, pStr))
|
||||
COMUtils.checkRC(HRESULT(hr))
|
||||
val bstr = WTypes.BSTR(pStr.value)
|
||||
val stringValue = bstr.value
|
||||
OleAuto.INSTANCE.SysFreeString(bstr)
|
||||
return stringValue
|
||||
}
|
||||
|
||||
@Throws(COMException::class)
|
||||
fun GetName(): String? = getNativeString(VTable.GetName)
|
||||
|
||||
@Throws(COMException::class)
|
||||
fun GetDescription(): String? = getNativeString(VTable.GetDescription)
|
||||
|
||||
@Throws(COMException::class)
|
||||
fun IsConnected(): Boolean {
|
||||
val bool = VARIANT_BOOLByReference()
|
||||
val hr = _invokeNativeInt(VTable.IsConnected, arrayOf(pointer, bool))
|
||||
COMUtils.checkRC(HRESULT(hr))
|
||||
return bool.value.booleanValue()
|
||||
}
|
||||
|
||||
@Throws(COMException::class)
|
||||
fun GetConnectivity(): Set<ConnectivityFlags> {
|
||||
val connectivity = IntByReference()
|
||||
val hr = _invokeNativeInt(VTable.GetConnectivity, arrayOf(pointer, connectivity))
|
||||
COMUtils.checkRC(HRESULT(hr))
|
||||
return ConnectivityFlags.fromInt(connectivity.value)
|
||||
}
|
||||
|
||||
@Throws(COMException::class)
|
||||
fun GetCategory(): NetworkCategory? {
|
||||
val category = IntByReference()
|
||||
val hr = _invokeNativeInt(VTable.GetCategory, arrayOf(pointer, category))
|
||||
COMUtils.checkRC(HRESULT(hr))
|
||||
return NetworkCategory.fromInt(category.value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network List Manager API wrapper
|
||||
* @see <a href="https://learn.microsoft.com/en-us/windows/win32/nla/about-the-network-list-manager-api">Network List Manager API</a>
|
||||
* @throws COMException
|
||||
*/
|
||||
class COMNetworkManager
|
||||
@Throws(COMException::class)
|
||||
constructor() : AutoCloseable {
|
||||
var instance: INetworkListManager
|
||||
private var shouldUninitialize = false
|
||||
|
||||
init {
|
||||
shouldUninitialize = COMUtils.SUCCEEDED(Ole32.INSTANCE.CoInitialize(null))
|
||||
|
||||
val CLSID_NetworkListManager = CLSID("dcb00c01-570f-4a9b-8d69-199fdba5723b")
|
||||
val IID_INetworkListManager = IID("dcb00000-570f-4a9b-8d69-199fdba5723b")
|
||||
|
||||
val ptr = PointerByReference()
|
||||
val hr = Ole32.INSTANCE.CoCreateInstance(
|
||||
CLSID_NetworkListManager,
|
||||
null,
|
||||
WTypes.CLSCTX_ALL,
|
||||
IID_INetworkListManager,
|
||||
ptr,
|
||||
)
|
||||
try {
|
||||
COMUtils.checkRC(hr)
|
||||
} catch (err: Exception) {
|
||||
if (shouldUninitialize) {
|
||||
Ole32.INSTANCE.CoUninitialize()
|
||||
}
|
||||
throw err
|
||||
}
|
||||
|
||||
instance = INetworkListManager(ptr.value)
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
instance.close()
|
||||
if (shouldUninitialize) {
|
||||
Ole32.INSTANCE.CoUninitialize()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun enumerateNetworks(): List<NetworkInfo>? {
|
||||
if (OperatingSystem.currentPlatform != OperatingSystem.WINDOWS) {
|
||||
return null
|
||||
}
|
||||
try {
|
||||
// Full command as a single string
|
||||
val command = "powershell.exe -Command \"(Get-NetConnectionProfile).NetworkCategory\""
|
||||
|
||||
// Use ProcessBuilder with the full command passed through the shell
|
||||
val processBuilder = ProcessBuilder(command.split(" "))
|
||||
processBuilder.redirectErrorStream(true)
|
||||
val process = processBuilder.start()
|
||||
|
||||
// Capture the output
|
||||
val output = BufferedReader(InputStreamReader(process.inputStream)).useLines { lines ->
|
||||
lines.joinToString("\n")
|
||||
COMNetworkManager().use { netmgr ->
|
||||
netmgr.instance.GetNetworkConnections().use { conns ->
|
||||
return generateSequence { conns.Next() }
|
||||
.map { conn ->
|
||||
conn.use {
|
||||
conn.GetNetwork().use { network ->
|
||||
NetworkInfo(
|
||||
network.GetName(),
|
||||
network.GetDescription(),
|
||||
network.GetCategory(),
|
||||
network.GetConnectivity(),
|
||||
network.IsConnected(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}.toList()
|
||||
}
|
||||
}
|
||||
val exitCode = process.waitFor()
|
||||
if (exitCode != 0) {
|
||||
return null
|
||||
}
|
||||
return when (output.trim()) {
|
||||
"Private" -> NetworkProfile.PRIVATE
|
||||
"Public" -> NetworkProfile.PUBLIC
|
||||
else -> null
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
return null
|
||||
} catch (err: Exception) {
|
||||
println(err.stackTraceToString())
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
class NetworkProfileChecker(private val server: VRServer) {
|
||||
private val updateTickTimer = Timer("NetworkProfileCheck")
|
||||
private var lastPublicNetworkStatus: UInt = 0u
|
||||
private var currentNetworkProfile: NetworkProfile? = null
|
||||
private var numPublicNetworks = 0
|
||||
|
||||
init {
|
||||
if (OperatingSystem.currentPlatform == OperatingSystem.WINDOWS) {
|
||||
this.updateTickTimer.scheduleAtFixedRate(0, 3000) {
|
||||
val profile = checkNetworkProfile()
|
||||
if (profile != currentNetworkProfile) {
|
||||
currentNetworkProfile = profile
|
||||
if (lastPublicNetworkStatus == 0u && profile == NetworkProfile.PUBLIC) {
|
||||
val currentNumPublicNetworks = enumerateNetworks()?.filter { net ->
|
||||
net.connected == true && net.category == NetworkCategory.PUBLIC
|
||||
} ?: listOf()
|
||||
val currentNumPublicNetworksCount = currentNumPublicNetworks.count()
|
||||
|
||||
if (numPublicNetworks != currentNumPublicNetworksCount) {
|
||||
numPublicNetworks = currentNumPublicNetworksCount
|
||||
if (lastPublicNetworkStatus != 0u) {
|
||||
server.statusSystem.removeStatus(lastPublicNetworkStatus)
|
||||
lastPublicNetworkStatus = 0u
|
||||
}
|
||||
|
||||
if (lastPublicNetworkStatus == 0u && numPublicNetworks > 0) {
|
||||
lastPublicNetworkStatus = server.statusSystem.addStatus(
|
||||
StatusDataUnion().apply {
|
||||
type = StatusData.StatusPublicNetwork
|
||||
value = StatusPublicNetworkT()
|
||||
value = StatusPublicNetworkT().apply {
|
||||
adapters = currentNumPublicNetworks.map { it.name }.toTypedArray()
|
||||
}
|
||||
},
|
||||
false,
|
||||
)
|
||||
} else if (lastPublicNetworkStatus != 0u && profile != NetworkProfile.PUBLIC) {
|
||||
server.statusSystem.removeStatus(lastPublicNetworkStatus)
|
||||
lastPublicNetworkStatus = 0u
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
package dev.slimevr.desktop.games.vrchat
|
||||
|
||||
import com.sun.jna.Memory
|
||||
import com.sun.jna.platform.win32.Advapi32
|
||||
import com.sun.jna.platform.win32.Advapi32Util
|
||||
import com.sun.jna.platform.win32.WinNT
|
||||
import com.sun.jna.platform.win32.WinReg
|
||||
import com.sun.jna.ptr.IntByReference
|
||||
import dev.slimevr.games.vrchat.VRCAvatarMeasurementType
|
||||
import dev.slimevr.games.vrchat.VRCConfigHandler
|
||||
import dev.slimevr.games.vrchat.VRCConfigValues
|
||||
@@ -15,64 +9,17 @@ import io.eiren.util.OperatingSystem
|
||||
import java.util.Timer
|
||||
import kotlin.concurrent.timerTask
|
||||
|
||||
// Vrchat is dumb and write 64 bit doubles in the registry as DWORD instead of QWORD.
|
||||
// so we have to be creative
|
||||
fun getQwordValue(path: String, key: String): Double? {
|
||||
val hKey = WinReg.HKEY_CURRENT_USER
|
||||
val phkResult = WinReg.HKEYByReference()
|
||||
|
||||
// Open the registry key
|
||||
if (Advapi32.INSTANCE.RegOpenKeyEx(hKey, path, 0, WinNT.KEY_READ, phkResult) != 0) {
|
||||
println("Error: Cannot open registry key")
|
||||
return null
|
||||
}
|
||||
|
||||
val lpData = Memory(8)
|
||||
val lpcbData = IntByReference(8)
|
||||
|
||||
val result = Advapi32.INSTANCE.RegQueryValueEx(
|
||||
phkResult.value,
|
||||
key,
|
||||
0,
|
||||
null,
|
||||
lpData,
|
||||
lpcbData,
|
||||
)
|
||||
Advapi32.INSTANCE.RegCloseKey(phkResult.value)
|
||||
|
||||
if (result != 0) {
|
||||
println("Error: Cannot read registry key")
|
||||
return null
|
||||
}
|
||||
return lpData.getDouble(0)
|
||||
}
|
||||
|
||||
fun getDwordValue(path: String, key: String): Int? = try {
|
||||
val data = Advapi32Util.registryGetIntValue(WinReg.HKEY_CURRENT_USER, path, key)
|
||||
data
|
||||
} catch (e: Exception) {
|
||||
println("Error reading DWORD: ${e.message}")
|
||||
null
|
||||
}
|
||||
|
||||
fun getVRChatKeys(path: String): Map<String, String> {
|
||||
val keysMap = mutableMapOf<String, String>()
|
||||
|
||||
try {
|
||||
Advapi32Util.registryGetValues(WinReg.HKEY_CURRENT_USER, path).forEach {
|
||||
keysMap[it.key.replace("""_h\d+$""".toRegex(), "")] = it.key
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
println("Error reading Values from VRC registry: ${e.message}")
|
||||
}
|
||||
return keysMap
|
||||
}
|
||||
|
||||
const val VRC_REG_PATH = "Software\\VRChat\\VRChat"
|
||||
|
||||
class DesktopVRCConfigHandler : VRCConfigHandler() {
|
||||
|
||||
private val getDevicesTimer = Timer("FetchVRCConfigTimer")
|
||||
private val regEdit: AbstractRegEdit =
|
||||
if (OperatingSystem.currentPlatform == OperatingSystem.WINDOWS) {
|
||||
RegEditWindows()
|
||||
} else {
|
||||
RegEditLinux()
|
||||
}
|
||||
|
||||
private var configState: VRCConfigValues? = null
|
||||
private var vrcConfigKeys: Map<String, String>
|
||||
@@ -80,24 +27,26 @@ class DesktopVRCConfigHandler : VRCConfigHandler() {
|
||||
|
||||
private fun intValue(key: String): Int? {
|
||||
val realKey = vrcConfigKeys[key] ?: return null
|
||||
return getDwordValue(VRC_REG_PATH, realKey)
|
||||
return regEdit.getDwordValue(VRC_REG_PATH, realKey)
|
||||
}
|
||||
|
||||
private fun doubleValue(key: String): Double? {
|
||||
val realKey = vrcConfigKeys[key] ?: return null
|
||||
return getQwordValue(VRC_REG_PATH, realKey)
|
||||
return regEdit.getQwordValue(VRC_REG_PATH, realKey)
|
||||
}
|
||||
|
||||
init {
|
||||
vrcConfigKeys = if (OperatingSystem.currentPlatform === OperatingSystem.WINDOWS) {
|
||||
getVRChatKeys(VRC_REG_PATH)
|
||||
vrcConfigKeys = if (OperatingSystem.currentPlatform == OperatingSystem.WINDOWS ||
|
||||
OperatingSystem.currentPlatform == OperatingSystem.LINUX
|
||||
) {
|
||||
regEdit.getVRChatKeys(VRC_REG_PATH)
|
||||
} else {
|
||||
mapOf()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateCurrentState() {
|
||||
vrcConfigKeys = getVRChatKeys(VRC_REG_PATH)
|
||||
vrcConfigKeys = regEdit.getVRChatKeys(VRC_REG_PATH)
|
||||
val newConfig = VRCConfigValues(
|
||||
legacyMode = intValue("VRC_IK_LEGACY") == 1,
|
||||
shoulderTrackingDisabled = intValue("VRC_IK_DISABLE_SHOULDER_TRACKING") == 1,
|
||||
@@ -116,7 +65,10 @@ class DesktopVRCConfigHandler : VRCConfigHandler() {
|
||||
}
|
||||
|
||||
override val isSupported: Boolean
|
||||
get() = OperatingSystem.currentPlatform === OperatingSystem.WINDOWS && vrcConfigKeys.isNotEmpty()
|
||||
get() = (
|
||||
OperatingSystem.currentPlatform == OperatingSystem.WINDOWS ||
|
||||
OperatingSystem.currentPlatform == OperatingSystem.LINUX
|
||||
) && vrcConfigKeys.isNotEmpty()
|
||||
|
||||
override fun initHandler(onChange: (config: VRCConfigValues) -> Unit) {
|
||||
this.onChange = onChange
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
package dev.slimevr.desktop.games.vrchat
|
||||
|
||||
import com.sun.jna.Memory
|
||||
import com.sun.jna.platform.win32.Advapi32
|
||||
import com.sun.jna.platform.win32.Advapi32Util
|
||||
import com.sun.jna.platform.win32.WinNT
|
||||
import com.sun.jna.platform.win32.WinReg
|
||||
import com.sun.jna.ptr.IntByReference
|
||||
import io.eiren.util.logging.LogManager
|
||||
import java.io.BufferedReader
|
||||
import java.io.FileReader
|
||||
import java.io.InvalidObjectException
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.exists
|
||||
|
||||
abstract class AbstractRegEdit {
|
||||
abstract fun getQwordValue(path: String, key: String): Double?
|
||||
abstract fun getDwordValue(path: String, key: String): Int?
|
||||
abstract fun getVRChatKeys(path: String): Map<String, String>
|
||||
}
|
||||
|
||||
class RegEditWindows : AbstractRegEdit() {
|
||||
// Vrchat is dumb and write 64 bit doubles in the registry as DWORD instead of QWORD.
|
||||
// so we have to be creative
|
||||
override fun getQwordValue(path: String, key: String): Double? {
|
||||
val hKey = WinReg.HKEY_CURRENT_USER
|
||||
val phkResult = WinReg.HKEYByReference()
|
||||
|
||||
// Open the registry key
|
||||
if (Advapi32.INSTANCE.RegOpenKeyEx(hKey, path, 0, WinNT.KEY_READ, phkResult) != 0) {
|
||||
LogManager.severe("[VRChatRegEdit] Error: Cannot open registry key")
|
||||
return null
|
||||
}
|
||||
|
||||
val lpData = Memory(8)
|
||||
val lpcbData = IntByReference(8)
|
||||
|
||||
val result = Advapi32.INSTANCE.RegQueryValueEx(
|
||||
phkResult.value,
|
||||
key,
|
||||
0,
|
||||
null,
|
||||
lpData,
|
||||
lpcbData,
|
||||
)
|
||||
Advapi32.INSTANCE.RegCloseKey(phkResult.value)
|
||||
|
||||
if (result != 0) {
|
||||
LogManager.severe("[VRChatRegEdit] Error: Cannot read registry key")
|
||||
return null
|
||||
}
|
||||
return lpData.getDouble(0)
|
||||
}
|
||||
|
||||
override fun getDwordValue(path: String, key: String): Int? = try {
|
||||
val data = Advapi32Util.registryGetIntValue(WinReg.HKEY_CURRENT_USER, path, key)
|
||||
data
|
||||
} catch (e: Exception) {
|
||||
LogManager.severe("[VRChatRegEdit] Error reading DWORD: ${e.message}")
|
||||
null
|
||||
}
|
||||
|
||||
override fun getVRChatKeys(path: String): Map<String, String> {
|
||||
val keysMap = mutableMapOf<String, String>()
|
||||
|
||||
try {
|
||||
Advapi32Util.registryGetValues(WinReg.HKEY_CURRENT_USER, path).forEach {
|
||||
keysMap[it.key.replace("""_h\d+$""".toRegex(), "")] = it.key
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
LogManager.severe("[VRChatRegEdit] Error reading Values from VRC registry: ${e.message}")
|
||||
}
|
||||
return keysMap
|
||||
}
|
||||
}
|
||||
|
||||
class RegEditLinux : AbstractRegEdit() {
|
||||
init {
|
||||
if (USER_REG_PATH == null) {
|
||||
LogManager.info("[VRChatRegEdit] Couldn't find any VRChat registry file")
|
||||
} else {
|
||||
LogManager.info("[VRChatRegEdit] Using VRChat registry file: $USER_REG_PATH")
|
||||
}
|
||||
}
|
||||
lateinit var registry: Map<String, String>
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
override fun getQwordValue(path: String, key: String): Double? {
|
||||
val value = registry[key] ?: return null
|
||||
if (!value.startsWith("hex(4):")) {
|
||||
LogManager.severe("[VRChatRegEdit] Couldn't find value with the expected type")
|
||||
return null
|
||||
}
|
||||
return ByteBuffer.wrap(value.substring(7).hexToByteArray(HEX_FORMAT))
|
||||
.order(ByteOrder.LITTLE_ENDIAN)
|
||||
.double
|
||||
}
|
||||
|
||||
override fun getDwordValue(path: String, key: String): Int? = try {
|
||||
val value = registry[key] ?: return null
|
||||
if (value.startsWith("dword:")) {
|
||||
value.substring(6).toInt()
|
||||
} else {
|
||||
throw InvalidObjectException("The requested key is not a DWORD but it is instead a $value")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
LogManager.severe("[VRChatRegEdit] Error reading DWORD: ${e.message}")
|
||||
null
|
||||
}
|
||||
|
||||
override fun getVRChatKeys(path: String): Map<String, String> {
|
||||
val keysMap = mutableMapOf<String, String>()
|
||||
val map = mutableMapOf<String, String>()
|
||||
|
||||
try {
|
||||
BufferedReader(FileReader(USER_REG_PATH?.toFile() ?: return keysMap)).use { reader ->
|
||||
// The reg file uses double backward-slash for paths
|
||||
val actualPath = "[${path.replace("\\", """\\""")}]"
|
||||
while (reader.ready()) {
|
||||
val line = reader.readLine()
|
||||
if (!line.startsWith(actualPath)) continue
|
||||
// Skip the `#time` line
|
||||
reader.readLine()
|
||||
while (reader.ready()) {
|
||||
val keyValue = reader.readLine()
|
||||
if (keyValue == "") break
|
||||
|
||||
KEY_VALUE_PATTERN.matchEntire(keyValue)?.let {
|
||||
map[it.groupValues[1]] = it.groupValues[2]
|
||||
keysMap[it.groupValues[1].replace("""_h\d+$""".toRegex(), "")] = it.groupValues[1]
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
LogManager.severe("[VRChatRegEdit] Error reading Values from VRC registry: ${e.message}")
|
||||
}
|
||||
registry = map
|
||||
return keysMap
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val USER_REG_SUBPATH = "steamapps/compatdata/438100/pfx/user.reg"
|
||||
val USER_REG_PATH =
|
||||
System.getenv("HOME")?.let {
|
||||
Path(it, ".steam", "root", USER_REG_SUBPATH).let { if (it.exists()) it else null }
|
||||
?: Path(it, ".steam", "debian-installation", USER_REG_SUBPATH).let { if (it.exists()) it else null }
|
||||
?: Path(it, ".var", "app", "com.valvesoftware.Steam", "data", "Steam", USER_REG_SUBPATH).let { if (it.exists()) it else null }
|
||||
}
|
||||
val KEY_VALUE_PATTERN = Regex(""""(.+)"=(.+)""")
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
val HEX_FORMAT = HexFormat {
|
||||
upperCase = false
|
||||
bytes.byteSeparator = ","
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -201,6 +201,8 @@ abstract class ProtobufBridge(@JvmField protected val bridgeName: String) : ISte
|
||||
|
||||
"fast_reset" -> instance.resetTrackersYaw(resetSourceName)
|
||||
|
||||
"mounting_reset" -> instance.resetTrackersMounting(resetSourceName)
|
||||
|
||||
"pause_tracking" ->
|
||||
instance
|
||||
.togglePauseTracking(resetSourceName)
|
||||
|
||||
Submodule solarxr-protocol updated: 737d007b4f...f62b57ee9e
Reference in New Issue
Block a user