Scene split and json export
0.5.0 - feat: add a render operator that render all scene - feat: split selected object to a separated scene - feat: crop border to objects - feat: export AE position coordinates to replace layers - change: ops gp.clean_compo_tree now take context.scene - fix: tick `use_compositing` and untick `sequencer` in new scenesmain
parent
852b893f8a
commit
2aa4ecc00e
|
@ -14,6 +14,14 @@ Activate / deactivate layer opaticty according to prefix
|
|||
Activate / deactivate all masks using MA layers
|
||||
-->
|
||||
|
||||
0.5.0
|
||||
|
||||
- feat: add a render operator that render all scene
|
||||
- feat: split selected object to a separated scene
|
||||
- feat: crop border to objects
|
||||
- feat: export AE position coordinates to replace layers
|
||||
- change: ops gp.clean_compo_tree now take context.scene
|
||||
- fix: tick `use_compositing` and untick `sequencer` in new scenes
|
||||
|
||||
0.4.1
|
||||
|
||||
|
|
|
@ -89,9 +89,29 @@ class GPEXP_OT_add_objects_to_render(bpy.types.Operator):
|
|||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class GPEXP_OT_split_to_scene(bpy.types.Operator):
|
||||
bl_idname = "gp.split_to_scene"
|
||||
bl_label = "Split Objects To Scene"
|
||||
bl_description = "Take selected objects and send them to separate scene"
|
||||
bl_options = {"REGISTER"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.object and context.object.type == 'GPENCIL'
|
||||
|
||||
mode : bpy.props.StringProperty(default='ALL', options={'SKIP_SAVE'})
|
||||
|
||||
def execute(self, context):
|
||||
fn.split_object_to_scene()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
classes=(
|
||||
GPEXP_OT_add_layer_to_render,
|
||||
GPEXP_OT_add_objects_to_render,
|
||||
GPEXP_OT_split_to_scene,
|
||||
)
|
||||
|
||||
def register():
|
||||
|
|
11
OP_clean.py
11
OP_clean.py
|
@ -98,11 +98,12 @@ class GPEXP_OT_clean_compo_tree(bpy.types.Operator):
|
|||
# box.prop(self, 'fo_clear_disconnected')
|
||||
|
||||
def execute(self, context):
|
||||
render = bpy.data.scenes.get('Render')
|
||||
if not render:
|
||||
print('SKIP, no Render scene')
|
||||
return {"CANCELLED"}
|
||||
|
||||
# render = bpy.data.scenes.get('Render')
|
||||
# if not render:
|
||||
# print('SKIP, no Render scene')
|
||||
# return {"CANCELLED"}
|
||||
render = context.scene
|
||||
|
||||
nodes = render.node_tree.nodes
|
||||
if self.clear_unused_view_layers:
|
||||
used_rlayer_names = [n.layer for n in nodes if n.type == 'R_LAYERS']
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
import bpy
|
||||
from . import fn
|
||||
|
||||
class GPEXP_OT_set_crop_from_selection(bpy.types.Operator):
|
||||
bl_idname = "gp.set_crop_from_selection"
|
||||
bl_label = "Set Crop"
|
||||
bl_description = "Automatic set crop from selection"
|
||||
bl_options = {"REGISTER"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
scn = context.scene
|
||||
fn.set_box_from_selected_objects(scn=scn, cam=scn.camera)
|
||||
scn.render.use_border = True
|
||||
scn.render.use_crop_to_border = True
|
||||
return {"FINISHED"}
|
||||
|
||||
class GPEXP_OT_export_crop_coord_to_json(bpy.types.Operator):
|
||||
bl_idname = "gp.export_crop_coord_to_json"
|
||||
bl_label = "Set Crop"
|
||||
bl_description = "Export json of all scenes borders (when enabled)" # Automatic set crop from selection
|
||||
bl_options = {"REGISTER"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
# scn = context.scene
|
||||
# if not scn.render.use_border or not scn.render.use_crop_to_border:
|
||||
# self.report({'ERROR'}, 'Current scene have cropping disabled or use crop_to_border disabled!')
|
||||
# return {'CANCELLED'}
|
||||
fn.export_crop_to_json()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
classes=(
|
||||
GPEXP_OT_set_crop_from_selection,
|
||||
GPEXP_OT_export_crop_coord_to_json,
|
||||
)
|
||||
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
def unregister():
|
||||
for cls in reversed(classes):
|
||||
bpy.utils.unregister_class(cls)
|
|
@ -0,0 +1,47 @@
|
|||
import bpy
|
||||
from . import fn
|
||||
from time import time
|
||||
|
||||
class GPEXP_OT_render_all_scenes(bpy.types.Operator):
|
||||
bl_idname = "gp.render_all_scenes"
|
||||
bl_label = "Render all scenes"
|
||||
bl_description = "Render all scene except Render"
|
||||
bl_options = {"REGISTER"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
start = time()
|
||||
ct = 0
|
||||
for scn in bpy.data.scenes:
|
||||
if scn.name == 'Scene':
|
||||
continue
|
||||
if not scn.use_nodes:
|
||||
continue
|
||||
if not [n for n in scn.node_tree.nodes if n.type == 'OUTPUT_FILE' and n.mute]:
|
||||
# skip if no fileout
|
||||
print(f'\n -!-> Skip {scn.name}, No output file, or all muted')
|
||||
continue
|
||||
|
||||
print(f'\n --> Rendering {scn.name}')
|
||||
# bpy.context.window.scene = scn
|
||||
bpy.ops.render.render(animation=True, scene=scn.name)
|
||||
ct += 1
|
||||
|
||||
print(f'\nDone. {ct} scenes rendered in {time()-start:.2f}s')
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
classes=(
|
||||
GPEXP_OT_render_all_scenes,
|
||||
)
|
||||
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
def unregister():
|
||||
for cls in reversed(classes):
|
||||
bpy.utils.unregister_class(cls)
|
|
@ -6,7 +6,7 @@
|
|||
- on renaming, correct also names in GP modifiers !!!
|
||||
- opt: multi-merge : also merge merged NG automatically disabling AA without group (or externalise AA node ?)
|
||||
- opt : How to disable main output
|
||||
|
||||
- To add : Reconnect inside nodegroup for nodes when using clean nodes
|
||||
|
||||
## Done
|
||||
- set exlude VL on non-used layers
|
||||
|
|
|
@ -2,7 +2,7 @@ bl_info = {
|
|||
"name": "GP Render",
|
||||
"description": "Organise export of gp layers through compositor output",
|
||||
"author": "Samuel Bernou",
|
||||
"version": (0, 4, 1),
|
||||
"version": (0, 5, 0),
|
||||
"blender": (2, 93, 0),
|
||||
"location": "View3D",
|
||||
"warning": "",
|
||||
|
@ -17,6 +17,8 @@ from . import OP_clean
|
|||
from . import OP_connect_toggle
|
||||
from . import OP_manage_outputs
|
||||
from . import OP_scene_switch
|
||||
from . import OP_crop_to_object
|
||||
from . import OP_render_scenes
|
||||
# from . import OP_check_layer_status
|
||||
from . import OP_render_pdf
|
||||
from . import prefs
|
||||
|
@ -37,6 +39,8 @@ def register():
|
|||
OP_merge_layers.register()
|
||||
OP_manage_outputs.register()
|
||||
OP_scene_switch.register()
|
||||
OP_crop_to_object.register()
|
||||
OP_render_scenes.register()
|
||||
# OP_check_layer_status.register()
|
||||
OP_render_pdf.register()
|
||||
OP_setup_layers.register()
|
||||
|
@ -51,6 +55,8 @@ def unregister():
|
|||
OP_setup_layers.unregister()
|
||||
# OP_check_layer_status.unregister()
|
||||
OP_render_pdf.unregister()
|
||||
OP_render_scenes.unregister()
|
||||
OP_crop_to_object.unregister()
|
||||
OP_scene_switch.unregister()
|
||||
OP_manage_outputs.unregister()
|
||||
OP_merge_layers.unregister()
|
||||
|
|
382
fn.py
382
fn.py
|
@ -1,10 +1,12 @@
|
|||
from typing import Coroutine
|
||||
import bpy
|
||||
import re
|
||||
from mathutils import Vector
|
||||
from pathlib import Path
|
||||
from math import isclose
|
||||
from collections import defaultdict
|
||||
|
||||
from time import time
|
||||
import json
|
||||
|
||||
def create_node(type, tree=None, **kargs):
|
||||
'''Get a type, a tree to add in, and optionnaly multiple attribute to set
|
||||
|
@ -112,6 +114,8 @@ def set_settings(scene=None):
|
|||
scene.eevee.taa_render_samples = 1
|
||||
scene.grease_pencil_settings.antialias_threshold = 0
|
||||
scene.render.film_transparent = True
|
||||
scene.render.use_compositing = True
|
||||
scene.render.use_sequencer = False
|
||||
scene.view_settings.view_transform = 'Standard'
|
||||
|
||||
scene.render.resolution_percentage = 100
|
||||
|
@ -119,10 +123,41 @@ def set_settings(scene=None):
|
|||
# output (fast write settings since this is just to delete afterwards...)
|
||||
scene.render.filepath = '//render/preview/preview_'
|
||||
scene.render.image_settings.file_format = 'JPEG'
|
||||
scene.render.image_settings.color_mode = 'BW'
|
||||
scene.render.image_settings.color_mode = 'RGB'
|
||||
scene.render.image_settings.quality = 0
|
||||
|
||||
|
||||
def new_scene_from(name, src_scn=None, regen=True, crop=True, link_cam=True, link_light=True):
|
||||
'''Get / Create a scene from name and source scene to get settings from'''
|
||||
scn = bpy.data.scenes.get(name)
|
||||
if scn and not regen:
|
||||
return scn
|
||||
elif scn and regen:
|
||||
bpy.data.scenes.remove(scn)
|
||||
|
||||
src_scn = src_scn or bpy.context.scene # given scene, or active scene
|
||||
scn = bpy.data.scenes.new(name)
|
||||
## copy original settings over to new scene
|
||||
# copy_settings(current, scn) # BAD
|
||||
for attr in ['frame_start', 'frame_end', 'frame_current', 'camera', 'world']:
|
||||
setattr(scn, attr, getattr(src_scn, attr))
|
||||
copy_settings(src_scn.render, scn.render)
|
||||
|
||||
## link cameras (and lights ?)
|
||||
for ob in src_scn.objects:
|
||||
if link_cam and ob.type == 'CAMERA':
|
||||
scn.collection.objects.link(ob)
|
||||
if link_light and ob.type == 'LIGHT':
|
||||
scn.collection.objects.link(ob)
|
||||
|
||||
# set adapted render settings (no AA)
|
||||
set_settings(scn)
|
||||
|
||||
if crop:
|
||||
scn.render.use_border = True
|
||||
scn.render.use_crop_to_border = True
|
||||
scn.use_nodes = True
|
||||
return scn
|
||||
|
||||
def get_render_scene():
|
||||
'''Get / Create a scene named Render'''
|
||||
|
@ -677,4 +712,345 @@ def show_message_box(_message = "", _title = "Message Box", _icon = 'INFO'):
|
|||
|
||||
if isinstance(_message, str):
|
||||
_message = [_message]
|
||||
bpy.context.window_manager.popup_menu(draw, title = _title, icon = _icon)
|
||||
bpy.context.window_manager.popup_menu(draw, title = _title, icon = _icon)
|
||||
|
||||
|
||||
def get_bbox_3d(ob):
|
||||
bbox_coords = ob.bound_box
|
||||
return [ob.matrix_world @ Vector(b) for b in bbox_coords]
|
||||
|
||||
def get_crop_pixel_coord(scn):
|
||||
# width height probably not needed. might need
|
||||
px_width = (scn.render.border_max_x - scn.render.border_min_x) * scn.render.resolution_x
|
||||
px_height = (scn.render.border_max_y - scn.render.border_min_y) * scn.render.resolution_y
|
||||
|
||||
pos_x = (scn.render.border_min_x + ((scn.render.border_max_x - scn.render.border_min_x) / 2)) * scn.render.resolution_x
|
||||
|
||||
## coord y > image center coord from bottom-left (Blender)
|
||||
# pos_y = (scn.render.border_min_y + ((scn.render.border_max_y - scn.render.border_min_y) / 2)) * scn.render.resolution_y,
|
||||
|
||||
## image center coord from top-left (AE)
|
||||
pos_y = ((1 - scn.render.border_max_y) + ((scn.render.border_max_y - scn.render.border_min_y) / 2)) * scn.render.resolution_y
|
||||
|
||||
coord = {
|
||||
'position_x' : round(pos_x),
|
||||
'position_y' : round(pos_y),
|
||||
'width' : round(px_width),
|
||||
'height' : round(px_height),
|
||||
}
|
||||
return coord
|
||||
|
||||
def export_crop_to_json():
|
||||
'''Export crop to json coords for AE
|
||||
'''
|
||||
|
||||
blend = Path(bpy.data.filepath)
|
||||
json_path = blend.parent / 'render' / f'{blend.stem}.json' #f'{ob.name}.json'
|
||||
|
||||
## per scene : json_path = Path(bpy.data.filepath).parent / 'render' / f'{scn.name}.json'
|
||||
# json_path = Path(bpy.data.filepath).parent / 'render' / f'{scn.name}.json' #f'{ob.name}.json'
|
||||
|
||||
coord_dic = {}
|
||||
|
||||
for scn in bpy.data.scenes:
|
||||
# if scn.name in {'Scene', 'Render'}:
|
||||
if scn.name == 'Scene':
|
||||
continue
|
||||
if scn.render.use_border:
|
||||
scn_border = get_crop_pixel_coord(scn)
|
||||
for ob in [o for o in scn.objects if o.type == 'GPENCIL']:
|
||||
coord_dic[ob.name] = scn_border
|
||||
|
||||
# save bbox
|
||||
with json_path.open('w') as fd:
|
||||
json.dump(coord_dic, fd, indent='\t')
|
||||
|
||||
print(f'coord saved at: {json_path}')
|
||||
return coord_dic
|
||||
|
||||
def set_border_region_from_coord(coords, scn=None, margin=30, export_json=True):
|
||||
'''Get a list of point coord in worldcamera view space (0 to 1) on each axis'''
|
||||
|
||||
scn = scn or bpy.context.scene
|
||||
|
||||
coords2d_x = sorted([c[0] for c in coords])
|
||||
coords2d_y = sorted([c[1] for c in coords])
|
||||
|
||||
margin_width = margin / scn.render.resolution_x
|
||||
margin_height = margin / scn.render.resolution_y
|
||||
|
||||
# set crop
|
||||
scn.render.border_min_x = coords2d_x[0] - margin_width
|
||||
scn.render.border_max_x = coords2d_x[-1] + margin_width
|
||||
|
||||
scn.render.border_min_y = coords2d_y[0] - margin_height
|
||||
scn.render.border_max_y = coords2d_y[-1] + margin_height
|
||||
|
||||
## get clamped relative value
|
||||
# relative_bbox2d_coords = [
|
||||
# (scn.render.border_min_x, scn.render.border_min_y),
|
||||
# (scn.render.border_min_x, scn.render.border_max_y),
|
||||
# (scn.render.border_max_x, scn.render.border_max_y),
|
||||
# (scn.render.border_max_x, scn.render.border_min_y),
|
||||
# ]
|
||||
|
||||
pixel_bbox2d_coords = [
|
||||
(scn.render.border_min_x*scn.render.resolution_x, scn.render.border_min_y*scn.render.resolution_y),
|
||||
(scn.render.border_min_x*scn.render.resolution_x, scn.render.border_max_y*scn.render.resolution_y),
|
||||
(scn.render.border_max_x*scn.render.resolution_x, scn.render.border_max_y*scn.render.resolution_y),
|
||||
(scn.render.border_max_x*scn.render.resolution_x, scn.render.border_min_y*scn.render.resolution_y),
|
||||
]
|
||||
# if export_json:
|
||||
# export_crop_to_json(scn)
|
||||
return pixel_bbox2d_coords
|
||||
|
||||
|
||||
def get_gp_box_all_frame(ob, cam=None):
|
||||
'''set crop to object bounding box considering whole animation. Cam should not be animated (render in bg_cam)
|
||||
return 2d bbox in pixels
|
||||
'''
|
||||
from bpy_extras.object_utils import world_to_camera_view
|
||||
coords_cam_list = []
|
||||
scn = bpy.context.scene
|
||||
cam = cam or scn.camera
|
||||
start = time()
|
||||
|
||||
if ob.animation_data and ob.animation_data.action: # use frame set on all frames
|
||||
print(f'{ob.name} has anim')
|
||||
# frame_nums = sorted(list(set([f.frame_number for l in ob.data.layers if len(l.frames) for f in l.frames if len(f.strokes) and scn.frame_start <= f.frame_number <= scn.frame_end])))
|
||||
for num in range(scn.frame_start, scn.frame_end+1):
|
||||
scn.frame_set(num)
|
||||
for l in ob.data.layers:
|
||||
if l.hide or l.opacity == 0.0:
|
||||
continue
|
||||
if l.active_frame:
|
||||
for s in l.active_frame.strokes:
|
||||
if len(s.points) == 1: # skip isolated points
|
||||
continue
|
||||
coords_cam_list += [world_to_camera_view(scn, cam, ob.matrix_world @ p.co) for p in s.points]
|
||||
else:
|
||||
# if object is not animated no need to frame_set to update object position
|
||||
print(f'{ob.name} no anim')
|
||||
for l in ob.data.layers:
|
||||
if l.hide or l.opacity == 0.0:
|
||||
continue
|
||||
for f in l.frames:
|
||||
if not (scn.frame_start <= f.frame_number <= scn.frame_end):
|
||||
continue
|
||||
for s in f.strokes:
|
||||
if len(s.points) == 1: # skip isolated points
|
||||
continue
|
||||
coords_cam_list += [world_to_camera_view(scn, cam, ob.matrix_world @ p.co) for p in s.points]
|
||||
|
||||
print(time() - start) # Dbg-time
|
||||
return coords_cam_list
|
||||
|
||||
def has_anim(ob):
|
||||
# TODO make a better check (check if there is only one key in each channel, count as not animated)
|
||||
return ob.animation_data and ob.animation_data.action
|
||||
|
||||
def get_gp_box_all_frame_selection(oblist=None, scn=None, cam=None):
|
||||
'''
|
||||
get points of all selection
|
||||
return 2d bbox in pixels
|
||||
'''
|
||||
|
||||
from bpy_extras.object_utils import world_to_camera_view
|
||||
|
||||
coords_cam_list = []
|
||||
scn = scn or bpy.context.scene
|
||||
oblist = oblist or [o for o in scn.objects if o.select_get()]
|
||||
|
||||
cam = cam or scn.camera
|
||||
start = time()
|
||||
|
||||
if any(has_anim(ob) for ob in oblist):
|
||||
print(f'at least one is animated: {oblist}')
|
||||
for num in range(scn.frame_start, scn.frame_end+1):
|
||||
scn.frame_set(num)
|
||||
for ob in oblist:
|
||||
for l in ob.data.layers:
|
||||
if l.hide or l.opacity == 0.0:
|
||||
continue
|
||||
if not l.active_frame:
|
||||
continue
|
||||
for s in l.active_frame.strokes:
|
||||
if len(s.points) == 1: # skip isolated points
|
||||
continue
|
||||
coords_cam_list += [world_to_camera_view(scn, cam, ob.matrix_world @ p.co) for p in s.points]
|
||||
else:
|
||||
print(f'No anim')
|
||||
for ob in oblist:
|
||||
# if object is not animated no need to frame_set to update object position
|
||||
for l in ob.data.layers:
|
||||
if l.hide or l.opacity == 0.0:
|
||||
continue
|
||||
for f in l.frames:
|
||||
if not (scn.frame_start <= f.frame_number <= scn.frame_end):
|
||||
continue
|
||||
for s in f.strokes:
|
||||
if len(s.points) == 1: # skip isolated points
|
||||
continue
|
||||
coords_cam_list += [world_to_camera_view(scn, cam, ob.matrix_world @ p.co) for p in s.points]
|
||||
|
||||
print(f'{len(coords_cam_list)} gp points listed {time() - start:.1f}s')
|
||||
return coords_cam_list
|
||||
|
||||
def get_bbox_2d(ob, cam=None):
|
||||
from bpy_extras.object_utils import world_to_camera_view
|
||||
scn = bpy.context.scene
|
||||
cam = cam or scn.camera
|
||||
coords2d = [world_to_camera_view(scn, cam, p) for p in get_bbox_3d(ob)]
|
||||
coords2d_x = sorted([c[0] for c in coords2d])
|
||||
coords2d_y = sorted([c[1] for c in coords2d])
|
||||
|
||||
bbox2d_coords = [
|
||||
(coords2d_x[0], coords2d_y[0]),
|
||||
(coords2d_x[0], coords2d_y[-1]),
|
||||
(coords2d_x[-1], coords2d_y[-1]),
|
||||
(coords2d_x[-1], coords2d_y[0]),
|
||||
]
|
||||
|
||||
return [Vector(b) for b in bbox2d_coords]
|
||||
|
||||
|
||||
def set_box_from_selected_objects(scn=None, cam=None, export_json=False):
|
||||
scn = scn or bpy.context.scene
|
||||
cam = cam or scn.camera
|
||||
|
||||
selection = [o for o in scn.objects if o.select_get()] # selected_objects
|
||||
coords = get_gp_box_all_frame_selection(oblist=selection, scn=scn, cam=cam)
|
||||
_bbox_px = set_border_region_from_coord(coords, margin=30, scn=scn, export_json=export_json)
|
||||
|
||||
|
||||
def get_collection_childs_recursive(col, cols=[]):
|
||||
'''return a list of all the sub-collections in passed col'''
|
||||
for sub in col.children:
|
||||
if sub not in cols:
|
||||
cols.append(sub)
|
||||
if len(sub.children):
|
||||
cols = get_collection_childs_recursive(sub, cols)
|
||||
return cols
|
||||
|
||||
def unlink_objects_from_scene(oblist, scn):
|
||||
all_col = [scn.collection]
|
||||
all_col += get_collection_childs_recursive(scn.collection)
|
||||
for col in all_col:
|
||||
for ob in reversed(col.objects):
|
||||
if ob in oblist:
|
||||
col.objects.unlink(ob)
|
||||
|
||||
def remove_scene_nodes_by_obj_names(scn, name_list, negative=False):
|
||||
for n in reversed(scn.node_tree.nodes):
|
||||
if negative:
|
||||
if (n.parent and n.parent.label not in name_list) or (n.type == 'FRAME' and n.label not in name_list):
|
||||
scn.node_tree.nodes.remove(n)
|
||||
else:
|
||||
if (n.parent and n.parent.label in name_list) or (n.type == 'FRAME' and n.label in name_list):
|
||||
scn.node_tree.nodes.remove(n)
|
||||
|
||||
def split_object_to_scene():
|
||||
'''Create a new scene from object selection'''
|
||||
|
||||
active = bpy.context.object
|
||||
scene_name = active.name
|
||||
objs = [o for o in bpy.context.selected_objects]
|
||||
|
||||
if bpy.data.scenes.get(scene_name):
|
||||
print(f'Scene "{scene_name}" Already Exists')
|
||||
raise Exception(f'Scene "{scene_name}" Already Exists')
|
||||
|
||||
src = bpy.context.scene
|
||||
|
||||
bpy.ops.scene.new(type='LINK_COPY')
|
||||
new = bpy.context.scene
|
||||
new.name = scene_name
|
||||
|
||||
## unlink unwanted objects from collection
|
||||
all_col = [new.collection]
|
||||
all_col += get_collection_childs_recursive(new.collection)
|
||||
for col in all_col:
|
||||
for sob in reversed(col.objects):
|
||||
if sob.type in ('CAMERA', 'LIGHT'):
|
||||
continue
|
||||
if sob not in objs:
|
||||
col.objects.unlink(sob)
|
||||
|
||||
frame_names = [n.label for n in new.node_tree.nodes if n.type == 'FRAME' if new.objects.get(n.label)]
|
||||
remove_scene_nodes_by_obj_names(new, frame_names, negative=True)
|
||||
|
||||
bpy.ops.gp.clean_compo_tree()
|
||||
|
||||
# add crop
|
||||
new.render.use_border = True
|
||||
new.render.use_crop_to_border = True
|
||||
new.render.use_compositing = True
|
||||
new.render.use_sequencer = False
|
||||
|
||||
## remove asset from original scene
|
||||
#src_frame_names = [n.label for n in src.node_tree.nodes if n.type == 'FRAME' and n.label in [o.name for o in objs]]
|
||||
#remove_scene_nodes_by_obj_names(src, src_frame_names)
|
||||
remove_scene_nodes_by_obj_names(src, frame_names, negative=False)
|
||||
|
||||
# unlink objects ?
|
||||
unlink_objects_from_scene(objs, src)
|
||||
|
||||
# border to GP objects of the scene
|
||||
gp_objs = [o for o in new.objects if o.type == 'GPENCIL']
|
||||
coords = get_gp_box_all_frame_selection(oblist=gp_objs, scn=new, cam=new.camera)
|
||||
set_border_region_from_coord(coords, margin=30, scn=new, export_json=True)
|
||||
|
||||
export_crop_to_json()
|
||||
|
||||
|
||||
"""
|
||||
def split_object_to_scene():
|
||||
'''Create a new scene from selection'''
|
||||
|
||||
# send objects in a new render scene
|
||||
## define new scene name with active object names
|
||||
active = bpy.context.object
|
||||
scene_name = active.name
|
||||
objs = [o for o in bpy.context.selected_objects]
|
||||
|
||||
rd_scn = bpy.data.scenes.get('Render')
|
||||
## create scene and copy settings from render scene or current
|
||||
# src_scn = bpy.data.scenes.get('Render')
|
||||
# src_scn = src_scn or bpy.context.scene
|
||||
# if src_scn.name == scene_name:
|
||||
# print('! Problem ! Trying to to create new render scene without source')
|
||||
# return
|
||||
|
||||
|
||||
## From current scene (might be Render OR Scene)
|
||||
src_scn = bpy.context.scene
|
||||
|
||||
new = new_scene_from(scene_name, src_scn=src_scn, regen=True) # crop=True, link_cam=True, link_light=True
|
||||
|
||||
for ob in objs:
|
||||
new.collection.objects.link(ob)
|
||||
if ob.type == 'GPENCIL':
|
||||
# recreate VL
|
||||
vl_names = [l.viewlayer_render for l in ob.data.layers if l.viewlayer_render]
|
||||
for names in vl_names:
|
||||
new.view_layers.new(names)
|
||||
# get_set_viewlayer_from_gp(ob, l, scene=new)
|
||||
|
||||
|
||||
def set_crop_bbox_2d(ob, cam=None):
|
||||
'''Basic crop using bouding box on current frame'''
|
||||
from bpy_extras.object_utils import world_to_camera_view
|
||||
|
||||
scn = bpy.context.scene
|
||||
cam = cam or scn.camera
|
||||
# bbox = [ob.matrix_world @ Vector(b) for b in bbox_coords]
|
||||
coords2d = [world_to_camera_view(scn, cam, p) for p in get_bbox_3d(ob)]
|
||||
|
||||
coords2d_x = sorted([c[0] for c in coords2d])
|
||||
coords2d_y = sorted([c[1] for c in coords2d])
|
||||
scn.render.border_min_x = coords2d_x[0]
|
||||
scn.render.border_max_x = coords2d_x[-1]
|
||||
scn.render.border_min_y = coords2d_y[0]
|
||||
scn.render.border_max_y = coords2d_y[-1]
|
||||
return
|
||||
"""
|
10
ui.py
10
ui.py
|
@ -118,6 +118,16 @@ class GPEXP_PT_gp_node_ui(Panel):
|
|||
col.operator('gp.clear_render_tree', icon='X', text='Clear Framed Nodes')
|
||||
col.operator('gp.clear_render_tree', icon='X', text='Clear & Delete Render Scene').mode = "COMPLETE"
|
||||
|
||||
layout.separator()
|
||||
layout.label(text='Sub Scenes:')
|
||||
layout.operator('gp.split_to_scene', icon='DUPLICATE', text='Split To Scene')
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator('gp.set_crop_from_selection', icon='CON_OBJECTSOLVER', text='Set Crop')
|
||||
row.operator('gp.export_crop_coord_to_json', icon='FILE', text='Export json')
|
||||
|
||||
layout.operator('gp.render_all_scenes', icon='RENDER_ANIMATION', text='Render All Sub-Scene')
|
||||
|
||||
layout.prop(prefs, 'advanced', text='Show Advanced Options')
|
||||
# layout.operator('gp.add_object_to_render', icon='RENDERLAYERS', text='Layer To Render').mode = 'ALL'
|
||||
# layout.operator('gp.add_object_to_render', icon='RENDERLAYERS', text='Layer To Render').mode = 'SELECTED'
|
||||
|
|
Loading…
Reference in New Issue