build: baked lighting pass on kit_demo test map

Restructured kit_demo.tscn from linear demo to enclosed 5.12x5.12 room:
- 2x2 floor tiles, 8 wall segments (including doorway + window)
- Pillar at room center, beam overhead, accent panels
- West/east walls rotated 90° Y for proper enclosure

Lighting infrastructure:
- WorldEnvironment with ambient light
- DirectionalLight3D (sun key light, 45° angle, shadows enabled)
- OmniLight3D (warm interior fill light)
- ReflectionProbe (box projection, room-sized extents)
- LightmapGI (Quality=High, bounces=3, denoiser=true)
- Tool script prints bake status in editor

Scripts:
- bake_lighting.gd: validates LightmapGI config from CLI
- validate_scene.gd: full scene validation (17 checks, all pass)

Note: Godot 4.7 removed LightmapGI.bake() from runtime API.
Baking must be done in the editor: select LightmapGI node > Bake Lightmap.
This commit is contained in:
2026-07-01 00:28:55 -04:00
parent aa0b80b570
commit 16e062c739
4 changed files with 188 additions and 99 deletions
+55 -53
View File
@@ -1,70 +1,72 @@
extends Node
extends SceneTree
func _ready():
print("=== LightmapGI Baked Lighting Baker ===")
print("Opening scene: res://assets/scenes/modular/kit_demo.tscn")
# Check LightmapGI config on kit_demo.tscn scene.
# No bake possible at runtime in Godot 4.7 — LightmapGI baking is editor-only.
# This script validates that the scene is configured correctly for baking.
#
# Usage:
# xvfb-run godot --display-driver x11 --rendering-driver opengl3 \
# --audio-driver Dummy --script scripts/bake_lighting.gd
#
# Bake from editor:
# Open kit_demo.tscn in Godot editor, select LightmapGI node,
# click "Bake Lightmap" in the Inspector toolbar.
const SCENE_PATH := "res://assets/scenes/modular/kit_demo.tscn"
func _init():
print("=== LightmapGI Config Check ===")
print("Note: Godot 4.7 LightmapGI baking is editor-only.")
print("This script validates configuration only.")
print("")
# Load the scene
var scene = ResourceLoader.load("res://assets/scenes/modular/kit_demo.tscn")
var scene = ResourceLoader.load(SCENE_PATH)
if not scene:
print("ERROR: Failed to load scene!")
get_tree().quit(1)
printerr("ERROR: Failed to load scene!")
quit(1)
return
var instance = scene.instantiate()
get_tree().root.add_child(instance)
root.add_child(instance)
# Find the LightmapGI node
var lightmap = instance.find_child("LightmapGI", true, false)
if not lightmap:
print("ERROR: No LightmapGI node found in scene!")
get_tree().quit(1)
var lightmap = _find_node(instance, "LightmapGI")
if not lightmap or not (lightmap is LightmapGI):
printerr("ERROR: LightmapGI node not found!")
quit(1)
return
if not lightmap is LightmapGI:
print("ERROR: Found node 'LightmapGI' is not a LightmapGI type!")
get_tree().quit(1)
return
print("LightmapGI found. Configuration:")
print("LightmapGI configuration:")
match lightmap.quality:
0: print(" Quality: Low")
1: print(" Quality: Medium")
2: print(" Quality: High")
3: print(" Quality: Ultra")
print(" Bounces: ", lightmap.bounces)
print(" Texel Scale: ", lightmap.texel_scale)
print(" Texel Subdiv: ", lightmap.texel_subdiv)
print(" Max Texture Size: ", lightmap.max_texture_size)
print(" Interior: ", lightmap.interior)
# Start baking
print(" Denoiser: ", lightmap.is_using_denoiser())
print("")
print("=== Starting LightmapGI bake ===")
print("(This may take a while...)")
# Wait one frame for scene to fully initialize
await get_tree().process_frame
var result = lightmap.bake(instance, null)
if result != OK:
print("ERROR: Lightmap bake failed with error code: ", result)
get_tree().quit(1)
return
if lightmap.light_data != null:
print("LightmapGI has baked data!")
else:
print("LightmapGI NOT YET BAKED.")
print("To bake: Open scene in Godot editor > Select LightmapGI >")
print("click 'Bake Lightmap' button in the Inspector toolbar.")
print("")
print("Or use the Godot editor menu: Scene > Bake LightmapGI")
print("")
print("=== Lightmap bake successful! ===")
# Save the scene with baked data
print("Saving scene with baked lightmap data...")
var packed = PackedScene.new()
packed.pack(instance)
var save_result = ResourceSaver.save(packed, "res://assets/scenes/modular/kit_demo.tscn")
if save_result != OK:
print("ERROR: Failed to save scene! Error code: ", save_result)
get_tree().quit(1)
return
print("Scene saved successfully!")
print("Baked lighting data is now embedded in kit_demo.tscn")
print("(The LightmapGI node's light_data property contains the baked data)")
print("")
print("=== Done ===")
get_tree().quit(0)
print("=== Config check complete ===")
quit(0)
func _find_node(parent: Node, name: String) -> Node:
for child in parent.get_children():
if child.name == name:
return child
var found = _find_node(child, name)
if found:
return found
return null
+101
View File
@@ -0,0 +1,101 @@
extends SceneTree
# Validate kit_demo.tscn scene structure.
# Usage: godot --display-driver x11 --rendering-driver opengl3 --script scripts/validate_scene.gd
func _init():
print("=== Validating kit_demo.tscn ===")
var scene = ResourceLoader.load("res://assets/scenes/modular/kit_demo.tscn")
if not scene:
printerr("ERROR: Could not load kit_demo.tscn!")
quit(1)
return
print("Scene loaded successfully.")
var instance = scene.instantiate()
if not instance:
printerr("ERROR: Could not instantiate scene!")
quit(1)
return
root.add_child(instance)
print("Scene instantiated successfully.")
print("")
# Validate key nodes
var checks = _check_nodes(instance)
# Print results
var passed = 0
var failed = 0
for check in checks:
var status = "" if check[1] else ""
if check[1]:
passed += 1
else:
failed += 1
print(" ", status, " ", check[0])
# General info
var node_count = _count_nodes(instance)
print("")
print("Scene stats:")
print(" Total nodes: ", node_count)
print(" Checks passed: ", passed)
print(" Checks failed: ", failed)
print("")
if failed > 0:
printerr("Validation FAILED — ", failed, " check(s) failed.")
quit(1)
else:
print("Validation PASSED.")
quit(0)
func _check_nodes(root_node: Node) -> Array:
var r = []
# Built-in lighting nodes
r.append(["WorldEnvironment (Environment+Sky)", root_node.find_child("WorldEnvironment", true, false) != null])
r.append(["DirectionalLight3D (SunLight)", root_node.find_child("SunLight", true, false) != null])
r.append(["OmniLight3D (FillLight)", root_node.find_child("FillLight", true, false) != null])
r.append(["ReflectionProbe", root_node.find_child("ReflectionProbe", true, false) != null])
r.append(["LightmapGI", root_node.find_child("LightmapGI", true, false) != null])
# Lightmap config
var lm = root_node.find_child("LightmapGI", true, false)
if lm:
r.append(["LightmapGI.bounces = 3", lm.bounces == 3])
r.append(["LightmapGI.interior = true", lm.interior == true])
r.append(["LightmapGI.use_denoiser = true", lm.use_denoiser == true])
r.append(["LightmapGI.texel_scale = 1.0", abs(lm.texel_scale - 1.0) < 0.01])
# Module pieces (floor, walls, pillar, beam, accent panels)
r.append(["Floor tiles (4 pieces, one ceramic)", _count_instances(root_node, "Floor") >= 4])
r.append(["Wall segments (8 total)", _count_instances(root_node, "Wall") + _count_instances(root_node, "South") + _count_instances(root_node, "North") + _count_instances(root_node, "East") + _count_instances(root_node, "West") >= 8])
r.append(["Pillar (center)", root_node.find_child("Pillar", true, false) != null])
r.append(["Beam (overhead)", root_node.find_child("Beam", true, false) != null])
r.append(["AccentRed panel", root_node.find_child("AccentRed", true, false) != null])
r.append(["AccentBlue panel", root_node.find_child("AccentBlue", true, false) != null])
r.append(["Doorway segment", root_node.find_child("SouthDoorway", true, false) != null])
r.append(["Window segment", root_node.find_child("NorthWindow", true, false) != null])
return r
func _count_nodes(parent: Node) -> int:
var count = 1
for child in parent.get_children():
count += _count_nodes(child)
return count
func _count_instances(parent: Node, name_prefix: String) -> int:
var count = 0
for child in parent.get_children():
if child.name.begins_with(name_prefix):
count += 1
return count