Scene Manager: LOD switching; Shot Viewer renamed Shot Manager

LODs are child collections of the asset collection (COL_{asset}_LOD{n}).
Overrides hide the other LOD collections (viewport and render), keeping
the rig, pose and animation; collection instances instance the LOD
collection. Each instance has its own LOD, set per instance or for the
selection in the Loaded panel, kept through save, reload and updates.
Loading shows SCENE_MANAGER_DEFAULT_LOD (0).

The Shot Viewer panel is now "Shot Manager" and starts closed. Code
names and SHOT_VIEWER_* config keys are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
NDemaerschalkandClaude Opus 5.5 committed 2026-10-27 16:42:24 +01:00
1 parent 9fe38ce58a
commit bac7a6f5da
10 files changed
+258 -45

No files matched your search

+41 -1
View File
@@ -109,7 +109,7 @@ check(
"chars are posable overrides",
all(ob.override_library for ob in bpy.data.collections["COL_bob"].all_objects),
)
check("shot viewer groups bob as character", "Characters" in shot_viewer.rig_groups(bpy.context.view_layer))
check("shot manager groups bob as character", "Characters" in shot_viewer.rig_groups(bpy.context.view_layer))
bpy.ops.wtd_scene.load()
pump()
check("load again adds nothing", message().startswith("Nothing new to load"), message())
@@ -129,6 +129,44 @@ bpy.ops.wtd_scene.load(asset_oid=CAR)
pump()
check("car loaded once downloaded", loaded().get("car", (None,))[0] == "v001", message())
print("LOD")
tree_lib, car_lib = bpy.data.libraries["tree"], bpy.data.libraries["car"]
tree, car = lib.instances(tree_lib)[0], lib.instances(car_lib)[0]
check("instance shows the default LOD", lib.current_lod(tree_lib, tree) == 0 and tree.instance_collection.name == "COL_tree_LOD0")
check("override shows the default LOD", lib.current_lod(car_lib, car) == 0)
hidden = {c.name: (c.hide_viewport, c.hide_render) for c in lib.lods(car_lib, car).values()}
check(
"other LODs hidden (viewport and render)",
hidden == {"COL_car_LOD0": (False, False), "COL_car_LOD1": (True, True), "COL_car_LOD2": (True, True)},
str(hidden),
)
evaluated = {ob.name for ob in bpy.context.evaluated_depsgraph_get().objects}
check("hidden LODs not evaluated", "GEO_COL_car_LOD0" in evaluated and not evaluated & {"GEO_COL_car_LOD1", "GEO_COL_car_LOD2"})
bpy.ops.wtd_scene.set_lod(lod=2, library="car", instance=car.name)
check("override switched to LOD 2", lib.current_lod(car_lib, car) == 2)
for ob in bpy.context.view_layer.objects:
ob.select_set(ob in (tree, bpy.data.objects["RIG_COL_car"]))
check("LOD of selected", bpy.ops.wtd_scene.set_lod(lod=1, selected=True) == {"FINISHED"})
check("both switched to LOD 1", (lib.current_lod(tree_lib, tree), lib.current_lod(car_lib, car)) == (1, 1))
check("missing LOD refused", bpy.ops.wtd_scene.set_lod(lod=5, library="tree", instance="tree") == {"CANCELLED"})
second = lib.add_instance(scene, bpy.context.view_layer, car_lib)
check(
"second override: LODs found by their linked names",
list(lib.lods(car_lib, second)) == [0, 1, 2] and lib.current_lod(car_lib, second) == 0,
str([c.name for c in second.children]),
)
lib.remove_instance(second)
lib.update("tree", {"name": "v002", "path": os.path.join(WORKDIR, "lib", "tree", "v002", "model.blend")})
tree = lib.instances(bpy.data.libraries["tree"])[0]
check("LOD kept by an update", lib.current_lod(bpy.data.libraries["tree"], tree) == 1)
check("content outside the LODs: instance refused", bpy.ops.wtd_scene.set_lod(lod=0, library="tree", instance="tree") == {"CANCELLED"})
lib.update("tree", {"name": "v003", "path": os.path.join(WORKDIR, "lib", "tree", "v003", "model.blend")})
tree = lib.instances(bpy.data.libraries["tree"])[0]
check("LODs gone: whole collection instanced", tree.instance_collection.name == "COL_tree" and not lib.lods(bpy.data.libraries["tree"], tree))
lib.update("tree", {"name": "v001", "path": os.path.join(WORKDIR, "lib", "tree", "v001", "model.blend")})
tree = lib.instances(bpy.data.libraries["tree"])[0]
check("LODs back: default LOD", lib.current_lod(bpy.data.libraries["tree"], tree) == 0)
print("Versions")
check("bob is the latest", sm.version_status(bpy.data.libraries["bob"])[0] == "LATEST")
fake("/_test/publish", {"file_oid": BOB_FILE, "name": "v003", "available": False})
@@ -217,6 +255,8 @@ bpy.ops.wm.save_as_mainfile(filepath=path)
bpy.ops.wm.open_mainfile(filepath=path)
sm._on_load_post()
check("everything found after reopening", set(loaded()) == {"bob", "car", "lamp", "rock", "tree"})
car_lib = bpy.data.libraries["car"]
check("LOD kept after reopening", lib.current_lod(car_lib, lib.instances(car_lib)[0]) == 1)
sm.connect(bpy.context.scene)
pump()
check("versions checked again", sm.version_status(bpy.data.libraries["bob"])[0] == "UPDATE")