Skip to content

Workspace

Your DigitalHeaven workspace (default: Documents/DigitalHeaven) contains source pallets, compiled output, configuration, and cache data. It’s shared across all tools — Studio, CLI, and game mods.

  • DirectoryDigitalHeaven/
    • DirectorySource/ Your source pallets live here
      • Directorydev.example/
        • Directoryavatars/
          • Directorymayu-custom/ Pallet ID: dev.example.avatars.mayu-custom
            • pallet.dh Pallet manifest
            • mayu-custom.dh-avatar Avatar definition
            • Directorytextures/
              • base-color-1001.png
              • base-color-1002.png
            • Directorymaterials/
              • mayu-custom-body.dh-mat
              • mayu-custom-head.dh-mat
            • Directoryaccessories/
              • Directoryround-glasses/
                • round-glasses.dh-obj
                • model.fbx
      • Directorytech.azuki/
        • Directoryavatars/
          • Directorymayu/ Pallet ID: tech.azuki.avatars.mayu
            • pallet.dh
            • mayu-tora.db-avatar
            • Directorymodels/
              • body-tora.fbx
            • Directoryrigs/
              • tora.dh-rig
            • Directorytextures/
              • tora-base-color-1001.png
              • tora-base-color-1002.png
            • Directorymaterials/
              • mayu-body.dh-mat
              • mayu-head.dh-mat
    • DirectoryPallets/ Compiled .pallet files
      • dev.example.avatars.mayu-custom.pallet
      • tech.azuki.avatars.mayu.pallet
    • DirectoryScripts/ Compiler-managed scripts
      • fbx_to_glb.py FBX→GLB conversion script (generated; rewritten on every build)
      • fbx_to_glb.user.py Optional user override (absent by default; never touched by the compiler)
    • Directory.dh/ Compiler data and cache
      • Directoryfbx-cache/ Cached GLB conversions of FBX source files
        • …
      • Directoryreceived/ Pallets received from other players via transfers
        • …
      • Directorytemp/ Temporary build artifacts
        • …
    • Directory.vscode/ Workspace settings
      • …
    • dh-config.jsonc Main configuration
    • dh-studio.jsonc Studio preferences
    • dh-friends.jsonc Friend list for pallet sharing
    • .gitignore
    • .gitattributes LFS tracking for binary assets

The workspace also includes a convenience script:

ScriptCommand
dh.bat / dh.shdh interactive

This launches the interactive menu, where you can build, watch, validate, and more. See CLI: Interactive Mode for details.

If the script doesn’t work, dh probably isn’t in your PATH. See the Setup page.

Pallets are organized by reverse-DNS ID: dev.example.avatars.mayu-custom lives at Source/dev.example/avatars/mayu-custom/. See Pallets & Assets for details.

Scripts/fbx_to_glb.py is compiler-managed — it’s rewritten from DigitalHeaven’s built-in template whenever that template changes, so fixes always reach you on your next build. Don’t edit it directly; your changes would be overwritten.

To customize the conversion instead, copy fbx_to_glb.py to fbx_to_glb.user.py in the same Scripts/ folder and edit the copy. The managed script checks for that file every time it runs and, if it’s there, hands the whole conversion to it — same arguments, same environment — instead of running its own logic. The compiler never creates, edits, or deletes fbx_to_glb.user.py; it only checks whether it exists, so your override survives every build. Deleting it goes back to the managed conversion.

The conversion cache keys on the content of whichever script actually ran, so editing your override (or removing it) reconverts affected FBX files on the next build, same as an update to the managed script would.

Always loaded from the default workspace root (Documents/DigitalHeaven, or DH_WORKSPACE when set) — never from workspaceRoot itself. All properties are optional; omit them to use defaults.

PropertyTypeDefaultDescription
workspaceRootstring?Documents/DigitalHeavenWorkspace root directory
sourceDirectorystring?{workspaceRoot}/SourceSource pallets location
palletsDirectorystring?{workspaceRoot}/PalletsCompiled output location
cacheDirectorystring?{workspaceRoot}/.dhCache directory
watchDebounceMsint?1000Milliseconds a change must settle before it is acted on (source auto-compile, and compiled-pallet discovery)
cookCacheBudgetByteslong?4294967296 (4 GiB)Byte budget shared by the cooked-collision (one entry per map cook), map-picture and model stand-in caches. Only swept when over budget, and only entries the current build is not using are ever deleted
importCacheBudgetByteslong?8589934592 (8 GiB)Byte budget of the importers’ cache (import-cache/): the models and textures dh import converted, kept for every map and later import that uses the same files (What an import reuses). Swept the same way
textureCacheBudgetByteslong?34359738368 (32 GiB)Byte budget of the compiler’s texture encode cache (texture-cache/), kept apart from the cook cache’s because one large imported pallet’s encodes outgrow it. Swept the same way
proxyTrianglesint?512The most triangles a model’s compiled stand-in may carry. A model with fewer keeps its own; a larger one is simplified to at most this many. 0 ships bounds alone
proxyBytesint?6144The most bytes a model’s compiled stand-in may encode to, header included; a silhouette over it is simplified further
textureEncode{ imported?, authored? }{ "imported": "fast", "authored": "balanced" }The UASTC encode effort of the texture roles that default to balanced (how): imported for pallets an importer made, authored for the rest. Each is "fast", "balanced" or "best"; an unknown value warns and takes the default. Changing it re-encodes only the textures whose effort changed
additionalSourcePathsstring[][]Extra directories the compiler searches for source pallets (cross-pallet refs).
additionalPalletDirectoriesstring[][]Extra directories every host (engine, Unity runtime, game mods) discovers compiled .pallet files in. Relative entries resolve against workspaceRoot; a missing or unreadable entry is skipped with a warning naming it
importobject?unsetSettings of the importers that turn another game’s content into pallets. Today one setting: a cap on texture size, with overrides per game
blenderPathstring?auto-detectPath to Blender executable, used to convert FBX to GLB
enginePathstring?unsetPath to DigitalHeaven.Engine.Host — the build shells out to it to photograph map thumbnails and to cook mesh-mode collision. Unset is not an error: the build warns, packs the PNG already beside the map, and ships mesh-mode maps without a cook, so they cook at load. When set, the engine’s bundled content folder next to it is also a compiled-pallet directory, so its built-in maps reach game mods with no other setup

Example:

dh-config.jsonc
{
"watchDebounceMs": 500,
"textureEncode": { "imported": "fast", "authored": "balanced" },
"additionalSourcePaths": [
"D:/SharedPalletSources"
],
"additionalPalletDirectories": [
"D:/SharedPallets"
],
"blenderPath": "C:/Program Files/Blender Foundation/Blender 4.0/blender.exe",
"enginePath": "C:/DigitalHeaven/DigitalHeaven.Engine.Host.exe"
}

dh import keeps every texture at the size the game stores it unless you opt in to a cap. The import block holds the cap and its per-game overrides:

dh-config.jsonc
{
"import": {
"maxTextureSize": null,
"games": {
"com.facepunch.sbox": { "maxTextureSize": 1024 }
}
}
}
PropertyTypeDefaultDescription
import.maxTextureSizeint?unsetThe longest side, in texels, an imported texture keeps. Unset, null and 0 cap nothing. A value that is not a power of two is rounded down to one
import.games.<game>.maxTextureSizeint?the default aboveThe cap for one game. 0 lifts the default for that game; a game with no size inherits the default

A game is named by its platform ID or an alias (com.facepunch.sbox or sbox, as --game takes them) — the same ids the importers write into a pallet’s game and originGame metadata. The Source importer looks a map up by its originGame, so a Half-Life 2 map imported through Garry’s Mod takes Half-Life 2’s cap.

A texture whose longest side is over the cap is scaled down before it is written, both sides by the same factor so the aspect ratio holds and the longer side lands on the cap. The scaling is a Lanczos filter: a color texture is filtered in linear light, a mask, roughness or height as the numbers it is, and a normal map as vectors that are made unit length again. A texture that fits is not touched, byte for byte. Every kind of texture gets the same cap, and a texture made from capped ones (a metallic-roughness pack, an emission, a flipbook atlas) comes out capped too, so layers and masks stay aligned. The cap is part of the key the import cache files a texture under only when it changes the texture, so changing the cap redoes just the textures it touches and imports under different caps share one cache.

What the cap leaves alone, because the engine needs it at its exact size: lightmap atlases, sky images, reflection cubemaps and probe data, and high dynamic range textures (the cap filters 8-bit images). The import report and the console summary say the cap and how many textures it scaled down.

Every host discovers compiled pallets through one shared list, in this order:

  1. palletsDirectory (the workspace)
  2. Folders the host ships itself (the engine’s own content)
  3. additionalPalletDirectories, in file order
  4. The content folder next to enginePath
  5. Pallets received from other players (.dh/received)

A folder reached twice (say, the engine’s content listed by hand as well) is scanned once, at its first position; paths compare case-insensitively on Windows. When two folders hold a pallet with the same id, the one earlier in the list is the one you see, so a workspace build of a pallet shadows the engine’s bundled copy, and a received pallet never shadows anything installed locally. Every listed folder is watched, so a rebuilt pallet in any of them hot reloads.

VariableDescription
DH_WORKSPACEOverride the workspace root directory. Takes priority over workspaceRoot in dh-config.jsonc. Useful for Proton/Wine where Documents resolves to the Wine prefix instead of the real user directory.

Definition files use custom .dh-* extensions. Each type has its own:

Asset TypeExtensionType ID
Pallet manifestpallet.dh—
Avatar.dh-avatardh.avatar
Object.dh-objdh.object
Material.dh-matdh.material
Texture.dh-texdh.texture
Rig.dh-rigdh.rig
Map.dh-mapdh.map
Look.dh-lookdh.look
Voxel.dh-voxdh.voxel
Block.dh-blockdh.block

See the Definition Types pages for what goes in each file.

All definition files are JSONC, JSON with two extras:

  • Comments: // line and /* block */
  • Trailing commas: allowed in arrays and objects
avatar.dh-avatar
{
// This is fine
"name": "My Avatar",
"tags": [
"custom",
"v2", // trailing comma OK
],
}

Parsed with System.Text.Json. Relaxed on formatting, strict on validation. The compiler catches errors at build time with file paths, line numbers, and helpful messages.