Bistro: all imported materials are opaque — foliage and glass lose their alpha
Symptom
In the Bistro sample the storefront glass renders as an opaque wall showing a blurred copy of the HDRI skyline, hiding the interior entirely. Foliage renders as solid overlapping cards rather than cut-out leaves.
Both have the same root cause: every material imported from Bistro is fully opaque.
All 277 generated .material artifacts in .cache/assets/ carry
{"blend": "disabled", "alpha_mask": false, "depth_write": true} — not one exception.
Root cause 1 — the importer ignores BaseColor alpha
fbx_wrap.c derives the alpha mode purely from ufbx's opacity map:
int opacity_textured = pbr->opacity.texture != NULL;
int opacity_transparent = pbr->opacity.has_value && pbr->opacity.value_real < 0.999;
o->alpha_mode = (opacity_textured || opacity_transparent) ? FBX_WRAP_ALPHA_BLEND : FBX_WRAP_ALPHA_OPAQUE;Dumping ufbx's material properties for all 132 exterior materials shows the FBX carries no transparency signal — every single material reports:
pbr.opacity has=0 val=0.0000 tex=0
fbx.transparency_fac has=1 val=1.0000 | color has=1 (0.000 0.000 0.000)
raw Opacity = 1.0000
raw TransparencyFactor= 1.0000So the condition can never fire, and everything falls through to opaque.
The actual signal is in the textures, and the ORCA README.txt shipped with the asset
states the convention explicitly:
- BaseColor RGB channels: BaseColor value Alpha channel: Opacity
17 BaseColor maps carry a real mask that is currently discarded (% of texels below alpha 250, measured at full resolution):
| % masked | texture |
|---|---|
| 100.00 | Paris_Table_cloth_01_BaseColor.dds |
| 100.00 | Paris_LiquorBottle_01_Labels_BaseColor.dds |
| 100.00 | MASTER_Glass_Dirty_MASKED_BaseColor.dds |
| 88.07 | MASTER_Forge_Metal_BaseColor.dds |
| 81.92 | Foliage_Leaves_BaseColor.dds |
| 75.83 | Foliage_Flowers_BaseColor.dds |
| 75.20 | Foliage_Linde_Tree_Large_Orange_Leaves_BaseColor.dds |
| 75.19 | Foliage_Linde_Tree_Large_Green_Leaves_BaseColor.dds |
| 66.04 | Plants_plants_BaseColor.dds |
| 65.85 | MASTER_Side_Letters_BaseColor.dds |
| 53.39 | Foliage_Ivy_leaf_a_BaseColor.dds |
| 49.61 | Plates_Details_BaseColor.dds |
| 48.14 | Foliage_Bux_Hedges46_BaseColor.dds |
| 33.58 | MASTER_Curtains_BaseColor.dds |
| 13.39 | Lantern_BaseColor.dds, LanternEmissive_BaseColor.dds, MASTER_Interior_01_Paris_Lantern_BaseColor.dds |
These are almost all near-binary masks (partial-alpha texels are 1–9% of the image), so
they want alpha_mask (cutout), not blend — cheap, order-independent, and correct
for foliage.
Note the engine side is already in place (#26 (closed) delivered BlendMode + alpha_mask and
ModelDerivedAssets.zig maps them onto the material asset). Only the detection is
missing.
Root cause 2 — the storefront glass has no transparency signal anywhere
The plain glass materials (MASTER_Glass_Exterior, MASTER_Glass_Dirty,
MASTER_Focus_Glass, MASTER_Frosted_Glass, TransparentGlass, the liquor bottles)
have alpha 255 across the entire BaseColor map and no FBX opacity. Fixing root cause 1
will not make them transparent.
That is not an oversight in the asset: in the ORCA distribution this information lives
outside the FBX, in the Falcor .pyscene script shipped in vendor-source/:
glass = sceneBuilder.getMaterial("TransparentGlass")
glass.roughness = 0
glass.metallic = 0
glass.indexOfRefraction = 1.55
glass.specularTransmission = 1
glass.doubleSided = TrueTurian has no equivalent. Worse, the generated .material artifacts live under .cache/,
which is gitignored — so hand-editing a material through the sub-asset inspector fixes it
locally but cannot ship with the sample project, and is wiped by "Reimport All".
What is needed is a per-material import-override mechanism that persists in the
tracked .meta file (alongside import_settings), keyed by source material name.
That is the general fix — it makes any DCC-authored model correctable without touching
the source asset, and it is what lets the Bistro sample ship correct glass.
Why the glass reads as a mirror specifically
All the glass *_Specular.dds maps are solid black, i.e. roughness 0 / metalness 0.
That is the asset's way of saying "perfectly smooth dielectric", which is correct — but
combined with an opaque surface it produces a mirror of the only reflection source the
renderer has, the distant HDRI. Even once the glass is alpha-blended it will keep
reflecting the sky rather than the street until local reflections exist (see the GI
umbrella issue).
Proposed work
- Derive alpha mode from the BaseColor texture's alpha channel when the model format
supplies no opacity, classifying near-binary masks as
alpha_maskand smooth gradients asblend. Applies to FBX and glTF alike. - Add per-material import overrides to the model
.meta, keyed by source material name, merged over the derived material at cook time. Ship Bistro's glass overrides in the sample repo. - Expose the overrides in the Studio sub-asset inspector so edits write to
.metarather than to the gitignored cache artifact.
Verification
Capture the terrace view before/after; the interior must be visible through the storefront glass and the potted topiary must read as individual leaves rather than a solid blob. Compare against the ORCA reference render.
Related
- #132 Bistro sample
- #26 (closed) Materials: transparency blend modes — engine side, already delivered
- #2 (closed) Asset metadata / MetaFile — where the overrides would live