fixes ui and mutiuser case
parent
a3ab7644a7
commit
3a334404a2
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -7,8 +7,20 @@ if objects has multiple user without being linked in render scene :
|
||||||
OR always duplicate (safe but heavy scenes...)
|
OR always duplicate (safe but heavy scenes...)
|
||||||
|
|
||||||
if duplicate, need to "connect" with namespace ('_duprender') or something
|
if duplicate, need to "connect" with namespace ('_duprender') or something
|
||||||
|
|
||||||
|
Activate / deactivate layer opaticty according to prefix
|
||||||
|
Activate / deactivate all masks using MA layers
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
0.2.5
|
||||||
|
|
||||||
|
- ui: removed menu above layer stack
|
||||||
|
- ui: All function in gp dopesheet new tab GP Render
|
||||||
|
- fix: node rearrange
|
||||||
|
- fix: merge from dopesheet
|
||||||
|
- feat: merge can also create the Render scene
|
||||||
|
- feat: multi-user warning with mini tutorial procedure
|
||||||
|
|
||||||
0.2.4
|
0.2.4
|
||||||
|
|
||||||
- fix: scene world transfer
|
- fix: scene world transfer
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import bpy
|
import bpy
|
||||||
import re
|
import re
|
||||||
from . import fn
|
from . import fn
|
||||||
|
from . import gen_vlayer
|
||||||
|
|
||||||
def merge_layers(rlayers, obname=None, active=None, disconnect=True):
|
def merge_layers(rlayers, obname=None, active=None, disconnect=True):
|
||||||
|
|
||||||
|
@ -120,9 +121,13 @@ class GPEXP_OT_merge_selected_dopesheet_layers(bpy.types.Operator):
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
# merge_selected_layers() # function to merge from GP dopesheet
|
# merge_selected_layers() # function to merge from GP dopesheet
|
||||||
ob = bpy.context.object
|
ob = bpy.context.object
|
||||||
layer_names = [l.info for l in ob.data.layers if l.select and not l.hide]
|
layers = [l for l in ob.data.layers if l.select and not l.hide]
|
||||||
|
act = ob.data.layers.active
|
||||||
|
if not act:
|
||||||
|
self.report({'ERROR'}, f'An active layer is needed to set merge output name')
|
||||||
|
return {"CANCELLED"}
|
||||||
|
|
||||||
if len(layer_names) < 2:
|
if len(layers) < 2:
|
||||||
self.report({'ERROR'}, f'Should select multiple layers for merging')
|
self.report({'ERROR'}, f'Should select multiple layers for merging')
|
||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
|
|
||||||
|
@ -132,19 +137,29 @@ class GPEXP_OT_merge_selected_dopesheet_layers(bpy.types.Operator):
|
||||||
|
|
||||||
clean_ob_name = bpy.path.clean_name(ob.name)
|
clean_ob_name = bpy.path.clean_name(ob.name)
|
||||||
rlayers = []
|
rlayers = []
|
||||||
for l in layer_names:
|
for l in layers:
|
||||||
## identifier is clean_name(ob.name).layer_name
|
idname = f'{clean_ob_name} / {l.info}'
|
||||||
|
rlayer = rl = None
|
||||||
idname = f'{clean_ob_name}.{l}'
|
|
||||||
|
|
||||||
# check the render layer that have a parent frame
|
# check the render layer that have a parent frame
|
||||||
rlayer = [n for n in nodes if n.type == 'R_LAYERS' and n.layer == idname and n.parent]
|
if not render:
|
||||||
|
_vl, rl = gen_vlayer.get_set_viewlayer_from_gp(ob, l)
|
||||||
|
render = bpy.data.scenes.get('Render')
|
||||||
|
nodes = render.node_tree.nodes
|
||||||
|
|
||||||
|
if not rl:
|
||||||
|
rlayer = [n for n in nodes if n.type == 'R_LAYERS' and n.layer == idname and n.parent]
|
||||||
if not rlayer:
|
if not rlayer:
|
||||||
# send to function to generate the rlayer and connect
|
# send to function to generate the rlayer and connect
|
||||||
# rlayer = creation
|
_vl, rl = gen_vlayer.get_set_viewlayer_from_gp(ob, l)
|
||||||
continue
|
|
||||||
|
|
||||||
rlayers.append(rlayer)
|
else:
|
||||||
|
rlayer.sort(key=lambda n: n.location.y, reverse=True)
|
||||||
|
rl = rlayer[0]
|
||||||
|
|
||||||
|
if act == l:
|
||||||
|
nodes.active = rl # make it active so the merge use this one
|
||||||
|
|
||||||
|
rlayers.append(rl)
|
||||||
|
|
||||||
merge_layers(rlayers, disconnect=self.disconnect)
|
merge_layers(rlayers, disconnect=self.disconnect)
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ bl_info = {
|
||||||
"name": "GP exporter",
|
"name": "GP exporter",
|
||||||
"description": "Organise export of gp layers through compositor output",
|
"description": "Organise export of gp layers through compositor output",
|
||||||
"author": "Samuel Bernou",
|
"author": "Samuel Bernou",
|
||||||
"version": (0, 2, 3),
|
"version": (0, 2, 5),
|
||||||
"blender": (2, 93, 0),
|
"blender": (2, 93, 0),
|
||||||
"location": "View3D",
|
"location": "View3D",
|
||||||
"warning": "",
|
"warning": "",
|
||||||
|
|
5
fn.py
5
fn.py
|
@ -231,15 +231,16 @@ def rearrange_frames(node_tree):
|
||||||
frames = [[f, v[0], v[1].y] for f, v in frame_d.items()] # [frame_node, real_loc, real dimensions]
|
frames = [[f, v[0], v[1].y] for f, v in frame_d.items()] # [frame_node, real_loc, real dimensions]
|
||||||
|
|
||||||
top = frames[0][1].y # upper node location.y
|
top = frames[0][1].y # upper node location.y
|
||||||
|
# top = 0 #always start a 0
|
||||||
offset = 0
|
offset = 0
|
||||||
for f in frames:
|
for f in frames:
|
||||||
## f[1] : real loc Vector
|
## f[1] : real loc Vector
|
||||||
## f[0] : frame
|
## f[0] : frame
|
||||||
|
|
||||||
## move frame by offset needed (delta between real_loc and "fake" loc , minus offset)
|
## move frame by offset needed (delta between real_loc and "fake" loc , minus offset)
|
||||||
f[0].location.y = (f[1].y - f[0].location.y) - top - offset + 40 # + 40 to avoid offset when recalculating from 0 top
|
f[0].location.y = (f[1].y - f[0].location.y) - offset # avoid offset when recalculating from 0 top
|
||||||
# f[0].location.y = f[1].y - top - offset
|
# f[0].location.y = f[1].y - top - offset
|
||||||
offset += f[2] + 300 # gap
|
offset += f[2] + 200 # gap
|
||||||
|
|
||||||
f[0].update()
|
f[0].update()
|
||||||
|
|
||||||
|
|
|
@ -217,7 +217,11 @@ def get_set_viewlayer_from_gp(ob, l, scene=None):
|
||||||
|
|
||||||
in_rds = scene.collection.all_objects.get(ob.name)
|
in_rds = scene.collection.all_objects.get(ob.name)
|
||||||
if not in_rds:
|
if not in_rds:
|
||||||
# TODO : link with a ob.data copy if its a multiuser object !
|
# TODO : ? duplicate the object with name with a specific suffix '_renderdupe' to still parse it ?
|
||||||
|
## make single user if its a multiuser object ? maybe let the user do it
|
||||||
|
if ob.data.users > 1:
|
||||||
|
print(f'/!!\ {ob.name} data has multiple users ! ({ob.data.users})')
|
||||||
|
# ob.data = ob.data.copy() # create duplicate (this will also affect the one in original scene !!!)
|
||||||
scene.collection.objects.link(ob)
|
scene.collection.objects.link(ob)
|
||||||
ob.hide_viewport = ob.hide_render = False
|
ob.hide_viewport = ob.hide_render = False
|
||||||
|
|
||||||
|
|
48
ui.py
48
ui.py
|
@ -63,8 +63,8 @@ class GPEXP_PT_gp_node_ui(Panel):
|
||||||
|
|
||||||
col=layout.column()
|
col=layout.column()
|
||||||
col.label(text='Delete Options:')
|
col.label(text='Delete Options:')
|
||||||
col.operator('gp.clear_render_tree', icon='X', text='Clear Render Tree')
|
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"
|
col.operator('gp.clear_render_tree', icon='X', text='Clear & Delete Render Scene').mode = "COMPLETE"
|
||||||
|
|
||||||
|
|
||||||
# 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'
|
||||||
|
@ -75,8 +75,8 @@ class GPEXP_PT_gp_node_ui(Panel):
|
||||||
class GPEXP_PT_gp_dopesheet_ui(Panel):
|
class GPEXP_PT_gp_dopesheet_ui(Panel):
|
||||||
bl_space_type = 'DOPESHEET_EDITOR'
|
bl_space_type = 'DOPESHEET_EDITOR'
|
||||||
bl_region_type = 'UI'
|
bl_region_type = 'UI'
|
||||||
# bl_category = "Item"
|
bl_category = "GP Render"
|
||||||
bl_parent_id='DOPESHEET_PT_gpencil_mode'
|
# bl_parent_id='DOPESHEET_PT_gpencil_mode'
|
||||||
bl_label = "Gpencil Render Manager"
|
bl_label = "Gpencil Render Manager"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -85,7 +85,12 @@ class GPEXP_PT_gp_dopesheet_ui(Panel):
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
layout.label(text=context.object.name)
|
if context.object:
|
||||||
|
layout.label(text=context.object.name)
|
||||||
|
if context.object.data.users > 1:
|
||||||
|
row = layout.row()
|
||||||
|
row.label(text=f'Multiple users ({context.object.data.users})', icon='ERROR')
|
||||||
|
row.operator("wm.call_menu", text="", icon='QUESTION').name = "GPEXP_MT_multi_user_doc"
|
||||||
|
|
||||||
## On layers
|
## On layers
|
||||||
if context.object and context.object.type == 'GPENCIL':
|
if context.object and context.object.type == 'GPENCIL':
|
||||||
|
@ -102,6 +107,34 @@ class GPEXP_PT_gp_dopesheet_ui(Panel):
|
||||||
row.operator('gp.merge_selected_dopesheet_layers', text=txt, )
|
row.operator('gp.merge_selected_dopesheet_layers', text=txt, )
|
||||||
row.enabled= ct > 1
|
row.enabled= ct > 1
|
||||||
|
|
||||||
|
## all and objects
|
||||||
|
if context.scene.name != '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='All GP at once').mode='ALL'
|
||||||
|
|
||||||
|
|
||||||
|
class GPEXP_MT_multi_user_doc(bpy.types.Menu):
|
||||||
|
# bl_idname = "OBJECT_MT_custom_menu"
|
||||||
|
bl_label = "Case of multiuser objects"
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
# call another menu
|
||||||
|
#layout.operator("wm.call_menu", text="Unwrap").name = "VIEW3D_MT_uv_map"
|
||||||
|
#**Behavior from context mode**
|
||||||
|
col = layout.column()
|
||||||
|
col.label(text='Multi user data will be rendered all together on last generated viewlayers.', icon='INFO')
|
||||||
|
col.label(text='Make them single user if needed to render separately.')
|
||||||
|
# col.label(text='Select objects > call search pop-up (F3) > "Make single user" > tick "object data" > ok')
|
||||||
|
col.label(text='Procedure:')
|
||||||
|
|
||||||
|
col.label(text='- select concerned objects')
|
||||||
|
col.label(text='- call search pop-up (F3)')
|
||||||
|
col.label(text='- search "Make single user"')
|
||||||
|
col.label(text='- tick only "object data"')
|
||||||
|
|
||||||
|
## not registered for now (better to place render menu in GP dopesheet)
|
||||||
def manager_ui(self, context):
|
def manager_ui(self, context):
|
||||||
'''appended to DATA_PT_gpencil_layers'''
|
'''appended to DATA_PT_gpencil_layers'''
|
||||||
|
|
||||||
|
@ -136,6 +169,7 @@ def manager_ui(self, context):
|
||||||
#-# REGISTER
|
#-# REGISTER
|
||||||
|
|
||||||
classes=(
|
classes=(
|
||||||
|
GPEXP_MT_multi_user_doc,
|
||||||
GPEXP_PT_gp_node_ui,
|
GPEXP_PT_gp_node_ui,
|
||||||
GPEXP_PT_gp_dopesheet_ui,
|
GPEXP_PT_gp_dopesheet_ui,
|
||||||
)
|
)
|
||||||
|
@ -143,9 +177,9 @@ GPEXP_PT_gp_dopesheet_ui,
|
||||||
def register():
|
def register():
|
||||||
for cls in classes:
|
for cls in classes:
|
||||||
bpy.utils.register_class(cls)
|
bpy.utils.register_class(cls)
|
||||||
bpy.types.DATA_PT_gpencil_layers.prepend(manager_ui)
|
# bpy.types.DATA_PT_gpencil_layers.prepend(manager_ui)
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
bpy.types.DATA_PT_gpencil_layers.remove(manager_ui)
|
# bpy.types.DATA_PT_gpencil_layers.remove(manager_ui)
|
||||||
for cls in reversed(classes):
|
for cls in reversed(classes):
|
||||||
bpy.utils.unregister_class(cls)
|
bpy.utils.unregister_class(cls)
|
Loading…
Reference in New Issue