layers info json exporter

0.9.3

- feat: export a json with layers info for compo. Masks, opacity, blend mode
main
Pullusb 2022-01-24 23:14:12 +01:00
parent 89abd181e2
commit 5cca446fc0
5 changed files with 141 additions and 6 deletions

View File

@ -14,6 +14,9 @@ Activate / deactivate layer opaticty according to prefix
Activate / deactivate all masks using MA layers
-->
0.9.3
- feat: export a json with layers info for compo. Masks, opacity, blend mode
0.9.2

View File

@ -7,9 +7,138 @@ from bpy.props import (FloatProperty,
from . import fn
import math
import re
import json
from pathlib import Path
## TODO : export json info to re-setup layers in AE automagically
def check_outname(ob, l):
vl_name = l.viewlayer_render
if vl_name in {'exclude', 'View Layer'}:
return
ng_name = f'NG_{ob.name}'
## check in wich node tree this exists
for scn in bpy.data.scenes:
if scn.name == 'Scene':
continue
ng = scn.node_tree.nodes.get(ng_name)
if ng:
break
print(scn.name)
if not ng:
print(f'Skip {vl_name}: Not found nodegroup {ng_name}' )
return
ng_socket = ng.outputs.get(vl_name)
if not ng_socket:
print(f'Skip {vl_name}: Not found in nodegroup {ng_name} sockets')
return
if not len(ng_socket.links):
print(f' socket is disconnected in {ng_name} nodegroup')
return
fo_node = ng_socket.links[0].to_node
fo_socket = ng_socket.links[0].to_socket
if fo_node.type != 'OUTPUT_FILE':
print(f'Skip {vl_name}: node is not an output_file {fo_node.name}')
return
# fo_socket.name isn't right, have to iterate in paths
idx = [i for i in fo_node.inputs].index(fo_socket)
subpath = fo_node.file_slots[idx].path
# fp = Path(fo_node.base_path.rstrip('/')) / subpath
# fp = Path(bpy.path.abspath(str(fp)).rstrip("/")) # abspath on disk
outname = subpath.split('/')[0] # folder name on disk
return outname
class GPEXP_OT_export_infos_for_compo(bpy.types.Operator):
bl_idname = "gp.export_infos_for_compo"
bl_label = "Export Infos For Compo"
bl_description = "Export informations for compositing, including layers with masks, fusion mode, opacity"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
def invoke(self, context, event):
self.l_infos = Path(bpy.data.filepath).parent / 'render' / 'infos.json'
if self.l_infos.exists():
return context.window_manager.invoke_props_dialog(self)
return self.execute(context)
def draw(self, context):
layout = self.layout
layout.label(text='An infos Json already exists', icon = 'ERROR')
layout.label(text='Do you want to overwrite ?')
layout.label(text='Note: Must export before "Check Layers" step', icon='INFO')
def execute(self, context):
dic = {}
pool = [o for o in context.scene.objects if o.type == 'GPENCIL']
for o in pool:
# if not o.visible_get():
# continue
for l in o.data.layers:
# skip non rendered layers
if l.hide:
continue
if l.info.startswith('MA_'):
# No point in storing information of masking layers...
continue
## Can't check viewlayers and final fileout name if Render scene not even created...
""" if not l.viewlayer_render or l.viewlayer_render == 'exclude':
continue
fo_name = check_outname(o, l) # get name used for output file folder (same in AE)
if not fo_name:
print(f'! Could not found fileout name for {o.name} > {l.info}')
continue
"""
ldic = {}
## Check opacity, blend mode
if l.opacity < 1.0:
ldic['opacity'] = l.opacity
if l.blend_mode != 'REGULAR':
ldic['blend_mode'] = l.blend_mode
if l.use_mask_layer:
multi_mask = {}
for i, ml in enumerate(l.mask_layers):
mask = {}
if ml.hide:
continue
mask['name'] = ml.name
if ml.invert: # create key get only if inverted
mask['invert'] = ml.invert
# multi_mask[ml.name] = mask
multi_mask[i] = mask
if multi_mask:
ldic['masks'] = multi_mask
## add to full dic
if ldic:
# add source object ? might be usefull to pin point layer
ldic['object'] = o.name
dic[fn.normalize_layer_name(l, get_only=True)] = ldic
if dic:
with self.l_infos.open('w') as fd:
json.dump(dic, fd, indent='\t')
self.report({'INFO'}, f'Exported json at: {self.l_infos.as_posix()}')
else:
self.report({'WARNING'}, f'No custom data to write with those objects/layers')
return {"CANCELLED"}
return {"FINISHED"}
class GPEXP_OT_layers_state(bpy.types.Operator):
bl_idname = "gp.layers_state"
bl_label = "Set Layers State"
@ -380,9 +509,10 @@ class GPEXP_OT_check_masks(bpy.types.Operator):
return {"FINISHED"}
classes=(
GPEXP_OT_layers_state,
GPEXP_OT_lower_layers_name,
GPEXP_OT_auto_number_object,
GPEXP_OT_lower_layers_name,
GPEXP_OT_export_infos_for_compo,
GPEXP_OT_layers_state,
GPEXP_OT_check_masks,
)

View File

@ -2,7 +2,7 @@ bl_info = {
"name": "GP Render",
"description": "Organise export of gp layers through compositor output",
"author": "Samuel Bernou",
"version": (0, 9, 2),
"version": (0, 9, 3),
"blender": (2, 93, 0),
"location": "View3D",
"warning": "",

5
fn.py
View File

@ -728,7 +728,7 @@ def normalize(text):
return text.lower().replace('-', '_')
PATTERN = r'^(?P<grp>-\s)?(?P<tag>[A-Z]{2}_)?(?P<name>.*?)(?P<sfix>_[A-Z]{2})?(?P<inc>\.\d{3})?$' # numering
def normalize_layer_name(layer, prefix='', desc='', suffix='', lower=True, dash_to_underscore=True):
def normalize_layer_name(layer, prefix='', desc='', suffix='', lower=True, dash_to_underscore=True, get_only=False):
'''GET a layer and argument to build and assign name'''
import re
@ -772,7 +772,8 @@ def normalize_layer_name(layer, prefix='', desc='', suffix='', lower=True, dash_
name = name.replace('-', '_')
new = f'{grp}{tag}{name}{sfix}' # lower suffix ?
if get_only:
return new
if new != layer.info:
old = layer.info
print(f'{old} >> {new}')

3
ui.py
View File

@ -211,8 +211,9 @@ class GPEXP_PT_gp_dopesheet_ui(Panel):
row = col.row(align=True)
row.operator('gp.auto_number_object', icon='OBJECT_DATAMODE', text='Renumber Objects')
row.operator('gp.auto_number_object', icon='X', text='').delete = True
col.operator('gp.layers_state', icon='CHECKMARK', text='Check layers')
col.operator('gp.lower_layers_name', icon='SYNTAX_OFF', text='Rename Lowercase')
col.operator('gp.export_infos_for_compo', icon='FILE', text='Export Layers Infos')
col.operator('gp.layers_state', icon='CHECKMARK', text='Check layers')
col.operator('gp.check_masks', icon='MOD_MASK', text='Has Masks')
# row = layout.row()