The .drft container · interface

DrftMaterial

One surface, stored once per mesh rather than once per vertex.

Explained in The .drft container.

interface DrftMaterial
import type { DrftMaterial } from '@driftengine/drft';

In depth

Parallel to the MESH chunks by ordinal: material n describes mesh n. A parallel array rather than a field inside MESH because a material is small and a mesh is megabytes, and the loader wants every material before it uploads anything, so it can create each texture once and share it between the meshes that name it.

Properties

NameTypeDescription
namereadonlystringWhat the source called this surface. Diagnostic, and more than diagnostic.
More

A bought asset frequently carries no material data worth the name and yet names its materials perfectly well: the one this format was built against stores every surface as the same default grey with no transparency, while calling them body, glass, chrome, tire_mat5 and calipers. The name is then the only thing in the file that says what a surface is, so it is carried rather than discarded, and a scene can dress an import without editing the import.

colorreadonlyreadonly [number, number, number]
specularreadonlynumber
roughnessreadonlynumber
emissivereadonlynumber
emissiveColorreadonlyreadonly [number, number, number]
opacityreadonlynumber1 is opaque. Below 1 the surface blends, and the loader draws it after the solid ones.
albedoreadonlynumberIndex into the asset's textures for the colour map, or -1 for an untextured surface.
normalMapreadonlynumberIndex into the asset's textures for the surface-space normal map, or -1 for none.
More

Written by any baker that can derive it, and bound by nothing yet. Normal maps ship in the renderer; carrying one through the container is the plan after ORM's. The field is defined here rather than appended later so that all four indices cost the format one minor version instead of three — see MATERIAL_INDICES.

ormMapreadonlynumberOcclusion in R, roughness in G, metallic in B, or -1 for none. glTF's packing.
emissiveMapreadonlynumberIndex into the asset's textures for the emissive map, or -1. Written, not yet bound.
roughnessScalereadonlynumberglTF's roughnessFactor where an ormMap supplies the roughness, and 1 where it does not.
More

A factor that multiplies a texture is not a value, which is the mistake this pair exists to stop repeating. roughness and specular above are the scalars a material states when it has no map; these are what the same numbers mean when it has one.

metallicScalereadonlynumberglTF's metallicFactor where an ormMap supplies the metallic, and 1 where it does not.
occlusionStrengthreadonlynumberglTF's occlusionTexture.strength, or 0 where nothing establishes that the ORM map's R channel holds occlusion at all.
More

glTF assigns G and B of a metallicRoughnessTexture and says nothing about R, so occlusion is only there when occlusionTexture names the same image — which is what the ORM convention is. Anywhere else this is 0, because reading R would be reading whatever the exporter left.

reflectivityreadonlynumberHow much of the environment this surface mirrors, 0 to 1.
More

Distinct from specular, which is the strength of a highlight from a light. A surface can take a sharp highlight and reflect nothing, which is most plastic, or reflect its surroundings strongly, which is what makes paint and chrome read as what they are.

cutoutreadonlynumberAlpha below which a fragment is discarded, 0 for a surface that discards nothing.
More

A test, not an opacity, and the difference is the whole reason this field exists. A cutout says which texels of a surface are there at all — the gaps in a grille, the space between leaves, the holes in a fence — and leaves everything it keeps at full strength. An opacity says how much of the light passes through the surface that is there. A format with only the second has to spend it on the first, and SurfaceMaterial.cutout had no way through the container until this: an alpha-tested surface arrived as a solid rectangle or as nothing.

glTF states it exactly — alphaMode: 'MASK' with alphaCutoff, which defaults to 0.5 — and it is what a vehicle format's alpha-test reference is, on the materials that say they are tested.

blendreadonlyoptionalbooleanWhether the surface blends: its texture's alpha is coverage, times opacity. 1.18.
More

A flag, because opacity could not say it. glTF's BLEND makes alpha the factor times the texture, and a factor of 1 left opacity at 1: every loader drew the material opaque and a leaf's shape, which is all in its texture's alpha, was never read. Absent means false, which is what every file before 1.18 meant.

doubleSidedreadonlyoptionalbooleanWhether both faces of the surface are seen: bit 1 of the flags word, 1.18.
More

glTF's doubleSided, and what a curtain, a flag and a leaf card are. A one-sided surface is culled from behind, so a curtain seen from the other side of its arch is a hole, and half a tree's cards are not drawn. Absent means false, which is how every surface before it was drawn.

transmissionreadonlyoptionalnumberGlass, 1.19: the share of light that passes, 0 opaque to 1 clear. Absent or 0 is not glass, which is what every surface before it was. See the engine's glass.ts.
frostreadonlyoptionalnumberHow milky the glass is, 0 see-through to 1 fully diffusing. 1.19.
tintreadonlyoptionalreadonly [number, number, number]The colour light takes through the glass; white is clear. 1.19.