Refactor to allow separate gp render scene and compositing scene
1.7.0 - fix: problem when removing render layers - changed: node distribution refactor, allow separate compositing scene - Compositing scene (holding nodes) can be separated from render scene (holding GP objects and related viewlayers) - Default render named changed from `Render` to `RenderGP` - New properties in exposed Dopesheet N panel to manually set Render scene and Compo scene - Operator expose a `node_scene` parameter to separate where to send nodes - Switch scene button can have an extra button to go in compo scene if foundmain
parent
fb810c1256
commit
c6c9c7f56c
|
@ -1,2 +1,4 @@
|
||||||
__pycache__
|
__pycache__
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
|
pyrightconfig.json
|
||||||
|
*.vscode
|
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -14,6 +14,16 @@ Activate / deactivate layer opacity according to prefix
|
||||||
Activate / deactivate all masks using MA layers
|
Activate / deactivate all masks using MA layers
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
1.7.0
|
||||||
|
|
||||||
|
- fix: problem when removing render layers
|
||||||
|
- changed: node distribution refactor, allow separate compositing scene
|
||||||
|
- Compositing scene (holding nodes) can be separated from render scene (holding GP objects and related viewlayers)
|
||||||
|
- Default render named changed from `Render` to `RenderGP`
|
||||||
|
- New properties in exposed Dopesheet N panel to manually set Render scene and Compo scene
|
||||||
|
- Operator expose a `node_scene` parameter to separate where to send nodes
|
||||||
|
- Switch scene button can have an extra button to go in compo scene if found
|
||||||
|
|
||||||
1.6.4
|
1.6.4
|
||||||
|
|
||||||
- changed: remove poll checking gp active on some operators limiting use in script call
|
- changed: remove poll checking gp active on some operators limiting use in script call
|
||||||
|
|
133
OP_add_layer.py
133
OP_add_layer.py
|
@ -1,6 +1,40 @@
|
||||||
import bpy
|
import bpy
|
||||||
from . import gen_vlayer, fn
|
from . import gen_vlayer, fn
|
||||||
|
|
||||||
|
|
||||||
|
def add_layer_to_render(ob, node_scene=None):
|
||||||
|
'''Send GP object to render layer
|
||||||
|
return a tuple with report message'''
|
||||||
|
|
||||||
|
# ob = ob or bpy.context.object
|
||||||
|
layer = ob.data.layers.active
|
||||||
|
if not layer:
|
||||||
|
return ('ERROR', 'No active layer')
|
||||||
|
|
||||||
|
node_scene = fn.get_compo_scene(scene_name=node_scene, create=True)
|
||||||
|
|
||||||
|
ct = 0
|
||||||
|
# send scene ?
|
||||||
|
hidden = 0
|
||||||
|
for l in ob.data.layers:
|
||||||
|
if not l.select:
|
||||||
|
if not l.viewlayer_render:
|
||||||
|
# TODO : need to link, can raise error if object is not linked in Render scene yet
|
||||||
|
l.viewlayer_render = fn.get_view_layer('exclude').name
|
||||||
|
continue
|
||||||
|
|
||||||
|
gen_vlayer.get_set_viewlayer_from_gp(ob, l, node_scene=node_scene)
|
||||||
|
|
||||||
|
if l.hide:
|
||||||
|
hidden += 1
|
||||||
|
ct += 1
|
||||||
|
|
||||||
|
if hidden:
|
||||||
|
return ('WARNING', f'{hidden}/{ct} layers are hidden!')
|
||||||
|
|
||||||
|
else:
|
||||||
|
return ('INFO', f'{ct} layer(s) added')
|
||||||
|
|
||||||
class GPEXP_OT_add_layer_to_render(bpy.types.Operator):
|
class GPEXP_OT_add_layer_to_render(bpy.types.Operator):
|
||||||
bl_idname = "gp.add_layer_to_render"
|
bl_idname = "gp.add_layer_to_render"
|
||||||
bl_label = "Add Gp Layer as render nodes"
|
bl_label = "Add Gp Layer as render nodes"
|
||||||
|
@ -12,73 +46,76 @@ class GPEXP_OT_add_layer_to_render(bpy.types.Operator):
|
||||||
return context.object and context.object.type == 'GPENCIL'
|
return context.object and context.object.type == 'GPENCIL'
|
||||||
|
|
||||||
# mode : bpy.props.StringProperty(options={'SKIP_SAVE'})
|
# mode : bpy.props.StringProperty(options={'SKIP_SAVE'})
|
||||||
|
node_scene : bpy.props.StringProperty(default='',
|
||||||
|
description='Scene where to add nodes, Abort if not found', options={'SKIP_SAVE'})
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
ob = context.object
|
ret = add_layer_to_render(context.object, node_scene=self.node_scene)
|
||||||
layer = ob.data.layers.active
|
if isinstance(ret, tuple):
|
||||||
if not layer:
|
self.report({ret[0]}, ret[1])
|
||||||
self.report({'ERROR'}, 'No active layer')
|
if ret[0] == 'ERROR':
|
||||||
return {"CANCELLED"}
|
return {'CANCELLED'}
|
||||||
|
|
||||||
ct = 0
|
|
||||||
# send scene ?
|
|
||||||
hided = 0
|
|
||||||
for l in ob.data.layers:
|
|
||||||
if not l.select:
|
|
||||||
if not l.viewlayer_render:
|
|
||||||
# TODO : need to link, can raise error if object is not linked in Render scene yet
|
|
||||||
l.viewlayer_render = fn.get_view_layer('exclude').name
|
|
||||||
continue
|
|
||||||
gen_vlayer.get_set_viewlayer_from_gp(ob, l)
|
|
||||||
|
|
||||||
if l.hide:
|
|
||||||
hided += 1
|
|
||||||
ct += 1
|
|
||||||
|
|
||||||
if hided:
|
|
||||||
self.report({'WARNING'}, f'{hided}/{ct} layers are hided !')
|
|
||||||
|
|
||||||
else:
|
|
||||||
self.report({'INFO'}, f'{ct} layer(s) added to scene "Render"')
|
|
||||||
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
def add_object_to_render(mode='ALL', scene='', node_scene=''):
|
||||||
|
context = bpy.context
|
||||||
|
|
||||||
|
if scene:
|
||||||
|
scn = fn.get_render_scene(scene)
|
||||||
|
else:
|
||||||
|
scn = fn.get_render_scene()
|
||||||
|
|
||||||
|
if node_scene:
|
||||||
|
node_scn = fn.get_compo_scene(scene_name=node_scene, create=True)
|
||||||
|
if not node_scn:
|
||||||
|
return ('ERROR', f'/!\ Node Scene "{node_scene}" not found ! Abort "Add object to Render" !')
|
||||||
|
else:
|
||||||
|
# if not passed add in render scene
|
||||||
|
node_scn = scn
|
||||||
|
|
||||||
|
excludes = [] # ['MA', 'IN'] # Get list dynamically
|
||||||
|
if mode == 'SELECTED':
|
||||||
|
gen_vlayer.export_gp_objects([o for o in context.selected_objects if o.type == 'GPENCIL'], exclude_list=excludes, scene=scn, node_scene=node_scn)
|
||||||
|
|
||||||
|
elif mode == 'ALL':
|
||||||
|
gen_vlayer.export_gp_objects([o for o in context.scene.objects if o.type == 'GPENCIL' and not o.hide_get() and fn.is_valid_name(o.name)], exclude_list=excludes, scene=scn, node_scene=node_scn)
|
||||||
|
|
||||||
|
|
||||||
## send operator with mode ALL or SELECTED to batch build
|
## send operator with mode ALL or SELECTED to batch build
|
||||||
class GPEXP_OT_add_objects_to_render(bpy.types.Operator):
|
class GPEXP_OT_add_objects_to_render(bpy.types.Operator):
|
||||||
bl_idname = "gp.add_object_to_render"
|
bl_idname = "gp.add_object_to_render"
|
||||||
bl_label = "Add all Gp Layer of active object as render nodes"
|
bl_label = "Add all Gp Layer of active object as render nodes"
|
||||||
bl_description = "Setup GP object in render scene\
|
bl_description = "Setup GP object in render scene\
|
||||||
\nNote: 'send all' mode skip hided objects"
|
\nNote: 'send all' mode skip hidden objects"
|
||||||
bl_options = {"REGISTER"}
|
bl_options = {"REGISTER"}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return context.object and context.object.type == 'GPENCIL'
|
return context.object and context.object.type == 'GPENCIL'
|
||||||
|
|
||||||
mode : bpy.props.StringProperty(default='ALL', options={'SKIP_SAVE'})
|
mode : bpy.props.EnumProperty(
|
||||||
|
items=(
|
||||||
|
('ALL', 'All', 'All objects', 0),
|
||||||
|
('SELECTED', 'Selected', 'Selected objects', 0),
|
||||||
|
),
|
||||||
|
default='ALL', options={'SKIP_SAVE'},
|
||||||
|
description='Choice to send all or only selected objects')
|
||||||
|
|
||||||
|
scene : bpy.props.StringProperty(default='',
|
||||||
|
description='Scene where to link object and create viewlayer (create if not exists)', options={'SKIP_SAVE'})
|
||||||
|
|
||||||
|
node_scene : bpy.props.StringProperty(default='',
|
||||||
|
description='Scene where to add nodes, Abort if not found', options={'SKIP_SAVE'})
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
# create render scene
|
ret = add_object_to_render(mode=self.mode, scene=self.scene, node_scene=self.node_scene)
|
||||||
if context.scene.name == 'Scene':
|
if isinstance(ret, tuple):
|
||||||
scn = fn.get_render_scene()
|
self.report({ret[0]}, ret[1])
|
||||||
else:
|
if ret[0] == 'ERROR':
|
||||||
scn = context.scene
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
|
||||||
excludes = [] # ['MA', 'IN'] # Get list dynamically
|
|
||||||
if self.mode == 'SELECTED':
|
|
||||||
gen_vlayer.export_gp_objects([o for o in context.selected_objects if o.type == 'GPENCIL'], exclude_list=excludes, scene=scn)
|
|
||||||
|
|
||||||
elif self.mode == 'ALL':
|
|
||||||
# scn = bpy.data.scenes.get('Scene')
|
|
||||||
# if not scn:
|
|
||||||
# self.report({'ERROR'}, 'Could not found default scene')
|
|
||||||
# return {"CANCELLED"}
|
|
||||||
gen_vlayer.export_gp_objects([o for o in context.scene.objects if o.type == 'GPENCIL' and not o.hide_get() and fn.is_valid_name(o.name)], exclude_list=excludes, scene=scn)
|
|
||||||
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class GPEXP_OT_split_to_scene(bpy.types.Operator):
|
class GPEXP_OT_split_to_scene(bpy.types.Operator):
|
||||||
bl_idname = "gp.split_to_scene"
|
bl_idname = "gp.split_to_scene"
|
||||||
bl_label = "Split Objects To Scene"
|
bl_label = "Split Objects To Scene"
|
||||||
|
|
|
@ -2,9 +2,10 @@ import bpy
|
||||||
import re
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from . import gen_vlayer, fn
|
from . import gen_vlayer, fn
|
||||||
|
from bpy.props import (BoolProperty, StringProperty)
|
||||||
|
|
||||||
|
|
||||||
def batch_setup_render_scene(context=None, render_scn=None, preview=True):
|
def batch_setup_render_scene(context=None, render_scn=None, node_scene=None, preview=True):
|
||||||
'''A series of setup actions for Render scene:
|
'''A series of setup actions for Render scene:
|
||||||
- renumber fileout
|
- renumber fileout
|
||||||
- Clean compo Tree
|
- Clean compo Tree
|
||||||
|
@ -15,10 +16,12 @@ def batch_setup_render_scene(context=None, render_scn=None, preview=True):
|
||||||
if context is None:
|
if context is None:
|
||||||
context = bpy.context
|
context = bpy.context
|
||||||
if render_scn is None:
|
if render_scn is None:
|
||||||
render_scn = bpy.data.scenes.get('Render')
|
render_scn = fn.get_render_scene(create=False)
|
||||||
if not render_scn:
|
if not render_scn:
|
||||||
print('"Render" scene not found in batch_setup_render_scene')
|
print('Render scene not found in batch_setup_render_scene')
|
||||||
return
|
return
|
||||||
|
if node_scene is None:
|
||||||
|
node_scene = render_scn
|
||||||
|
|
||||||
## Renumber File outputs
|
## Renumber File outputs
|
||||||
print('Renumber File outputs')
|
print('Renumber File outputs')
|
||||||
|
@ -49,8 +52,7 @@ def batch_setup_render_scene(context=None, render_scn=None, preview=True):
|
||||||
## Clean compo Tree
|
## Clean compo Tree
|
||||||
print('Clean compo Tree')
|
print('Clean compo Tree')
|
||||||
|
|
||||||
bpy.ops.gp.clean_compo_tree('EXEC_DEFAULT', use_render_scene=True)
|
bpy.ops.gp.clean_compo_tree('EXEC_DEFAULT', scene=render_scn.name, node_scene=node_scene.name)
|
||||||
# bpy.ops.gp.clean_compo_tree('INVOKE_DEFAULT', use_render_scene=True)
|
|
||||||
|
|
||||||
## Trigger check file before finishing ?
|
## Trigger check file before finishing ?
|
||||||
# bpy.ops.gp.check_render_scene('INVOKE_DEFAULT')
|
# bpy.ops.gp.check_render_scene('INVOKE_DEFAULT')
|
||||||
|
@ -63,55 +65,57 @@ class GPEXP_OT_render_auto_build(bpy.types.Operator):
|
||||||
bl_description = "Trigger all operation to make build render scene with default settings"
|
bl_description = "Trigger all operation to make build render scene with default settings"
|
||||||
bl_options = {"REGISTER"}
|
bl_options = {"REGISTER"}
|
||||||
|
|
||||||
# @classmethod
|
scene : StringProperty(name="Target Scene Name",
|
||||||
# def poll(cls, context):
|
description="Render scene to send GP to on a named scene, abort if not exists (not exposed)",
|
||||||
# return context.object and context.object.type == 'GPENCIL'
|
default='', options={'SKIP_SAVE'})
|
||||||
|
|
||||||
# timer : bpy.props.FloatProperty(default=0.1, options={'SKIP_SAVE'})
|
node_scene : StringProperty(name="Compositing Scene",
|
||||||
|
description="Name of the scene holding compositing nodes",
|
||||||
|
default='', options={'SKIP_SAVE'})
|
||||||
|
|
||||||
make_gp_single_user : bpy.props.BoolProperty(name='Set Single User Data', default=True,
|
make_gp_single_user : BoolProperty(name='Set Single User Data', default=True,
|
||||||
description='Set single user on all objects GP data')
|
description='Set single user on all objects GP data')
|
||||||
|
|
||||||
excluded_prefix : bpy.props.StringProperty(
|
excluded_prefix : StringProperty(
|
||||||
name='Excluded Layer By Prefix', default='GP, RG, PO, MA',
|
name='Excluded Layer By Prefix', default='GP, RG, PO, MA',
|
||||||
description='Exclude layer to send to render by prefix (comma separated list)')
|
description='Exclude layer to send to render by prefix (comma separated list)')
|
||||||
|
|
||||||
clean_name_and_visibility : bpy.props.BoolProperty(name='Clean Name And Visibility', default=True,
|
clean_name_and_visibility : BoolProperty(name='Clean Name And Visibility', default=True,
|
||||||
description='Add object name to layer name when there is only prefix (ex: "CO_")\
|
description='Add object name to layer name when there is only prefix (ex: "CO_")\
|
||||||
\nEnable visibility for layer with prefix included in Prefix Filter')
|
\nEnable visibility for layer with prefix included in Prefix Filter')
|
||||||
|
|
||||||
clean_material_duplication : bpy.props.BoolProperty(name='Clean Material Duplication', default=True,
|
clean_material_duplication : BoolProperty(name='Clean Material Duplication', default=True,
|
||||||
description='Clean material stack. i.e: Replace "mat.001" in material stack if "mat" exists and has same color')
|
description='Clean material stack. i.e: Replace "mat.001" in material stack if "mat" exists and has same color')
|
||||||
|
|
||||||
prefix_filter : bpy.props.StringProperty(name='Prefix Filter', default='CO, CU, FX, TO', # , MA # exclude MA if mask are applied
|
prefix_filter : StringProperty(name='Prefix Filter', default='CO, CU, FX, TO', # , MA # exclude MA if mask are applied
|
||||||
description='Comma separated prefix to render. Set the other prefix and non-prefixed layer to exluded viewlayer')
|
description='Comma separated prefix to render. Set the other prefix and non-prefixed layer to exluded viewlayer')
|
||||||
|
|
||||||
set_layers_colors : bpy.props.BoolProperty(name='Set Layers Colors', default=True,
|
set_layers_colors : BoolProperty(name='Set Layers Colors', default=True,
|
||||||
description='Set colors for on layers according to prefix (hadrcoded color set)')
|
description='Set colors for on layers according to prefix (hadrcoded color set)')
|
||||||
|
|
||||||
trigger_rename_lowercase : bpy.props.BoolProperty(name='Trigger Rename Lowercase', default=True,
|
trigger_rename_lowercase : BoolProperty(name='Trigger Rename Lowercase', default=True,
|
||||||
description='Rename all layer names lowercase')
|
description='Rename all layer names lowercase')
|
||||||
|
|
||||||
trigger_renumber_by_distance : bpy.props.BoolProperty(name='Trigger Renumber By Distance', default=True,
|
trigger_renumber_by_distance : BoolProperty(name='Trigger Renumber By Distance', default=True,
|
||||||
description='Renumber object accordind to distance from camera and In-Front value')
|
description='Renumber object accordind to distance from camera and In-Front value')
|
||||||
|
|
||||||
export_layer_infos : bpy.props.BoolProperty(name='Export Layer Infos', default=True,
|
export_layer_infos : BoolProperty(name='Export Layer Infos', default=True,
|
||||||
description='Export layers infos to a Json file')
|
description='Export layers infos to a Json file')
|
||||||
|
|
||||||
group_all_adjacent_layer_type : bpy.props.BoolProperty(name='Group All Adjacent Layer Type', default=False,
|
group_all_adjacent_layer_type : BoolProperty(name='Group All Adjacent Layer Type', default=False,
|
||||||
description='Fuse output Viewlayer according to adjacent Prefix in layer stack')
|
description='Fuse output Viewlayer according to adjacent Prefix in layer stack')
|
||||||
|
|
||||||
change_to_gp_workspace : bpy.props.BoolProperty(name='Change To Gp Workspace', default=True,
|
change_to_gp_workspace : BoolProperty(name='Change To Gp Workspace', default=True,
|
||||||
description='Switch to "GP Render" workspace shipped with addon')
|
description='Switch to "GP Render" workspace shipped with addon')
|
||||||
|
|
||||||
batch_setup_render_scene : bpy.props.BoolProperty(name='Batch Setup Render Scene', default=True,
|
batch_setup_render_scene : BoolProperty(name='Batch Setup Render Scene', default=True,
|
||||||
description='- Renumber fileoutputs\
|
description='- Renumber fileoutputs\
|
||||||
\n- Clean compo Tree\
|
\n- Clean compo Tree\
|
||||||
\n- Go to camera view in visible viewports\
|
\n- Go to camera view in visible viewports\
|
||||||
\n- Swap to bg cam'
|
\n- Swap to bg cam'
|
||||||
)
|
)
|
||||||
|
|
||||||
add_preview : bpy.props.BoolProperty(name='Add Preview', default=True,
|
add_preview : BoolProperty(name='Add Preview', default=True,
|
||||||
description='Create preview with stacked alpha over on render layers')
|
description='Create preview with stacked alpha over on render layers')
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
|
@ -153,10 +157,21 @@ class GPEXP_OT_render_auto_build(bpy.types.Operator):
|
||||||
prefix_to_render = [p.strip() for p in self.prefix_filter.split(',')]
|
prefix_to_render = [p.strip() for p in self.prefix_filter.split(',')]
|
||||||
print('prefix_to_render: ', prefix_to_render)
|
print('prefix_to_render: ', prefix_to_render)
|
||||||
|
|
||||||
render_scn = bpy.data.scenes.get('Render')
|
render_scn = fn.get_render_scene(create=False)
|
||||||
if render_scn:
|
if self.scene:
|
||||||
self.report({'ERROR'}, 'A "Render" scene already exists')
|
render_scn = bpy.data.scenes.get(self.scene)
|
||||||
return {'CANCELLED'}
|
if render_scn:
|
||||||
|
self.report({'ERROR'}, f'Abort, scene "{render_scn.name}" already exists')
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
if self.node_scene:
|
||||||
|
node_scene = fn.get_compo_scene(scene_name=self.node_scene, create=True) # create if not exists
|
||||||
|
## Set scene target in source scene
|
||||||
|
context.scene.gp_render_settings.node_scene = node_scene.name
|
||||||
|
else:
|
||||||
|
node_scene = fn.get_compo_scene(create=True)
|
||||||
|
if not node_scene:
|
||||||
|
node_scene = render_scn
|
||||||
|
|
||||||
all_gp_objects = [o for o in context.scene.objects if o.type == 'GPENCIL']
|
all_gp_objects = [o for o in context.scene.objects if o.type == 'GPENCIL']
|
||||||
|
|
||||||
|
@ -206,13 +221,11 @@ class GPEXP_OT_render_auto_build(bpy.types.Operator):
|
||||||
if self.trigger_rename_lowercase:
|
if self.trigger_rename_lowercase:
|
||||||
print('Trigger rename lowercase')
|
print('Trigger rename lowercase')
|
||||||
bpy.ops.gp.lower_layers_name('EXEC_DEFAULT')
|
bpy.ops.gp.lower_layers_name('EXEC_DEFAULT')
|
||||||
# bpy.ops.gp.lower_layers_name('INVOKE_DEFAULT')
|
|
||||||
|
|
||||||
## Trigger renumber by distance
|
## Trigger renumber by distance
|
||||||
if self.trigger_renumber_by_distance:
|
if self.trigger_renumber_by_distance:
|
||||||
print('Trigger renumber by distance')
|
print('Trigger renumber by distance')
|
||||||
bpy.ops.gp.auto_number_object('EXEC_DEFAULT')
|
bpy.ops.gp.auto_number_object('EXEC_DEFAULT')
|
||||||
# bpy.ops.gp.auto_number_object('INVOKE_DEFAULT')
|
|
||||||
|
|
||||||
## Export layer infos ? (skip if json already exists)
|
## Export layer infos ? (skip if json already exists)
|
||||||
if self.export_layer_infos:
|
if self.export_layer_infos:
|
||||||
|
@ -226,13 +239,13 @@ class GPEXP_OT_render_auto_build(bpy.types.Operator):
|
||||||
o.data = o.data.copy()
|
o.data = o.data.copy()
|
||||||
|
|
||||||
## Send all GP to render scene
|
## Send all GP to render scene
|
||||||
print('Send all GP to render scene')
|
print('Send all GP to render scene (Create render scene if needed)')
|
||||||
# bpy.ops.gp.add_object_to_render(mode="ALL") # Ops to send all
|
# bpy.ops.gp.add_object_to_render(mode="ALL") # Ops to send all
|
||||||
gen_vlayer.export_gp_objects(ob_list, exclude_list=self.excluded_prefix) # Create render scene OTF
|
gen_vlayer.export_gp_objects(ob_list, exclude_list=self.excluded_prefix, scene=render_scn, node_scene=node_scene) # Create render scene OTF
|
||||||
|
|
||||||
## Switch to new Render Scene
|
## Switch to new Render Scene
|
||||||
print('Switch to new Render Scene')
|
print('Switch to new Render Scene')
|
||||||
render_scn = bpy.data.scenes.get('Render')
|
render_scn = fn.get_render_scene(create=False)
|
||||||
if not render_scn:
|
if not render_scn:
|
||||||
self.report({'ERROR'}, 'No render scene found')
|
self.report({'ERROR'}, 'No render scene found')
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
@ -268,13 +281,13 @@ class GPEXP_OT_render_auto_build(bpy.types.Operator):
|
||||||
# context.scene.update_tag()
|
# context.scene.update_tag()
|
||||||
|
|
||||||
## Batch setup render scene
|
## Batch setup render scene
|
||||||
batch_setup_render_scene(render_scn=render_scn)
|
batch_setup_render_scene(render_scn=render_scn, node_scene=node_scene)
|
||||||
|
|
||||||
## create preview
|
## create preview
|
||||||
|
|
||||||
if self.add_preview:
|
if self.add_preview:
|
||||||
from .OP_merge_layers import merge_compositor_preview
|
from .OP_merge_layers import merge_compositor_preview
|
||||||
merge_compositor_preview(scene=render_scn)
|
merge_compositor_preview(scene=node_scene)
|
||||||
|
|
||||||
## No need for timer anymore !
|
## No need for timer anymore !
|
||||||
# if batch_setup_render_scene:
|
# if batch_setup_render_scene:
|
||||||
|
@ -295,7 +308,6 @@ class GPEXP_OT_render_auto_build(bpy.types.Operator):
|
||||||
# bpy.ops.gp.check_render_scene('INVOKE_DEFAULT')
|
# bpy.ops.gp.check_render_scene('INVOKE_DEFAULT')
|
||||||
## Note: After all these operation, a ctrl+Z might crash
|
## Note: After all these operation, a ctrl+Z might crash
|
||||||
|
|
||||||
|
|
||||||
print('\nDone.\n')
|
print('\nDone.\n')
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ def check_broken_modifier_target(pool=None, reports=None):
|
||||||
reports.append(f'Broken modifier target :{o.name} > {m.name} > {m.layer}')
|
reports.append(f'Broken modifier target :{o.name} > {m.name} > {m.layer}')
|
||||||
# else:
|
# else:
|
||||||
# print(f'Modifier target :{o.name} > {m.name} > ok')
|
# print(f'Modifier target :{o.name} > {m.name} > ok')
|
||||||
|
|
||||||
|
|
||||||
return reports
|
return reports
|
||||||
|
|
||||||
|
@ -72,10 +71,19 @@ def check_file_output_numbering(reports=None):
|
||||||
if not prenum.match(fo.base_path.split('/')[-1]):
|
if not prenum.match(fo.base_path.split('/')[-1]):
|
||||||
reports.append(f'No object numbering : node {fo.name}')
|
reports.append(f'No object numbering : node {fo.name}')
|
||||||
pct = 0
|
pct = 0
|
||||||
slots = fo.layer_slots if fo.format.file_format == 'OPEN_EXR_MULTILAYER' else fo.file_slots
|
if fo.format.file_format == 'OPEN_EXR_MULTILAYER':
|
||||||
for fs in slots:
|
## multilayer use layer_slots > slot.name
|
||||||
if not prenum.match(fs.path.split('/')[0]):
|
slots = fo.layer_slots
|
||||||
pct += 1
|
for fs in slots:
|
||||||
|
if not prenum.match(fs.name.split('/')[0]):
|
||||||
|
pct += 1
|
||||||
|
else:
|
||||||
|
## classic use file_slots > path
|
||||||
|
slots = fo.file_slots
|
||||||
|
for fs in slots:
|
||||||
|
if not prenum.match(fs.path.split('/')[0]):
|
||||||
|
pct += 1
|
||||||
|
|
||||||
if pct:
|
if pct:
|
||||||
reports.append(f'{pct}/{len(slots)} slots not numbered: node {fo.name}')
|
reports.append(f'{pct}/{len(slots)} slots not numbered: node {fo.name}')
|
||||||
|
|
||||||
|
@ -96,11 +104,11 @@ class GPEXP_OT_check_render_scene(bpy.types.Operator):
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
return self.execute(context)
|
return self.execute(context)
|
||||||
return context.window_manager.invoke_props_dialog(self)
|
# return context.window_manager.invoke_props_dialog(self)
|
||||||
|
|
||||||
def draw(self, context):
|
# def draw(self, context):
|
||||||
layout = self.layout
|
# layout = self.layout
|
||||||
# layout.prop(self, 'clear_unused_view_layers')
|
# # layout.prop(self, 'clear_unused_view_layers')
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
reports = []
|
reports = []
|
||||||
|
|
53
OP_clean.py
53
OP_clean.py
|
@ -1,4 +1,5 @@
|
||||||
import bpy
|
import bpy
|
||||||
|
from bpy.props import BoolProperty, StringProperty
|
||||||
from . import fn
|
from . import fn
|
||||||
|
|
||||||
class GPEXP_OT_clean_compo_tree(bpy.types.Operator):
|
class GPEXP_OT_clean_compo_tree(bpy.types.Operator):
|
||||||
|
@ -8,31 +9,35 @@ class GPEXP_OT_clean_compo_tree(bpy.types.Operator):
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
# Internal prop (use when launching from python)
|
# Internal prop (use when launching from python)
|
||||||
use_render_scene : bpy.props.BoolProperty(name="Use Render Scene",
|
scene : StringProperty(name="Rener Scene",
|
||||||
description="Force the clean on scene named Render, abort if not exists (not exposed)",
|
description="Scene to clear node from",
|
||||||
default=False, options={'SKIP_SAVE'})
|
default='', options={'SKIP_SAVE'})
|
||||||
|
|
||||||
clear_unused_view_layers : bpy.props.BoolProperty(name="Clear unused view layers",
|
node_scene : StringProperty(name="Compositing Scene",
|
||||||
|
description="Scene containing nodes, using current scene when calling operator from UI to clean node",
|
||||||
|
default='', options={'SKIP_SAVE'})
|
||||||
|
|
||||||
|
clear_unused_view_layers : BoolProperty(name="Clear unused view layers",
|
||||||
description="Delete view layer that aren't used in the nodetree anymore",
|
description="Delete view layer that aren't used in the nodetree anymore",
|
||||||
default=True)
|
default=True)
|
||||||
|
|
||||||
arrange_rl_nodes : bpy.props.BoolProperty(name="Arrange Render Node In Frames",
|
arrange_rl_nodes : BoolProperty(name="Arrange Render Node In Frames",
|
||||||
description="Re-arrange Render Layer Nodes Y positions within each existing frames" ,
|
description="Re-arrange Render Layer Nodes Y positions within each existing frames" ,
|
||||||
default=True)
|
default=True)
|
||||||
|
|
||||||
arrange_frames : bpy.props.BoolProperty(name="Arrange Frames",
|
arrange_frames : BoolProperty(name="Arrange Frames",
|
||||||
description="Re-arrange all frames Y positions" ,
|
description="Re-arrange all frames Y positions" ,
|
||||||
default=True)
|
default=True)
|
||||||
|
|
||||||
reorder_inputs : bpy.props.BoolProperty(name="Reorder I/O Sockets",
|
reorder_inputs : BoolProperty(name="Reorder I/O Sockets",
|
||||||
description="Reorder inputs/outputs of all 'NG_' nodegroup and their connected file output",
|
description="Reorder inputs/outputs of all 'NG_' nodegroup and their connected file output",
|
||||||
default=True)
|
default=True)
|
||||||
|
|
||||||
clear_isolated_node_in_groups : bpy.props.BoolProperty(name="Clear Isolated Node In Groups",
|
clear_isolated_node_in_groups : BoolProperty(name="Clear Isolated Node In Groups",
|
||||||
description="Clean content of 'NG_' nodegroup bpy deleting isolated nodes)",
|
description="Clean content of 'NG_' nodegroup bpy deleting isolated nodes)",
|
||||||
default=True)
|
default=True)
|
||||||
|
|
||||||
fo_clear_disconnected : bpy.props.BoolProperty(name="Remove Disconnected Export Inputs",
|
fo_clear_disconnected : BoolProperty(name="Remove Disconnected Export Inputs",
|
||||||
description="Clear any disconnected intput of every 'file output' node",
|
description="Clear any disconnected intput of every 'file output' node",
|
||||||
default=False)
|
default=False)
|
||||||
|
|
||||||
|
@ -62,29 +67,39 @@ class GPEXP_OT_clean_compo_tree(bpy.types.Operator):
|
||||||
# box.prop(self, 'fo_clear_disconnected')
|
# box.prop(self, 'fo_clear_disconnected')
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
if self.use_render_scene:
|
|
||||||
render = bpy.data.scenes.get('Render')
|
if self.scene:
|
||||||
if not render:
|
scn = bpy.data.scenes.get(self.scene)
|
||||||
print('SKIP clean_compo_tree, No "Render" scene !')
|
if not scn:
|
||||||
|
print(f'SKIP clean_compo_tree, No "{self.scene}" render scene found!')
|
||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
else:
|
else:
|
||||||
render = context.scene
|
scn = fn.get_render_scene(create=False)
|
||||||
|
|
||||||
|
if self.node_scene:
|
||||||
|
node_scene = bpy.data.scenes.get(self.node_scene)
|
||||||
|
if not scn:
|
||||||
|
print(f'SKIP clean_compo_tree, No "{self.node_scene}" compo scene found!')
|
||||||
|
return {"CANCELLED"}
|
||||||
|
else:
|
||||||
|
node_scene = context.scene
|
||||||
|
|
||||||
nodes = render.node_tree.nodes
|
nodes = node_scene.node_tree.nodes
|
||||||
for n in nodes:
|
for n in nodes:
|
||||||
n.update()
|
n.update()
|
||||||
|
|
||||||
if self.clear_unused_view_layers:
|
if self.clear_unused_view_layers:
|
||||||
used_rlayer_names = [n.layer for n in nodes if n.type == 'R_LAYERS']
|
used_rlayer_names = [n.layer for n in nodes if n.type == 'R_LAYERS']
|
||||||
for rl in reversed(render.view_layers):
|
for rl in reversed(scn.view_layers):
|
||||||
if rl.name in used_rlayer_names or rl.name == 'View Layer':
|
if rl.name in used_rlayer_names or rl.name == 'View Layer':
|
||||||
continue
|
continue
|
||||||
render.view_layers.remove(rl)
|
scn.view_layers.remove(rl)
|
||||||
|
|
||||||
if self.arrange_rl_nodes:
|
if self.arrange_rl_nodes:
|
||||||
fn.rearrange_rlayers_in_frames(render.node_tree)
|
fn.rearrange_rlayers_in_frames(node_scene.node_tree)
|
||||||
|
|
||||||
if self.arrange_frames:
|
if self.arrange_frames:
|
||||||
fn.rearrange_frames(render.node_tree)
|
fn.rearrange_frames(node_scene.node_tree)
|
||||||
|
|
||||||
if self.reorder_inputs:
|
if self.reorder_inputs:
|
||||||
for n in nodes:
|
for n in nodes:
|
||||||
|
|
49
OP_clear.py
49
OP_clear.py
|
@ -1,5 +1,6 @@
|
||||||
import bpy
|
import bpy
|
||||||
|
from . import fn
|
||||||
|
from .constant import RD_SCENE_NAME
|
||||||
|
|
||||||
class GPEXP_OT_clear_render_tree(bpy.types.Operator):
|
class GPEXP_OT_clear_render_tree(bpy.types.Operator):
|
||||||
bl_idname = "gp.clear_render_tree"
|
bl_idname = "gp.clear_render_tree"
|
||||||
|
@ -15,43 +16,47 @@ class GPEXP_OT_clear_render_tree(bpy.types.Operator):
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
|
||||||
render = bpy.data.scenes.get('Render')
|
render = fn.get_render_scene(create=False)
|
||||||
if not render:
|
if not render:
|
||||||
print('SKIP, no Render scene')
|
print(f'SKIP, no {RD_SCENE_NAME} scene')
|
||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
|
|
||||||
# clear all nodes in frames
|
scn = context.scene
|
||||||
if render.use_nodes:
|
|
||||||
for i in range(len(render.node_tree.nodes))[::-1]:
|
|
||||||
|
|
||||||
# skip frames to delete later
|
# Clear all nodes in frames
|
||||||
if render.node_tree.nodes[i].type == 'FRAME':
|
if scn.use_nodes:
|
||||||
|
for i in range(len(scn.node_tree.nodes))[::-1]:
|
||||||
|
|
||||||
|
# Skip frames to delete later
|
||||||
|
if scn.node_tree.nodes[i].type == 'FRAME':
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# skip unparented nodes
|
# Skip unparented nodes
|
||||||
if not render.node_tree.nodes[i].parent:
|
if not scn.node_tree.nodes[i].parent:
|
||||||
continue
|
continue
|
||||||
render.node_tree.nodes.remove(render.node_tree.nodes[i])
|
scn.node_tree.nodes.remove(scn.node_tree.nodes[i])
|
||||||
|
|
||||||
# delete all framesWorki
|
# Delete all frames
|
||||||
if render.use_nodes:
|
if scn.use_nodes:
|
||||||
for i in range(len(render.node_tree.nodes))[::-1]:
|
for i in range(len(scn.node_tree.nodes))[::-1]:
|
||||||
if render.node_tree.nodes[i].type == 'FRAME':
|
if scn.node_tree.nodes[i].type == 'FRAME':
|
||||||
render.node_tree.nodes.remove(render.node_tree.nodes[i])
|
scn.node_tree.nodes.remove(scn.node_tree.nodes[i])
|
||||||
|
|
||||||
# clear all view_layers
|
# Clear all "NG_" nodegroups
|
||||||
for vl in reversed(render.view_layers):
|
|
||||||
if ' / ' in vl.name:
|
|
||||||
render.view_layers.remove(vl)
|
|
||||||
|
|
||||||
# clear all "NG_" nodegroups
|
|
||||||
for ng in reversed(bpy.data.node_groups):
|
for ng in reversed(bpy.data.node_groups):
|
||||||
if ng.name.startswith('NG_'):
|
if ng.name.startswith('NG_'):
|
||||||
ng.use_fake_user = False
|
ng.use_fake_user = False
|
||||||
bpy.data.node_groups.remove(ng)
|
bpy.data.node_groups.remove(ng)
|
||||||
|
|
||||||
|
|
||||||
if self.mode == 'COMPLETE':
|
if self.mode == 'COMPLETE':
|
||||||
bpy.data.scenes.remove(render)
|
bpy.data.scenes.remove(render)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
# Clear all view_layers
|
||||||
|
for vl in reversed(render.view_layers):
|
||||||
|
if ' / ' in vl.name:
|
||||||
|
render.view_layers.remove(vl)
|
||||||
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
|
@ -66,16 +66,17 @@ class GPEXP_OT_delete_render_layer(bpy.types.Operator):
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# mode : bpy.props.StringProperty(default='NORMAL', options={'SKIP_SAVE'})
|
def execute(self, context):
|
||||||
|
## Compositing scene where nodes lives
|
||||||
|
compo_scene = context.scene
|
||||||
|
|
||||||
def execute(self, context):
|
## render scene where viewlayer lives
|
||||||
|
rd_scn = fn.get_render_scene(create=False)
|
||||||
rd_scn = bpy.data.scenes.get('Render')
|
|
||||||
if not rd_scn:
|
if not rd_scn:
|
||||||
self.report({'ERROR'}, 'Viewlayers needs to be generated first!')
|
self.report({'ERROR'}, 'No render scene found')
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
|
||||||
nodes = rd_scn.node_tree.nodes
|
nodes = compo_scene.node_tree.nodes
|
||||||
# list layers and viewlayers
|
# list layers and viewlayers
|
||||||
# vls = [rd_scn.view_layers.get(l.viewlayer_render) for l in layers
|
# vls = [rd_scn.view_layers.get(l.viewlayer_render) for l in layers
|
||||||
# if l.viewlayer_render and l.viewlayer_render != act.viewlayer_render and rd_scn.view_layers.get(l.viewlayer_render)]
|
# if l.viewlayer_render and l.viewlayer_render != act.viewlayer_render and rd_scn.view_layers.get(l.viewlayer_render)]
|
||||||
|
@ -112,25 +113,44 @@ class GPEXP_OT_delete_render_layer(bpy.types.Operator):
|
||||||
|
|
||||||
inside_nodes = []
|
inside_nodes = []
|
||||||
ngroup = grp.node_tree
|
ngroup = grp.node_tree
|
||||||
for i in range(len(grp.inputs))[::-1]:
|
|
||||||
if grp.inputs[i].name == sockin.name:
|
if bpy.app.version < (4,0,0):
|
||||||
ngroup.inputs.remove(ngroup.inputs[i])
|
for i in range(len(grp.inputs))[::-1]:
|
||||||
|
if grp.inputs[i].name == sockin.name:
|
||||||
|
ngroup.inputs.remove(ngroup.inputs[i])
|
||||||
|
|
||||||
gp_in_socket = ngroup.nodes['Group Input'].outputs[i]
|
gp_in_socket = ngroup.nodes['Group Input'].outputs[i]
|
||||||
for lnk in gp_in_socket.links:
|
for lnk in gp_in_socket.links:
|
||||||
inside_nodes += fn.all_connected_forward(lnk.to_node)
|
inside_nodes += fn.all_connected_forward(lnk.to_node)
|
||||||
list(set(inside_nodes))
|
list(set(inside_nodes))
|
||||||
break
|
break
|
||||||
for i in range(len(grp.outputs))[::-1]:
|
for i in range(len(grp.outputs))[::-1]:
|
||||||
if grp.outputs[i].name == sockout.name:
|
if grp.outputs[i].name == sockout.name:
|
||||||
ngroup.outputs.remove(ngroup.outputs[i])
|
ngroup.outputs.remove(ngroup.outputs[i])
|
||||||
break
|
break
|
||||||
|
else:
|
||||||
|
g_inputs = [s for s in ngroup.interface.items_tree if s.in_out == 'INPUT']
|
||||||
|
g_outputs = [s for s in ngroup.interface.items_tree if s.in_out == 'OUTPUT']
|
||||||
|
for i in range(len(grp.inputs))[::-1]:
|
||||||
|
if grp.inputs[i].name == sockin.name:
|
||||||
|
ngroup.interface.remove(g_inputs[i])
|
||||||
|
|
||||||
|
gp_in_socket = ngroup.nodes['Group Input'].outputs[i]
|
||||||
|
for lnk in gp_in_socket.links:
|
||||||
|
inside_nodes += fn.all_connected_forward(lnk.to_node)
|
||||||
|
list(set(inside_nodes))
|
||||||
|
break
|
||||||
|
|
||||||
|
for i in range(len(grp.outputs))[::-1]:
|
||||||
|
if grp.outputs[i].name == sockout.name:
|
||||||
|
ngroup.interface.remove(g_outputs[i])
|
||||||
|
break
|
||||||
|
|
||||||
for sub_n in reversed(inside_nodes):
|
for sub_n in reversed(inside_nodes):
|
||||||
ngroup.nodes.remove(sub_n)
|
ngroup.nodes.remove(sub_n)
|
||||||
|
|
||||||
# remove render_layer node
|
# Remove render_layer node
|
||||||
rd_scn.node_tree.nodes.remove(n)
|
nodes.remove(n)
|
||||||
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
|
@ -228,12 +228,12 @@ class GPEXP_OT_reset_render_settings(bpy.types.Operator):
|
||||||
use_native_aa = False
|
use_native_aa = False
|
||||||
break
|
break
|
||||||
|
|
||||||
if scn.use_aa != use_native_aa:
|
if scn.gp_render_settings.use_aa != use_native_aa:
|
||||||
print(f'Scene {scn.name}: changed scene AA settings, native AA = {use_native_aa}')
|
print(f'Scene {scn.name}: changed scene AA settings, native AA = {use_native_aa}')
|
||||||
fn.scene_aa(scene=scn, toggle=use_native_aa)
|
fn.scene_aa(scene=scn, toggle=use_native_aa)
|
||||||
|
|
||||||
# set propertie on scn to reflect changes (without triggering update)
|
# set propertie on scn to reflect changes (without triggering update)
|
||||||
scn['use_aa'] = use_native_aa
|
scn.gp_render_settings['use_aa'] = use_native_aa
|
||||||
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ from . import gen_vlayer
|
||||||
# --> need to delete/mute AA internal node
|
# --> need to delete/mute AA internal node
|
||||||
|
|
||||||
def merge_layers(rlayers, obname=None, active=None, disconnect=True, color=None):
|
def merge_layers(rlayers, obname=None, active=None, disconnect=True, color=None):
|
||||||
|
'''merge render layers, node_tree is found using first render layer (with id_data)'''
|
||||||
|
|
||||||
print(f'Merging {len(rlayers)} layers')
|
print(f'Merging {len(rlayers)} layers')
|
||||||
print('->', [r.layer for r in rlayers])
|
print('->', [r.layer for r in rlayers])
|
||||||
|
@ -111,7 +112,7 @@ def merge_layers(rlayers, obname=None, active=None, disconnect=True, color=None)
|
||||||
# fn.clean_nodegroup_inputs(dg)
|
# fn.clean_nodegroup_inputs(dg)
|
||||||
# # fn.clear_nodegroup_content_if_disconnected(dg.node_tree)
|
# # fn.clear_nodegroup_content_if_disconnected(dg.node_tree)
|
||||||
|
|
||||||
bpy.context.scene.use_aa = False # trigger fn.scene_aa(toggle=False)
|
bpy.context.scene.gp_render_settings.use_aa = False # trigger fn.scene_aa(toggle=False)
|
||||||
|
|
||||||
return ng, out
|
return ng, out
|
||||||
|
|
||||||
|
@ -420,20 +421,20 @@ class GPEXP_OT_merge_selected_dopesheet_layers(bpy.types.Operator):
|
||||||
self.report({'ERROR'}, f'Should select multiple layers for merging')
|
self.report({'ERROR'}, f'Should select multiple layers for merging')
|
||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
|
|
||||||
render = bpy.data.scenes.get('Render')
|
rd_scene = fn.get_render_scene(create=False)
|
||||||
if render:
|
if rd_scene:
|
||||||
nodes = render.node_tree.nodes
|
nodes = rd_scene.node_tree.nodes
|
||||||
|
|
||||||
clean_ob_name = bpy.path.clean_name(ob.name)
|
clean_ob_name = bpy.path.clean_name(ob.name)
|
||||||
rlayers = []
|
rlayers = []
|
||||||
for l in layers:
|
for l in layers:
|
||||||
idname = f'{clean_ob_name} / {l.info}'
|
idname = f'{clean_ob_name} / {l.info}'
|
||||||
rlayer = rl = None
|
rlayer = rl = None
|
||||||
# check the render layer that have a parent frame
|
# check the rd_scene layer that have a parent frame
|
||||||
if not render:
|
if not rd_scene:
|
||||||
_vl, rl = gen_vlayer.get_set_viewlayer_from_gp(ob, l)
|
_vl, rl = gen_vlayer.get_set_viewlayer_from_gp(ob, l)
|
||||||
render = bpy.data.scenes.get('Render')
|
rd_scene = fn.get_render_scene(create=False)
|
||||||
nodes = render.node_tree.nodes
|
nodes = rd_scene.node_tree.nodes
|
||||||
|
|
||||||
if not rl:
|
if not rl:
|
||||||
rlayer = [n for n in nodes if n.type == 'R_LAYERS' and n.layer == idname and n.parent]
|
rlayer = [n for n in nodes if n.type == 'R_LAYERS' and n.layer == idname and n.parent]
|
||||||
|
@ -467,16 +468,8 @@ class GPEXP_OT_merge_selected_viewlayer_nodes(bpy.types.Operator):
|
||||||
disconnect : bpy.props.BoolProperty(default=True, options={'SKIP_SAVE'})
|
disconnect : bpy.props.BoolProperty(default=True, options={'SKIP_SAVE'})
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
if context.scene.name == 'Scene':
|
|
||||||
render = bpy.data.scenes.get('Render')
|
|
||||||
else:
|
|
||||||
render = context.scene
|
|
||||||
|
|
||||||
if not render:
|
nodes = context.scene.node_tree.nodes
|
||||||
self.report({'ERROR'}, 'No render scene')
|
|
||||||
return {"CANCELLED"}
|
|
||||||
|
|
||||||
nodes = render.node_tree.nodes
|
|
||||||
selection = [n for n in nodes if n.select and n.type == 'R_LAYERS']
|
selection = [n for n in nodes if n.select and n.type == 'R_LAYERS']
|
||||||
|
|
||||||
if not nodes.active in selection:
|
if not nodes.active in selection:
|
||||||
|
@ -490,8 +483,8 @@ class GPEXP_OT_merge_selected_viewlayer_nodes(bpy.types.Operator):
|
||||||
color = None
|
color = None
|
||||||
if nodes.active.use_custom_color and nodes.active.color:
|
if nodes.active.use_custom_color and nodes.active.color:
|
||||||
color = nodes.active.color
|
color = nodes.active.color
|
||||||
merge_layers(selection, active=nodes.active, disconnect=self.disconnect, color=color)
|
|
||||||
|
|
||||||
|
merge_layers(selection, active=nodes.active, disconnect=self.disconnect, color=color)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
classes=(
|
classes=(
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import bpy
|
import bpy
|
||||||
from .import fn
|
from .import fn
|
||||||
|
from .constant import RD_SCENE_NAME
|
||||||
|
|
||||||
class GPEXP_OT_render_scene_switch(bpy.types.Operator):
|
class GPEXP_OT_render_scene_switch(bpy.types.Operator):
|
||||||
bl_idname = "gp.render_scene_switch"
|
bl_idname = "gp.render_scene_switch"
|
||||||
|
@ -11,7 +12,7 @@ class GPEXP_OT_render_scene_switch(bpy.types.Operator):
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# mode : bpy.props.StringProperty(default='NORMAL', options={'SKIP_SAVE'})
|
scene : bpy.props.StringProperty(default='', options={'SKIP_SAVE'})
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
scenes = bpy.data.scenes
|
scenes = bpy.data.scenes
|
||||||
|
@ -19,7 +20,17 @@ class GPEXP_OT_render_scene_switch(bpy.types.Operator):
|
||||||
self.report({'ERROR'},'No other scene to go to')
|
self.report({'ERROR'},'No other scene to go to')
|
||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
|
|
||||||
if context.scene.name == 'Render':
|
## Case where a scene name is passed
|
||||||
|
if self.scene:
|
||||||
|
scn = bpy.data.scenes.get(self.scene)
|
||||||
|
if not scn:
|
||||||
|
self.report({'ERROR'},f'No scene "{self.scene}"')
|
||||||
|
return {"CANCELLED"}
|
||||||
|
self.report({'INFO'},f'Switched to scene "{scn.name}"')
|
||||||
|
bpy.context.window.scene = scn
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
if context.scene.name == RD_SCENE_NAME:
|
||||||
scn = scenes.get('Scene')
|
scn = scenes.get('Scene')
|
||||||
if not scn: # get the next available scene
|
if not scn: # get the next available scene
|
||||||
self.report({'WARNING'},'No scene named "Scene"')
|
self.report({'WARNING'},'No scene named "Scene"')
|
||||||
|
@ -27,12 +38,11 @@ class GPEXP_OT_render_scene_switch(bpy.types.Operator):
|
||||||
scn = scenes[(slist.index(bpy.context.scene.name) + 1) % len(scenes)]
|
scn = scenes[(slist.index(bpy.context.scene.name) + 1) % len(scenes)]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
scn = scenes.get('Render')
|
scn = fn.get_render_scene(create=False)
|
||||||
if not scn:
|
if not scn:
|
||||||
self.report({'ERROR'},'No "Render" scene yet')
|
self.report({'ERROR'},f'No "{RD_SCENE_NAME}" scene yet')
|
||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
|
|
||||||
|
|
||||||
self.report({'INFO'},f'Switched to scene "{scn.name}"')
|
self.report({'INFO'},f'Switched to scene "{scn.name}"')
|
||||||
bpy.context.window.scene = scn
|
bpy.context.window.scene = scn
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
|
@ -562,6 +562,11 @@ class GPEXP_OT_select_layer_in_comp(bpy.types.Operator):
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return context.object and context.object.type == 'GPENCIL'
|
return context.object and context.object.type == 'GPENCIL'
|
||||||
|
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
self.additive = event.shift
|
||||||
|
return self.execute(context)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
gp = context.object.data
|
gp = context.object.data
|
||||||
act = gp.layers.active
|
act = gp.layers.active
|
||||||
|
@ -574,12 +579,19 @@ class GPEXP_OT_select_layer_in_comp(bpy.types.Operator):
|
||||||
self.report({'ERROR'}, 'No compo node-tree in active scene')
|
self.report({'ERROR'}, 'No compo node-tree in active scene')
|
||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
|
|
||||||
nodes = context.scene.node_tree.nodes
|
scn = context.scene
|
||||||
|
node_scene = fn.get_compo_scene(create=False) or scn
|
||||||
|
nodes = node_scene.node_tree.nodes
|
||||||
rl_nodes = [n for n in nodes if n.type == 'R_LAYERS']
|
rl_nodes = [n for n in nodes if n.type == 'R_LAYERS']
|
||||||
if not rl_nodes:
|
if not rl_nodes:
|
||||||
self.report({'ERROR'}, 'No render layers nodes in active scene')
|
self.report({'ERROR'}, 'No render layers nodes in active scene')
|
||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
|
|
||||||
|
# Deselect all nodes if shift is not pressed
|
||||||
|
if not self.additive:
|
||||||
|
for n in nodes:
|
||||||
|
n.select = False
|
||||||
|
|
||||||
used_vl = [n.layer for n in rl_nodes]
|
used_vl = [n.layer for n in rl_nodes]
|
||||||
selected = []
|
selected = []
|
||||||
infos = []
|
infos = []
|
||||||
|
@ -619,6 +631,9 @@ class GPEXP_OT_select_layer_in_comp(bpy.types.Operator):
|
||||||
infos = infos + [f'-- Selected {len(selected)} nodes --'] + selected
|
infos = infos + [f'-- Selected {len(selected)} nodes --'] + selected
|
||||||
fn.show_message_box(_message=infos, _title="Selected viewlayer in compo", _icon='INFO')
|
fn.show_message_box(_message=infos, _title="Selected viewlayer in compo", _icon='INFO')
|
||||||
|
|
||||||
|
# Change viewed scene if not in current scene
|
||||||
|
if selected and scn != node_scene:
|
||||||
|
context.window.scene = node_scene
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
classes=(
|
classes=(
|
||||||
|
|
22
__init__.py
22
__init__.py
|
@ -11,6 +11,7 @@ bl_info = {
|
||||||
"category": "Object" }
|
"category": "Object" }
|
||||||
|
|
||||||
|
|
||||||
|
from . import properties
|
||||||
from . import OP_add_layer
|
from . import OP_add_layer
|
||||||
from . import OP_merge_layers
|
from . import OP_merge_layers
|
||||||
from . import OP_clear
|
from . import OP_clear
|
||||||
|
@ -29,9 +30,10 @@ from . import OP_setup_layers
|
||||||
from . import OP_auto_build
|
from . import OP_auto_build
|
||||||
from . import ui
|
from . import ui
|
||||||
|
|
||||||
from .fn import scene_aa
|
# from .fn import scene_aa
|
||||||
|
|
||||||
bl_modules = (
|
bl_modules = (
|
||||||
|
properties,
|
||||||
prefs,
|
prefs,
|
||||||
OP_add_layer,
|
OP_add_layer,
|
||||||
OP_clear,
|
OP_clear,
|
||||||
|
@ -51,36 +53,18 @@ bl_modules = (
|
||||||
ui,
|
ui,
|
||||||
)
|
)
|
||||||
|
|
||||||
def update_scene_aa(context, scene):
|
|
||||||
scene_aa(toggle=bpy.context.scene.use_aa)
|
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
# if bpy.app.background:
|
|
||||||
# return
|
|
||||||
|
|
||||||
for mod in bl_modules:
|
for mod in bl_modules:
|
||||||
mod.register()
|
mod.register()
|
||||||
|
|
||||||
# bpy.types.Scene.pgroup_name = bpy.props.PointerProperty(type = PROJ_PGT_settings)
|
|
||||||
bpy.types.Scene.use_aa = bpy.props.BoolProperty(
|
|
||||||
name='Use Native Anti Aliasing',
|
|
||||||
default=True,
|
|
||||||
description='\
|
|
||||||
Should be Off only if tree contains a merge_NG or alpha-over-combined renderlayers.\n\
|
|
||||||
Auto-set to Off when using node merge button\n\
|
|
||||||
Toggle: AA settings of and muting AA nested-nodegroup',
|
|
||||||
update=update_scene_aa)
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
# if bpy.app.background:
|
|
||||||
# return
|
|
||||||
|
|
||||||
for mod in reversed(bl_modules):
|
for mod in reversed(bl_modules):
|
||||||
mod.unregister()
|
mod.unregister()
|
||||||
|
|
||||||
del bpy.types.Scene.use_aa
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
register()
|
register()
|
|
@ -0,0 +1 @@
|
||||||
|
RD_SCENE_NAME = 'RenderGP'
|
91
fn.py
91
fn.py
|
@ -9,6 +9,7 @@ from collections import defaultdict
|
||||||
from time import time
|
from time import time
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from . constant import RD_SCENE_NAME
|
||||||
|
|
||||||
### -- rules
|
### -- rules
|
||||||
|
|
||||||
|
@ -208,10 +209,20 @@ def scene_aa(scene=None, toggle=True):
|
||||||
if not scene:
|
if not scene:
|
||||||
scene=bpy.context.scene
|
scene=bpy.context.scene
|
||||||
|
|
||||||
# enable/disable native anti-alias on active scene
|
# Enable/disable native anti-alias on active scene
|
||||||
set_scene_aa_settings(scene=scene, aa=toggle)
|
set_scene_aa_settings(scene=scene, aa=toggle)
|
||||||
# mute/unmute AA nodegroups
|
|
||||||
for n in scene.node_tree.nodes:
|
## Set AA on scene where object and viewlayers exists
|
||||||
|
local_nodes = scene.node_tree.nodes
|
||||||
|
if (group_node := next((n for n in local_nodes if n.name.startswith('NG_')), None)):
|
||||||
|
# Get a viewlayer connected to a NG_ and check which scene is referred
|
||||||
|
if (rlayer := next((i.links[0].from_node for i in group_node.inputs if i.links and i.links[0].from_node.type == 'R_LAYERS'), None)):
|
||||||
|
if rlayer.scene and rlayer.scene != scene:
|
||||||
|
print(f'Set AA to {toggle} on scene "{rlayer.scene.name}"')
|
||||||
|
set_scene_aa_settings(scene=rlayer.scene, aa=toggle)
|
||||||
|
|
||||||
|
## Mute/Unmute AA nodegroups
|
||||||
|
for n in local_nodes:
|
||||||
if n.type == 'GROUP' and n.name.startswith('NG_'):
|
if n.type == 'GROUP' and n.name.startswith('NG_'):
|
||||||
# n.mute = False # mute whole nodegroup ?
|
# n.mute = False # mute whole nodegroup ?
|
||||||
for gn in n.node_tree.nodes:
|
for gn in n.node_tree.nodes:
|
||||||
|
@ -250,17 +261,58 @@ def new_scene_from(name, src_scn=None, regen=True, crop=True, link_cam=True, lin
|
||||||
scn.use_nodes = True
|
scn.use_nodes = True
|
||||||
return scn
|
return scn
|
||||||
|
|
||||||
def get_render_scene():
|
def get_compo_scene(scene_name=None, create=True):
|
||||||
'''Get / Create a scene named Render'''
|
'''Get / Create a dedicated compositing scene to link GP
|
||||||
render_scn = bpy.data.scenes.get('Render')
|
use passed scene name, if no name is passed fall back to compo_scene propertie
|
||||||
if render_scn:
|
return None if field is empty'''
|
||||||
return render_scn
|
|
||||||
|
scene_name = scene_name or bpy.context.scene.gp_render_settings.node_scene
|
||||||
|
if not scene_name:
|
||||||
|
# return None or render scene
|
||||||
|
return
|
||||||
|
|
||||||
|
scn = bpy.data.scenes.get(scene_name)
|
||||||
|
if scn:
|
||||||
|
return scn
|
||||||
|
|
||||||
|
if not create:
|
||||||
|
return
|
||||||
|
|
||||||
## -- Create render scene
|
## -- Create render scene
|
||||||
current = bpy.context.scene
|
current = bpy.context.scene
|
||||||
|
|
||||||
## With data
|
## With data
|
||||||
render_scn = bpy.data.scenes.new('Render')
|
scn = bpy.data.scenes.new(scene_name)
|
||||||
|
|
||||||
|
## copy original settings over to new scene
|
||||||
|
for attr in ['frame_start', 'frame_end', 'frame_current']: # , 'camera', 'world'
|
||||||
|
setattr(scn, attr, getattr(current, attr))
|
||||||
|
copy_settings(current.render, scn.render)
|
||||||
|
|
||||||
|
scn.use_nodes = True
|
||||||
|
|
||||||
|
## Clear node tree
|
||||||
|
scn.node_tree.nodes.clear()
|
||||||
|
set_settings(scn)
|
||||||
|
scn.gp_render_settings['use_aa'] = True
|
||||||
|
return scn
|
||||||
|
|
||||||
|
def get_render_scene(scene_name=None, create=True):
|
||||||
|
'''Get / Create a dedicated render scene to link GP'''
|
||||||
|
|
||||||
|
scene_name = scene_name or RD_SCENE_NAME
|
||||||
|
|
||||||
|
render_scn = bpy.data.scenes.get(scene_name)
|
||||||
|
if render_scn:
|
||||||
|
return render_scn
|
||||||
|
if not create:
|
||||||
|
return
|
||||||
|
|
||||||
|
## -- Create render scene
|
||||||
|
current = bpy.context.scene
|
||||||
|
|
||||||
|
## With data
|
||||||
|
render_scn = bpy.data.scenes.new(scene_name)
|
||||||
|
|
||||||
## copy original settings over to new scene
|
## copy original settings over to new scene
|
||||||
# copy_settings(current, render_scn) # BAD
|
# copy_settings(current, render_scn) # BAD
|
||||||
|
@ -281,15 +333,16 @@ def get_render_scene():
|
||||||
# render_scn.node_tree.nodes.remove(n)
|
# render_scn.node_tree.nodes.remove(n)
|
||||||
|
|
||||||
set_settings(render_scn)
|
set_settings(render_scn)
|
||||||
render_scn['use_aa'] = True
|
render_scn.gp_render_settings['use_aa'] = True
|
||||||
return render_scn
|
return render_scn
|
||||||
|
|
||||||
def get_view_layer(name, scene=None):
|
def get_view_layer(name, scene=None):
|
||||||
'''get viewlayer name
|
'''get viewlayer name
|
||||||
return existing/created viewlayer
|
return existing/created viewlayer
|
||||||
'''
|
'''
|
||||||
if not scene:
|
|
||||||
scene = get_render_scene()
|
scene = scene or get_render_scene()
|
||||||
|
|
||||||
### pass double letter prefix as suffix
|
### pass double letter prefix as suffix
|
||||||
## pass_name = re.sub(r'^([A-Z]{2})(_)(.*)', r'\3\2\1', 'name')
|
## pass_name = re.sub(r'^([A-Z]{2})(_)(.*)', r'\3\2\1', 'name')
|
||||||
## pass_name = f'{name}_{passe}'
|
## pass_name = f'{name}_{passe}'
|
||||||
|
@ -336,6 +389,9 @@ def get_frame_transform(f, node_tree=None):
|
||||||
# return real_loc(f), f.dimensions
|
# return real_loc(f), f.dimensions
|
||||||
|
|
||||||
childs = [n for n in node_tree.nodes if n.parent == f]
|
childs = [n for n in node_tree.nodes if n.parent == f]
|
||||||
|
if not childs:
|
||||||
|
return f.location, f.dimensions
|
||||||
|
|
||||||
# real_locs = [f.location + n.location for n in childs]
|
# real_locs = [f.location + n.location for n in childs]
|
||||||
|
|
||||||
xs = [n.location.x for n in childs] + [n.location.x + n.dimensions.x for n in childs]
|
xs = [n.location.x for n in childs] + [n.location.x + n.dimensions.x for n in childs]
|
||||||
|
@ -470,7 +526,7 @@ def remove_nodes_by_viewlayer(viewlayer_list, scene=None):
|
||||||
# Remove render_layer node
|
# Remove render_layer node
|
||||||
scene.node_tree.nodes.remove(n)
|
scene.node_tree.nodes.remove(n)
|
||||||
|
|
||||||
def merge_gplayer_viewlayers(ob=None, act=None, layers=None):
|
def merge_gplayer_viewlayers(ob=None, act=None, layers=None, scene=None):
|
||||||
'''ob is not needed if active and layers are passed'''
|
'''ob is not needed if active and layers are passed'''
|
||||||
if ob is None:
|
if ob is None:
|
||||||
ob = bpy.context.object
|
ob = bpy.context.object
|
||||||
|
@ -482,9 +538,8 @@ def merge_gplayer_viewlayers(ob=None, act=None, layers=None):
|
||||||
if act is None:
|
if act is None:
|
||||||
return ({'ERROR'}, 'Active layer not found. Should be active layer on active object!')
|
return ({'ERROR'}, 'Active layer not found. Should be active layer on active object!')
|
||||||
|
|
||||||
rd_scn = bpy.data.scenes.get('Render')
|
rd_scn = scene or get_render_scene(create=False) # bpy.context.scene
|
||||||
if not rd_scn:
|
node_scene = get_compo_scene(create=False) or rd_scn
|
||||||
return ({'ERROR'}, 'Viewlayers needs to be generated first!')
|
|
||||||
|
|
||||||
if not act.viewlayer_render:
|
if not act.viewlayer_render:
|
||||||
return ({'ERROR'}, f'Active layer {act.info} has no viewlayer assigned')
|
return ({'ERROR'}, f'Active layer {act.info} has no viewlayer assigned')
|
||||||
|
@ -495,8 +550,10 @@ def merge_gplayer_viewlayers(ob=None, act=None, layers=None):
|
||||||
# Remove duplication
|
# Remove duplication
|
||||||
vls = list(set(vls))
|
vls = list(set(vls))
|
||||||
|
|
||||||
|
|
||||||
# Remove viewlayer related nodes
|
# Remove viewlayer related nodes
|
||||||
remove_nodes_by_viewlayer(vls, rd_scn)
|
# FIXME it's possible nodes are nodes searched in the right scene if launched from RenderGP
|
||||||
|
remove_nodes_by_viewlayer(vls, node_scene) # send compositing scene
|
||||||
|
|
||||||
# Assign view layer from active to selected
|
# Assign view layer from active to selected
|
||||||
for l in layers:
|
for l in layers:
|
||||||
|
|
108
gen_vlayer.py
108
gen_vlayer.py
|
@ -2,19 +2,30 @@ import bpy
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
from . import fn
|
from . import fn
|
||||||
|
|
||||||
|
def add_rlayer(layer_name, scene=None, node_scene=None, location=None, color=None, node_name=None, width=400):
|
||||||
|
'''Create a render layer node
|
||||||
|
|
||||||
def add_rlayer(layer_name, scene=None, location=None, color=None, node_name=None, width=400):
|
args:
|
||||||
'''create a render layer node
|
layer_name (str): Name of the viewlayer
|
||||||
if node_name is not specified, use passed layer name
|
scene (Scene): Scene holding the viewlayer
|
||||||
|
node_scene (Scene): scene where to add render layer
|
||||||
|
if None: fallback scene pointed by compo scene property
|
||||||
|
if compo scene property is empty: fallback to render scene.
|
||||||
|
location (Vector2): Location of the node
|
||||||
|
color (tuple): Color of the node
|
||||||
|
node_name (str): if not specified, use layer_name
|
||||||
|
width (int): width of the node
|
||||||
'''
|
'''
|
||||||
|
|
||||||
if not node_name:
|
if not node_name:
|
||||||
node_name = layer_name # 'RL_' +
|
node_name = layer_name # 'RL_' +
|
||||||
|
|
||||||
if not scene:
|
if not node_scene:
|
||||||
scene=bpy.context.scene
|
node_scene=fn.get_compo_scene(create=False)
|
||||||
|
if not node_scene:
|
||||||
|
node_scene=fn.get_render_scene(create=False)
|
||||||
|
|
||||||
nodes = scene.node_tree.nodes
|
nodes = node_scene.node_tree.nodes
|
||||||
|
|
||||||
comp = nodes.get(node_name)
|
comp = nodes.get(node_name)
|
||||||
if comp:
|
if comp:
|
||||||
|
@ -40,13 +51,11 @@ def add_rlayer(layer_name, scene=None, location=None, color=None, node_name=None
|
||||||
return comp
|
return comp
|
||||||
|
|
||||||
def connect_render_layer(rlayer, ng=None, out=None, frame=None):
|
def connect_render_layer(rlayer, ng=None, out=None, frame=None):
|
||||||
if bpy.context.scene.name == 'Scene':
|
|
||||||
scene = fn.get_render_scene()
|
|
||||||
else:
|
|
||||||
scene = bpy.context.scene
|
|
||||||
|
|
||||||
nodes = scene.node_tree.nodes
|
node_tree = rlayer.id_data # get node_tree from rlayer
|
||||||
links = scene.node_tree.links
|
|
||||||
|
nodes = node_tree.nodes
|
||||||
|
links = node_tree.links
|
||||||
|
|
||||||
vl_name = rlayer.layer
|
vl_name = rlayer.layer
|
||||||
if not vl_name or vl_name == 'View Layer':
|
if not vl_name or vl_name == 'View Layer':
|
||||||
|
@ -88,7 +97,7 @@ def connect_render_layer(rlayer, ng=None, out=None, frame=None):
|
||||||
print(f'create nodegroup {ng_name}')
|
print(f'create nodegroup {ng_name}')
|
||||||
ngroup = bpy.data.node_groups.new(ng_name, 'CompositorNodeTree')
|
ngroup = bpy.data.node_groups.new(ng_name, 'CompositorNodeTree')
|
||||||
|
|
||||||
ng = fn.create_node('CompositorNodeGroup', tree=scene.node_tree, location=(fn.real_loc(rlayer)[0] + 600, fn.real_loc(rlayer)[1]), width=400) # (rlayer.location[0] + 600, rlayer.location[1])
|
ng = fn.create_node('CompositorNodeGroup', tree=node_tree, location=(fn.real_loc(rlayer)[0] + 600, fn.real_loc(rlayer)[1]), width=400) # (rlayer.location[0] + 600, rlayer.location[1])
|
||||||
if frame:
|
if frame:
|
||||||
ng.parent= frame
|
ng.parent= frame
|
||||||
ng.node_tree = ngroup
|
ng.node_tree = ngroup
|
||||||
|
@ -163,8 +172,17 @@ def connect_render_layer(rlayer, ng=None, out=None, frame=None):
|
||||||
ngroup.links.new(ng_in.outputs[vl_name], aa.inputs[0]) # node_tree
|
ngroup.links.new(ng_in.outputs[vl_name], aa.inputs[0]) # node_tree
|
||||||
ngroup.links.new(aa.outputs[0], ng_out.inputs[vl_name]) # node_tree
|
ngroup.links.new(aa.outputs[0], ng_out.inputs[vl_name]) # node_tree
|
||||||
|
|
||||||
aa.mute = scene.use_aa # mute if native AA is used
|
|
||||||
|
|
||||||
|
## Get use_aa prop from render scene ?
|
||||||
|
# rd_scn = fn.get_render_scene(create=False)
|
||||||
|
# if rd_scn:
|
||||||
|
# aa.mute = rd_scn.gp_render_settings.use_aa # mute if native AA is used
|
||||||
|
scene = next((s for s in bpy.data.scenes if s.node_tree == node_tree), None)
|
||||||
|
if scene:
|
||||||
|
print(f'set AA from scene {scene.name}')
|
||||||
|
aa.mute = scene.gp_render_settings.use_aa # mute if native AA is used
|
||||||
|
else:
|
||||||
|
print('/!\ Scene could not be found to define if internal AA should be muted')
|
||||||
|
|
||||||
fn.reorganise_NG_nodegroup(ng) # decorative
|
fn.reorganise_NG_nodegroup(ng) # decorative
|
||||||
|
|
||||||
|
@ -206,7 +224,7 @@ def connect_render_layer(rlayer, ng=None, out=None, frame=None):
|
||||||
out = nodes.get(out_name)
|
out = nodes.get(out_name)
|
||||||
if not out:
|
if not out:
|
||||||
# color = (0.2,0.3,0.5)
|
# color = (0.2,0.3,0.5)
|
||||||
out = fn.create_node('CompositorNodeOutputFile', tree=scene.node_tree, location=(fn.real_loc(ng)[0]+500, fn.real_loc(ng)[1]+50), width=600) # =(ng.location[0]+600, ng.location[1]+50)
|
out = fn.create_node('CompositorNodeOutputFile', tree=node_tree, location=(fn.real_loc(ng)[0]+500, fn.real_loc(ng)[1]+50), width=600) # =(ng.location[0]+600, ng.location[1]+50)
|
||||||
fn.set_file_output_format(out)
|
fn.set_file_output_format(out)
|
||||||
out.name = out_name
|
out.name = out_name
|
||||||
out.parent = frame
|
out.parent = frame
|
||||||
|
@ -233,16 +251,20 @@ def connect_render_layer(rlayer, ng=None, out=None, frame=None):
|
||||||
return ng, out
|
return ng, out
|
||||||
|
|
||||||
|
|
||||||
def get_set_viewlayer_from_gp(ob, l, scene=None):
|
def get_set_viewlayer_from_gp(ob, l, scene=None, node_scene=None):
|
||||||
'''setup ouptut from passed gp obj > layer'''
|
'''setup ouptut from passed gp obj > layer
|
||||||
if not scene:
|
scene: scene to set viewlayer
|
||||||
if bpy.context.scene.name != 'Scene':
|
node_scene: where to add compo node (use scene if not passed)'''
|
||||||
scene = bpy.context.scene
|
|
||||||
else:
|
|
||||||
scene = fn.get_render_scene()
|
|
||||||
|
|
||||||
# print('Set viewlayer Scene: ', scene.name)
|
scene = scene or fn.get_render_scene()
|
||||||
node_tree = scene.node_tree
|
node_scene = node_scene or fn.get_compo_scene() or scene
|
||||||
|
|
||||||
|
print('Viewlayer Scene: ', scene.name)
|
||||||
|
print('Compo Scene: ', node_scene.name)
|
||||||
|
|
||||||
|
## If not passed, identical to scene holding viewlayers
|
||||||
|
|
||||||
|
node_tree = node_scene.node_tree
|
||||||
nodes = node_tree.nodes
|
nodes = node_tree.nodes
|
||||||
|
|
||||||
in_rds = scene.collection.all_objects.get(ob.name)
|
in_rds = scene.collection.all_objects.get(ob.name)
|
||||||
|
@ -265,18 +287,26 @@ def get_set_viewlayer_from_gp(ob, l, scene=None):
|
||||||
if nob:
|
if nob:
|
||||||
nob.select_set(True)
|
nob.select_set(True)
|
||||||
|
|
||||||
# create viewlayer
|
# Create viewlayer
|
||||||
vl_name = f'{ob.name} / {l.info}'
|
vl_name = f'{ob.name} / {l.info}'
|
||||||
vl = fn.get_view_layer(vl_name, scene=scene)
|
vl = fn.get_view_layer(vl_name, scene=scene)
|
||||||
vl_name = vl.name
|
vl_name = vl.name
|
||||||
# affect layer to this vl
|
|
||||||
|
## To avoid potential error, transfer compo scene prop to renderscene.
|
||||||
|
scn = bpy.context.scene
|
||||||
|
if scn.gp_render_settings.node_scene and scn != scene:
|
||||||
|
if scn.gp_render_settings.node_scene != scene.gp_render_settings.node_scene:
|
||||||
|
print(f'Transfer compo scene target prop to render scene: "{scn.gp_render_settings.node_scene}"')
|
||||||
|
scene.gp_render_settings.node_scene = scn.gp_render_settings.node_scene
|
||||||
|
|
||||||
|
# Affect layer to this vl
|
||||||
l.viewlayer_render = vl_name
|
l.viewlayer_render = vl_name
|
||||||
|
|
||||||
# check if already exists
|
# Check if already exists
|
||||||
rlayer_list = [n for n in nodes if n.type == 'R_LAYERS' and n.layer == vl_name]
|
rlayer_list = [n for n in nodes if n.type == 'R_LAYERS' and n.layer == vl_name]
|
||||||
|
|
||||||
# get frame object and their contents
|
# Get frame object and their contents
|
||||||
# dict like : {objname : [layer_nodeA, layer_nodeB,...]}
|
# dict model : {objname : [layer_nodeA, layer_nodeB,...]}
|
||||||
frame_dic = {f.label: [n for n in nodes if n.type == 'R_LAYERS' and n.parent and n.parent.name == f.name and '/' in n.layer] # n.layer != 'View Layer'
|
frame_dic = {f.label: [n for n in nodes if n.type == 'R_LAYERS' and n.parent and n.parent.name == f.name and '/' in n.layer] # n.layer != 'View Layer'
|
||||||
for f in nodes if f.type == 'FRAME'}
|
for f in nodes if f.type == 'FRAME'}
|
||||||
|
|
||||||
|
@ -323,7 +353,7 @@ def get_set_viewlayer_from_gp(ob, l, scene=None):
|
||||||
frame.label_size = 50
|
frame.label_size = 50
|
||||||
frame.location = (loc[0], loc[1] + 20)
|
frame.location = (loc[0], loc[1] + 20)
|
||||||
|
|
||||||
cp = add_rlayer(vl_name, scene=scene, location=loc)
|
cp = add_rlayer(vl_name, scene=scene, node_scene=node_scene, location=loc)
|
||||||
cp.parent = frame
|
cp.parent = frame
|
||||||
# use same color as layer
|
# use same color as layer
|
||||||
if fn.has_channel_color(l):
|
if fn.has_channel_color(l):
|
||||||
|
@ -341,23 +371,24 @@ def get_set_viewlayer_from_gp(ob, l, scene=None):
|
||||||
print(f'\n {ob.name} -> {l.info} (connect to existing)')
|
print(f'\n {ob.name} -> {l.info} (connect to existing)')
|
||||||
|
|
||||||
## object frame exists: get framing and insert
|
## object frame exists: get framing and insert
|
||||||
cp = add_rlayer(vl_name, scene=scene, location=(0,0))
|
cp = add_rlayer(vl_name, scene=scene, node_scene=node_scene, location=(0,0))
|
||||||
if cp.layer != vl_name:
|
if cp.layer != vl_name:
|
||||||
print(f'problem with {cp}: {cp.layer} != {vl_name}')
|
print(f'problem with {cp}: {cp.layer} != {vl_name}')
|
||||||
return
|
return
|
||||||
|
|
||||||
if fn.has_channel_color(l):
|
if fn.has_channel_color(l):
|
||||||
cp.use_custom_color = True
|
cp.use_custom_color = True
|
||||||
cp.color = l.channel_color
|
cp.color = l.channel_color
|
||||||
|
|
||||||
frame = [f for f in nodes if f.type == 'FRAME' and f.label == ob.name][0]
|
frame = next((f for f in nodes if f.type == 'FRAME' and f.label == ob.name), None)
|
||||||
rl_nodes = frame_dic[frame.label] # get nodes from
|
rl_nodes = frame_dic[frame.label] # get nodes from
|
||||||
if rl_nodes:
|
if rl_nodes:
|
||||||
# get nodes order to insert
|
# get nodes order to insert
|
||||||
rl_nodes.sort(key=lambda n: fn.real_loc(n).y, reverse=True) # descending
|
rl_nodes.sort(key=lambda n: fn.real_loc(n).y, reverse=True) # descending
|
||||||
top_loc = fn.real_loc(rl_nodes[0])
|
top_loc = fn.real_loc(rl_nodes[0])
|
||||||
else:
|
else:
|
||||||
top_loc = fn.get_frame_transform(frame[1], node_tree) - 60
|
print('!! gen_viewlayer: No Render layers nodes !!')
|
||||||
|
top_loc = fn.get_frame_transform(frame, node_tree) - 60
|
||||||
|
|
||||||
# cp.location = (top_loc[0], top_loc[1] + 100) # temp location to adjust x loc
|
# cp.location = (top_loc[0], top_loc[1] + 100) # temp location to adjust x loc
|
||||||
|
|
||||||
|
@ -381,10 +412,9 @@ def get_set_viewlayer_from_gp(ob, l, scene=None):
|
||||||
# set x loc from first node in list (maybe use leftmost ?)
|
# set x loc from first node in list (maybe use leftmost ?)
|
||||||
n.location = Vector((fn.real_loc(ref_node)[0], top_loc[1] - offset)) - n.parent.location
|
n.location = Vector((fn.real_loc(ref_node)[0], top_loc[1] - offset)) - n.parent.location
|
||||||
offset += 180
|
offset += 180
|
||||||
|
|
||||||
n.update()
|
n.update()
|
||||||
# reorder render layers nodes within frame
|
|
||||||
|
|
||||||
|
# reorder render layers nodes within frame
|
||||||
connect_render_layer(cp, frame=frame)
|
connect_render_layer(cp, frame=frame)
|
||||||
|
|
||||||
# re-arrange all frames (since the offset probably overlapped)
|
# re-arrange all frames (since the offset probably overlapped)
|
||||||
|
@ -392,7 +422,7 @@ def get_set_viewlayer_from_gp(ob, l, scene=None):
|
||||||
|
|
||||||
return vl, cp
|
return vl, cp
|
||||||
|
|
||||||
def export_gp_objects(oblist, exclude_list=[], scene=None):
|
def export_gp_objects(oblist, exclude_list=[], scene=None, node_scene=None):
|
||||||
# Skip layer containing element in exclude list
|
# Skip layer containing element in exclude list
|
||||||
if not isinstance(oblist, list):
|
if not isinstance(oblist, list):
|
||||||
oblist = [oblist]
|
oblist = [oblist]
|
||||||
|
@ -410,4 +440,4 @@ def export_gp_objects(oblist, exclude_list=[], scene=None):
|
||||||
l.viewlayer_render = fn.get_view_layer('exclude', scene=scene).name # assign "exclude"
|
l.viewlayer_render = fn.get_view_layer('exclude', scene=scene).name # assign "exclude"
|
||||||
continue
|
continue
|
||||||
|
|
||||||
_vl, _cp = get_set_viewlayer_from_gp(ob, l, scene=scene) # scene=fn.get_render_scene())
|
_vl, _cp = get_set_viewlayer_from_gp(ob, l, scene=scene, node_scene=node_scene) # scene=fn.get_render_scene())
|
|
@ -0,0 +1,44 @@
|
||||||
|
import bpy
|
||||||
|
from .fn import scene_aa
|
||||||
|
|
||||||
|
def update_scene_aa(context, scene):
|
||||||
|
scene_aa(toggle=bpy.context.scene.gp_render_settings.use_aa)
|
||||||
|
|
||||||
|
class GPRENDER_PGT_settings(bpy.types.PropertyGroup) :
|
||||||
|
|
||||||
|
show_scene_setup :bpy.props.BoolProperty(
|
||||||
|
name='Show scene setup',
|
||||||
|
default=False,
|
||||||
|
description='Show scene setup, options to tweak render scene and compo scene')
|
||||||
|
|
||||||
|
render_scene : bpy.props.StringProperty(
|
||||||
|
name="Render Scene", description="Link object and create viewlayers in render scene, create if necessary",
|
||||||
|
default="", maxlen=0, options={'HIDDEN'})
|
||||||
|
|
||||||
|
node_scene : bpy.props.StringProperty(
|
||||||
|
name="Compo Scene", description="Add nodes in compositing scene, if empty, add in Render scene nodeTree",
|
||||||
|
default="", maxlen=0, options={'HIDDEN'})
|
||||||
|
|
||||||
|
use_aa : bpy.props.BoolProperty(
|
||||||
|
name='Use Native Anti Aliasing',
|
||||||
|
default=True,
|
||||||
|
description='\
|
||||||
|
Should be Off only if tree contains a merge_NG or alpha-over-combined renderlayers.\n\
|
||||||
|
Auto-set to Off when using node merge button\n\
|
||||||
|
Toggle: AA settings of and muting AA nested-nodegroup',
|
||||||
|
update=update_scene_aa)
|
||||||
|
|
||||||
|
classes=(
|
||||||
|
GPRENDER_PGT_settings,
|
||||||
|
)
|
||||||
|
|
||||||
|
def register():
|
||||||
|
for cls in classes:
|
||||||
|
bpy.utils.register_class(cls)
|
||||||
|
bpy.types.Scene.gp_render_settings = bpy.props.PointerProperty(type=GPRENDER_PGT_settings)
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
for cls in reversed(classes):
|
||||||
|
bpy.utils.unregister_class(cls)
|
||||||
|
|
||||||
|
del bpy.types.Scene.gp_render_settings
|
50
ui.py
50
ui.py
|
@ -2,6 +2,7 @@ import bpy
|
||||||
from bpy.types import Panel
|
from bpy.types import Panel
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from .prefs import get_addon_prefs
|
from .prefs import get_addon_prefs
|
||||||
|
from .constant import RD_SCENE_NAME
|
||||||
# from .preferences import get_addon_prefs
|
# from .preferences import get_addon_prefs
|
||||||
|
|
||||||
# Node view panel
|
# Node view panel
|
||||||
|
@ -16,7 +17,17 @@ class GPEXP_PT_gp_node_ui(Panel):
|
||||||
prefs = get_addon_prefs()
|
prefs = get_addon_prefs()
|
||||||
advanced = prefs.advanced
|
advanced = prefs.advanced
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
layout.operator('gp.render_scene_switch', icon='SCENE_DATA', text='Switch Scene')
|
row = layout.row(align=True)
|
||||||
|
row.operator('gp.render_scene_switch', icon='SCENE_DATA', text='Switch Scene')
|
||||||
|
|
||||||
|
## Check if there is another scene reference current scene in it's comp renderlayers
|
||||||
|
## If so, expose a button to go in (expensive check ?)
|
||||||
|
node_scn = next((s for s in bpy.data.scenes
|
||||||
|
if s != context.scene and s.use_nodes
|
||||||
|
and next((n for n in s.node_tree.nodes if n.type == 'R_LAYERS' and n.scene == context.scene), None)
|
||||||
|
),None)
|
||||||
|
if node_scn:
|
||||||
|
row.operator('gp.render_scene_switch', icon='NODETREE', text='Node Scene').scene = node_scn.name
|
||||||
|
|
||||||
scn = context.scene
|
scn = context.scene
|
||||||
|
|
||||||
|
@ -166,7 +177,7 @@ class GPEXP_PT_gp_node_ui(Panel):
|
||||||
col.label(text='Post-Render:')
|
col.label(text='Post-Render:')
|
||||||
col.operator('gp.renumber_files_on_disk', icon='FILE', text='Renumber Files On Disk')
|
col.operator('gp.renumber_files_on_disk', icon='FILE', text='Renumber Files On Disk')
|
||||||
|
|
||||||
layout.prop(context.scene, 'use_aa', text='Use Native AA Settings')
|
layout.prop(context.scene.gp_render_settings, 'use_aa', text='Use Native AA Settings')
|
||||||
layout.prop(prefs, 'advanced', text='Show Advanced Options')
|
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 = 'ALL'
|
||||||
# layout.operator('gp.add_object_to_render', icon='RENDERLAYERS', text='Layer To Render').mode = 'SELECTED'
|
# layout.operator('gp.add_object_to_render', icon='RENDERLAYERS', text='Layer To Render').mode = 'SELECTED'
|
||||||
|
@ -186,8 +197,20 @@ class GPEXP_PT_gp_dopesheet_ui(Panel):
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
|
settings = context.scene.gp_render_settings
|
||||||
|
|
||||||
|
icon = 'DISCLOSURE_TRI_DOWN' if settings.show_scene_setup else 'DISCLOSURE_TRI_RIGHT'
|
||||||
|
col = layout.column()
|
||||||
|
col.prop(settings, 'show_scene_setup', icon=icon, text='Scenes Setup', emboss=False )
|
||||||
|
if settings.show_scene_setup:
|
||||||
|
col.prop(settings, 'render_scene', icon='SCENE_DATA', placeholder=RD_SCENE_NAME)
|
||||||
|
|
||||||
|
nodetree_placeholder = settings.render_scene or RD_SCENE_NAME
|
||||||
|
col.prop(settings, 'node_scene', icon='NODETREE', placeholder=nodetree_placeholder)
|
||||||
|
|
||||||
|
op = layout.operator('gp_export.render_auto_build')
|
||||||
|
op.node_scene = settings.node_scene
|
||||||
|
|
||||||
layout.operator('gp_export.render_auto_build')
|
|
||||||
if context.object:
|
if context.object:
|
||||||
layout.label(text=f'Object: {context.object.name}')
|
layout.label(text=f'Object: {context.object.name}')
|
||||||
if context.object.data.users > 1:
|
if context.object.data.users > 1:
|
||||||
|
@ -207,7 +230,9 @@ class GPEXP_PT_gp_dopesheet_ui(Panel):
|
||||||
txt = f'{len([l for l in context.object.data.layers if l.select])} Layer(s) To Render'
|
txt = f'{len([l for l in context.object.data.layers if l.select])} Layer(s) To Render'
|
||||||
else:
|
else:
|
||||||
txt = 'Layer To Render'
|
txt = 'Layer To Render'
|
||||||
col.operator('gp.add_layer_to_render', icon='RENDERLAYERS', text=txt)
|
|
||||||
|
op = col.operator('gp.add_layer_to_render', icon='RENDERLAYERS', text=txt)
|
||||||
|
op.node_scene = settings.node_scene
|
||||||
|
|
||||||
|
|
||||||
# merge (only accessible if multiple layers selected)
|
# merge (only accessible if multiple layers selected)
|
||||||
|
@ -232,8 +257,14 @@ class GPEXP_PT_gp_dopesheet_ui(Panel):
|
||||||
col = layout.column()
|
col = layout.column()
|
||||||
col.label(text='Whole Objects:')
|
col.label(text='Whole Objects:')
|
||||||
txt = f'{len([o for o in context.selected_objects if o.type == "GPENCIL" and o.select_get()])} Selected Object(s) To Render'
|
txt = f'{len([o for o in context.selected_objects if o.type == "GPENCIL" and o.select_get()])} Selected Object(s) To Render'
|
||||||
col.operator('gp.add_object_to_render', icon='RENDERLAYERS', text=txt).mode='SELECTED'
|
|
||||||
col.operator('gp.add_object_to_render', icon='RENDERLAYERS', text='All Visible GP To Render').mode='ALL'
|
op = col.operator('gp.add_object_to_render', icon='RENDERLAYERS', text=txt)
|
||||||
|
op.mode='SELECTED'
|
||||||
|
op.node_scene = settings.node_scene
|
||||||
|
|
||||||
|
op = col.operator('gp.add_object_to_render', icon='RENDERLAYERS', text='All Visible GP To Render')
|
||||||
|
op.mode='ALL'
|
||||||
|
op.node_scene = settings.node_scene
|
||||||
|
|
||||||
layout.separator()
|
layout.separator()
|
||||||
col = layout.column()
|
col = layout.column()
|
||||||
|
@ -357,9 +388,10 @@ def manager_ui(self, context):
|
||||||
|
|
||||||
## On objects
|
## On objects
|
||||||
# txt = 'Selected Object To Render'
|
# txt = 'Selected Object To Render'
|
||||||
if context.scene.name != 'Render':
|
# if context.scene.name != RD_SCENE_NAME:
|
||||||
txt = f'{len([o for o in context.selected_objects if o.type == "GPENCIL" and o.select_get()])} Selected Object(s) To Render'
|
txt = f'{len([o for o in context.selected_objects if o.type == "GPENCIL" and o.select_get()])} Selected Object(s) To Render'
|
||||||
layout.operator('gp.add_object_to_render', icon='RENDERLAYERS', text=txt).mode='SELECTED'
|
layout.operator('gp.add_object_to_render', icon='RENDERLAYERS', text=txt).mode='SELECTED'
|
||||||
|
|
||||||
layout.operator('gp.add_object_to_render', icon='RENDERLAYERS', text='All GP at once').mode='ALL'
|
layout.operator('gp.add_object_to_render', icon='RENDERLAYERS', text='All GP at once').mode='ALL'
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue