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 scenes
This commit is contained in:
		
							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'] | ||||
|  | ||||
							
								
								
									
										51
									
								
								OP_crop_to_object.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								OP_crop_to_object.py
									
									
									
									
									
										Normal file
									
								
							| @ -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) | ||||
							
								
								
									
										47
									
								
								OP_render_scenes.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								OP_render_scenes.py
									
									
									
									
									
										Normal file
									
								
							| @ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user