Files
Pierre Kisters 4819ea26a7 VR pet: model sources in the repo
The Toon Cat (sources/toon-cat/), the Tuxedo Cat glTF (sources/tuxedo-cat/)
and the Quaternius Shiba Inu and Fox (models/{shiba,fox}/model.glb), each
byte-identical to the previously fetched file and with a LICENSE.md
(author, original URL, licence, changes). The build no longer downloads
them; three.js (preview only) still comes from npm. The baked frames,
model dirs and icons are unchanged.
2026-10-01 04:46:53 +02:00

110 lines
5.4 KiB
Markdown

# VR pet
`pet.*`, module `pet` ([options](../README.md#options)).
A [SteamVR dashboard patch](ui-patches.md#steamvr-dashboard-patches).
## What you get
A 3D pet in SteamVR's scene, next to your windows: the Toon Cat in five
coats (Ginger, Tuxedo, Blue, Cream, Snow), a Shiba Inu, a Fox and a
Dachshund, plus your own models (`pet.extraModels`). It lives in the
dashboard's scene, so it is there in SteamVR Home and over games while the
dashboard runs.
- **On its own** it walks around you, sits, lies down, sleeps, follows you
beyond 2.5 m and reappears in front of you beyond 8 m. The cat also
grooms, stretches and shakes; the dogs and the fox sniff and look around.
- **Pick it up:** point the laser just above it (a grip bar shows up, like
a window's), press and drag like a window (the thumbstick pushes and
pulls). It dangles by the scruff, turns with the controller and falls
where you let go. No controller button is read; a falling pet is not
caught.
- **Pet it:** rest or move a controller slowly (< 0.4 m/s) on its head or
back (within 10 cm) for 0.6 s: it purrs (a dog wags its tail) while the
strokes go on. A fast swipe (> 1 m/s) close by startles it.
- **Its controls**, below the grip bar: X hides it (kept across SteamVR
restarts); ⋯ opens its menu: the models (a check on the current one, kept
across restarts), then Summon (1 m in front of you), Sit, Lie down,
Sleep. Buttons act on a press and a release on them, not during a drag or
0.3 s after. The menu closes on a pick, a press elsewhere, 1 s after the
laser left it, and when the pet hides.
- **Bring it back:** "Pet" in the dashboard's "+" menu (its icon is the
current model's), i.e. `vr-pet show`: at its spot while that is within
3 m and in view, else 1 m in front of you.
The `vr-pet` command:
```text
vr-pet show [--summon] show it (--summon: always 1 m in front of you)
vr-pet hide | status hide it; its state as JSON
vr-pet models the models (* the current one)
vr-pet model <id> switch (a hidden pet switches too)
```
## Configuration
```nix
steamFrame.pet.enable = true;
# steamFrame.pet.defaultModel = "shiba";
# steamFrame.pet.options = { walkSpeed = 0.3; follow = 3; }; # core.js DEFAULTS
# steamFrame.pet.extraModels.corgi = ./pets/corgi; # a model folder
```
The first switch bakes the models: a few minutes on the Frame and about
0.5 GB in the store (1.1 GB without `auto-optimise-store`, which hardlinks
the coats' copies of the cat's frames); the model sources are in this
repository (no model download, no Blender needed for the built-in models). Adding your own model, the spec and the
animation mapping: [VR pet models](pet-models.md). A model of a character
someone else owns belongs in your own `extraModels` with `"private": true`
([private models](pet-models.md#private-models)), never in this repository.
Without a headset: `nix run github:lhns/steam-frame-nix#pet-preview` plays
the pet in a browser (mouse as the hands, a debug panel).
## Limitations
- vrcompositor draws every mounted frame: a few hundred static OBJs per
model, mounted gradually (`pet.mount`); switching to another animal takes
a few seconds to load.
- The pet is drawn by the dashboard's page: it is gone while SteamVR's
dashboard process restarts, and the patch reattaches within 15 s.
- It walks on the floor of the standing space and knows nothing of your
room's furniture.
- The "+" menu icon changes when the model does; a running Steam picks it
up when it rescans its icons (a few seconds).
## Credits
The models' authors and licences: [README, Credits](../README.md#credits).
## How it works
`vr-pet`, with [persistent state](ui-patches.md#persistent-state) (the
spot, pose, model and whether it is hidden). Debugging in the `systemui`
page: `window.__sfuiPet.cat.command('sit' | 'summon' | …)`,
`.cat.state()`, `.stats()`; the CLI calls only `window.__sfuiPet.api`.
Journal: `journalctl --user -t vr-pet -u steam-ui-patches`.
- **Build** (`modules/pet/package.nix`): bake.py retargets and hand-keys
the Toon Cat's clips and bakes one OBJ per animation frame; bake_gltf.py
does the same for a rigged glTF (the Quaternius animals); index.py makes
the catalog (coats as recoloured textures of the cat's frames, one
directory per model, 48 px thumbnails) and the "+" menu icons. Each
animal is a derivation of its own.
- **Behaviour** (`core.js`): poses, clips and transitions, wandering,
following, petting, the scruff drag and the fall, independent of where
it is drawn (also used by the preview and the tests).
- **Drawing** (`systemui.js`): every frame is a render model node under one
world-locked root in the dashboard's scene graph; the frames a clip needs
are mounted ahead, unused ones dropped. Each change resends the
dashboard's last scene graph message with only the pet's subtree
replaced. The grip bar is a panel like a window's handle; while dragged
the root is parented to the controller, so the compositor carries it.
- **"+" menu icon:** `~/.local/share/icons/hicolor/256x256/apps/vr-pet.png`
links to `/run/user/1000/steam-frame-nix/vr-pet/icon.png`, which the
oneshot user service `steam-frame-nix-pet-icon` points to the current
model's icon: on switch, at login and when the state file changes (path
unit); it bumps hicolor's mtime so a running Steam rescans. Lifetime and
cleanup: [Changes outside Nix](../README.md#changes-outside-nix-exceptions).