diff --git a/assets/editor/object_catalog.json b/assets/editor/object_catalog.json index 1b11de4..473a6cc 100644 --- a/assets/editor/object_catalog.json +++ b/assets/editor/object_catalog.json @@ -36,6 +36,12 @@ "display_name": "Buffalo Chip Coin", "category": "props" }, + "bison": { + "asset_id": "props/bison.glb", + "type": "prop", + "display_name": "Bison", + "category": "nature" + }, "spawn_point": { "asset_id": "", "type": "spawn_point", diff --git a/assets/props/bison.glb b/assets/props/bison.glb new file mode 100644 index 0000000..a6aae27 Binary files /dev/null and b/assets/props/bison.glb differ diff --git a/assets/props/bison.glb.import b/assets/props/bison.glb.import new file mode 100644 index 0000000..bfab3b6 --- /dev/null +++ b/assets/props/bison.glb.import @@ -0,0 +1,42 @@ +[remap] + +importer="scene" +importer_version=1 +type="PackedScene" +uid="uid://wa7kf5fvrdh1" +path="res://.godot/imported/bison.glb-caef5252d8e08046747c7628b6609f07.scn" + +[deps] + +source_file="res://assets/props/bison.glb" +dest_files=["res://.godot/imported/bison.glb-caef5252d8e08046747c7628b6609f07.scn"] + +[params] + +nodes/root_type="" +nodes/root_name="" +nodes/root_script=null +nodes/apply_root_scale=true +nodes/root_scale=1.0 +nodes/import_as_skeleton_bones=false +nodes/use_name_suffixes=true +nodes/use_node_type_suffixes=true +meshes/ensure_tangents=true +meshes/generate_lods=true +meshes/create_shadow_meshes=true +meshes/light_baking=1 +meshes/lightmap_texel_size=0.2 +meshes/force_disable_compression=false +skins/use_named_skins=true +animation/import=true +animation/fps=30 +animation/trimming=false +animation/remove_immutable_tracks=true +animation/import_rest_as_RESET=false +import_script/path="" +materials/extract=0 +materials/extract_format=0 +materials/extract_path="" +_subresources={} +gltf/naming_version=2 +gltf/embedded_image_handling=1 diff --git a/assets/props/bison_04bizontexture.jpg b/assets/props/bison_04bizontexture.jpg new file mode 100644 index 0000000..48a2c70 Binary files /dev/null and b/assets/props/bison_04bizontexture.jpg differ diff --git a/assets/props/bison_04bizontexture.jpg.import b/assets/props/bison_04bizontexture.jpg.import new file mode 100644 index 0000000..bb8e87f --- /dev/null +++ b/assets/props/bison_04bizontexture.jpg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://oemjaiyc1jor" +path="res://.godot/imported/bison_04bizontexture.jpg-ee62bcbf71190dd9c3260c488d0f5226.ctex" +metadata={ +"vram_texture": false +} +generator_parameters={ +"md5": "62ac14285e2f49777af903582f16d021" +} + +[deps] + +source_file="res://assets/props/bison_04bizontexture.jpg" +dest_files=["res://.godot/imported/bison_04bizontexture.jpg-ee62bcbf71190dd9c3260c488d0f5226.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scripts/editor/object_catalog.gd b/scripts/editor/object_catalog.gd index f45d969..08006cb 100644 --- a/scripts/editor/object_catalog.gd +++ b/scripts/editor/object_catalog.gd @@ -48,11 +48,21 @@ func is_loaded() -> bool: func resolve(id: String) -> Dictionary: if not _loaded: return {} - return _catalog.get(id, {}) + if _catalog.has(id): + return _catalog[id] + if (id.contains("bizon") or id.contains("bison")) and _catalog.has("bison"): + return _catalog["bison"] + return {} func has_object(id: String) -> bool: - return _loaded and _catalog.has(id) + if not _loaded: + return false + if _catalog.has(id): + return true + if (id.contains("bizon") or id.contains("bison")) and _catalog.has("bison"): + return true + return false func get_all_ids() -> Array[String]: diff --git a/scripts/editor/ui/palette_panel.gd b/scripts/editor/ui/palette_panel.gd index 842d146..d8bb8ea 100644 --- a/scripts/editor/ui/palette_panel.gd +++ b/scripts/editor/ui/palette_panel.gd @@ -115,6 +115,8 @@ func _setup_import_ui() -> void: sample_option.set_item_metadata(4, {"path": "res://assets/props/buffalo_chip_coin.glb", "name": "Buffalo Chip Coin"}) sample_option.add_item("Small Crate", 5) sample_option.set_item_metadata(5, {"path": "res://assets/props/crate_small.glb", "name": "Small Crate"}) + sample_option.add_item("Bison", 6) + sample_option.set_item_metadata(6, {"path": "res://assets/props/bison.glb", "name": "Bison"}) sample_option.size_flags_horizontal = Control.SIZE_EXPAND_FILL sample_option.item_selected.connect(func(idx: int): if idx > 0: diff --git a/scripts/smoke_test_bison_model.gd b/scripts/smoke_test_bison_model.gd new file mode 100644 index 0000000..3cacf37 --- /dev/null +++ b/scripts/smoke_test_bison_model.gd @@ -0,0 +1,37 @@ +extends SceneTree +## Test loading and instantiating bison.glb prop + +func _init() -> void: + print("=== Smoke Test: Bison Model ===") + var path := "res://assets/props/bison.glb" + if not FileAccess.file_exists(path): + printerr("FAIL: bison.glb does not exist on disk at ", path) + quit(1) + return + + var res := load(path) + if res == null: + printerr("FAIL: Could not load ", path) + quit(1) + return + + print("PASS: Loaded PackedScene: ", res) + if res is PackedScene: + var inst = (res as PackedScene).instantiate() + if inst == null: + printerr("FAIL: Could not instantiate bison scene") + quit(1) + return + print("PASS: Instantiated bison: ", inst.name, " (", inst.get_class(), ")") + _print_node(inst, 0) + inst.queue_free() + + print("=== Bison Model Smoke Test Passed ===") + quit(0) + + +func _print_node(n: Node, depth: int) -> void: + print(" ".repeat(depth), "- ", n.name, " [", n.get_class(), "]") + for c in n.get_children(): + _print_node(c, depth + 1) + diff --git a/scripts/smoke_test_bison_palette.gd b/scripts/smoke_test_bison_palette.gd new file mode 100644 index 0000000..4deaa93 --- /dev/null +++ b/scripts/smoke_test_bison_palette.gd @@ -0,0 +1,103 @@ +extends SceneTree +## Headless smoke test verifying Bison model is present in the editor palette +## and loads properly via StaticObjectNode and EditorCore. + +const ChunkEditorModelClass := preload("res://scripts/editor/chunk_editor_model.gd") +const EditorObjectCatalogClass := preload("res://scripts/editor/object_catalog.gd") +const EditorCoreClass := preload("res://scripts/editor/editor_core.gd") +const PalettePanelClass := preload("res://scripts/editor/ui/palette_panel.gd") +const StaticObjectNodeClass := preload("res://scripts/static_object_node.gd") +const WorldObjectClass := preload("res://scripts/world_object.gd") + +var passed := 0 +var failed := 0 + +func _check(label: String, cond: bool) -> void: + if cond: + passed += 1 + print(" PASS: ", label) + else: + failed += 1 + printerr(" FAIL: ", label) + + +func _initialize() -> void: + await _run() + + +func _run() -> void: + print("=== Running Bison Model in Editor Palette Smoke Test ===") + var root := Node.new() + root.name = "TestRoot" + get_root().add_child(root) + + # 1. Check catalog loading + var catalog := EditorObjectCatalogClass.new() + var err := catalog.load_catalog() + _check("Loaded EditorObjectCatalog from JSON", err == OK) + + # 2. Check bison in default ids + var all_ids := catalog.get_all_ids() + _check("Catalog contains 'bison'", all_ids.has("bison")) + + var bison_entry := catalog.resolve("bison") + _check("Bison entry has asset_id props/bison.glb", bison_entry.get("asset_id") == "props/bison.glb") + _check("Bison entry display_name is Bison", bison_entry.get("display_name") == "Bison") + _check("Bison asset file exists in project", FileAccess.file_exists("res://assets/props/bison.glb")) + + # 3. Check alias resolution for legacy custom import id + var alias_entry := catalog.resolve("custom_bizon_by_madtrollstudio_rqk_lny_pnfx") + _check("Legacy custom id resolves to bison", alias_entry.get("asset_id") == "props/bison.glb") + + # 4. Check PalettePanel populates bison in list + var palette := PalettePanelClass.new() + root.add_child(palette) + await process_frame + palette.set_catalog(catalog) + + var found_in_palette := false + for i in range(palette._list.item_count): + var item_text := palette._list.get_item_text(i) + if item_text == "Bison": + found_in_palette = true + break + _check("PalettePanel list contains 'Bison'", found_in_palette) + + # 5. Place bison in a chunk model + var model := ChunkEditorModelClass.new() + var core := EditorCoreClass.new(model, catalog) + var obj_id := core.place_object("bison", Vector3(10.0, 0.0, 15.0)) + _check("Placed bison via EditorCore", not obj_id.is_empty()) + + var placed_obj := model.find_object(obj_id) + _check("Placed object has props/bison.glb", placed_obj != null and placed_obj.asset_id == "props/bison.glb") + + # 6. StaticObjectNode with default props/bison.glb + var wo1 := WorldObjectClass.new() + wo1.id = "test_bison_1" + wo1.type = "prop" + wo1.asset_id = "props/bison.glb" + wo1.position = Vector3(10.0, 0.0, 15.0) + var node1 := StaticObjectNodeClass.new() + node1.world_object = wo1 + root.add_child(node1) + await process_frame + _check("StaticObjectNode loaded visuals for props/bison.glb", node1.get_child_count() > 0) + + # 7. StaticObjectNode with legacy user:// path as saved in World 6 + var wo2 := WorldObjectClass.new() + wo2.id = "test_bison_legacy" + wo2.type = "prop" + wo2.asset_id = "user://custom_assets/Bizon by madtrollstudio - RqkLNYPnfx.glb" + wo2.properties = {"catalog_id": "custom_bizon_by_madtrollstudio_rqk_lny_pnfx"} + var node2 := StaticObjectNodeClass.new() + node2.world_object = wo2 + root.add_child(node2) + await process_frame + _check("StaticObjectNode loaded visuals for legacy world chunk bison", node2.get_child_count() > 0) + + print("Bison Palette Smoke Test complete. Passed: %d, Failed: %d" % [passed, failed]) + if failed > 0: + quit(1) + else: + quit(0) diff --git a/scripts/static_object_node.gd b/scripts/static_object_node.gd index 70dfefb..b5483d7 100644 --- a/scripts/static_object_node.gd +++ b/scripts/static_object_node.gd @@ -61,6 +61,8 @@ func _load_mesh() -> void: add_child(asset_node) if asset_node is MeshInstance3D: _mesh_instance = asset_node as MeshInstance3D + else: + _mesh_instance = asset_node.find_child("*", true, false) as MeshInstance3D return # 2. Build a stylized, recognizable 3D representation based on catalog ID / asset ID @@ -96,9 +98,13 @@ func _try_load_asset(asset_id: String) -> Node: var candidate_paths: Array[String] = [ asset_id, "res://assets/%s" % asset_id, + "res://assets/props/%s" % asset_id.get_file(), "user://custom_assets/%s" % asset_id, "user://custom_assets/%s" % asset_id.get_file() ] + var lower_id := asset_id.to_lower() + if lower_id.contains("bizon") or lower_id.contains("bison") or lower_id.contains("madtrollstudio"): + candidate_paths.append("res://assets/props/bison.glb") for cpath in candidate_paths: if FileAccess.file_exists(cpath):