Blender Kitsu: Include Episode in Shot Builder Paths & Improve 3d_start #192
@ -34,7 +34,9 @@ Sets are more tricky to define, since they can differ even on a shot level. In p
|
||||
|
||||
## Shots
|
||||
|
||||
**Location:** `{project root}/pro/shots/{sequence number}/{shot identifier}/{shot identifier}-{task identifier}.blend`
|
||||
**Standard Location:** `{project root}/pro/shots/{sequence number}/{shot identifier}/{shot identifier}-{task identifier}.blend`
|
||||
|
||||
**TV Show Location:** `{project root}/pro/shots/{episode identifier}/{sequence number}/{shot identifier}/{shot identifier}-{task identifier}.blend`
|
||||
|
||||
##
|
||||
Example: `prop-dresser_wood.faded-modeling.png`
|
||||
|
@ -49,7 +49,7 @@ class KITSU_PT_vi3d_anim_tools(bpy.types.Panel):
|
||||
def poll(cls, context: bpy.types.Context) -> bool:
|
||||
return bool(
|
||||
prefs.session_auth(context)
|
||||
and cache.task_type_active_get().name == 'Animation'
|
||||
and cache.task_type_active_get().name in ['Animation', 'Layout']
|
||||
)
|
||||
|
||||
def draw(self, context: bpy.types.Context) -> None:
|
||||
|
@ -87,7 +87,6 @@ class KITSU_OT_con_episodes_load(bpy.types.Operator):
|
||||
return bool(prefs.session_auth(context) and cache.project_active_get())
|
||||
|
||||
def execute(self, context: bpy.types.Context) -> Set[str]:
|
||||
|
||||
# Store vars to check if project / seq / shot changed.
|
||||
zep_prev_id = cache.episode_active_get().id
|
||||
|
||||
@ -303,7 +302,17 @@ class KITSU_OT_con_detect_context(bpy.types.Operator):
|
||||
# Update kitsu metadata.
|
||||
filepath = Path(bpy.data.filepath)
|
||||
active_project = cache.project_active_get()
|
||||
|
||||
# TODO REFACTOR THIS WHOLE THING, BAD HACK
|
||||
# Path is different for tvshow
|
||||
if (
|
||||
active_project.production_type == 'tvshow'
|
||||
and filepath.parents[3].name == bkglobals.SHOT_DIR_NAME
|
||||
):
|
||||
category = filepath.parents[3].name
|
||||
else:
|
||||
category = filepath.parents[2].name
|
||||
|
||||
item_group = filepath.parents[1].name
|
||||
item = filepath.parents[0].name
|
||||
item_task_type = filepath.stem.split(bkglobals.FILE_DELIMITER)[-1]
|
||||
|
@ -288,14 +288,9 @@ def get_frame_range(): # TODO return type
|
||||
|
||||
# Pull update for shot.
|
||||
cache.shot_active_pull_update()
|
||||
if "3d_start" not in active_shot.data:
|
||||
logger.warning(
|
||||
"Failed to check frame range. Shot %s missing '3d_start' attribute on server",
|
||||
active_shot.name,
|
||||
)
|
||||
return
|
||||
frame_in = int(active_shot.data["3d_start"])
|
||||
frame_out = int(active_shot.data["3d_start"]) + int(active_shot.nb_frames) - 1
|
||||
kitsu_3d_start = active_shot.get_3d_start()
|
||||
frame_in = kitsu_3d_start
|
||||
frame_out = kitsu_3d_start + int(active_shot.nb_frames) - 1
|
||||
return frame_in, frame_out
|
||||
|
||||
|
||||
|
@ -493,12 +493,11 @@ def draw_frame_range_warning(self, context):
|
||||
layout.label(
|
||||
text="Frame Range on server does not match the active shot. Please 'pull' the correct frame range from the server"
|
||||
)
|
||||
layout.label(
|
||||
text=f" File Frame Range: {context.scene.frame_start}-{context.scene.frame_end}"
|
||||
)
|
||||
layout.label(text=f" File Frame Range: {context.scene.frame_start}-{context.scene.frame_end}")
|
||||
if active_shot:
|
||||
kitsu_3d_start = active_shot.get_3d_start()
|
||||
layout.label(
|
||||
text=f' Server Frame Range: {int(active_shot.data["3d_start"])}-{int(active_shot.data["3d_start"]) + int(active_shot.nb_frames) - 1}'
|
||||
text=f'Server Frame Range: {kitsu_3d_start}-{kitsu_3d_start + int(active_shot.nb_frames) - 1}'
|
||||
)
|
||||
else:
|
||||
layout.label(text=f' Server Frame Range: not found')
|
||||
|
@ -55,9 +55,7 @@ def remove_all_data():
|
||||
for obj in bpy.data.objects:
|
||||
bpy.data.objects.remove(obj)
|
||||
|
||||
bpy.ops.outliner.orphans_purge(
|
||||
do_local_ids=True, do_linked_ids=True, do_recursive=True
|
||||
)
|
||||
bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True)
|
||||
|
||||
|
||||
def set_shot_scene(context: bpy.types.Context, scene_name: str) -> bpy.types.Scene:
|
||||
@ -84,19 +82,12 @@ def set_resolution_and_fps(project: Project, scene: bpy.types.Scene):
|
||||
scene.render.resolution_percentage = 100
|
||||
|
||||
|
||||
def get_3d_start(shot: Shot):
|
||||
if shot.data and shot.data.get("3d_start"): # shot.data and
|
||||
return int(shot.data.get("3d_start"))
|
||||
else:
|
||||
return bkglobals.FRAME_START
|
||||
|
||||
|
||||
def set_frame_range(shot: Shot, scene: bpy.types.Scene):
|
||||
start_3d = get_3d_start(shot)
|
||||
scene.frame_start = start_3d
|
||||
kitsu_start_3d = shot.get_3d_start()
|
||||
scene.frame_start = kitsu_start_3d
|
||||
if not shot.nb_frames:
|
||||
raise Exception(f"{shot.name} has missing frame duration information")
|
||||
scene.frame_end = start_3d + shot.nb_frames - 1
|
||||
scene.frame_end = kitsu_start_3d + shot.nb_frames - 1
|
||||
|
||||
|
||||
def link_data_block(file_path: str, data_block_name: str, data_block_type: str):
|
||||
@ -151,9 +142,7 @@ def link_camera_rig(
|
||||
output_collection.objects.link(camera_object)
|
||||
return
|
||||
|
||||
collection_name = (
|
||||
"CA-camera_rig" # TODO Rename the asset itself, this breaks convention
|
||||
)
|
||||
collection_name = "CA-camera_rig" # TODO Rename the asset itself, this breaks convention
|
||||
|
||||
override_camera_col = link_and_override_collection(
|
||||
file_path=path, collection_name=collection_name, scene=scene
|
||||
@ -180,9 +169,7 @@ def create_task_type_output_collection(
|
||||
scene.collection.children.link(output_collection)
|
||||
|
||||
for view_layer in scene.view_layers:
|
||||
view_layer_output_collection = view_layer.layer_collection.children.get(
|
||||
output_col_name
|
||||
)
|
||||
view_layer_output_collection = view_layer.layer_collection.children.get(output_col_name)
|
||||
view_layer_output_collection.exclude = True
|
||||
return output_collection
|
||||
|
||||
@ -192,11 +179,9 @@ def link_task_type_output_collections(shot: Shot, task_type: TaskType):
|
||||
if bkglobals.OUTPUT_COL_LINK_MAPPING.get(task_type_short_name) == None:
|
||||
return
|
||||
for short_name in bkglobals.OUTPUT_COL_LINK_MAPPING.get(task_type_short_name):
|
||||
external_filepath = shot.get_shot_filepath(bpy.context, short_name)
|
||||
external_filepath = shot.get_filepath(bpy.context, short_name)
|
||||
if not Path(external_filepath).exists():
|
||||
print(
|
||||
f"Unable to link output collection for {Path(external_filepath).name}"
|
||||
)
|
||||
print(f"Unable to link output collection for {Path(external_filepath).name}")
|
||||
file_path = external_filepath.__str__()
|
||||
colection_name = shot.get_output_collection_name(short_name)
|
||||
link_data_block(file_path, colection_name, 'Collection')
|
||||
|
@ -2,7 +2,6 @@ import bpy
|
||||
from .. import prefs
|
||||
from pathlib import Path
|
||||
import re
|
||||
from .core import get_3d_start
|
||||
|
||||
|
||||
def editorial_export_get_latest(
|
||||
@ -41,7 +40,7 @@ def editorial_export_get_latest(
|
||||
|
||||
# Update shift frame range prop.
|
||||
frame_in = shot.data.get("frame_in")
|
||||
frame_3d_start = get_3d_start(shot)
|
||||
frame_3d_start = shot.get_3d_start()
|
||||
frame_3d_offset = frame_3d_start - addon_prefs.shot_builder_frame_offset
|
||||
edit_export_offset = addon_prefs.edit_export_frame_offset
|
||||
|
||||
|
@ -23,9 +23,7 @@ from .hooks import Hooks
|
||||
active_project = None
|
||||
|
||||
|
||||
def get_shots_for_seq(
|
||||
self: Any, context: bpy.types.Context
|
||||
) -> List[Tuple[str, str, str]]:
|
||||
def get_shots_for_seq(self: Any, context: bpy.types.Context) -> List[Tuple[str, str, str]]:
|
||||
if self.seq_id != '':
|
||||
seq = active_project.get_sequence(self.seq_id)
|
||||
shot_enum = cache.get_shots_enum_for_seq(self, context, seq)
|
||||
@ -34,9 +32,7 @@ def get_shots_for_seq(
|
||||
return [('NONE', "No Shots Found", '')]
|
||||
|
||||
|
||||
def get_tasks_for_shot(
|
||||
self: Any, context: bpy.types.Context
|
||||
) -> List[Tuple[str, str, str]]:
|
||||
def get_tasks_for_shot(self: Any, context: bpy.types.Context) -> List[Tuple[str, str, str]]:
|
||||
global active_project
|
||||
if not (self.shot_id == '' or self.shot_id == 'NONE'):
|
||||
shot = active_project.get_shot(self.shot_id)
|
||||
@ -186,9 +182,7 @@ class KITSU_OT_build_new_shot(bpy.types.Operator):
|
||||
|
||||
self.production_name = project.name
|
||||
|
||||
return cast(
|
||||
Set[str], context.window_manager.invoke_props_dialog(self, width=400)
|
||||
)
|
||||
return cast(Set[str], context.window_manager.invoke_props_dialog(self, width=400))
|
||||
|
||||
def _get_task_type_for_shot(self, context, shot):
|
||||
for task_type in shot.get_all_task_types():
|
||||
@ -202,13 +196,13 @@ class KITSU_OT_build_new_shot(bpy.types.Operator):
|
||||
shot = active_project.get_shot(self.shot_id)
|
||||
task_type = self._get_task_type_for_shot(context, shot)
|
||||
task_type_short_name = task_type.get_short_name()
|
||||
shot_file_path_str = shot.get_shot_filepath(context, task_type_short_name)
|
||||
shot_file_path_str = shot.get_filepath(context, task_type_short_name)
|
||||
|
||||
# Open Template File
|
||||
replace_workspace_with_template(context, task_type_short_name)
|
||||
|
||||
# Set Up Scene + Naming
|
||||
shot_task_name = shot.get_shot_task_name(task_type.get_short_name())
|
||||
shot_task_name = shot.get_task_name(task_type.get_short_name())
|
||||
scene = set_shot_scene(context, shot_task_name)
|
||||
remove_all_data()
|
||||
set_resolution_and_fps(active_project, scene)
|
||||
@ -250,9 +244,7 @@ class KITSU_OT_build_new_shot(bpy.types.Operator):
|
||||
if self.save_file:
|
||||
save_shot_builder_file(file_path=shot_file_path_str)
|
||||
|
||||
self.report(
|
||||
{"INFO"}, f"Successfully Built Shot:`{shot.name}` Task: `{task_type.name}`"
|
||||
)
|
||||
self.report({"INFO"}, f"Successfully Built Shot:`{shot.name}` Task: `{task_type.name}`")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
@ -1868,22 +1868,8 @@ class KITSU_OT_sqe_pull_edit(bpy.types.Operator):
|
||||
def _apply_strip_slip_from_shot(
|
||||
self, context: bpy.types.Context, strip: bpy.types.Sequence, shot: Shot
|
||||
) -> None:
|
||||
if "3d_start" not in shot.data:
|
||||
logger.warning(
|
||||
"%s no update to frame_start_offset. '3d_start' key not in shot.data",
|
||||
shot.name,
|
||||
)
|
||||
return
|
||||
|
||||
if not shot.data["3d_start"]:
|
||||
logger.warning(
|
||||
"%s no update to frame_start_offset. '3d_start' key invalid value: %i",
|
||||
shot.name,
|
||||
shot.data["3d_start"],
|
||||
)
|
||||
return
|
||||
# get offset
|
||||
offset = strip.kitsu_frame_start - int(shot.data["3d_start"])
|
||||
offset = strip.kitsu_frame_start - int(shot.get_3d_start())
|
||||
|
||||
# Deselect everything.
|
||||
if context.selected_sequences:
|
||||
|
@ -33,11 +33,7 @@ logger = LoggerFactory.getLogger()
|
||||
def shot_meta(strip: bpy.types.Sequence, shot: Shot) -> None:
|
||||
# Update shot info.
|
||||
|
||||
# Only set 3d_start if none is found
|
||||
try:
|
||||
kitsu_3d_start = shot.data["3d_start"]
|
||||
except:
|
||||
kitsu_3d_start = bkglobals.FRAME_START
|
||||
kitsu_3d_start = shot.get_3d_start()
|
||||
shot.name = strip.kitsu.shot_name
|
||||
shot.description = strip.kitsu.shot_description
|
||||
shot.data["frame_in"] = strip.frame_final_start
|
||||
|
@ -596,22 +596,36 @@ class Shot(Entity):
|
||||
gazu.shot.update_shot(asdict(self))
|
||||
return self
|
||||
|
||||
def get_shot_task_name(self, task_type_short_name: str) -> str: #
|
||||
def get_3d_start(self) -> int:
|
||||
try:
|
||||
logger.info(f"3d_start not found on server, defaulting to '{bkglobals.FRAME_START}'")
|
||||
return int(self.data["3d_start"])
|
||||
except:
|
||||
return bkglobals.FRAME_START
|
||||
|
||||
def get_task_name(self, task_type_short_name: str) -> str: #
|
||||
return f"{self.name}{bkglobals.FILE_DELIMITER}{task_type_short_name}"
|
||||
|
||||
def get_output_collection_name(self, task_type_short_name: str) -> str:
|
||||
return f"{self.get_shot_task_name(task_type_short_name)}{bkglobals.FILE_DELIMITER}output"
|
||||
return f"{self.get_task_name(task_type_short_name)}{bkglobals.FILE_DELIMITER}output"
|
||||
|
||||
def get_shot_dir(self, context) -> str:
|
||||
def get_dir(self, context) -> str:
|
||||
project_root_dir = prefs.project_root_dir_get(context)
|
||||
all_shots_dir = project_root_dir.joinpath('pro').joinpath('shots')
|
||||
|
||||
# Add Episode to Path if avaliable
|
||||
if self.episode_id:
|
||||
base_dir = all_shots_dir.joinpath(self.episode_name)
|
||||
else:
|
||||
base_dir = all_shots_dir
|
||||
|
||||
seq = self.get_sequence()
|
||||
shot_dir = all_shots_dir.joinpath(seq.name).joinpath(self.name)
|
||||
shot_dir = base_dir.joinpath(seq.name).joinpath(self.name)
|
||||
return shot_dir.__str__()
|
||||
|
||||
def get_shot_filepath(self, context, task_type_short_name: str) -> str:
|
||||
file_name = self.get_shot_task_name(task_type_short_name) + '.blend'
|
||||
return Path(self.get_shot_dir(context)).joinpath(file_name).__str__()
|
||||
def get_filepath(self, context, task_type_short_name: str) -> str:
|
||||
file_name = self.get_task_name(task_type_short_name) + '.blend'
|
||||
return Path(self.get_dir(context)).joinpath(file_name).__str__()
|
||||
|
||||
def update_data(self, data: Dict[str, Any]) -> Shot:
|
||||
gazu.shot.update_shot_data(asdict(self), data=data)
|
||||
|
Loading…
Reference in New Issue
Block a user