acron.materials

Apply PBR material data onto loaded GLBs that ship names but no textures.

Only needed when the game’s GLBs ship material NAMES but no embedded textures (the tell-tale: model.find_all_textures() returns 0 but find_all_materials() is non-zero, and every geom is flat/gray). This module owns the plain data shape (ParsedMaterial: absolute texture paths + scalar/colour fallbacks + flags) and re-applies each geom’s resolved material using the native PBR shader’s TextureStage setup (base -> ORM -> normal -> emission), binding to the UVs the GLB already carries. The Godot .tres parser and directory index that PRODUCE ParsedMaterials live in acron.gdformat.materials.

acron.pbr.init() owns the shader: the native generator reads each geom’s Material (as a PbrMaterial, PbrMaterial.from_material()) and its maps. Do NOT add a Y->Z rotation (acron-gltf already did the up-axis conversion).

The ORM texture (Occlusion=R / Roughness=G / Metallic=B) goes straight into an M_occlusion_metallic_roughness stage with no channel shuffling – Godot packs it the same glTF-standard way (metallic_channel=2 / roughness_channel=1 / AO=R).

Apply to characters too, not just the level: player/NPC Actors from the same pipeline also load gray. You cannot verify texturing headless – screenshot on real GL.

MaterialApplier(loader, index[, anisotropy])

Applies a material index onto loaded GLBs, caching textures + Materials.

ParsedMaterial([name, tres_path, ...])

A Godot StandardMaterial3D flattened to what a port needs.

Global Functions

apply_materials(model_np, index, loader=None, applier=None)

Apply a material index onto every geom under model_np.

loader defaults to the global ShowBase loader. Pass a shared applier to reuse texture/Material caches across several GLBs. Returns the MaterialApplier used (so callers can read .textured / .textureless / .missed). Unmatched materials are logged, not fatal.

mark_srgb(tex) → None

Mark a colour texture as sRGB for correct gamma, without corrupting it.

Setting F_srgb directly on a texture whose RAM image is compressed or has stale mipmaps triggers a GL view_size assertion on upload (the RAM image no longer matches the declared format) – an abort that only surfaces on real GL, never headless. We first drop any RAM/mipmap image so the driver re-uploads from a clean base at the new format, and only apply sRGB when the component layout supports it (3 or 4 components).

primary_texcoord_name(vertex_format)

The UV column a geom’s textures should bind to, or None for the default.

Returns the bare texcoord when present (hand-built geometry and most Acron-native models), otherwise the lowest-numbered texcoord.N – glTF’s first UV set, which is the one materials are authored against. None means “leave the stage default”, i.e. the model carries no UVs at all.

srgb_to_linear(sample)

An sRGB colour in linear light, or None for None.

Takes and returns (r, g, b) or (r, g, b, a) – alpha is not colour and is passed through.

Two kinds of value need this, and both are easy to miss because the number in the file looks like a plain float:

  • a texture SAMPLE folded into a scalar factor by hand. The GPU does this conversion for every sRGB texture read, so a hand-folded stand-in has to as well, or it comes out visibly brighter than the map it stands for.

  • a COLOUR out of a Godot resource. Godot authors colours in sRGB and converts on the way to the renderer – srgb_to_linear() in render_scene_data_rd.cpp for the environment’s ambient and fog, the source_color uniform hint for a material’s albedo_color and emission. A .tres colour used as if it were linear is both too bright and too GREY, because the curve is steepest at the bottom: the TPS level’s ambient (0.259, 0.204, 0.153) is really (0.054, 0.034, 0.020), a third as bright and half again as saturated.