class_name StaticObjectNode extends Node3D ## A visual representation of a static world object within a chunk. ## ## This node is instantiated by [ChunkScene] for each static (non-dynamic) ## [WorldObject]. It renders the object's mesh (referenced by `asset_id`) at ## the object's transform. If the asset is not yet loaded, a placeholder mesh ## is shown. ## ## ## Structure ## ## ``` ## StaticObjectNode (Node3D) ## └── MeshInstance3D # the object's visual mesh ## ``` ## The world object data this node represents. var world_object: WorldObject = null ## The loaded mesh instance (or placeholder). var _mesh_instance: MeshInstance3D = null func _ready() -> void: if world_object == null: return _load_mesh() ## Load and attach the object's mesh. func _load_mesh() -> void: # 1. Try to load the asset directly if it exists on disk var asset_node := _try_load_asset(world_object.asset_id) if asset_node != null: add_child(asset_node) if asset_node is MeshInstance3D: _mesh_instance = asset_node as MeshInstance3D return # 2. Build a stylized, recognizable 3D representation based on catalog ID / asset ID var cat_id := _get_catalog_id() _build_procedural_object(cat_id) func _get_catalog_id() -> String: if world_object == null: return "" if world_object.properties is Dictionary and world_object.properties.has("catalog_id"): return str(world_object.properties["catalog_id"]).to_lower() return world_object.asset_id.get_file().get_basename().to_lower() func _try_load_asset(asset_id: String) -> Node: if asset_id.is_empty(): return null var path := "res://assets/%s" % asset_id if ResourceLoader.exists(path): var res := load(path) if res is PackedScene: return (res as PackedScene).instantiate() elif res is Mesh: var mi := MeshInstance3D.new() mi.name = "mesh" mi.mesh = res as Mesh return mi return null func _build_procedural_object(cat_id: String) -> void: var container := Node3D.new() container.name = "visuals" add_child(container) if "lamp" in cat_id: # Lamp post: dark iron vertical pole + glowing lantern + omni light var pole := MeshInstance3D.new() pole.name = "pole" var pole_mesh := CylinderMesh.new() pole_mesh.height = 3.2 pole_mesh.top_radius = 0.07 pole_mesh.bottom_radius = 0.1 var iron_mat := StandardMaterial3D.new() iron_mat.albedo_color = Color(0.2, 0.22, 0.25) iron_mat.metallic = 0.85 iron_mat.roughness = 0.35 pole_mesh.material = iron_mat pole.mesh = pole_mesh pole.position = Vector3(0, 1.6, 0) container.add_child(pole) _mesh_instance = pole var cap := MeshInstance3D.new() cap.name = "cap" var cap_mesh := CylinderMesh.new() cap_mesh.height = 0.15 cap_mesh.top_radius = 0.05 cap_mesh.bottom_radius = 0.3 cap_mesh.material = iron_mat cap.mesh = cap_mesh cap.position = Vector3(0, 3.2, 0) container.add_child(cap) var lantern := MeshInstance3D.new() lantern.name = "lantern" var lantern_mesh := BoxMesh.new() lantern_mesh.size = Vector3(0.32, 0.45, 0.32) var glow_mat := StandardMaterial3D.new() glow_mat.albedo_color = Color(1.0, 0.95, 0.7) glow_mat.emission_enabled = true glow_mat.emission = Color(1.0, 0.9, 0.4) glow_mat.emission_energy_multiplier = 4.0 lantern_mesh.material = glow_mat lantern.mesh = lantern_mesh lantern.position = Vector3(0, 2.9, 0) container.add_child(lantern) var light := OmniLight3D.new() light.name = "light" light.light_color = Color(1.0, 0.93, 0.72) light.light_energy = 1.6 light.omni_range = 10.0 light.position = Vector3(0, 2.9, 0) container.add_child(light) var col := _create_static_collision() var col_shape := CollisionShape3D.new() var cyl := CylinderShape3D.new() cyl.height = 3.2 cyl.radius = 0.2 col_shape.shape = cyl col_shape.position = Vector3(0, 1.6, 0) col.add_child(col_shape) container.add_child(col) elif "crate" in cat_id: var crate := MeshInstance3D.new() crate.name = "crate" var crate_mesh := BoxMesh.new() crate_mesh.size = Vector3(1.0, 1.0, 1.0) var wood_mat := StandardMaterial3D.new() wood_mat.albedo_color = Color(0.55, 0.36, 0.18) wood_mat.roughness = 0.8 crate_mesh.material = wood_mat crate.mesh = crate_mesh crate.position = Vector3(0, 0.5, 0) container.add_child(crate) _mesh_instance = crate var col := _create_static_collision() var col_shape := CollisionShape3D.new() var box := BoxShape3D.new() box.size = Vector3(1.0, 1.0, 1.0) col_shape.shape = box col_shape.position = Vector3(0, 0.5, 0) col.add_child(col_shape) container.add_child(col) elif "rock" in cat_id: var rock := MeshInstance3D.new() rock.name = "rock" var rock_mesh := SphereMesh.new() rock_mesh.radius = 0.65 rock_mesh.height = 1.0 rock_mesh.radial_segments = 8 rock_mesh.rings = 6 var stone_mat := StandardMaterial3D.new() stone_mat.albedo_color = Color(0.42, 0.44, 0.46) stone_mat.roughness = 0.9 rock_mesh.material = stone_mat rock.mesh = rock_mesh rock.position = Vector3(0, 0.5, 0) container.add_child(rock) _mesh_instance = rock var col := _create_static_collision() var col_shape := CollisionShape3D.new() var sphere := SphereShape3D.new() sphere.radius = 0.65 col_shape.shape = sphere col_shape.position = Vector3(0, 0.5, 0) col.add_child(col_shape) container.add_child(col) elif "tree" in cat_id: var trunk := MeshInstance3D.new() trunk.name = "trunk" var trunk_mesh := CylinderMesh.new() trunk_mesh.height = 1.4 trunk_mesh.top_radius = 0.2 trunk_mesh.bottom_radius = 0.25 var bark_mat := StandardMaterial3D.new() bark_mat.albedo_color = Color(0.35, 0.22, 0.12) trunk_mesh.material = bark_mat trunk.mesh = trunk_mesh trunk.position = Vector3(0, 0.7, 0) container.add_child(trunk) var leaves1 := MeshInstance3D.new() leaves1.name = "leaves1" var leaves1_mesh := CylinderMesh.new() leaves1_mesh.height = 2.0 leaves1_mesh.top_radius = 0.1 leaves1_mesh.bottom_radius = 1.4 var leaves_mat := StandardMaterial3D.new() leaves_mat.albedo_color = Color(0.12, 0.38, 0.15) leaves1_mesh.material = leaves_mat leaves1.mesh = leaves1_mesh leaves1.position = Vector3(0, 2.0, 0) container.add_child(leaves1) _mesh_instance = leaves1 var leaves2 := MeshInstance3D.new() leaves2.name = "leaves2" var leaves2_mesh := CylinderMesh.new() leaves2_mesh.height = 1.6 leaves2_mesh.top_radius = 0.0 leaves2_mesh.bottom_radius = 0.9 leaves2_mesh.material = leaves_mat leaves2.mesh = leaves2_mesh leaves2.position = Vector3(0, 3.2, 0) container.add_child(leaves2) var col := _create_static_collision() var col_shape := CollisionShape3D.new() var cyl := CylinderShape3D.new() cyl.height = 4.0 cyl.radius = 0.35 col_shape.shape = cyl col_shape.position = Vector3(0, 2.0, 0) col.add_child(col_shape) container.add_child(col) elif "chest" in cat_id: var chest := MeshInstance3D.new() chest.name = "chest" var chest_mesh := BoxMesh.new() chest_mesh.size = Vector3(1.0, 0.8, 0.6) var chest_mat := StandardMaterial3D.new() chest_mat.albedo_color = Color(0.48, 0.26, 0.12) chest_mesh.material = chest_mat chest.mesh = chest_mesh chest.position = Vector3(0, 0.4, 0) container.add_child(chest) _mesh_instance = chest var trim := MeshInstance3D.new() trim.name = "trim" var trim_mesh := BoxMesh.new() trim_mesh.size = Vector3(0.2, 0.25, 0.64) var gold_mat := StandardMaterial3D.new() gold_mat.albedo_color = Color(0.9, 0.75, 0.2) trim_mesh.material = gold_mat trim.mesh = trim_mesh trim.position = Vector3(0, 0.4, 0) container.add_child(trim) var col := _create_static_collision() var col_shape := CollisionShape3D.new() var box := BoxShape3D.new() box.size = Vector3(1.0, 0.8, 0.6) col_shape.shape = box col_shape.position = Vector3(0, 0.4, 0) col.add_child(col_shape) container.add_child(col) elif "spawn" in cat_id or (world_object != null and world_object.type == "spawn_point"): var pad := MeshInstance3D.new() pad.name = "pad" var pad_mesh := CylinderMesh.new() pad_mesh.height = 0.08 pad_mesh.top_radius = 1.2 pad_mesh.bottom_radius = 1.2 var spawn_mat := StandardMaterial3D.new() spawn_mat.albedo_color = Color(0.2, 0.75, 0.95) spawn_mat.emission_enabled = true spawn_mat.emission = Color(0.1, 0.6, 0.85) spawn_mat.emission_energy_multiplier = 2.0 pad_mesh.material = spawn_mat pad.mesh = pad_mesh pad.position = Vector3(0, 0.04, 0) container.add_child(pad) _mesh_instance = pad else: var mi := MeshInstance3D.new() mi.name = "mesh" mi.mesh = _make_placeholder_mesh() mi.position = Vector3(0, 0.5, 0) container.add_child(mi) _mesh_instance = mi var col := _create_static_collision() var col_shape := CollisionShape3D.new() var box := BoxShape3D.new() box.size = Vector3(1.0, 1.0, 1.0) col_shape.shape = box col_shape.position = Vector3(0, 0.5, 0) col.add_child(col_shape) container.add_child(col) func _create_static_collision() -> StaticBody3D: var sb := StaticBody3D.new() sb.name = "collision" sb.set_collision_layer_value(1, true) sb.set_collision_mask_value(1, true) return sb ## Attempt to load a mesh from the asset path. func _load_asset_mesh(asset_id: String) -> Mesh: if asset_id.is_empty(): return null var path := "res://assets/%s" % asset_id if ResourceLoader.exists(path): var res := load(path) if res is Mesh: return res as Mesh return null ## Create a simple placeholder box mesh when the real asset is unavailable. func _make_placeholder_mesh() -> Mesh: var mesh := BoxMesh.new() mesh.size = Vector3(1.0, 1.0, 1.0) return mesh ## Get the world object's display name. func get_display_name() -> String: if world_object == null: return "" return world_object.get_display_name()