feat: stream chunks in-game, render 3D catalog props, and ground chunk coordinates
checkpoint / checkpoint (push) Successful in 5m54s
checkpoint / checkpoint (push) Successful in 5m54s
This commit is contained in:
+14
-17
@@ -54,20 +54,16 @@ var is_dirty: bool = false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
# Position this chunk scene at its world-space origin
|
||||
global_position = bounds.position
|
||||
# Set up collision shape for the chunk bounds
|
||||
_setup_collision()
|
||||
# Position this chunk scene at its 3D world ground coordinates
|
||||
global_position = Vector3(
|
||||
float(coord.x * Chunk.CHUNK_SIZE_X),
|
||||
float(coord.z * Chunk.CHUNK_SIZE_Z),
|
||||
float(coord.y * Chunk.CHUNK_SIZE_Y)
|
||||
)
|
||||
|
||||
|
||||
func _setup_collision() -> void:
|
||||
# Add a collision shape for the chunk's bounding box
|
||||
var collision := CollisionShape3D.new()
|
||||
var shape := BoxShape3D.new()
|
||||
shape.size = bounds.size
|
||||
collision.shape = shape
|
||||
collision.name = "ChunkBounds"
|
||||
add_child(collision)
|
||||
pass
|
||||
|
||||
|
||||
## Add a tile layer to this chunk scene.
|
||||
@@ -91,9 +87,10 @@ func add_object(obj: WorldObject) -> void:
|
||||
func _add_static_object(obj: WorldObject) -> void:
|
||||
var static_obj := StaticObjectNode.new()
|
||||
static_obj.world_object = obj
|
||||
static_obj.position = obj.position
|
||||
static_obj.quaternion = Quaternion(obj.rotation.x, obj.rotation.y, obj.rotation.z, obj.rotation.w)
|
||||
static_obj.scale = obj.scale
|
||||
# Map chunk coordinates (X_ground, Y_ground, Z_height) to Godot 3D (X, Y_height, Z_ground)
|
||||
static_obj.position = Vector3(obj.position.x, obj.position.z, obj.position.y)
|
||||
static_obj.quaternion = Quaternion(obj.rotation.x, obj.rotation.z, obj.rotation.y, obj.rotation.w)
|
||||
static_obj.scale = Vector3(obj.scale.x, obj.scale.z, obj.scale.y)
|
||||
static_obj.name = "static_%s" % obj.id
|
||||
add_child(static_obj)
|
||||
|
||||
@@ -101,9 +98,9 @@ func _add_static_object(obj: WorldObject) -> void:
|
||||
func _add_dynamic_object(obj: WorldObject) -> void:
|
||||
var dynamic_obj := DynamicObjectNode.new()
|
||||
dynamic_obj.world_object = obj
|
||||
dynamic_obj.position = obj.position
|
||||
dynamic_obj.quaternion = Quaternion(obj.rotation.x, obj.rotation.y, obj.rotation.z, obj.rotation.w)
|
||||
dynamic_obj.scale = obj.scale
|
||||
dynamic_obj.position = Vector3(obj.position.x, obj.position.z, obj.position.y)
|
||||
dynamic_obj.quaternion = Quaternion(obj.rotation.x, obj.rotation.z, obj.rotation.y, obj.rotation.w)
|
||||
dynamic_obj.scale = Vector3(obj.scale.x, obj.scale.z, obj.scale.y)
|
||||
dynamic_obj.name = "dynamic_%s" % obj.id
|
||||
add_child(dynamic_obj)
|
||||
|
||||
|
||||
+92
-13
@@ -37,12 +37,65 @@ var _status_label: Label = null
|
||||
var _build_editor: InGameEditor = null
|
||||
|
||||
|
||||
var _stream_manager: ChunkStreamManager = null
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_show_build_stamp()
|
||||
if Session.editing:
|
||||
_open_editor_for_world()
|
||||
elif Session.active:
|
||||
_setup_networked()
|
||||
else:
|
||||
if Session.active:
|
||||
_setup_networked()
|
||||
_setup_chunk_streaming()
|
||||
|
||||
|
||||
func _setup_chunk_streaming() -> void:
|
||||
if Session.world_id == null or int(Session.world_id) <= 0:
|
||||
return
|
||||
if _stream_manager != null and is_instance_valid(_stream_manager):
|
||||
return
|
||||
|
||||
_stream_manager = ChunkStreamManager.new()
|
||||
_stream_manager.name = "ChunkStreamManager"
|
||||
_stream_manager.player = _player
|
||||
_stream_manager.streaming_radius = 2
|
||||
add_child(_stream_manager)
|
||||
|
||||
var chunk_source := HttpChunkSource.new()
|
||||
chunk_source.name = "HttpChunkSource"
|
||||
chunk_source.world_id = str(Session.world_id)
|
||||
chunk_source.room_slug = "main"
|
||||
chunk_source.stream_manager = _stream_manager
|
||||
_stream_manager.add_child(chunk_source)
|
||||
_stream_manager.set_server_rpc(chunk_source)
|
||||
_stream_manager.is_active = true
|
||||
|
||||
_stream_manager.chunk_loaded.connect(_on_chunk_loaded)
|
||||
_stream_manager._load_chunk(Vector3i.ZERO)
|
||||
|
||||
|
||||
func _on_chunk_loaded(_coord: Vector3i, chunk_scene: Node) -> void:
|
||||
if chunk_scene is ChunkScene:
|
||||
var cs := chunk_scene as ChunkScene
|
||||
if not cs.world_objects.is_empty() or not cs.tile_layers.is_empty():
|
||||
_hide_default_test_props()
|
||||
|
||||
|
||||
func _hide_default_test_props() -> void:
|
||||
var props := get_node_or_null("Level/Props")
|
||||
if props != null and props is Node3D:
|
||||
(props as Node3D).visible = false
|
||||
for child in props.get_children():
|
||||
if child is CollisionObject3D:
|
||||
(child as CollisionObject3D).set_collision_layer_value(1, false)
|
||||
(child as CollisionObject3D).set_collision_mask_value(1, false)
|
||||
var chest := get_node_or_null("Level/ScriptedChest")
|
||||
if chest != null and chest is Node3D:
|
||||
(chest as Node3D).visible = false
|
||||
if chest is CollisionObject3D:
|
||||
(chest as CollisionObject3D).set_collision_layer_value(1, false)
|
||||
(chest as CollisionObject3D).set_collision_mask_value(1, false)
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
@@ -89,7 +142,29 @@ func _open_build_mode() -> void:
|
||||
var room_slug := "main"
|
||||
var coord := Vector3i.ZERO
|
||||
|
||||
var model := ChunkEditorModel.create_empty(32, 32, world_id_str, room_slug, coord)
|
||||
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
||||
_player.set_physics_process(false)
|
||||
_player.set_process_unhandled_input(false)
|
||||
|
||||
var save_service: ChunkSaveService = null
|
||||
var model: ChunkEditorModel = null
|
||||
var base_revision := 0
|
||||
|
||||
if int(world_id_str) > 0:
|
||||
save_service = ChunkSaveService.new()
|
||||
save_service.world_id = world_id_str
|
||||
save_service.room_slug = room_slug
|
||||
add_child(save_service)
|
||||
|
||||
var fetch_result: Dictionary = await save_service.fetch_chunk(coord)
|
||||
if fetch_result.get("ok", false) and fetch_result.get("model") != null:
|
||||
model = fetch_result["model"]
|
||||
base_revision = int(fetch_result.get("revision", 0))
|
||||
|
||||
if model == null:
|
||||
model = ChunkEditorModel.create_empty(32, 32, world_id_str, room_slug, coord)
|
||||
base_revision = 0
|
||||
|
||||
var catalog := EditorObjectCatalog.new()
|
||||
var err := catalog.load_catalog()
|
||||
if err != OK:
|
||||
@@ -97,20 +172,12 @@ func _open_build_mode() -> void:
|
||||
var core := EditorCore.new(model, catalog)
|
||||
|
||||
_build_editor = InGameEditor.new(core)
|
||||
if int(world_id_str) > 0:
|
||||
var save_service := ChunkSaveService.new()
|
||||
save_service.world_id = world_id_str
|
||||
save_service.room_slug = room_slug
|
||||
add_child(save_service)
|
||||
_build_editor.setup_persistence(save_service, world_id_str, room_slug, coord, 0)
|
||||
if save_service != null:
|
||||
_build_editor.setup_persistence(save_service, world_id_str, room_slug, coord, base_revision)
|
||||
|
||||
_build_editor.closed.connect(_close_build_mode)
|
||||
add_child(_build_editor)
|
||||
|
||||
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
||||
_player.set_physics_process(false)
|
||||
_player.set_process_unhandled_input(false)
|
||||
|
||||
|
||||
## Shared teardown for both close paths (F2 again, or the editor's own
|
||||
## Close button/`closed` signal) -- safe to call twice: `queue_free()` on an
|
||||
@@ -127,6 +194,14 @@ func _close_build_mode() -> void:
|
||||
_player.set_process_unhandled_input(true)
|
||||
_player._refresh_mouse_mode()
|
||||
|
||||
if _stream_manager == null:
|
||||
_setup_chunk_streaming()
|
||||
elif is_instance_valid(_stream_manager):
|
||||
var source := _stream_manager.get_node_or_null("HttpChunkSource") as HttpChunkSource
|
||||
if source != null and Session.world_id != null and int(Session.world_id) > 0:
|
||||
source.world_id = str(Session.world_id)
|
||||
_stream_manager.force_reload_chunk(Vector3i.ZERO)
|
||||
|
||||
|
||||
## "Edit world" entry point from the world browser
|
||||
## (world_persistence_impl.md §6 D4 / §9 "Client/editor task"). Reached only
|
||||
@@ -198,6 +273,10 @@ func _leave_world() -> void:
|
||||
|
||||
## Close the ENet connection and stop it from calling us back mid-teardown.
|
||||
func _teardown_net() -> void:
|
||||
if _stream_manager != null and is_instance_valid(_stream_manager):
|
||||
_stream_manager.clear_all_chunks()
|
||||
_stream_manager.queue_free()
|
||||
_stream_manager = null
|
||||
if _net == null or not is_instance_valid(_net):
|
||||
return
|
||||
if _net.server_disconnected.is_connected(_on_server_lost):
|
||||
|
||||
+27
-8
@@ -263,9 +263,9 @@ func _build_chunk_collision(parent: Node3D, coord: Vector3i, data: ChunkDataDeco
|
||||
var body := StaticBody3D.new()
|
||||
body.name = "chunk_%d_%d_%d" % [coord.x, coord.y, coord.z]
|
||||
var chunk_origin := Vector3(
|
||||
coord.x * Chunk.CHUNK_SIZE_X,
|
||||
coord.y * Chunk.CHUNK_SIZE_Y,
|
||||
coord.z * Chunk.CHUNK_SIZE_Z
|
||||
float(coord.x * Chunk.CHUNK_SIZE_X),
|
||||
float(coord.z * Chunk.CHUNK_SIZE_Z),
|
||||
float(coord.y * Chunk.CHUNK_SIZE_Y)
|
||||
)
|
||||
|
||||
for layer in data.tile_layers:
|
||||
@@ -282,20 +282,39 @@ func _build_chunk_collision(parent: Node3D, coord: Vector3i, data: ChunkDataDeco
|
||||
var box := BoxShape3D.new()
|
||||
box.size = Vector3.ONE
|
||||
shape.shape = box
|
||||
shape.position = chunk_origin + Vector3(x + 0.5, y + 0.5, z + 0.5)
|
||||
shape.position = chunk_origin + Vector3(x + 0.5, z + 0.5, y + 0.5)
|
||||
body.add_child(shape)
|
||||
_collision_shapes_built += 1
|
||||
|
||||
for obj in data.objects:
|
||||
if obj.dynamic:
|
||||
continue # dynamic templates are spawnable entities, not static geometry
|
||||
if bool(obj.properties.get("walkable", true)):
|
||||
if obj.type == "spawn_point" or bool(obj.properties.get("walkable", false)):
|
||||
continue
|
||||
var shape := CollisionShape3D.new()
|
||||
var box := BoxShape3D.new()
|
||||
box.size = obj.scale
|
||||
shape.shape = box
|
||||
shape.position = obj.position
|
||||
var aid := str(obj.asset_id).to_lower()
|
||||
var cat_id := str(obj.properties.get("catalog_id", "")).to_lower()
|
||||
if "lamp" in aid or "lamp" in cat_id:
|
||||
box.size = Vector3(0.5, 3.2, 0.5)
|
||||
shape.shape = box
|
||||
shape.position = chunk_origin + Vector3(obj.position.x, obj.position.z + 1.6, obj.position.y)
|
||||
elif "tree" in aid or "tree" in cat_id:
|
||||
box.size = Vector3(0.8, 4.0, 0.8)
|
||||
shape.shape = box
|
||||
shape.position = chunk_origin + Vector3(obj.position.x, obj.position.z + 2.0, obj.position.y)
|
||||
elif "chest" in aid or "chest" in cat_id:
|
||||
box.size = Vector3(1.0, 0.8, 0.6)
|
||||
shape.shape = box
|
||||
shape.position = chunk_origin + Vector3(obj.position.x, obj.position.z + 0.4, obj.position.y)
|
||||
elif "rock" in aid or "rock" in cat_id:
|
||||
box.size = Vector3(1.2, 1.0, 1.2)
|
||||
shape.shape = box
|
||||
shape.position = chunk_origin + Vector3(obj.position.x, obj.position.z + 0.5, obj.position.y)
|
||||
else:
|
||||
box.size = Vector3(obj.scale.x, obj.scale.z, obj.scale.y)
|
||||
shape.shape = box
|
||||
shape.position = chunk_origin + Vector3(obj.position.x, obj.position.z + obj.scale.y * 0.5, obj.position.y)
|
||||
body.add_child(shape)
|
||||
_collision_shapes_built += 1
|
||||
|
||||
|
||||
@@ -29,16 +29,282 @@ func _ready() -> void:
|
||||
|
||||
## Load and attach the object's mesh.
|
||||
func _load_mesh() -> void:
|
||||
_mesh_instance = MeshInstance3D.new()
|
||||
_mesh_instance.name = "mesh"
|
||||
# 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
|
||||
|
||||
# Try to load the asset by its id (path). Fall back to a placeholder.
|
||||
var mesh := _load_asset_mesh(world_object.asset_id)
|
||||
if mesh == null:
|
||||
mesh = _make_placeholder_mesh()
|
||||
# 2. Build a stylized, recognizable 3D representation based on catalog ID / asset ID
|
||||
var cat_id := _get_catalog_id()
|
||||
_build_procedural_object(cat_id)
|
||||
|
||||
_mesh_instance.mesh = mesh
|
||||
add_child(_mesh_instance)
|
||||
|
||||
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.
|
||||
@@ -49,7 +315,7 @@ func _load_asset_mesh(asset_id: String) -> Mesh:
|
||||
if ResourceLoader.exists(path):
|
||||
var res := load(path)
|
||||
if res is Mesh:
|
||||
return res
|
||||
return res as Mesh
|
||||
return null
|
||||
|
||||
|
||||
|
||||
@@ -74,17 +74,12 @@ func _create_mesh_for_tile(tile_def: TileDef) -> void:
|
||||
_mesh_instances[tile_def.id] = mesh_instance
|
||||
|
||||
|
||||
## Convert a tile grid coordinate to a world-space position (center of tile).
|
||||
## Convert a tile grid coordinate to a chunk-local 3D position (center of tile).
|
||||
func _tile_to_world(x: int, y: int, z: int) -> Vector3:
|
||||
var origin := Vector3(
|
||||
chunk_coord.x * tile_layer.width,
|
||||
chunk_coord.y * tile_layer.height,
|
||||
chunk_coord.z * tile_layer.depth
|
||||
)
|
||||
return origin + Vector3(
|
||||
return Vector3(
|
||||
x * TILE_SIZE + TILE_SIZE * 0.5,
|
||||
y * TILE_SIZE + TILE_SIZE * 0.5,
|
||||
z * TILE_SIZE + TILE_SIZE * 0.5
|
||||
z * TILE_SIZE + TILE_SIZE * 0.5,
|
||||
y * TILE_SIZE + TILE_SIZE * 0.5
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user