Add disable use light in file checker

1.9.1

- fix: add error handling on palette linker when blend_path isn't valid anymore
- added: file checker entry, Disable all GP object `use lights` (True by default in addon pref `checklist`)
- added: WIP of Batch reproject all on cursor.
  - No UI. In search menu `Flat Reproject Selected On cursor` (idname `gp.batch_flat_reproject`)
gpv2
Pullusb 2022-01-18 22:53:08 +01:00
parent 4ac743cd32
commit 7985844226
8 changed files with 363 additions and 18 deletions

View File

@ -1,5 +1,12 @@
# Changelog
1.9.1
- fix: add error handling on palette linker when blend_path isn't valid anymore
- added: file checker entry, Disable all GP object `use lights` (True by default in addon pref `checklist`)
- added: WIP of Batch reproject all on cursor.
- No UI. In search menu `Flat Reproject Selected On cursor` (idname `gp.batch_flat_reproject`)
1.9.0
- feat: New shortcuts:

View File

@ -102,7 +102,7 @@ class ODM_PT_sudden_depth_panel(bpy.types.Panel):
layout = self.layout
row = layout.row()
row.operator('object.depth_proportional_move', text='Depth move', icon='TRANSFORM_ORIGINS')
"""
"""
### --- REGISTER ---

View File

@ -24,6 +24,7 @@ class GPTB_OT_file_checker(bpy.types.Operator):
# Set onion skin filter to 'All type'
# Set filepath type
# Set Lock object mode state
# Disable use light on all object
def invoke(self, context, event):
# need some self-control (I had to...)
@ -115,6 +116,14 @@ class GPTB_OT_file_checker(bpy.types.Operator):
if bpy.context.scene.tool_settings.gpencil_stroke_placement_view3d != 'ORIGIN':
problems.append('/!\\ Draw placement not "Origin" (Need Manual change if not Ok)')
## GP Use light disable
if fix.set_gp_use_lights_off:
gp_with_lights = [o for o in context.scene.objects if o.type == 'GPENCIL' and o.use_grease_pencil_lights]
if gp_with_lights:
problems.append(f'Disable "Use Lights" on {len(gp_with_lights)} Gpencil objects')
if apply:
for o in gp_with_lights:
o.use_grease_pencil_lights = False
## Disabled animation
if fix.list_disabled_anim:

160
OP_flat_reproject.py Normal file
View File

@ -0,0 +1,160 @@
import bpy
import mathutils
from mathutils import Matrix, Vector
from mathutils.geometry import intersect_line_plane
from math import pi
import numpy as np
from time import time
from .utils import (location_to_region, region_to_location)
"""
## Do not work on multiple object
def batch_flat_reproject(obj, proj_type='VIEW', all_strokes=True, restore_frame=False):
'''Reproject
:all_stroke: affect hided, locked layers
'''
if restore_frame:
oframe = bpy.context.scene.frame_current
omode = bpy.context.mode
# frame_list = [ f.frame_number for l in obj.data.layers for f in l.frames if len(f.strokes)]
# frame_list = list(set(frame_list))
# frame_list.sort()
# for fnum in frame_list:
# bpy.context.scene.frame_current = fnum
t0 = time()
scn = bpy.context.scene
laynum = len(obj.data.layers)
for i, l in enumerate(obj.data.layers):
## \x1b[2K\r ?
fnum = len(l.frames)
zf = len(str(fnum))
for j, f in enumerate(reversed(l.frames)): # whynot...
print(f'{obj.name} : {i+1}/{laynum} : {l.info} : {str(j+1).zfill(zf)}/{fnum}{" "*30}', end='\r')
scn.frame_set(f.frame_number) # more chance to update the matrix
bpy.context.view_layer.update() # update the matrix ?
bpy.context.scene.camera.location = bpy.context.scene.camera.location
scn.frame_current = f.frame_number
for s in f.strokes:
for p in s.points:
p.co = obj.matrix_world.inverted() @ region_to_location(location_to_region(obj.matrix_world @ p.co), scn.cursor.location)
if restore_frame:
bpy.context.scene.frame_current = oframe
print(' '*50,end='\x1b[1K\r') # clear the line
print(f'{obj.name} ok ({time()-t0:.2f})')
"""
"""
def batch_flat_reproject(obj):
'''Reproject all strokes on 3D cursor for all existing frame of passed GP object'''
scn = bpy.context.scene
cam = scn.camera
for l in obj.data.layers:
for f in l.frames:
scn.frame_set(f.frame_number)
cam_mat = cam.matrix_local.copy()
origin = cam.matrix_world.to_translation()
mat_inv = obj.matrix_world.inverted()
plane_no = Vector((0,0,1))
plane_no.rotate(cam_mat)
plane_co = scn.cursor.location
for s in f.strokes:
points_co = [obj.matrix_world @ p.co for p in s.points]
points_co = [mat_inv @ intersect_line_plane(origin, p, plane_co, plane_no) for p in points_co]
points_co = [co for vector in points_co for co in vector]
s.points.foreach_set('co', points_co)
s.points.add(1) # update
s.points.pop() # update
#for p in s.points:
# loc_2d = location_to_region(obj.matrix_world @ p.co)
# p.co = obj.matrix_world.inverted() @ region_to_location(loc_2d, scn.cursor.location)
"""
def batch_flat_reproject(obj):
'''Reproject strokes of passed GP object on 3D cursor full scene range'''
scn = bpy.context.scene
cam = scn.camera
for i in range(scn.frame_start, scn.frame_end + 1):
scn.frame_set(i)
cam_mat = cam.matrix_local.copy()
origin = cam.matrix_world.to_translation()
mat_inv = obj.matrix_world.inverted()
plane_no = Vector((0,0,1))
plane_no.rotate(cam_mat)
plane_co = scn.cursor.location
for l in obj.data.layers:
f = l.active_frame
if not f: # No active frame
continue
if f.frame_number != scn.frame_current:
f = l.frames.copy(f) # duplicate content of the previous frame
for s in f.strokes:
points_co = [obj.matrix_world @ p.co for p in s.points]
points_co = [mat_inv @ intersect_line_plane(origin, p, plane_co, plane_no) for p in points_co]
points_co = [co for vector in points_co for co in vector]
s.points.foreach_set('co', points_co)
s.points.add(1) # update
s.points.pop() # update
class GPTB_OT_batch_flat_reproject(bpy.types.Operator):
bl_idname = "gp.batch_flat_reproject"
bl_label = "Flat Reproject Selected On cursor"
bl_description = "Reproject all frames of all selected gp object on cursor"
bl_options = {"REGISTER"}
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'GPENCIL'
def execute(self, context):
for o in context.selected_objects:
if o.type != 'GPENCIL' or not o.select_get():
continue
batch_flat_reproject(o)
return {"FINISHED"}
### -- MENU ENTRY --
def flat_reproject_clean_menu(self, context):
if context.mode == 'EDIT_GPENCIL':
self.layout.operator_context = 'INVOKE_REGION_WIN' # needed for popup (also works with 'INVOKE_DEFAULT')
self.layout.operator('gp.batch_flat_reproject', icon='SMALL_TRI_RIGHT_VEC') # KEYTYPE_JITTER_VEC
def flat_reproject_context_menu(self, context):
if context.mode == 'EDIT_GPENCIL' and context.scene.tool_settings.gpencil_selectmode_edit == 'STROKE':
self.layout.operator_context = 'INVOKE_REGION_WIN' # needed for popup
self.layout.operator('gp.batch_flat_reproject', icon='SMALL_TRI_RIGHT_VEC') # KEYTYPE_JITTER_VEC
classes = (
GPTB_OT_batch_flat_reproject,
)
def register():
for cl in classes:
bpy.utils.register_class(cl)
# bpy.types.VIEW3D_MT_gpencil_edit_context_menu.append(flat_reproject_context_menu)
# bpy.types.GPENCIL_MT_cleanup.append(flat_reproject_clean_menu)
def unregister():
# bpy.types.GPENCIL_MT_cleanup.remove(flat_reproject_clean_menu)
# bpy.types.VIEW3D_MT_gpencil_edit_context_menu.remove(flat_reproject_context_menu)
for cl in reversed(classes):
bpy.utils.unregister_class(cl)

View File

@ -79,6 +79,10 @@ class GPTB_OT_import_obj_palette(Operator):
self.report({'ERROR'}, 'Not supported yet, use link')
return {'CANCELLED'}
if not Path(blend_path).exists():
utils.show_message_box([['gp.palettes_reload_blends', 'Invalid blend path! Click here to refresh source blends', 'FILE_REFRESH']], 'Invalid Palette', 'ERROR')
return {'CANCELLED'}
# get relative path
blend_path = bpy.path.relpath(blend_path)
@ -197,6 +201,109 @@ class GPTB_OT_palette_fuzzy_search_obj(Operator):
self.report({'INFO'}, f'Select {bl_props.objects[bl_props.ob_idx].name} (match at {final_ratio*100:.1f}% with "{context.object.name}")')
return {"FINISHED"}
## Unused for now, all libs are linked to one library data. need to replace material links one by one.
class GPTB_OT_palette_version_update(Operator):
bl_idname = "gptb.palette_version_update"
bl_label = "Update Palette Version"
bl_description = "Update linked material to selected palette version if curent link has same basename"
bl_options = {"REGISTER"}
mat_scope : EnumProperty(
name='Targeted Materials',
items=(('ALL', "All Materials", "Update all linked material in file to next version"),
('SELECTED', "Selected Objects", "Update all linked material on selected gp objects"),
),
default='ALL',
description='Choose material targeted for library update'
)
mat_type : EnumProperty(
name='Materials Type',
items=(('ALL', "All Materials", "Update both gp and obj materials"),
('GP', "Gpencil Materials", "update only grease pencil materials"),
('OBJ', "Non-Gpencil Materials", "update only non-gpencil objects materials"),
),
default='GP',
description='Filter material type for library update'
)
def invoke(self, context, event):
self.bl_props = context.scene.bl_palettes_props
if not self.bl_props.blends or not self.bl_props.blends[0].blend_path:
self.report({'ERROR'}, 'No blend selected')
return {"CANCELLED"}
return context.window_manager.invoke_props_dialog(self, width=450)
def draw(self, context):
layout = self.layout
layout.label(text=f'Update links path to palette: {self.bl_props.blends[self.bl_props.bl_idx].blend_name}', icon='LINK_BLEND')
self.bl_props
layout.prop(self, 'mat_scope')
layout.prop(self, 'mat_type')
col = layout.column(align=True)
col.label(text='Does not check if material exists in target blend', icon='INFO')
col.label(text='Just change source filepath if different version of same source name is found')
# col.label(text='version of same source name is found')
def execute(self, context):
if self.mat_scope == 'SELECTED' and not context.selected_objects:
self.report({'ERROR'}, 'No selected objects')
return {"CANCELLED"}
bl_props = context.scene.bl_palettes_props
bl = bl_props.blends[bl_props.bl_idx]
bl_name, bl_path = bl.blend_name, bl.blend_path
if not Path(bl_path).exists():
self.report({'ERROR'}, f'Current selected blend source seem unreachable, try to refresh\ninvalid path: {bl_path}')
return {"CANCELLED"}
reversion = re.compile(r'\d{2,4}$') # version padding from 2 to 4
bl_relpath = bpy.path.relpath(bl_path)
if self.mat_scope == 'SELECTED':
pool = []
for o in context.selected_objects:
for m in o.data.materials:
pool.append(m)
elif self.mat_scope == 'ALL':
pool = [m for m in bpy.data.materials]
ct = 0
for m in pool:
if not m.library:
continue
if self.mat_type == 'GP' and not m.is_grease_pencil:
continue
if self.mat_type == 'OBJ' and m.is_grease_pencil:
continue
cur_fp = m.library.filepath
if not cur_fp:
print(f'! {m.name} has an empty library filepath !')
continue
p_cur_fp = Path(cur_fp)
if p_cur_fp.stem == bl_name:
continue # already good
if reversion.sub('', p_cur_fp.stem) != reversion.sub('', bl_name):
continue # not same stem base
# Same stem without version, can update to this one
print(f'{m.name}: {p_cur_fp} >> {bl_relpath}')
ct += 1
m.library.filepath = bl_relpath
if ct:
self.report({'INFO'}, f'{ct} material link path updated')
else:
self.report({'WARNING'}, 'No material path updated')
return {"FINISHED"}
#--- UI LIST
class GPTB_UL_blend_list(UIList):
@ -452,6 +559,8 @@ GPTB_UL_object_list,
GPTB_PG_palette_settings,
GPTB_OT_import_obj_palette,
# GPTB_OT_palette_version_update,
# TEST_OT_import_obj_palette_test,
)

View File

@ -3,6 +3,13 @@ from .utils import get_addon_prefs
import bpy
from pathlib import Path
from bpy.types import Panel
from bpy.props import (
IntProperty,
BoolProperty,
StringProperty,
FloatProperty,
EnumProperty,
)
## UI in properties
@ -420,6 +427,8 @@ def palettes_path_ui(self, context):
col.prop(pl_prop, 'custom_dir', text='Custom Dir')
# if not pl_prop.custom_dir or pl_prop.show_path:
# col.prop(pl_prop, 'custom_dir', text='Custom Dir')
# col.operator('gptb.palette_version_update', text='Update Palette Version') # when update is ready
def palettes_lists_ui(self, context, popup=False):
@ -589,7 +598,16 @@ class GPTB_PT_tools_grease_pencil_interpolate(Panel):
def draw(self, context):
layout = self.layout
settings = context.tool_settings.gpencil_interpolate
layout.use_property_split = True
# settings = context.tool_settings.gpencil_interpolate # old 2.92 global settings
## access active tool settings
# settings = context.workspace.tools[0].operator_properties('gpencil.interpolate')
settings = context.workspace.tools.from_space_view3d_mode('PAINT_GPENCIL').operator_properties('gpencil.interpolate')
## custom curve access (still in gp interpolate tools)
interpolate_settings = context.tool_settings.gpencil_interpolate
# ex : interpolate_settings.interpolation_curve.curves[0].points[1].location
col = layout.column(align=True)
col.label(text="Interpolate Strokes")
@ -599,29 +617,62 @@ class GPTB_PT_tools_grease_pencil_interpolate(Panel):
col = layout.column(align=True)
col.label(text="Options:")
col.prop(settings, "interpolate_all_layers")
# col.prop(settings, "interpolate_all_layers") # now the enum "layers"
gpd = context.gpencil_data
if gpd.use_stroke_edit_mode:
col.prop(settings, "interpolate_selected_only")
col.prop(settings, "layers")
col.prop(settings, "flip")
col.prop(settings, "smooth_factor")
col.prop(settings, "smooth_steps")
'''## Sequence Options
seq_settings = context.window_manager.operators.get('GPENCIL_OT_interpolate_sequence')
col = layout.column(align=True)
col.label(text="Sequence Options:")
col.prop(settings, "step")
col.prop(settings, "type")
if settings.type == 'CUSTOM':
# TODO: Options for loading/saving curve presets?
col.template_curve_mapping(settings, "interpolation_curve", brush=True,
use_negative_slope=True)
elif settings.type != 'LINEAR':
col.prop(settings, "easing")
if not seq_settings:
# col.label(text='Launch Interpolate Sequence Once')
# col.operator('gpencil.interpolate_sequence',text='Interpolate Sequence Once')
col.label(text='Interpolate sequence', icon='INFO')
col.label(text='must be launched')
col.label(text="once per session")
col.label(text="to expose it's properties")
return
if settings.type == 'BACK':
layout.prop(settings, "back")
elif settings.type == 'ELASTIC':
col.prop(seq_settings, "step")
col.prop(seq_settings, "layers")
col.prop(seq_settings, "interpolate_selected_only")
col.prop(seq_settings, "flip")
col.prop(seq_settings, "smooth_factor")
col.prop(seq_settings, "smooth_steps")
col.prop(seq_settings, "type")
if seq_settings.type == 'CUSTOM':
# TODO: Options for loading/saving curve presets?
col.template_curve_mapping(interpolate_settings, "interpolation_curve", brush=True,
use_negative_slope=True)
elif seq_settings.type != 'LINEAR':
col.prop(seq_settings, "easing")
if seq_settings.type == 'BACK':
layout.prop(seq_settings, "back")
elif seq_settings.type == 'ELASTIC':
sub = layout.column(align=True)
sub.prop(settings, "amplitude")
sub.prop(settings, "period")
sub.prop(seq_settings, "amplitude")
sub.prop(seq_settings, "period")
'''
## recreate property group from operator options
# inspect context.window_manager.operators['GPENCIL_OT_interpolate_sequence']
# separate options from single interpolation and sequence interpolation
# class GPTB_PG_interpolate_sequence_prop(bpy.types.PropertyGroup):
# interpolate_selected_only : BoolProperty(
# name="Selected Only",
# description="",
# default=True,
# options={'HIDDEN'})
def interpolate_header_ui(self, context):
layout = self.layout

View File

@ -15,7 +15,7 @@ bl_info = {
"name": "GP toolbox",
"description": "Tool set for Grease Pencil in animation production",
"author": "Samuel Bernou, Christophe Seux",
"version": (1, 9, 0),
"version": (1, 9, 1),
"blender": (2, 91, 0),
"location": "Sidebar (N menu) > Gpencil > Toolbox / Gpencil properties",
"warning": "",
@ -47,6 +47,7 @@ from . import OP_brushes
from . import OP_file_checker
from . import OP_copy_paste
from . import OP_realign
from . import OP_flat_reproject
from . import OP_depth_move
from . import OP_key_duplicate_send
from . import OP_layer_manager
@ -574,6 +575,7 @@ class GPTB_prefs(bpy.types.AddonPreferences):
col.prop(self.fixprops, 'set_slider_n_sync')
col.prop(self.fixprops, 'check_front_axis')
col.prop(self.fixprops, 'check_placement')
col.prop(self.fixprops, 'set_gp_use_lights_off')
col.prop(self.fixprops, 'set_pivot_median_point')
col.prop(self.fixprops, 'disable_guide')
col.prop(self.fixprops, 'list_disabled_anim')
@ -686,6 +688,7 @@ def register():
OP_brushes.register()
OP_cursor_snap_canvas.register()
OP_copy_paste.register()
OP_flat_reproject.register()
OP_realign.register()
OP_depth_move.register()
OP_key_duplicate_send.register()
@ -728,6 +731,7 @@ def unregister():
OP_key_duplicate_send.unregister()
OP_depth_move.unregister()
OP_realign.unregister()
OP_flat_reproject.unregister()
OP_copy_paste.unregister()
OP_cursor_snap_canvas.unregister()
OP_brushes.unregister()

View File

@ -60,6 +60,11 @@ class GP_PG_FixSettings(bpy.types.PropertyGroup):
description="Toggle on the use of show slider and sync range",
default=True, options={'HIDDEN'})
set_gp_use_lights_off : BoolProperty(
name="Set Off Use lights On All Gpencil Objects",
description="Uncheck Use lights on all grease pencil objects\nAt object level, not layer level (Object properties > Visibility > GP uselight)",
default=True, options={'HIDDEN'})
check_front_axis : BoolProperty(
name="Check If Draw Axis is Front (X-Z)",
description="Alert if the current grease pencil draw axis is not front (X-Z)",