parent
07a44190c9
commit
492095d333
112
OP_realign.py
112
OP_realign.py
|
@ -5,7 +5,7 @@ from math import pi
|
|||
import numpy as np
|
||||
from time import time
|
||||
|
||||
def batch_reproject(obj, project_type='VIEW', all_strokes=True, restore_frame=False):
|
||||
def batch_reproject(obj, proj_type='VIEW', all_strokes=True, restore_frame=False):
|
||||
'''Reproject - ops method
|
||||
:all_stroke: affect hided, locked layers
|
||||
'''
|
||||
|
@ -30,7 +30,7 @@ def batch_reproject(obj, project_type='VIEW', all_strokes=True, restore_frame=Fa
|
|||
bpy.context.scene.frame_set(f.frame_number)
|
||||
# switch to edit to reproject through ops
|
||||
bpy.ops.gpencil.select_all(action='SELECT')
|
||||
bpy.ops.gpencil.reproject(type=project_type) # default is VIEW
|
||||
bpy.ops.gpencil.reproject(type=proj_type) # default is VIEW
|
||||
bpy.ops.gpencil.select_all(action='DESELECT')
|
||||
|
||||
# restore
|
||||
|
@ -46,7 +46,7 @@ def batch_reproject(obj, project_type='VIEW', all_strokes=True, restore_frame=Fa
|
|||
bpy.context.scene.frame_current = oframe
|
||||
|
||||
|
||||
def align_global(reproject=True, ref=None):
|
||||
def align_global(reproject=True, ref=None, all_strokes=True):
|
||||
|
||||
if not ref:
|
||||
ref = bpy.context.scene.camera
|
||||
|
@ -101,10 +101,10 @@ def align_global(reproject=True, ref=None):
|
|||
o.matrix_basis = new_mat
|
||||
|
||||
if reproject:
|
||||
batch_reproject(o, project_type='FRONT')
|
||||
batch_reproject(o, proj_type='FRONT', all_strokes=all_strokes)
|
||||
|
||||
|
||||
def align_all_frames(reproject=True, ref=None):
|
||||
def align_all_frames(reproject=True, ref=None, all_strokes=True):
|
||||
|
||||
print('aligning all frames...')
|
||||
|
||||
|
@ -128,9 +128,9 @@ def align_all_frames(reproject=True, ref=None):
|
|||
rot_keys = list(set(rot_keys))
|
||||
|
||||
# TODO # TOTHINK
|
||||
# for now the rotation of the object is adjusted at every check....
|
||||
# might be better to check camera rotation of the current frame only stored as copy.
|
||||
# else the object rotate following the camera...
|
||||
# for now the rotation of the object is adjusted at every frame....
|
||||
# might be better to check camera rotation of the current frame only (stored as copy).
|
||||
# else the object rotate following the cameraview vector (not constant)...
|
||||
|
||||
mat_90 = Matrix.Rotation(-pi/2, 4, 'X')
|
||||
|
||||
|
@ -191,7 +191,7 @@ def align_all_frames(reproject=True, ref=None):
|
|||
|
||||
|
||||
if reproject:
|
||||
batch_reproject(o, project_type='FRONT')
|
||||
batch_reproject(o, proj_type='FRONT', all_strokes=all_strokes)
|
||||
|
||||
return
|
||||
|
||||
|
@ -199,21 +199,60 @@ def align_all_frames(reproject=True, ref=None):
|
|||
class GPTB_OT_realign(bpy.types.Operator):
|
||||
bl_idname = "gp.realign"
|
||||
bl_label = "Realign GP"
|
||||
bl_description = "Realign the grease pencil on camera"
|
||||
bl_description = "Realign the grease pencil front axis with active camera"
|
||||
bl_options = {"REGISTER"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.object and context.object.type == 'GPENCIL'
|
||||
|
||||
reproject : bpy.props.BoolProperty(name='Reproject', default=True)
|
||||
reproject : bpy.props.BoolProperty(
|
||||
name='Reproject', default=True,
|
||||
description='Reproject stroke on the new alignment')
|
||||
|
||||
all_strokes : bpy.props.BoolProperty(
|
||||
name='All Strokes', default=False,
|
||||
description='Hided and locked layer will also be reprojected')
|
||||
|
||||
## add option to bake strokes if rotation anim is not constant ? might generate too many Keyframes
|
||||
|
||||
def invoke(self, context, event):
|
||||
if context.object.data.use_multiedit:
|
||||
self.report({'ERROR'}, 'Does not work in Multi-edit')
|
||||
return {"CANCELLED"}
|
||||
|
||||
self.alert = ''
|
||||
o = context.object
|
||||
if o.animation_data and o.animation_data.action:
|
||||
act = o.animation_data.action
|
||||
for chan in ('rotation_euler', 'rotation_quaternion'):
|
||||
if act.fcurves.find(chan):
|
||||
self.alert = 'Animated Rotation (CONSTANT interpolation)'
|
||||
interpos = [p for fcu in act.fcurves if fcu.data_path == chan for p in fcu.keyframe_points if p.interpolation != 'CONSTANT']
|
||||
if interpos:
|
||||
self.alert = f'Animated Rotation ! ({len(interpos)} key not constant)'
|
||||
break
|
||||
|
||||
return context.window_manager.invoke_props_dialog(self, width=450)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
if self.alert:
|
||||
layout.label(text=self.alert, icon='INFO')
|
||||
layout.label(text='(rotations key will be overwritten to face camera)')
|
||||
|
||||
layout.prop(self, "reproject")
|
||||
layout.label(text='Realign the GP front axis with active camera')
|
||||
if self.reproject:
|
||||
if not context.region_data.view_perspective == 'CAMERA':
|
||||
layout.label(text='Not in camera ! (reprojection is made from view)', icon='ERROR')
|
||||
layout.prop(self, "all_strokes")
|
||||
|
||||
def execute(self, context):
|
||||
t0 = time()
|
||||
oframe = context.scene.frame_current
|
||||
|
||||
o = bpy.context.object
|
||||
|
||||
o = bpy.context.object
|
||||
if o.animation_data and o.animation_data.action:
|
||||
if o.animation_data.action.fcurves.find('rotation_euler') or o.animation_data.action.fcurves.find('rotation_quaternion'):
|
||||
align_all_frames(reproject=self.reproject)
|
||||
|
@ -225,6 +264,7 @@ class GPTB_OT_realign(bpy.types.Operator):
|
|||
align_global(reproject=self.reproject)
|
||||
|
||||
context.scene.frame_current = oframe
|
||||
|
||||
print(f'\nGlobal Realign ({time()-t0:.2f}s)')
|
||||
|
||||
return {"FINISHED"}
|
||||
|
@ -232,39 +272,57 @@ class GPTB_OT_realign(bpy.types.Operator):
|
|||
|
||||
class GPTB_OT_batch_reproject_all_frames(bpy.types.Operator):
|
||||
bl_idname = "gp.batch_reproject_all_frames"
|
||||
bl_label = "Reproject All Frame"
|
||||
bl_description = "Reproject all frame of active object."
|
||||
bl_label = "Reproject All Frames"
|
||||
bl_description = "Reproject all frames of active object."
|
||||
bl_options = {"REGISTER"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.object and context.object.type == 'GPENCIL'
|
||||
|
||||
reproject : bpy.props.BoolProperty(name='Reproject', default=True)
|
||||
# reproject : bpy.props.BoolProperty(name='Reproject', default=True)
|
||||
|
||||
# TODO witch side ?
|
||||
all_strokes : bpy.props.BoolProperty(
|
||||
name='All Strokes', default=False,
|
||||
description='Hided and locked layer will also be reprojected')
|
||||
|
||||
type: bpy.props.EnumProperty(name='Type',
|
||||
items=(('FRONT', "Front", ""),
|
||||
('SIDE', "Side", ""),
|
||||
('TOP', "Top", ""),
|
||||
('VIEW', "View", ""),
|
||||
('SURFACE', "Surface", ""),
|
||||
('CURSOR', "Cursor", ""),
|
||||
),
|
||||
default='FRONT')
|
||||
|
||||
# def invoke(self, context, event):
|
||||
# return context.window_manager.invoke_props_dialog(self)
|
||||
# return self.execute(context)
|
||||
def invoke(self, context, event):
|
||||
if context.object.data.use_multiedit:
|
||||
self.report({'ERROR'}, 'Does not work in Multi-edit')
|
||||
return {"CANCELLED"}
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
def draw(self, context):
|
||||
self.layout.prop(self, "toggle", text="Toggle to redraw")
|
||||
for i in range(20):
|
||||
self.layout.label(str(i))
|
||||
layout = self.layout
|
||||
if not context.region_data.view_perspective == 'CAMERA':
|
||||
layout.label(text='Not in camera ! (reprojection is made from view)', icon='ERROR')
|
||||
layout.prop(self, "all_strokes")
|
||||
layout.prop(self, "type")
|
||||
|
||||
|
||||
def execute(self, context):
|
||||
t0 = time()
|
||||
batch_reproject(bpy.context.object, all_strokes=True, restore_frame=True) # all_strokes=True, restore_frame=False)
|
||||
self.report({'INFO'}, f'\nReprojected in ({time()-t0:.2f}s)' )
|
||||
|
||||
batch_reproject(context.object, proj_type=self.type, all_strokes=self.all_strokes, restore_frame=True)
|
||||
|
||||
self.report({'INFO'}, f'Reprojected in ({time()-t0:.2f}s)' )
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
classes = (
|
||||
GPTB_OT_realign,
|
||||
GPTB_OT_batch_reproject_all_frames
|
||||
# GPTB_OT_realign,
|
||||
GPTB_OT_batch_reproject_all_frames,
|
||||
)
|
||||
|
||||
def register():
|
||||
|
|
|
@ -112,6 +112,10 @@ Panel in sidebar : 3D view > sidebar 'N' > Gpencil
|
|||
|
||||
## Changelog:
|
||||
|
||||
1.0.9:
|
||||
|
||||
- feat: Reproject all frames operator
|
||||
|
||||
1.0.8:
|
||||
|
||||
- feat: Keyframe jump filter added in UI to change general behavior. Keymap own jump filter can override this new global settings if specified
|
||||
|
|
|
@ -160,15 +160,17 @@ class GPTB_PT_sidebar_panel(bpy.types.Panel):
|
|||
box = layout.box()
|
||||
box.label(text='Missing base material setup', icon='INFO')
|
||||
box.operator('gp.load_default_palette')
|
||||
|
||||
|
||||
else:
|
||||
layout.label(text='No GP object selected')
|
||||
|
||||
|
||||
layout.prop(context.scene.gptoolprops, 'edit_lines_opacity')
|
||||
|
||||
# layout.operator('gp.realign')
|
||||
layout.operator('gp.batch_reproject_all_frames') # text=Batch Reproject
|
||||
## Create empty frame on layer (ops stored under GP_colorize... might be best to separate in another panel )
|
||||
layout.operator('gp.create_empty_frames', icon = 'DECORATE_KEYFRAME')
|
||||
layout.operator('gp.create_empty_frames', icon='DECORATE_KEYFRAME')
|
||||
|
||||
## File checker
|
||||
row = layout.row(align=True)
|
||||
|
|
|
@ -15,7 +15,7 @@ bl_info = {
|
|||
"name": "GP toolbox",
|
||||
"description": "Set of tools for Grease Pencil in animation production",
|
||||
"author": "Samuel Bernou",
|
||||
"version": (1, 0, 8),
|
||||
"version": (1, 0, 9),
|
||||
"blender": (2, 91, 0),
|
||||
"location": "sidebar (N menu) > Gpencil > Toolbox / Gpencil properties",
|
||||
"warning": "",
|
||||
|
@ -44,6 +44,7 @@ from . import OP_palettes
|
|||
from . import OP_file_checker
|
||||
from . import OP_render
|
||||
from . import OP_copy_paste
|
||||
from . import OP_realign
|
||||
from . import keymaps
|
||||
|
||||
from .OP_pseudo_tint import GPT_OT_auto_tint_gp_layers
|
||||
|
@ -439,6 +440,7 @@ def register():
|
|||
OP_cursor_snap_canvas.register()
|
||||
OP_render.register()
|
||||
OP_copy_paste.register()
|
||||
OP_realign.register()
|
||||
UI_tools.register()
|
||||
keymaps.register()
|
||||
bpy.types.Scene.gptoolprops = bpy.props.PointerProperty(type = GP_PG_ToolsSettings)
|
||||
|
@ -460,6 +462,7 @@ def unregister():
|
|||
for cls in reversed(classes):
|
||||
bpy.utils.unregister_class(cls)
|
||||
UI_tools.unregister()
|
||||
OP_realign.unregister()
|
||||
OP_copy_paste.unregister()
|
||||
OP_render.unregister()
|
||||
OP_cursor_snap_canvas.unregister()
|
||||
|
|
Loading…
Reference in New Issue