feat(studio,stacking,animations,collections): M16 ModBison Studio, object stacking, model animations, and poly collections for MVP 2
checkpoint / checkpoint (push) Successful in 6m32s

This commit is contained in:
2026-09-22 18:30:57 +00:00
parent 654c1ec6bf
commit 721dbe5ae7
37 changed files with 5202 additions and 17 deletions
+137
View File
@@ -0,0 +1,137 @@
extends SceneTree
## Smoke test verifying Poly Model Collections dock, drag-and-drop placement, and client asset cache.
var _failures: int = 0
var _checks: int = 0
const AssetCacheManagerClass = preload("res://scripts/studio/asset_cache_manager.gd")
const StudioCollectionsDockClass = preload("res://scripts/studio/studio_collections_dock.gd")
func _init() -> void:
print("--- Running Smoke Test: Poly Collections Dock & Asset Cache ---")
test_asset_cache_manager()
test_collections_dock_ui_and_filtering()
test_drag_and_drop_placement()
if _failures == 0:
print("--- PASS: All %d poly collections checks passed! ---" % _checks)
quit(0)
else:
printerr("--- FAIL: %d failures out of %d checks ---" % [_failures, _checks])
quit(1)
func _check(desc: String, cond: bool) -> void:
_checks += 1
if cond:
print(" ✓ %s" % desc)
else:
_failures += 1
printerr(" ✗ FAIL: %s" % desc)
func test_asset_cache_manager() -> void:
print("Testing AssetCacheManager disk cache...")
var cache := AssetCacheManagerClass.new()
var test_key := "test_prop_chest_01"
var mock_bytes := "glTFmockbinarydata".to_utf8_buffer()
# Store
var path := cache.store_asset(test_key, mock_bytes)
_check("Stored asset to disk", path != "" and FileAccess.file_exists(path))
_check("Cache reports has_cached == true", cache.has_cached(test_key))
# Read
var read_back := cache.read_asset(test_key)
_check("Read back identical byte length", read_back.size() == mock_bytes.size())
_check("Read back identical content", read_back.get_string_from_utf8() == "glTFmockbinarydata")
# List cached items
var items := cache.get_cached_items()
_check("Cache item list contains stored file", items.size() > 0)
# Clean up
var cleared := cache.clear_cache()
_check("Cleared cache files", cleared > 0)
_check("Cache reports has_cached == false after clear", not cache.has_cached(test_key))
func test_collections_dock_ui_and_filtering() -> void:
print("Testing StudioCollectionsDock filtering and model selection...")
var dock = StudioCollectionsDockClass.new()
root.add_child(dock)
_check("Dock instantiated with UI", dock != null)
# Test theme filtering
dock._theme_option.selected = 1 # "fantasy"
dock._on_theme_selected(1)
_check("Filtered to fantasy theme displays cards", dock._collections_vbox.get_child_count() > 0)
# Test text search
dock._search_edit.text = "chest"
dock._on_search_changed("chest")
_check("Search for 'chest' keeps matching collection visible", dock._collections_vbox.get_child_count() > 0)
# Test non-matching search
dock._search_edit.text = "nonexistent_unicorn_blaster"
dock._on_search_changed("nonexistent_unicorn_blaster")
var status_text = dock._status_label.text
_check("Non-matching search shows 0 collections", status_text.contains("0 collections"))
# Reset filters
dock._search_edit.text = ""
dock._theme_option.selected = 0
dock._on_theme_selected(0)
_check("Reset filter restores all collections", dock._collections_vbox.get_child_count() >= 3)
dock.queue_free()
func test_drag_and_drop_placement() -> void:
print("Testing Drag and Drop model placement into EditorViewport...")
# Setup test core and viewport
var payload := ChunkDataDecoder.ChunkPayload.new()
var layer := ChunkDataDecoder.TileLayerData.new()
layer.name = "ground"
layer.layer_id = 0
layer.width = 16
layer.height = 16
layer.depth = 1
layer.tiles = PackedInt32Array()
layer.tiles.resize(16 * 16)
payload.tile_layers.append(layer)
var model := ChunkEditorModel.new(payload)
var catalog := EditorObjectCatalog.new()
catalog.load_catalog()
var core := EditorCore.new(model, catalog)
var vp := EditorViewport.new()
vp.setup(core, 0)
root.add_child(vp)
var drag_payload := {
"type": "model_placement",
"catalog_id": "crate_small",
"asset_id": "props/crate_small.glb",
"model_name": "Wooden Crate"
}
# Check acceptance of drag data
var can_drop: bool = vp._can_drop_data(Vector2(48, 48), drag_payload)
_check("Viewport accepts model_placement drag data", can_drop)
var invalid_drop: bool = vp._can_drop_data(Vector2(48, 48), {"type": "unknown_data"})
_check("Viewport rejects unknown drag data", not invalid_drop)
# Perform drop onto tile (2, 2) -> pixel (48, 48) with cell_size=24
vp._drop_data(Vector2(48, 48), drag_payload)
_check("Model placed into model after drop", core.model.payload.objects.size() == 1)
var placed_obj = core.model.payload.objects[0]
_check("Placed model has catalog_id crate_small", placed_obj.asset_id == "props/crate_small.glb" or placed_obj.properties.get("catalog_id") == "crate_small")
_check("Placed model X position is ~2.5", is_equal_approx(placed_obj.position.x, 2.5))
_check("Placed model Y position is ~2.5", is_equal_approx(placed_obj.position.y, 2.5))
vp.queue_free()