Fixed vehicle rendering

This commit is contained in:
2026-10-22 10:14:44 +03:00
parent ff81a9a657
commit c561fad79e
9 changed files with 625 additions and 69 deletions
+94 -19
View File
@@ -275,14 +275,17 @@ for m in used_mats:
print("gen_vehicle: unpacked texture '%s' -> %s" %
(img.name, tex_name))
# --- merged single material + ID map (CAR_MODELING.md) ---
# Every mesh part is re-materialized onto a single material named
# <base> so blender2ogre emits ONE submesh per part, and the exporter's
# fixed-function .material is replaced with the VehicleIdMap shader
# materials after the export. The ID map texture shares the color
# atlas UV space (all parts MUST use that one UV space with no texel
# shared between materials; fix the blend if not) and marks each
# fragment: red = paint (atlas color), green = bloom (lights),
# --- merged paint material + separate glass material (CAR_MODELING.md) ---
# Every mesh part is re-materialized onto TWO materials: <base> for
# paint/bloom faces and <base>-glass for glass faces, so blender2ogre
# emits one submesh per part (glass-less parts) or two (glass last).
# A single merged transparent submesh forced the WHOLE part (interior
# included) into the transparent render queue, where the depth-writing
# window glass culled interior parts that sorted behind it ("holes" in
# the cabin with the road showing through). The ID map texture shares
# the color atlas UV space (all parts MUST use that one UV space with
# no texel shared between materials; fix the blend if not) and marks
# each fragment: red = paint (atlas color), green = bloom (lights),
# blue = glass.
IDMAP_SIZE = 512
@@ -367,10 +370,10 @@ def bake_idmap(mesh_datas, path):
def move_glass_faces_last():
"""Re-order each mesh so glass faces export last: with the merged
transparent material (depth_write on) the cabin stays visible
through the windows only if glass triangles draw after the
interior."""
"""Re-order each mesh so glass faces export last. With glass on
its own material slot (see the consolidation below) blender2ogre
groups faces into submeshes by material anyway; the sort just
keeps the glass submesh's triangles contiguous at the end."""
for obj in bpy.data.objects:
if obj.type != 'MESH':
continue
@@ -410,13 +413,35 @@ for obj in bpy.data.objects:
move_glass_faces_last()
bake_idmap(all_mesh_datas, os.path.join(outdir, base + "-idmap.png"))
merged_mat = bpy.data.materials.new(base)
merged_mat = bpy.data.materials.get(base)
if merged_mat is None:
merged_mat = bpy.data.materials.new(base)
merged_mat.use_nodes = True
# The blend already HAS a "<base>-glass" material (source materials are
# renamed to <base>-<material>); reuse it — materials.new() would
# silently create "<base>-glass.001", and the exported submesh material
# name must match the .material template exactly.
glass_mat = bpy.data.materials.get(base + "-glass")
if glass_mat is None:
glass_mat = bpy.data.materials.new(base + "-glass")
glass_mat.use_nodes = True
for me in all_mesh_datas:
# materials.clear() zeroes every polygon's material_index, so
# classify faces against the original slots first.
glass_face = [
p.material_index < len(me.materials) and
me.materials[p.material_index] is not None and
"glass" in me.materials[p.material_index].name.lower()
for p in me.polygons
]
me.materials.clear()
me.materials.append(merged_mat)
print("gen_vehicle: consolidated %d mesh data blocks onto material "
"'%s'" % (len(all_mesh_datas), base))
if any(glass_face):
me.materials.append(glass_mat)
for p, g in zip(me.polygons, glass_face):
p.material_index = 1 if g else 0
print("gen_vehicle: consolidated %d mesh data blocks onto materials "
"'%s'/'%s-glass'" % (len(all_mesh_datas), base, base))
# --- JSON fragment (Ogre coordinates) ---
smn, smx = mesh_bbox(root.data)
@@ -664,6 +689,18 @@ bpy.ops.ogre.export(filepath=scene_file,
IDMAP_MATERIAL_TEMPLATE = """// written by gen_vehicle.py — merged ID-map vehicle material
// (CAR_MODELING.md). Not a blender2ogre product; hand edits are
// overwritten by every export.
//
// Three materials per car:
// @BASE@ paint/bloom submeshes, fully opaque.
// @BASE@-glass near glass submesh (<10 m): alpha-blended,
// depth_write OFF so it lands in OGRE's
// transparent collection and can never
// depth-block the interior/characters behind it
// (they render first, the glass blends over).
// @BASE@-glass-opaque far glass submesh (>=10 m): solid.
// The demo's SedanGlassLodListener swaps only the -glass submeshes
// between the near and far variants; paint submeshes always keep
// @BASE@.
material @BASE@
{
receive_shadows on
@@ -672,7 +709,42 @@ material @BASE@
pass
{
cull_hardware clockwise
depth_write on
vertex_program_ref VehicleIdMapVP
{
}
fragment_program_ref VehicleIdMapFP
{
param_named glassAlpha float 1.0
}
texture_unit
{
texture @BASE@-atlas.png
tex_address_mode wrap
filtering linear linear none
}
texture_unit
{
texture @BASE@-idmap.png
tex_address_mode clamp
filtering point point none
}
}
}
}
material @BASE@-glass
{
receive_shadows on
technique
{
pass
{
cull_hardware clockwise
depth_write off
scene_blend alpha_blend
vertex_program_ref VehicleIdMapVP
@@ -701,7 +773,7 @@ material @BASE@
}
}
material @BASE@-opaque
material @BASE@-glass-opaque
{
receive_shadows on
technique
@@ -742,8 +814,11 @@ with open(mat_path, "w") as f:
f.write(IDMAP_MATERIAL_TEMPLATE.replace("@BASE@", base))
print("gen_vehicle: wrote merged material %s" % mat_path)
# Drop the stale split-material files from pre-ID-map exports so the
# resource dir does not accumulate them.
# blender2ogre also writes a fixed-function <base>-glass.material for
# the glass material slot; drop it (and the stale pre-ID-map split
# files) — the template above defines every <base>* material and a
# duplicate definition from another script would shadow it depending
# on resource parse order.
for suffix in ("-color", "-color-bloom", "-glass"):
stale = os.path.join(outdir, base + suffix + ".material")
if os.path.exists(stale):