dh.texture
Extension: .dh-tex
Type ID: dh.texture
Texture definitions describe compile-time texture operations. The compiler processes them during dh build; runtimes just load the pre-baked results.
Two modes: channel packing (pack) or single source (source). They’re mutually exclusive.
Properties
Section titled “Properties”| Property | Type | Required | Description |
|---|---|---|---|
$type | "dh.texture" | no | Type identifier |
name | string | no | Display name |
description | string | no | Description |
pack | object | one of | Channel packing map (mutually exclusive with source) |
source | string | one of | Single source texture path (mutually exclusive with pack) |
size | [w, h] | no | Target dimensions. Omit to keep original size. |
filter | string | no | Filter mode: "point", "bilinear", "trilinear". Default: platform default. |
wrap | string | no | Wrap mode: "repeat", "clamp", "mirror". Default: platform default. |
The type is inferred from the file extension, so
$typeis not needed in source files. The compiler adds it automatically during builds.
Channel Packing
Section titled “Channel Packing”Combine channels from multiple source textures into a single output. Classic use case: ORM (Occlusion, Roughness, Metallic) packing.
{ "name": "Body ORM", "pack": { "r": "textures/occlusion.png@r", "g": "textures/roughness.png@r", "b": "textures/metallic.png@r" }}Each key is an output channel (r, g, b, a). Each value is either a texture reference (with optional @channel suffix) or a constant number (0–1).
Defaults for unspecified channels:
- RGB channels → 0 (black)
- Alpha channel → 255 (fully opaque)
Constant Values
Section titled “Constant Values”Pack channels can be set to a constant intensity using a number from 0 to 1:
| Value | Byte | Meaning |
|---|---|---|
0 | 0 | Black / fully transparent |
0.5 | 128 | Mid-gray / half opacity |
1 | 255 | White / fully opaque |
This is useful when you need a solid channel without creating a source texture:
{ "name": "Nipple base texture", "pack": { "r": 1, "g": 1, "b": 1, "a": "body-extensions/nipples/mask.png@r" }}White RGB with alpha from a mask. No separate white texture file needed.
Single Source
Section titled “Single Source”Process a single texture, whether to resize it or just reference a source:
{ "name": "Body Diffuse (1K)", "source": "textures/body-diffuse.png", "size": [1024, 1024]}If only one dimension is given in size, the other is calculated to preserve the aspect ratio.
@channel Syntax
Section titled “@channel Syntax”The @channel suffix extracts a single channel from a source texture. This works anywhere texture paths are used — in pack values, in source, and in material texture references.
| Syntax | Result |
|---|---|
textures/packed.png | Full RGBA texture |
textures/packed.png@r | Red channel as grayscale |
textures/packed.png@g | Green channel as grayscale |
textures/packed.png@b | Blue channel as grayscale |
textures/packed.png@a | Alpha channel as grayscale |
Works with cross-pallet barcodes too:
io.mltn.base:textures/packed-orm.png@rThe @ separator was chosen because it can’t appear in file paths and doesn’t conflict with : used for pallet references.
Import Settings
Section titled “Import Settings”Textures can specify filter mode and wrap mode to control how they’re sampled at runtime. These settings work with both pack and source modes.
Filter Mode
Section titled “Filter Mode”Controls how the texture is interpolated when sampled:
| Value | Aliases | Description |
|---|---|---|
"point" | "nearest" | Nearest-neighbor filtering. Sharp pixels, no interpolation. Best for pixel art. |
"bilinear" | — | Smooth interpolation between pixels (platform default). |
"trilinear" | — | Bilinear with mipmap blending. |
Wrap Mode
Section titled “Wrap Mode”Controls what happens when UV coordinates go outside [0, 1]:
| Value | Description |
|---|---|
"repeat" | Tiles the texture (platform default). |
"clamp" | Clamps to edge pixels. |
"mirror" | Mirrors the texture at boundaries. |
{ "source": "textures/sprite.png", "filter": "point", "wrap": "clamp"}When omitted, the platform default is used (typically bilinear filtering and repeat wrapping).
Sidecar Files
Section titled “Sidecar Files”Plain texture files (.png, .jpg, etc.) can carry import settings through a sidecar file without needing a full .dh-tex definition. Name the sidecar by appending .dh-tex-settings to the texture filename:
Directorytextures/
- sprite.png
- sprite.png.dh-tex-settings
The sidecar is a JSON file with the same filter and wrap properties:
{ "filter": "point", "wrap": "clamp"}Sidecar files are consumed during compilation and don’t appear in the compiled pallet. The settings are baked into the pallet’s file metadata.
When to use which:
.dh-tex— when you also need channel packing, resize, or other compile-time processing.dh-tex-settingssidecar — when you just need import settings on an existing texture file
Examples
Section titled “Examples”ORM Pack
Section titled “ORM Pack”{ "name": "Body ORM", "pack": { "r": "textures/body-ao.png@r", "g": "textures/body-roughness.png@r", "b": "textures/body-metallic.png@r" }}Downscale for Performance
Section titled “Downscale for Performance”{ "name": "Body Diffuse (Mobile)", "source": "textures/body-diffuse-4k.png", "size": [512, 512]}RGBA with Alpha
Section titled “RGBA with Alpha”{ "name": "Decal with Transparency", "pack": { "r": "textures/decal-color.png@r", "g": "textures/decal-color.png@g", "b": "textures/decal-color.png@b", "a": "textures/decal-mask.png@r" }}Pixel Art with Point Filtering
Section titled “Pixel Art with Point Filtering”{ "name": "Pixel Sprite", "source": "textures/sprite-16x16.png", "filter": "point", "wrap": "clamp"}Point filtering preserves sharp pixel edges, no blurry interpolation.