Skip to content

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.

PropertyTypeRequiredDescription
$type"dh.texture"noType identifier
namestringnoDisplay name
descriptionstringnoDescription
packobjectone ofChannel packing map (mutually exclusive with source)
sourcestringone ofSingle source texture path (mutually exclusive with pack)
size[w, h]noTarget dimensions. Omit to keep original size.
filterstringnoFilter mode: "point", "bilinear", "trilinear". Default: platform default.
wrapstringnoWrap mode: "repeat", "clamp", "mirror". Default: platform default.

The type is inferred from the file extension, so $type is not needed in source files. The compiler adds it automatically during builds.

Combine channels from multiple source textures into a single output. Classic use case: ORM (Occlusion, Roughness, Metallic) packing.

body-orm.dh-tex
{
"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 (01).

Defaults for unspecified channels:

  • RGB channels → 0 (black)
  • Alpha channel → 255 (fully opaque)

Pack channels can be set to a constant intensity using a number from 0 to 1:

ValueByteMeaning
00Black / fully transparent
0.5128Mid-gray / half opacity
1255White / fully opaque

This is useful when you need a solid channel without creating a source texture:

nipple-base.dh-tex
{
"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.

Process a single texture, whether to resize it or just reference a source:

body-diffuse-1k.dh-tex
{
"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.

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.

SyntaxResult
textures/packed.pngFull RGBA texture
textures/packed.png@rRed channel as grayscale
textures/packed.png@gGreen channel as grayscale
textures/packed.png@bBlue channel as grayscale
textures/packed.png@aAlpha channel as grayscale

Works with cross-pallet barcodes too:

io.mltn.base:textures/packed-orm.png@r

The @ separator was chosen because it can’t appear in file paths and doesn’t conflict with : used for pallet references.

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.

Controls how the texture is interpolated when sampled:

ValueAliasesDescription
"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.

Controls what happens when UV coordinates go outside [0, 1]:

ValueDescription
"repeat"Tiles the texture (platform default).
"clamp"Clamps to edge pixels.
"mirror"Mirrors the texture at boundaries.
sprite.dh-tex
{
"source": "textures/sprite.png",
"filter": "point",
"wrap": "clamp"
}

When omitted, the platform default is used (typically bilinear filtering and repeat wrapping).

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:

sprite.png.dh-tex-settings
{
"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-settings sidecar — when you just need import settings on an existing texture file
body-orm.dh-tex
{
"name": "Body ORM",
"pack": {
"r": "textures/body-ao.png@r",
"g": "textures/body-roughness.png@r",
"b": "textures/body-metallic.png@r"
}
}
body-diffuse-mobile.dh-tex
{
"name": "Body Diffuse (Mobile)",
"source": "textures/body-diffuse-4k.png",
"size": [512, 512]
}
decal.dh-tex
{
"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-sprite.dh-tex
{
"name": "Pixel Sprite",
"source": "textures/sprite-16x16.png",
"filter": "point",
"wrap": "clamp"
}

Point filtering preserves sharp pixel edges, no blurry interpolation.