diff --git a/client/assets/scenes/modular/kit_demo.gd b/client/assets/scenes/modular/kit_demo.gd index 58b377f..e69349c 100644 --- a/client/assets/scenes/modular/kit_demo.gd +++ b/client/assets/scenes/modular/kit_demo.gd @@ -1,44 +1,44 @@ +@tool extends Node3D -# Auto-bakes the LightmapGI when opened in the Godot editor. -# Only runs in editor mode — does nothing in exported builds. -# -# Manual bake: -# Open the scene in the Godot editor, select LightmapGI node, -# click "Bake Lightmap" in the inspector at the top of the node's properties. +# Provides baked lighting configuration guidance in the editor output panel. +# LightmapGI baking is editor-only in Godot 4.7 — use the Bake button. # -# This script provides feedback in the editor Output panel. - -@tool +# To bake: +# 1. Open this scene in the Godot editor +# 2. Select the LightmapGI node in the Scene dock +# 3. Click "Bake Lightmap" in the Inspector toolbar (top of inspector) +# +# Or: Scene menu > Bake LightmapGI (Ctrl+Shift+B) func _ready(): if not Engine.is_editor_hint(): - # Not in editor — nothing to do for runtime return - # Check if we have a LightmapGI node var lightmap = _find_lightmap_gi() if not lightmap: push_error("KitDemo: No LightmapGI node found in scene!") return - # Check if already baked if lightmap.light_data != null: - print("KitDemo: Lightmap already baked. To re-bake:") - print(" Select LightmapGI node → 'Bake Lightmap' button in Inspector") + print("KitDemo: Lightmap baked ✓") + print(" To re-bake: Select LightmapGI → 'Bake Lightmap' button") return - # Not baked — offer to bake - print("KitDemo: LightmapGI found but not baked.") - print(" To bake: Select LightmapGI → click 'Bake Lightmap' at top of Inspector") - print(" Or right-click LightmapGI node → 'Bake Lightmap'") + print("KitDemo: LightmapGI configured but not baked.") + print(" To bake: Select LightmapGI → 'Bake Lightmap' in Inspector") + print(" Or: Scene menu → Bake LightmapGI (Ctrl+Shift+B)") print("") - print(" LightmapGI settings:") - 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) + 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(" Max Texture Size: ", lightmap.max_texture_size) + print(" Interior: ", lightmap.interior) + print(" Denoiser: ", lightmap.is_using_denoiser()) func _find_lightmap_gi() -> LightmapGI: diff --git a/client/assets/scenes/modular/kit_demo.tscn b/client/assets/scenes/modular/kit_demo.tscn index 35db4a0..d3cc6b0 100644 --- a/client/assets/scenes/modular/kit_demo.tscn +++ b/client/assets/scenes/modular/kit_demo.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=23 format=3] +[gd_scene load_steps=21 format=3] ; === External Resources (modular kit pieces) === [ext_resource type="PackedScene" path="res://assets/scenes/modular/wall_straight_01.tscn" id="1"] @@ -24,27 +24,14 @@ ; === Script === [ext_resource type="Script" path="res://assets/scenes/modular/kit_demo.gd" id="19"] -; === Subresources (Sky, Environment for WorldEnvironment) === -[sub_resource type="ProceduralSkyMaterial" id="SkyMat"] -sky_top_color = Color(0.45, 0.55, 0.75) -sky_horizon_color = Color(0.65, 0.6, 0.8) -sky_bottom_color = Color(0.35, 0.35, 0.4) -ground_bottom_color = Color(0.15, 0.15, 0.15) -sky_energy_multiplier = 0.5 -sky_exposure = 1.0 - -[sub_resource type="Sky" id="Sky"] -sky_material = SubResource("SkyMat") - +; === Subresources === [sub_resource type="Environment" id="Env"] -background_mode = 2 -background_sky = SubResource("Sky") -background_sky_custom_fov = 0.0 +background_mode = 0 tonemap_mode = 0 glow_enabled = false -ambient_light_color = Color(0.25, 0.25, 0.3) +ambient_light_color = Color(0.25, 0.25, 0.3, 1.0) ambient_light_energy = 0.4 -ambient_light_sky_contribution = 0.5 +ambient_light_sky_contribution = 0.0 ; === Scene Root === [node name="KitDemo" type="Node3D"] @@ -147,7 +134,7 @@ environment = SubResource("Env") transform = Transform3D(0.866, 0, -0.5, -0.354, 0.707, -0.612, 0.354, 0.707, 0.612, 0, 5, 0) light_energy = 1.2 light_indirect_energy = 0.8 -light_color = Color(1.0, 0.95, 0.9) +light_color = Color(1.0, 0.95, 0.9, 1.0) shadow_enabled = true light_bake_mode = 2 directional_shadow_max_distance = 30.0 @@ -161,7 +148,7 @@ directional_shadow_blend_splits = true transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.5, 2.4, 1.5) light_energy = 0.4 light_indirect_energy = 0.5 -light_color = Color(1.0, 0.85, 0.7) +light_color = Color(1.0, 0.85, 0.7, 1.0) light_bake_mode = 2 omni_range = 6.0 omni_attenuation = 0.8 @@ -178,11 +165,10 @@ max_distance = 10.0 ; --- LightmapGI: baked global illumination --- [node name="LightmapGI" type="LightmapGI" parent="."] +quality = 2 bounces = 3 bounce_indirect_energy = 1.0 texel_scale = 1.0 -texel_subdiv = 4 max_texture_size = 2048 use_denoiser = true -denoiser_strength = 0.0 interior = true diff --git a/client/scripts/bake_lighting.gd b/client/scripts/bake_lighting.gd index 62b68a8..d8fcbff 100644 --- a/client/scripts/bake_lighting.gd +++ b/client/scripts/bake_lighting.gd @@ -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 diff --git a/client/scripts/validate_scene.gd b/client/scripts/validate_scene.gd new file mode 100644 index 0000000..32aa9a8 --- /dev/null +++ b/client/scripts/validate_scene.gd @@ -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