2021-09-17 16:31:26 +02:00
import bpy
from bpy . props import ( FloatProperty ,
BoolProperty ,
EnumProperty ,
StringProperty ,
IntProperty )
from . import fn
2022-01-22 19:13:11 +01:00
import math
import re
2021-09-17 16:31:26 +02:00
class GPEXP_OT_layers_state ( bpy . types . Operator ) :
bl_idname = " gp.layers_state "
bl_label = " Set Layers State "
bl_description = " Display state of layer that migh need adjustement "
bl_options = { " REGISTER " } # , "UNDO"
# clear_unused_view_layers :BoolProperty(name="Clear unused view layers",
# description="Delete view layer that aren't used in the nodetree anymore",
# default=True)
# TODO : (optional) export layer opacity to json and/or text
# (that way compo artists can re-affect opacity quickly or at least have a reminder)
all_objects : BoolProperty ( name = ' On All Object ' ,
2021-09-22 18:35:52 +02:00
default = True , description = ' On All object, else use selected objects ' ) # , options={'SKIP_SAVE'}
2021-09-17 16:31:26 +02:00
set_full_opacity : BoolProperty ( name = ' Set Full Opacity ' ,
default = True , description = ' Check/Set full opacity ' ) # , options={'SKIP_SAVE'}
set_use_lights : BoolProperty ( name = ' Disable Use Light ' ,
default = True , description = ' Check/Set use lights disabling ' ) # , options={'SKIP_SAVE'}
set_blend_mode : BoolProperty ( name = ' Set Regular Blend Mode ' ,
default = True , description = ' Check/Set blend mode to regular ' ) # , options={'SKIP_SAVE'}
2021-09-22 18:35:52 +02:00
2021-10-11 16:47:22 +02:00
clear_frame_out_of_range : BoolProperty ( name = ' Clear Frames Out Of Scene Range ' ,
default = False , description = ' Delete frames that before scene start and after scene end range \n With a tolerance of one frame to avoid problem \n Affect all layers) ' ) # , options={'SKIP_SAVE'}
2021-09-22 18:35:52 +02:00
opacity_exclude_list : StringProperty ( name = ' Skip ' ,
default = ' MA ' , description = ' Skip prefixes from this list when changing opacity \n Separate multiple value with a comma (ex: MA,IN) ' ) # , options={'SKIP_SAVE'}
2021-09-17 16:31:26 +02:00
2021-10-11 16:47:22 +02:00
2021-09-17 16:31:26 +02:00
@classmethod
def poll ( cls , context ) :
return context . object and context . object . type == ' GPENCIL '
def invoke ( self , context , event ) :
# self.ctrl=event.ctrl
# self.alt=event.alt
if event . alt :
self . all_objects = True
# return self.execute(context)
return context . window_manager . invoke_props_dialog ( self )
def draw ( self , context ) :
layout = self . layout
layout . prop ( self , ' all_objects ' )
2021-10-11 16:47:22 +02:00
total = len ( [ o for o in context . scene . objects if o . type == ' GPENCIL ' ] )
target_num = total if self . all_objects else len ( [ o for o in context . selected_objects if o . type == ' GPENCIL ' ] )
layout . label ( text = f ' { target_num } / { total } targeted GP ' )
layout . separator ( )
layout . prop ( self , ' clear_frame_out_of_range ' )
2021-09-17 16:31:26 +02:00
layout . separator ( )
layout . label ( text = ' Set (or only perform a check): ' )
2021-09-22 18:35:52 +02:00
row = layout . row ( )
row . prop ( self , ' set_full_opacity ' )
if self . set_full_opacity :
row . prop ( self , ' opacity_exclude_list ' )
2021-09-17 16:31:26 +02:00
layout . prop ( self , ' set_use_lights ' )
layout . prop ( self , ' set_blend_mode ' )
# layout.prop(self, 'clear_unused_view_layers')
def execute ( self , context ) :
if self . all_objects :
pool = [ o for o in context . scene . objects if o . type == ' GPENCIL ' ]
else :
pool = [ o for o in context . selected_objects if o . type == ' GPENCIL ' ]
# pool = [context.object]
changes = [ ]
for ob in pool :
changes . append ( f ' >> { ob . name } ' )
layers = ob . data . layers
2021-10-11 16:47:22 +02:00
if self . clear_frame_out_of_range :
ct = fn . clear_frame_out_of_range ( ob , verbose = False )
if ct :
changes . append ( f ' { ct } out of range frame deleted ' )
2021-09-17 16:31:26 +02:00
for l in layers :
used = False
## mask check
# if l.mask_layers:
# print(f'-> masks')
# state = '' if l.use_mask_layer else ' (disabled)'
# print(f'{ob.name} > {l.info}{state}:')
# used = True
# for ml in l.mask_layers:
# mlstate = ' (disabled)' if ml.hide else ''
# mlinvert = ' <>' if ml.invert else ''
# print(f'{ml.info}{mlstate}{mlinvert}')
if l . opacity != 1 :
2021-09-22 18:35:52 +02:00
# TODO Skip zeroed opacity ?
# check if there is an exclusion word
if any ( x . strip ( ) + ' _ ' in l . info for x in self . opacity_exclude_list . strip ( ' , ' ) . split ( ' , ' ) if x ) :
print ( f ' Skipped layer : { l . info } ' )
else :
full_opacity_state = ' ' if self . set_full_opacity else ' (check only) '
mess = f ' { l . info } : opacity { l . opacity : .2f } >> 1.0 { full_opacity_state } '
print ( mess )
changes . append ( mess )
if self . set_full_opacity :
l . opacity = 1.0
used = True
2021-09-17 16:31:26 +02:00
if l . use_lights :
use_lights_state = ' ' if self . set_use_lights else ' (check only) '
mess = f ' { l . info } : disable use lights { use_lights_state } '
print ( mess )
2022-01-24 11:43:47 +01:00
# changes.append(mess) # don't report disable use_light... too many messages
2021-09-17 16:31:26 +02:00
if self . set_use_lights :
l . use_lights = False
used = True
if l . blend_mode != ' REGULAR ' :
blend_mode_state = ' ' if self . set_blend_mode else ' (check only) '
mess = f ' { l . info } : blend mode " { l . blend_mode } " >> regular { blend_mode_state } '
print ( mess )
changes . append ( mess )
if self . set_blend_mode :
l . blend_mode = ' REGULAR '
used = True
2021-10-11 16:47:22 +02:00
2021-09-17 16:31:26 +02:00
if used :
print ( )
if changes :
changes . append ( ' ' )
fn . show_message_box ( _message = changes , _title = " Layers Check Report " , _icon = ' INFO ' )
# render = bpy.data.scenes.get('Render')
# if not render:
# print('SKIP, no Render scene')
# return {"CANCELLED"}
return { " FINISHED " }
2021-09-21 18:23:25 +02:00
class GPEXP_OT_lower_layers_name ( bpy . types . Operator ) :
bl_idname = " gp.lower_layers_name "
2021-09-22 12:06:40 +02:00
bl_label = " Normalize Layers Name "
bl_description = " Make the object and layers name lowercase with dashed converted to underscore (without touching layer prefix and suffix) "
2021-09-21 18:23:25 +02:00
bl_options = { " REGISTER " , " UNDO " }
@classmethod
def poll ( cls , context ) :
return context . object and context . object . type == ' GPENCIL '
all_objects : BoolProperty ( name = ' On All Object ' ,
default = False , description = ' On All object, else use selected objects ' ) # , options={'SKIP_SAVE'}
2021-09-22 12:06:40 +02:00
object_name : BoolProperty ( name = ' Normalize Object Name ' ,
2021-09-21 18:23:25 +02:00
default = True , description = ' Make the object name lowercase ' ) # , options={'SKIP_SAVE'}
2021-09-22 12:06:40 +02:00
layer_name : BoolProperty ( name = ' Normalize Layers Names ' ,
2021-09-21 18:23:25 +02:00
default = True , description = ' Make the layers name lowercase ' ) # , options={'SKIP_SAVE'}
2021-09-22 12:06:40 +02:00
# dash_to_undescore : BoolProperty(name='Dash To Underscore',
# default=True, description='Make the layers name lowercase') # , options={'SKIP_SAVE'}
2021-09-21 18:23:25 +02:00
def invoke ( self , context , event ) :
# self.ctrl=event.ctrl
# self.alt=event.alt
if event . alt :
self . all_objects = True
# return self.execute(context)
return context . window_manager . invoke_props_dialog ( self )
def draw ( self , context ) :
layout = self . layout
layout . prop ( self , ' all_objects ' )
if self . all_objects :
gp_ct = len ( [ o for o in context . scene . objects if o . type == ' GPENCIL ' ] )
else :
gp_ct = len ( [ o for o in context . selected_objects if o . type == ' GPENCIL ' ] )
layout . label ( text = f ' { gp_ct } to lower-case ' )
layout . separator ( )
layout . label ( text = f ' Choose what to rename: ' )
layout . prop ( self , ' object_name ' )
layout . prop ( self , ' layer_name ' )
2021-09-22 12:06:40 +02:00
# if self.layer_name:
# box = layout.box()
# box.prop(self, 'dash_to_undescore')
2021-09-21 18:23:25 +02:00
if not self . object_name and not self . layer_name :
layout . label ( text = f ' At least one choice! ' , icon = ' ERROR ' )
def execute ( self , context ) :
if self . all_objects :
pool = [ o for o in context . scene . objects if o . type == ' GPENCIL ' ]
else :
pool = [ o for o in context . selected_objects if o . type == ' GPENCIL ' ]
for ob in pool :
if self . object_name :
rename_data = ob . name == ob . data . name
2021-09-22 12:06:40 +02:00
ob . name = ob . name . lower ( ) . replace ( ' - ' , ' _ ' )
2021-09-21 18:23:25 +02:00
if rename_data :
2021-09-22 12:06:40 +02:00
ob . data . name = ob . name
2021-09-21 18:23:25 +02:00
if self . layer_name :
for l in ob . data . layers :
2021-09-22 12:28:35 +02:00
# if self.dash_to_undescore:
l . info = l . info . replace ( ' - ' , ' _ ' )
2021-09-22 12:06:40 +02:00
fn . normalize_layer_name ( l ) # default : lower=True, dash_to_underscore=self.dash_to_undescore
2021-09-21 18:23:25 +02:00
return { " FINISHED " }
2022-01-22 19:13:11 +01:00
class GPEXP_OT_auto_number_object ( bpy . types . Operator ) :
bl_idname = " gp.auto_number_object "
bl_label = " Auto Number Object "
bl_description = " Automatic prefix number based on origin distance to camera and in_front values \n Ctrl + Clic to delete name to delete numbering "
bl_options = { " REGISTER " , " UNDO " }
@classmethod
def poll ( cls , context ) :
return context . object and context . object . type == ' GPENCIL '
all_objects : BoolProperty ( name = ' On All GP Object ' ,
default = False , description = ' On All object, else use selected Grease Pencil objects ' ) # , options={'SKIP_SAVE'}
rename_data : BoolProperty ( name = ' Rename Gpencil Data ' ,
default = True , description = ' Rename Also the Grease Pencil data using same name as object ' ) # , options={'SKIP_SAVE'}
delete : BoolProperty ( default = False , options = { ' SKIP_SAVE ' } )
def invoke ( self , context , event ) :
# if event.alt:
# self.all_objects=True
if event . ctrl or self . delete :
regex_num = re . compile ( r ' ^( \ d {3} )_ ' )
ct = 0
gps = [ o for o in context . selected_objects if o . type == ' GPENCIL ' ]
for o in gps :
if regex_num . match ( o . name ) :
o . name = o . name [ 4 : ]
ct + = 1
self . report ( { ' INFO ' } , f ' { ct } / { len ( gps ) } number prefix removed from object names ' )
return { " FINISHED " }
return context . window_manager . invoke_props_dialog ( self )
def draw ( self , context ) :
layout = self . layout
layout . prop ( self , ' all_objects ' )
if self . all_objects :
gp_ct = len ( [ o for o in context . scene . objects if o . type == ' GPENCIL ' ] )
else :
gp_ct = len ( [ o for o in context . selected_objects if o . type == ' GPENCIL ' ] )
layout . prop ( self , ' rename_data ' )
layout . label ( text = f ' { gp_ct } objects to renumber ' )
if not gp_ct :
layout . label ( text = ' No Gpencil object to renumber ' , icon = ' ERROR ' )
def execute ( self , context ) :
if self . all_objects :
pool = [ o for o in context . scene . objects if o . type == ' GPENCIL ' ]
else :
pool = [ o for o in context . selected_objects if o . type == ' GPENCIL ' ]
def reversed_enumerate ( collection : list ) :
for i in range ( len ( collection ) - 1 , - 1 , - 1 ) :
yield i , collection [ i ]
fronts = [ ]
## separate In Front objects:
for i , o in reversed_enumerate ( pool ) :
if o . show_in_front :
fronts . append ( pool . pop ( i ) )
cam_loc = context . scene . camera . matrix_world . to_translation ( )
# filter by distance to camera object (considering origins)
pool . sort ( key = lambda x : math . dist ( x . matrix_world . to_translation ( ) , cam_loc ) )
fronts . sort ( key = lambda x : math . dist ( x . matrix_world . to_translation ( ) , cam_loc ) )
# re-insert fitlered infront object before others
pool = fronts + pool
ct = 10
regex_num = re . compile ( r ' ^( \ d {3} )_ ' )
for o in pool :
renum = regex_num . search ( o . name )
if not renum :
o . name = f ' { str ( ct ) . zfill ( 3 ) } _ { o . name } '
else :
## either replace or leave untouched
# continue
o . name = f ' { str ( ct ) . zfill ( 3 ) } _ { o . name [ 4 : ] } '
ct + = 10
if self . rename_data and o . name != o . data . name :
o . data . name = o . name
return { " FINISHED " }
2021-09-17 16:31:26 +02:00
classes = (
GPEXP_OT_layers_state ,
2022-01-22 19:13:11 +01:00
GPEXP_OT_lower_layers_name ,
GPEXP_OT_auto_number_object
2021-09-17 16:31:26 +02:00
)
def register ( ) :
for cls in classes :
bpy . utils . register_class ( cls )
def unregister ( ) :
for cls in reversed ( classes ) :
bpy . utils . unregister_class ( cls )