Wall spots were markers for where SteamVR panels/windows could dock on a home's walls (Meta's WallPlacementComponent). Removed the feature end to end: the WorldWallSpot type and its JSON/binary serialization, the loader that read WallPlacementComponent, auto-generation of spots and the hotspots placed in front of them, the Studio editor's Wall Spot kind (Add menu, properties, overlay draw, picking, selection, report), the debug overlay, the home report counts, and the walktest viz. The PanelDock subsystem (src/panel_dock.h) existed only to dock panels onto wall spots and is deleted with its use in Frame Home's frame loop. Baked scene format is bumped to v14: new bakes omit wall spots, and older baked scenes still load (the wall-spot block is read past). Collision walls (WorldCollider::Wall) are unaffected — only the panel-placement markers are gone. Docs updated. Note: the pre-existing FRAME_HOME_PC Windows build break in walkgrid.h (the `near` macro) is unrelated and left as-is. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
8.9 KiB
Environments and the .fenv format
A Frame environment is one .fenv file. It holds everything Frame Home needs to show one home: the scene, where
you can walk and sit, your edits, a sound, a thumbnail and the home's own settings. Frame Studio writes it; Frame
Home and the Environments panel load it directly.
What a .fenv is
A .fenv is a ZIP file. You can open it with any ZIP tool to look inside (don't change it that way, though).
There are two kinds:
| Kind | What's inside | Notes |
|---|---|---|
| Baked (native) | The home already converted for the Steam Frame: meshes, textures in the headset's GPU format with their smaller levels, animations, the world layer | Opens several times faster and uses less memory. What Studio and fenv-tool make today. |
| Wrapped (older) | The original Meta .apk files, plus edits and a world.json |
Frame Home converts it every time it loads. Made by older versions, or when a home could not be baked. fenv-tool upgrade turns it into a baked one. |
fenv-tool info tells you which kind a file is: it prints "baked scene" or "loads from its homes".
What's inside
| Member | In | What it is |
|---|---|---|
fenv.json |
every .fenv |
The manifest: name, where the home came from (package and version of each .apk), and which other members are there. It also holds the environment's own settings. |
scene.bin.lz |
baked | The baked scene, packed with LZMA (much smaller than ZIP's own packing for textures). It is unpacked once, into a cache, then loaded from there. |
apks/... |
wrapped | The original Meta home files (a room, and maybe its vista) |
world.json |
wrapped | The world layer: spawn points, seats, teleport hotspots, walkable surfaces, gravity zones. A baked scene carries this inside scene.bin. |
edits.json |
when you edited | What you changed in Studio: moved and deleted objects, the added sound and how loud the home's own sound plays |
| a sound file | when you added one | The sound you added in Studio (.ogg, .opus, .wav, .mp3, .flac), named in edits.json |
thumb.png |
usually | The picture the Environments panel shows |
models/... |
when imported | Models from Blender or Unity, with their texture files (wrapped files only; a baked scene holds their meshes) |
Unpacked copies live in a cache: ~/.cache/frame-home/fenv on the headset and on Linux and macOS, and
%LOCALAPPDATA%\FrameStudio\cache on Windows. They are keyed by the file's path, size and time, so a changed file is
unpacked again. You can delete the cache at any time.
Typical sizes
Measured over a selector of 48 environments:
| Size | |
|---|---|
| Average | about 70 MB |
| Median | about 48 MB |
| Smallest | 3 MB |
| Largest | 218 MB |
A home with a room and its vista is usually larger than either alone.
Making a .fenv in Frame Studio
- Open a home: File > Open....
- Edit it if you like.
- File > Save to My Environments (Ctrl+S) or File > Save As....
Two options in the File menu change how Studio saves:
| Option | What it does |
|---|---|
| Faster Saving | Packs on all CPU cores: about 3 times quicker, about 1% bigger. Off: the smallest file. Nothing is lost either way. |
| Leave Out What Can't Be Seen | Leaves out geometry nobody can see from anywhere they can walk, sit or teleport to (the backs of things, what's behind walls), so the headset draws less. Moved and deleted objects are kept whole. Saving takes up to a minute longer. |
Converting homes with fenv-tool
fenv-tool is a command-line converter. It comes with Frame Home and is installed on the headset as
~/frame-home/bin/fenv-tool. Run it over SSH (ssh frame, see headset-setup.md).
It reads Meta home .apk files (HSL, Clay .opa, glTF .ovrscene) and models (.glb, .gltf, .fbx, .obj).
First copy the files to the headset, for example:
scp haven2025.apk calming.apk frame:
ssh frame
convert: one .fenv per home
~/frame-home/bin/fenv-tool convert ~/frame-home/envs haven2025.apk courtyard.apk
Makes haven2025.fenv and courtyard.fenv in ~/frame-home/envs. To pair a room with its vista, join them with
+:
~/frame-home/bin/fenv-tool convert ~/frame-home/envs haven2025.apk+calming.apk
This makes haven2025+calming.fenv.
pack: one .fenv from chosen inputs
~/frame-home/bin/fenv-tool pack ~/frame-home/envs/haven-calming.fenv --name "Haven + Calming" haven2025.apk calming.apk
| Option | Meaning |
|---|---|
--name NAME |
The name the panel shows. Default: made from the package names. |
--edits edits.json |
Include edits made in Studio |
--thumb thumb.png |
Include a thumbnail |
--keep-apks |
Store the original .apk files as well |
--fast |
Pack in parallel: several times quicker, about 3% bigger |
--no-optimize |
Keep geometry that can't be seen from anywhere you can be (also works with convert and upgrade) |
upgrade: re-bake with the current version
~/frame-home/bin/fenv-tool upgrade ~/frame-home/envs/*.fenv
Re-bakes each file's scene with this version and keeps everything else (world, edits, sound, picture). A file that
needs nothing is left alone. A wrapped file is baked: the scene replaces its .apk files.
info: what a .fenv holds
~/frame-home/bin/fenv-tool info ~/frame-home/envs/haven-calming.fenv
Prints the name, baked or wrapped, its sources and models, whether it has edits and a thumbnail, and its settings.
report: what a home costs and what's wrong with it
~/frame-home/bin/fenv-tool report ~/frame-home/envs/haven-calming.fenv
~/frame-home/bin/fenv-tool report --json haven2025.apk+calming.apk
Lists what the home is made of, what it costs, and problems found. It ends with exit code 3 when the home is broken. Studio shows the same under Window > Home Report....
Per-world settings
Every home costs the headset a different amount. A .fenv can carry its own settings, which win over the headset's
while you are in it. Every field is optional; an unset field uses the headset's setting.
| Field | Values | Meaning |
|---|---|---|
resolution |
0.5 to 2 | A fixed resolution scale (1 = SteamVR's recommended size) |
msaa |
1, 2, 4 | Anti-aliasing samples |
anisotropy |
4, 8, 16 | Texture filtering |
foveation |
1 to 3 (in between too) | Foveation strength: 1 gentle, 2 medium, 3 strong |
frameRate |
0 or 1 | 0 the display's rate, 1 half rate |
Resolution, foveation and frame rate apply live. A different anti-aliasing or filtering makes Frame Home restart
itself when you switch to that home. A .fenv keeps these in its fenv.json. A Meta .apk in envs/ keeps them in
a file beside it, <name>.apk.settings.json.
Setting them by hand
~/frame-home/bin/fenv-tool settings my-home.fenv # show
~/frame-home/bin/fenv-tool settings my-home.fenv '{"resolution": 1.2, "foveation": 2}' # set
~/frame-home/bin/fenv-tool settings my-home.fenv --clear # back to the headset's
In Studio, the open environment's world settings have the same fields (Resolution, Foveation, MSAA, Frame rate), with Use Headset Settings to clear them.
Letting the headset work them out: fenv-tool tune
fenv-tool tune finds the best-looking settings the headset keeps up with in one home. Run it on the headset:
~/frame-home/bin/fenv-tool tune ~/frame-home/envs/my-home.fenv
How it works:
- It starts from your headset settings.
- For each try, it walks through the home for 30 seconds, paced at the display's rate, like a visitor: walking, snap turns, teleports and fast head turns. It runs beside the live compositor, as on the headset in use.
- A try keeps up when it holds 120 fps with at most 1% of frames late.
- If your settings keep up, it tries sharper foveation, half a step at a time, while it still keeps up. If not, it tries stronger foveation, then lower resolution (10% at a time), and as a last resort half frame rate. Your anti-aliasing stays as it is.
- Before each try it waits until the headset is below 80 °C, so every try starts alike.
- It stores the result in the
.fenv(or<apk>.settings.json) with a note of when and how it was tuned, and prints it as JSON.
It takes a few minutes. Options:
| Option | Default | Meaning |
|---|---|---|
--seconds S |
30 | Length of each walk (5 to 300) |
--late F |
0.01 | Share of frames allowed to be late (0.01 = 1%) |
--hz N |
120 | Frame rate to keep up with |
--cool C |
80 | Wait until the headset is below C °C before each try |
--dry-run |
Print the result without storing it | |
--bench PATH |
frame-home beside fenv-tool |
The Frame Home program to walk with |
From Studio, Calculate Best Settings in the environment's world settings does the same: it sends the home to the headset, runs the tune there, and keeps the result in the open environment. Save to keep it in the file.
See performance.md for the bigger picture.