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>
602 lines
23 KiB
Python
602 lines
23 KiB
Python
import textwrap
|
|
|
|
import bpy
|
|
|
|
from .. import config, rename, shot_viewer, tools_ops
|
|
from ..scene_manager import ipc
|
|
from ..scene_manager import library as sm_library
|
|
from ..scene_manager import operators as sm
|
|
from ..core import CheckContext, Severity, checks_for_context, contexts, runner
|
|
from ..core.runner import Status
|
|
from .operators import EXPANDED
|
|
|
|
STATUS_ICONS = {
|
|
Status.NOT_RUN: "DOT",
|
|
Status.PASSED: "CHECKMARK",
|
|
Status.INFO: "INFO",
|
|
Status.WARNING: "ERROR",
|
|
Status.ERROR: "CANCEL",
|
|
Status.SKIPPED: "REMOVE",
|
|
Status.CRASHED: "GHOST_ENABLED",
|
|
}
|
|
SEVERITY_ICONS = {
|
|
Severity.INFO: "INFO",
|
|
Severity.WARNING: "ERROR",
|
|
Severity.ERROR: "CANCEL",
|
|
}
|
|
|
|
|
|
def _wrapped_label(layout, context, text, icon="NONE", margin=0):
|
|
"""Label wrapped to the region width. ``margin`` is the room (in characters) taken by buttons."""
|
|
char_width = 6.5 * context.preferences.system.ui_scale
|
|
width = max(16, int(context.region.width / char_width) - 6 - margin)
|
|
col = layout.column(align=True)
|
|
for index, line in enumerate(textwrap.wrap(text, width) or [""]):
|
|
col.label(text=line, icon=icon if index == 0 else "BLANK1")
|
|
|
|
|
|
def draw_sanity_checks_panel(self, context):
|
|
layout = self.layout
|
|
scene = context.scene
|
|
settings = scene.wtd_sanity
|
|
report = runner.LAST_REPORT
|
|
if report is not None and report.context_id != settings.context:
|
|
report = None
|
|
|
|
layout.prop(settings, "context", text="")
|
|
if settings.context in contexts.ASSET:
|
|
row = layout.row(align=True)
|
|
row.prop(settings, "asset_name", text="Asset")
|
|
if not settings.asset_name:
|
|
guessed = CheckContext(scene, settings.context).asset_name
|
|
row.label(text=guessed or "(from file name)")
|
|
|
|
row = layout.row(align=True)
|
|
row.scale_y = 1.4
|
|
row.operator("wtd_sanity.run_checks", text="Run Checks", icon="PLAY")
|
|
row.operator("wtd_sanity.fix_all", text="Fix All", icon="TOOL_SETTINGS")
|
|
row.operator("wtd_sanity.write_report", text="", icon="TEXT")
|
|
|
|
if settings.context == contexts.CHAR_MOD:
|
|
layout.operator("wtd_sanity.export_char_reference", icon="EXPORT")
|
|
|
|
if report is not None:
|
|
counts = report.counts()
|
|
row = layout.row()
|
|
for status in (
|
|
Status.PASSED,
|
|
Status.WARNING,
|
|
Status.ERROR,
|
|
Status.SKIPPED,
|
|
Status.CRASHED,
|
|
):
|
|
if counts[status]:
|
|
row.label(text=str(counts[status]), icon=STATUS_ICONS[status])
|
|
|
|
disabled = runner.disabled_check_ids(scene)
|
|
box = None
|
|
category = None
|
|
for check in checks_for_context(settings.context):
|
|
if check.category != category:
|
|
category = check.category
|
|
box = layout.box()
|
|
box.label(text=category)
|
|
_draw_check(box, context, check, report, check.id in disabled)
|
|
|
|
|
|
def _draw_check(layout, context, check, report, disabled):
|
|
result = report.results.get(check.id) if report else None
|
|
status = result.status if result else Status.NOT_RUN
|
|
expanded = check.id in EXPANDED
|
|
|
|
row = layout.row(align=True)
|
|
row.operator(
|
|
"wtd_sanity.toggle_check",
|
|
text="",
|
|
emboss=False,
|
|
icon="CHECKBOX_DEHLT" if disabled else "CHECKBOX_HLT",
|
|
).check_id = check.id
|
|
row.operator(
|
|
"wtd_sanity.toggle_expand",
|
|
text="",
|
|
emboss=False,
|
|
icon="DOWNARROW_HLT" if expanded else "RIGHTARROW",
|
|
).check_id = check.id
|
|
|
|
label_row = row.row(align=True)
|
|
label_row.enabled = not disabled
|
|
label_row.alert = status in {Status.ERROR, Status.CRASHED}
|
|
count = f" ({len(result.issues)})" if result and result.issues else ""
|
|
label_row.label(text=check.label + count, icon=STATUS_ICONS[status])
|
|
|
|
buttons = row.row(align=True)
|
|
if result and any(issue.id_name or issue.elements for issue in result.issues):
|
|
buttons.operator(
|
|
"wtd_sanity.select_issues", text="", icon="RESTRICT_SELECT_OFF"
|
|
).check_id = check.id
|
|
if result and result.failed and check.can_fix():
|
|
buttons.operator(
|
|
"wtd_sanity.fix_check", text="", icon="TOOL_SETTINGS"
|
|
).check_id = check.id
|
|
buttons.operator(
|
|
"wtd_sanity.run_checks", text="", icon="FILE_REFRESH"
|
|
).check_id = check.id
|
|
|
|
if not expanded:
|
|
return
|
|
col = layout.column()
|
|
col.separator(factor=0.3)
|
|
_wrapped_label(col, context, check.description, icon="QUESTION")
|
|
if result is None:
|
|
return
|
|
if result.message:
|
|
_wrapped_label(
|
|
col,
|
|
context,
|
|
result.message.strip().splitlines()[-1],
|
|
icon=STATUS_ICONS[status],
|
|
)
|
|
for index, issue in enumerate(result.issues[: config.MAX_ISSUES_DRAWN]):
|
|
severity = issue.severity or check.severity
|
|
issue_row = col.row(align=True)
|
|
selectable = bool(issue.id_name or issue.elements)
|
|
_wrapped_label(
|
|
issue_row,
|
|
context,
|
|
issue.message,
|
|
icon=SEVERITY_ICONS[severity],
|
|
margin=7 if selectable else 0,
|
|
)
|
|
if selectable:
|
|
op = issue_row.operator(
|
|
"wtd_sanity.select_issues",
|
|
text="",
|
|
icon="RESTRICT_SELECT_OFF",
|
|
emboss=False,
|
|
)
|
|
op.check_id = check.id
|
|
op.issue_index = index
|
|
if check.editable_names:
|
|
id_block = issue.get_id()
|
|
if id_block is not None and id_block.library is None:
|
|
col.prop(id_block, "name", text="", icon="BLANK1")
|
|
hidden = len(result.issues) - config.MAX_ISSUES_DRAWN
|
|
if hidden > 0:
|
|
col.label(text=f"... {hidden} more (see Write Report)", icon="BLANK1")
|
|
col.separator(factor=0.5)
|
|
|
|
|
|
class _SanityPanel:
|
|
bl_region_type = "UI"
|
|
bl_category = "WTD"
|
|
bl_label = "Sanity Check"
|
|
bl_options = {"DEFAULT_CLOSED"} # panel starts collapsed
|
|
|
|
def draw(self, context):
|
|
draw_sanity_checks_panel(self, context)
|
|
|
|
|
|
class _ToolsPanel:
|
|
bl_region_type = "UI"
|
|
bl_category = "WTD"
|
|
bl_label = "Tools"
|
|
|
|
def draw(self, context) -> None:
|
|
layout = self.layout
|
|
layout.operator(
|
|
tools_ops.WTDGetOidForPath.bl_idname,
|
|
text=tools_ops.WTDGetOidForPath.bl_label,
|
|
)
|
|
|
|
# Adds a separator line
|
|
layout.separator()
|
|
|
|
|
|
class _RenamePanel:
|
|
bl_region_type = "UI"
|
|
bl_category = "WTD"
|
|
bl_label = "Rename"
|
|
bl_options = {"DEFAULT_CLOSED"} # panel starts collapsed
|
|
|
|
PREVIEW_COUNT = 10
|
|
|
|
def draw(self, context) -> None:
|
|
layout = self.layout
|
|
settings = context.scene.wtd_rename
|
|
layout.row().prop(settings, "target", expand=True)
|
|
|
|
col = layout.column()
|
|
col.use_property_split = True
|
|
col.use_property_decorate = False
|
|
col.prop(settings, "base_name")
|
|
col.prop(settings, "prefix_mode")
|
|
if settings.prefix_mode == "CUSTOM":
|
|
col.prop(settings, "prefix_custom", text=" ")
|
|
col.prop(settings, "side")
|
|
col.prop(settings, "suffix_mode")
|
|
if settings.suffix_mode == "CUSTOM":
|
|
col.prop(settings, "suffix_custom", text=" ")
|
|
col.prop(settings, "sort")
|
|
if settings.target == "OBJECTS":
|
|
col.prop(settings, "rename_data")
|
|
|
|
plan, skipped = rename.plan_renames(context, settings)
|
|
row = layout.row()
|
|
row.scale_y = 1.4
|
|
row.operator(
|
|
rename.WTD_OT_batch_rename.bl_idname,
|
|
text=f"Rename {len(plan)} Item(s)" if plan else "Rename",
|
|
icon="SORTALPHA",
|
|
)
|
|
if not plan and not skipped:
|
|
return
|
|
box = layout.box()
|
|
col = box.column(align=True)
|
|
for id_block, name in plan[: self.PREVIEW_COUNT]:
|
|
col.label(text=f"{id_block.name} → {name}")
|
|
hidden = len(plan) - self.PREVIEW_COUNT
|
|
if hidden > 0:
|
|
col.label(text=f"... {hidden} more")
|
|
for name in skipped:
|
|
col.label(text=f"{name}: no base name, set a name", icon="ERROR")
|
|
|
|
|
|
class WTD_UL_shots(bpy.types.UIList):
|
|
"""Camera markers of the scene, in time order."""
|
|
|
|
def draw_item(self, context, layout, data, item, icon, active_data, active_property, index=0, flt_flag=0):
|
|
start, end = shot_viewer.shot_range(item)
|
|
split = layout.split(factor=0.4)
|
|
split.label(text=item.name, icon="MARKER_HLT")
|
|
split = split.split(factor=0.6)
|
|
split.label(text=item.camera.name)
|
|
split.label(text=f"{start}-{end}")
|
|
|
|
def filter_items(self, context, data, propname):
|
|
markers = getattr(data, propname)
|
|
ordered = [marker.as_pointer() for marker in shot_viewer.shots(data)]
|
|
position = {pointer: index for index, pointer in enumerate(ordered)}
|
|
flags = [self.bitflag_filter_item if marker.as_pointer() in position else 0 for marker in markers]
|
|
if self.filter_name:
|
|
by_name = bpy.types.UI_UL_list.filter_items_by_name(
|
|
self.filter_name, self.bitflag_filter_item, markers, "name"
|
|
)
|
|
flags = [flag & name_flag for flag, name_flag in zip(flags, by_name)]
|
|
# Shots in time order, the markers without camera after them.
|
|
order = []
|
|
hidden = len(ordered)
|
|
for marker in markers:
|
|
pointer = marker.as_pointer()
|
|
if pointer in position:
|
|
order.append(position[pointer])
|
|
else:
|
|
order.append(hidden)
|
|
hidden += 1
|
|
return flags, order
|
|
|
|
|
|
class _ShotViewerPanel:
|
|
bl_region_type = "UI"
|
|
bl_category = "WTD"
|
|
bl_label = "Shot Manager"
|
|
bl_options = {"DEFAULT_CLOSED"} # panel starts collapsed
|
|
|
|
def draw(self, context) -> None:
|
|
layout = self.layout
|
|
settings = context.window_manager.wtd_shot_viewer
|
|
scene = context.scene
|
|
has_shots = bool(shot_viewer.shots(scene))
|
|
|
|
row = layout.row(align=True)
|
|
row.prop(scene, "wtd_sequence")
|
|
row.label(text=shot_viewer.sequence_name(scene))
|
|
|
|
row = layout.row(align=True)
|
|
row.scale_y = 1.4
|
|
row.enabled = has_shots
|
|
row.operator(shot_viewer.WTD_OT_shot_viewer_step.bl_idname, text="Previous", icon="TRIA_LEFT").step = -1
|
|
row.operator(shot_viewer.WTD_OT_shot_viewer_step.bl_idname, text="Next", icon="TRIA_RIGHT").step = 1
|
|
row = layout.row()
|
|
row.template_list("WTD_UL_shots", "", scene, "timeline_markers", settings, "shot_index", rows=5)
|
|
col = row.column(align=True)
|
|
col.operator(shot_viewer.WTD_OT_shot_viewer_new_shot.bl_idname, text="", icon="ADD")
|
|
col.operator(shot_viewer.WTD_OT_shot_viewer_delete_shot.bl_idname, text="", icon="REMOVE")
|
|
if not has_shots:
|
|
layout.label(text="No camera marker yet: add a shot with +", icon="INFO")
|
|
layout.prop(settings, "loop_shot")
|
|
|
|
groups = shot_viewer.rig_groups(context.view_layer)
|
|
if not groups:
|
|
layout.label(text="No rig in the scene", icon="INFO")
|
|
for label, rigs in groups.items():
|
|
box = layout.box()
|
|
box.label(text=label, icon="ARMATURE_DATA")
|
|
col = box.column(align=True)
|
|
for rig in rigs:
|
|
col.operator(
|
|
shot_viewer.WTD_OT_shot_viewer_select_rig.bl_idname,
|
|
text=rig.name,
|
|
depress=rig.select_get(),
|
|
).name = rig.name
|
|
|
|
|
|
MESSAGE_ICONS = {"INFO": "INFO", "WARNING": "ERROR", "ERROR": "CANCEL"}
|
|
|
|
|
|
class _SceneManagerPanel:
|
|
bl_region_type = "UI"
|
|
bl_category = "WTD"
|
|
bl_label = "Scene Manager"
|
|
|
|
def draw(self, context) -> None:
|
|
layout = self.layout
|
|
state = sm.STATE
|
|
scene = context.scene
|
|
|
|
row = layout.row(align=True)
|
|
if sm.connected():
|
|
session = sm.current_session()
|
|
text = f"{session.get('session_name', '?')} ({session.get('cluster_name', '')})"
|
|
if state.ping.get("user"):
|
|
text += f" {state.ping['user']} @ {state.ping.get('site', '')}"
|
|
row.label(text=text, icon="LINKED")
|
|
else:
|
|
row.label(text=state.connection_error, icon="UNLINKED")
|
|
row.operator(sm.WTD_OT_scene_connect.bl_idname, text="", icon="FILE_REFRESH")
|
|
if len(state.sessions) > 1:
|
|
col = layout.column(align=True)
|
|
chosen = context.window_manager.wtd_libreflow.session_uid
|
|
for uid, info in state.sessions.items():
|
|
col.operator(
|
|
sm.WTD_OT_scene_select_session.bl_idname,
|
|
text=f"{info.get('session_name', uid)}: {info.get('current_oid') or ''}",
|
|
depress=uid == chosen,
|
|
).uid = uid
|
|
|
|
layout.prop(scene.wtd_scene_manager, "context_oid", text="", icon="SEQUENCE")
|
|
row = layout.row(align=True)
|
|
row.operator(sm.WTD_OT_scene_use_current_oid.bl_idname, text="Libreflow Oid", icon="IMPORT")
|
|
row.operator(sm.WTD_OT_scene_pick_shot.bl_idname, text="Pick Shot", icon="VIEWZOOM")
|
|
text, valid = sm.context_label(sm.context_oid(scene))
|
|
if text:
|
|
row = layout.row()
|
|
row.alert = not valid
|
|
_wrapped_label(row, context, text, icon="CHECKMARK" if valid else "ERROR")
|
|
|
|
busy = ipc.busy_label()
|
|
if busy:
|
|
layout.label(text=f"{busy}...", icon="SORTTIME")
|
|
message, level = state.message
|
|
if message:
|
|
row = layout.row()
|
|
row.alert = level == "ERROR"
|
|
_wrapped_label(row, context, message, icon=MESSAGE_ICONS[level])
|
|
|
|
|
|
class _SceneCastingPanel:
|
|
bl_region_type = "UI"
|
|
bl_category = "WTD"
|
|
bl_label = "Casting"
|
|
bl_parent_id = "WTD_PT_scene_manager_view3d"
|
|
|
|
def draw(self, context) -> None:
|
|
layout = self.layout
|
|
oid = sm.context_oid(context.scene)
|
|
row = layout.row(align=True)
|
|
row.scale_y = 1.3
|
|
row.operator(sm.WTD_OT_scene_load.bl_idname, text="Load Missing", icon="IMPORT")
|
|
if sm.missing_files(context.scene):
|
|
row.operator(sm.WTD_OT_scene_request.bl_idname, text="Request Missing", icon="URL")
|
|
row.operator(sm.WTD_OT_scene_refresh.bl_idname, text="", icon="FILE_REFRESH")
|
|
casting = sm.STATE.casting.get(oid)
|
|
if casting is None:
|
|
text = "Not read yet: refresh" if sm.is_shot_context(oid) else "Set a shot as context"
|
|
layout.label(text=text, icon="INFO")
|
|
return
|
|
if not casting["assets"]:
|
|
layout.label(text="Nothing cast in this shot", icon="INFO")
|
|
|
|
found = sm_library.all_instances()
|
|
col = layout.column(align=True)
|
|
for asset in casting["assets"]:
|
|
library = sm_library.find_library(asset["file"]["file_oid"]) if asset.get("file") else None
|
|
have = len(found.get(library.name, [])) if library else 0
|
|
want = asset.get("nb_occurences") or 1
|
|
error = asset.get("error")
|
|
file = sm.file_data(asset["file"]["file_oid"], asset["file"]) if asset.get("file") else None
|
|
latest = (file or {}).get("latest") or {}
|
|
requested = bool(error) and latest.get("sync_status") == "Requested"
|
|
if requested:
|
|
error = f"{latest['name']} requested, waiting for the download"
|
|
icon = "SORTTIME" if requested else "ERROR" if error else "CHECKMARK" if have >= want else "IMPORT"
|
|
row = col.row(align=True)
|
|
row.alert = bool(error) and not requested
|
|
row.label(text=f"{asset['name']} ({asset.get('asset_type', '')})", icon=icon)
|
|
row.label(text=f"{have}/{want}")
|
|
if not error and have < want:
|
|
row.operator(sm.WTD_OT_scene_load.bl_idname, text="", icon="IMPORT").asset_oid = asset["oid"]
|
|
if sm.to_request(file):
|
|
row.operator(sm.WTD_OT_scene_request.bl_idname, text="", icon="URL").file_oid = file["file_oid"]
|
|
if error:
|
|
sub = col.row()
|
|
sub.alert = not requested
|
|
_wrapped_label(sub, context, error, icon="BLANK1")
|
|
|
|
cast = {asset["oid"] for asset in casting["assets"]}
|
|
extra = [library for library in sm_library.managed_libraries() if library["wtd_asset_oid"] not in cast]
|
|
if extra:
|
|
box = layout.box()
|
|
box.label(text="Loaded, no longer cast:", icon="ERROR")
|
|
for library in extra:
|
|
box.label(text=library.get("wtd_asset_name", library.name), icon="BLANK1")
|
|
|
|
|
|
class _SceneLoadedPanel:
|
|
bl_region_type = "UI"
|
|
bl_category = "WTD"
|
|
bl_label = "Loaded"
|
|
bl_parent_id = "WTD_PT_scene_manager_view3d"
|
|
|
|
def draw(self, context) -> None:
|
|
layout = self.layout
|
|
libraries = sm_library.managed_libraries()
|
|
if not libraries:
|
|
layout.label(text="Nothing loaded by the Scene Manager", icon="INFO")
|
|
return
|
|
row = layout.row(align=True)
|
|
row.operator(sm.WTD_OT_scene_update.bl_idname, text="Update All", icon="FILE_REFRESH")
|
|
row.operator(sm.WTD_OT_scene_refresh.bl_idname, text="", icon="VIEWZOOM")
|
|
|
|
found = sm_library.all_instances()
|
|
for library in libraries:
|
|
state, text = sm.version_status(library)
|
|
outdated = state in {"UPDATE", "PENDING"}
|
|
expanded = library.name in sm.EXPANDED
|
|
row = layout.row(align=True)
|
|
row.operator(
|
|
sm.WTD_OT_scene_toggle_expand.bl_idname,
|
|
text="",
|
|
emboss=False,
|
|
icon="DOWNARROW_HLT" if expanded else "RIGHTARROW",
|
|
).library = library.name
|
|
row.label(text=library.get("wtd_asset_name", library.name), icon="LIBRARY_DATA_DIRECT")
|
|
version = row.row(align=True)
|
|
version.alert = outdated
|
|
version.label(text=library.get("wtd_revision", "?"))
|
|
if state == "UPDATE":
|
|
row.operator(sm.WTD_OT_scene_update.bl_idname, text="", icon="FILE_REFRESH").library = library.name
|
|
elif state == "PENDING" and sm.to_request(sm.file_data(library["wtd_file_oid"])):
|
|
row.operator(sm.WTD_OT_scene_request.bl_idname, text="", icon="URL").file_oid = library["wtd_file_oid"]
|
|
row.operator(sm.WTD_OT_scene_pick_revision.bl_idname, text="", icon="COLLAPSEMENU").library = library.name
|
|
row.operator(sm.WTD_OT_scene_remove.bl_idname, text="", icon="X").library = library.name
|
|
if outdated:
|
|
sub = layout.row()
|
|
sub.alert = True
|
|
_wrapped_label(sub, context, text, icon="BLANK1")
|
|
if not expanded:
|
|
continue
|
|
col = layout.column(align=True)
|
|
for instance in found.get(library.name, []):
|
|
is_object = isinstance(instance, bpy.types.Object)
|
|
sub = col.row(align=True)
|
|
sub.label(text=instance.name, icon="OUTLINER_OB_EMPTY" if is_object else "OUTLINER_COLLECTION")
|
|
op = sub.operator(sm.WTD_OT_scene_select_instance.bl_idname, text="", icon="RESTRICT_SELECT_OFF")
|
|
op.library, op.instance = library.name, instance.name
|
|
sub.operator(sm.WTD_OT_scene_remove.bl_idname, text="", icon="X").instance = instance.name
|
|
lods = sm_library.lods(library, instance)
|
|
if lods:
|
|
current = sm_library.current_lod(library, instance)
|
|
sub = col.row(align=True)
|
|
sub.label(text="LOD", icon="BLANK1")
|
|
for number in lods:
|
|
op = sub.operator(sm.WTD_OT_scene_set_lod.bl_idname, text=str(number), depress=number == current)
|
|
op.lod, op.library, op.instance = number, library.name, instance.name
|
|
|
|
layout.separator()
|
|
row = layout.row(align=True)
|
|
row.operator(sm.WTD_OT_scene_update.bl_idname, text="Update Selected").selected = True
|
|
row.operator(sm.WTD_OT_scene_remove.bl_idname, text="Remove Selected").selected = True
|
|
selected = [
|
|
(library, instance)
|
|
for library, instance in sm_library.selected_instances(context.view_layer)
|
|
if sm_library.lods(library, instance)
|
|
]
|
|
if selected:
|
|
split = layout.split(factor=0.4, align=True)
|
|
split.label(text="Selected LOD")
|
|
row = split.row(align=True)
|
|
shown = {sm_library.current_lod(library, instance) for library, instance in selected}
|
|
for number in sorted({n for library, instance in selected for n in sm_library.lods(library, instance)}):
|
|
op = row.operator(sm.WTD_OT_scene_set_lod.bl_idname, text=str(number), depress=shown == {number})
|
|
op.lod, op.selected = number, True
|
|
|
|
|
|
class _SceneAssetizePanel:
|
|
bl_region_type = "UI"
|
|
bl_category = "WTD"
|
|
bl_label = "Assetize"
|
|
bl_parent_id = "WTD_PT_scene_manager_view3d"
|
|
bl_options = {"DEFAULT_CLOSED"}
|
|
|
|
def draw(self, context) -> None:
|
|
layout = self.layout
|
|
_wrapped_label(
|
|
layout, context, "Publish a local collection of this scene to Libreflow as an asset.", icon="INFO"
|
|
)
|
|
layout.operator(sm.WTD_OT_scene_assetize.bl_idname, text="Assetize Collection...", icon="EXPORT")
|
|
|
|
|
|
class WTD_PT_sanity_view3d(_SanityPanel, bpy.types.Panel):
|
|
bl_idname = "WTD_PT_sanity_view3d"
|
|
bl_space_type = "VIEW_3D"
|
|
|
|
|
|
class WTD_PT_tools_view3d(_ToolsPanel, bpy.types.Panel):
|
|
bl_idname = "WTD_PT_tools_view3d"
|
|
bl_space_type = "VIEW_3D"
|
|
|
|
|
|
class WTD_PT_rename_view3d(_RenamePanel, bpy.types.Panel):
|
|
bl_idname = "WTD_PT_rename_view3d"
|
|
bl_space_type = "VIEW_3D"
|
|
|
|
|
|
class WTD_PT_rename_node_editor(_RenamePanel, bpy.types.Panel):
|
|
bl_idname = "WTD_PT_rename_node_editor"
|
|
bl_space_type = "NODE_EDITOR"
|
|
|
|
|
|
class WTD_PT_shot_viewer_view3d(_ShotViewerPanel, bpy.types.Panel):
|
|
bl_idname = "WTD_PT_shot_viewer_view3d"
|
|
bl_space_type = "VIEW_3D"
|
|
|
|
|
|
class WTD_PT_scene_manager_view3d(_SceneManagerPanel, bpy.types.Panel):
|
|
bl_idname = "WTD_PT_scene_manager_view3d"
|
|
bl_space_type = "VIEW_3D"
|
|
|
|
|
|
class WTD_PT_scene_casting_view3d(_SceneCastingPanel, bpy.types.Panel):
|
|
bl_idname = "WTD_PT_scene_casting_view3d"
|
|
bl_space_type = "VIEW_3D"
|
|
|
|
|
|
class WTD_PT_scene_loaded_view3d(_SceneLoadedPanel, bpy.types.Panel):
|
|
bl_idname = "WTD_PT_scene_loaded_view3d"
|
|
bl_space_type = "VIEW_3D"
|
|
|
|
|
|
class WTD_PT_scene_assetize_view3d(_SceneAssetizePanel, bpy.types.Panel):
|
|
bl_idname = "WTD_PT_scene_assetize_view3d"
|
|
bl_space_type = "VIEW_3D"
|
|
|
|
|
|
class WTD_PT_sanity_sequencer(_SanityPanel, bpy.types.Panel):
|
|
bl_idname = "WTD_PT_sanity_sequencer"
|
|
bl_space_type = "SEQUENCE_EDITOR"
|
|
|
|
|
|
classes = (
|
|
WTD_UL_shots,
|
|
WTD_PT_sanity_view3d,
|
|
WTD_PT_tools_view3d,
|
|
WTD_PT_shot_viewer_view3d,
|
|
WTD_PT_scene_manager_view3d,
|
|
WTD_PT_scene_casting_view3d,
|
|
WTD_PT_scene_loaded_view3d,
|
|
WTD_PT_scene_assetize_view3d,
|
|
WTD_PT_rename_view3d,
|
|
WTD_PT_rename_node_editor,
|
|
WTD_PT_sanity_sequencer,
|
|
)
|
|
|
|
|
|
def register():
|
|
for cls in classes:
|
|
bpy.utils.register_class(cls)
|
|
|
|
|
|
def unregister():
|
|
for cls in reversed(classes):
|
|
bpy.utils.unregister_class(cls)
|