parent
07a44190c9
commit
492095d333
110
OP_realign.py
110
OP_realign.py
|
@ -5,7 +5,7 @@ from math import pi
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from time import time
|
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
|
'''Reproject - ops method
|
||||||
:all_stroke: affect hided, locked layers
|
: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)
|
bpy.context.scene.frame_set(f.frame_number)
|
||||||
# switch to edit to reproject through ops
|
# switch to edit to reproject through ops
|
||||||
bpy.ops.gpencil.select_all(action='SELECT')
|
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')
|
bpy.ops.gpencil.select_all(action='DESELECT')
|
||||||
|
|
||||||
# restore
|
# restore
|
||||||
|
@ -46,7 +46,7 @@ def batch_reproject(obj, project_type='VIEW', all_strokes=True, restore_frame=Fa
|
||||||
bpy.context.scene.frame_current = oframe
|
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:
|
if not ref:
|
||||||
ref = bpy.context.scene.camera
|
ref = bpy.context.scene.camera
|
||||||
|
@ -101,10 +101,10 @@ def align_global(reproject=True, ref=None):
|
||||||
o.matrix_basis = new_mat
|
o.matrix_basis = new_mat
|
||||||
|
|
||||||
if reproject:
|
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...')
|
print('aligning all frames...')
|
||||||
|
|
||||||
|
@ -128,9 +128,9 @@ def align_all_frames(reproject=True, ref=None):
|
||||||
rot_keys = list(set(rot_keys))
|
rot_keys = list(set(rot_keys))
|
||||||
|
|
||||||
# TODO # TOTHINK
|
# TODO # TOTHINK
|
||||||
# for now the rotation of the object is adjusted at every check....
|
# 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.
|
# might be better to check camera rotation of the current frame only (stored as copy).
|
||||||
# else the object rotate following the camera...
|
# else the object rotate following the cameraview vector (not constant)...
|
||||||
|
|
||||||
mat_90 = Matrix.Rotation(-pi/2, 4, 'X')
|
mat_90 = Matrix.Rotation(-pi/2, 4, 'X')
|
||||||
|
|
||||||
|
@ -191,7 +191,7 @@ def align_all_frames(reproject=True, ref=None):
|
||||||
|
|
||||||
|
|
||||||
if reproject:
|
if reproject:
|
||||||
batch_reproject(o, project_type='FRONT')
|
batch_reproject(o, proj_type='FRONT', all_strokes=all_strokes)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -199,21 +199,60 @@ def align_all_frames(reproject=True, ref=None):
|
||||||
class GPTB_OT_realign(bpy.types.Operator):
|
class GPTB_OT_realign(bpy.types.Operator):
|
||||||
bl_idname = "gp.realign"
|
bl_idname = "gp.realign"
|
||||||
bl_label = "Realign GP"
|
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"}
|
bl_options = {"REGISTER"}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return context.object and context.object.type == 'GPENCIL'
|
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):
|
def execute(self, context):
|
||||||
t0 = time()
|
t0 = time()
|
||||||
oframe = context.scene.frame_current
|
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 and o.animation_data.action:
|
||||||
if o.animation_data.action.fcurves.find('rotation_euler') or o.animation_data.action.fcurves.find('rotation_quaternion'):
|
if o.animation_data.action.fcurves.find('rotation_euler') or o.animation_data.action.fcurves.find('rotation_quaternion'):
|
||||||
align_all_frames(reproject=self.reproject)
|
align_all_frames(reproject=self.reproject)
|
||||||
|
@ -225,6 +264,7 @@ class GPTB_OT_realign(bpy.types.Operator):
|
||||||
align_global(reproject=self.reproject)
|
align_global(reproject=self.reproject)
|
||||||
|
|
||||||
context.scene.frame_current = oframe
|
context.scene.frame_current = oframe
|
||||||
|
|
||||||
print(f'\nGlobal Realign ({time()-t0:.2f}s)')
|
print(f'\nGlobal Realign ({time()-t0:.2f}s)')
|
||||||
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
@ -232,39 +272,57 @@ class GPTB_OT_realign(bpy.types.Operator):
|
||||||
|
|
||||||
class GPTB_OT_batch_reproject_all_frames(bpy.types.Operator):
|
class GPTB_OT_batch_reproject_all_frames(bpy.types.Operator):
|
||||||
bl_idname = "gp.batch_reproject_all_frames"
|
bl_idname = "gp.batch_reproject_all_frames"
|
||||||
bl_label = "Reproject All Frame"
|
bl_label = "Reproject All Frames"
|
||||||
bl_description = "Reproject all frame of active object."
|
bl_description = "Reproject all frames of active object."
|
||||||
bl_options = {"REGISTER"}
|
bl_options = {"REGISTER"}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return context.object and context.object.type == 'GPENCIL'
|
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')
|
||||||
|
|
||||||
# def invoke(self, context, event):
|
type: bpy.props.EnumProperty(name='Type',
|
||||||
# return context.window_manager.invoke_props_dialog(self)
|
items=(('FRONT', "Front", ""),
|
||||||
# return self.execute(context)
|
('SIDE', "Side", ""),
|
||||||
|
('TOP', "Top", ""),
|
||||||
|
('VIEW', "View", ""),
|
||||||
|
('SURFACE', "Surface", ""),
|
||||||
|
('CURSOR', "Cursor", ""),
|
||||||
|
),
|
||||||
|
default='FRONT')
|
||||||
|
|
||||||
|
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):
|
def draw(self, context):
|
||||||
self.layout.prop(self, "toggle", text="Toggle to redraw")
|
layout = self.layout
|
||||||
for i in range(20):
|
if not context.region_data.view_perspective == 'CAMERA':
|
||||||
self.layout.label(str(i))
|
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):
|
def execute(self, context):
|
||||||
t0 = time()
|
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"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
GPTB_OT_realign,
|
# GPTB_OT_realign,
|
||||||
GPTB_OT_batch_reproject_all_frames
|
GPTB_OT_batch_reproject_all_frames,
|
||||||
)
|
)
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
|
|
|
@ -112,6 +112,10 @@ Panel in sidebar : 3D view > sidebar 'N' > Gpencil
|
||||||
|
|
||||||
## Changelog:
|
## Changelog:
|
||||||
|
|
||||||
|
1.0.9:
|
||||||
|
|
||||||
|
- feat: Reproject all frames operator
|
||||||
|
|
||||||
1.0.8:
|
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
|
- feat: Keyframe jump filter added in UI to change general behavior. Keymap own jump filter can override this new global settings if specified
|
||||||
|
|
|
@ -167,6 +167,8 @@ class GPTB_PT_sidebar_panel(bpy.types.Panel):
|
||||||
|
|
||||||
layout.prop(context.scene.gptoolprops, 'edit_lines_opacity')
|
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 )
|
## 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')
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ bl_info = {
|
||||||
"name": "GP toolbox",
|
"name": "GP toolbox",
|
||||||
"description": "Set of tools for Grease Pencil in animation production",
|
"description": "Set of tools for Grease Pencil in animation production",
|
||||||
"author": "Samuel Bernou",
|
"author": "Samuel Bernou",
|
||||||
"version": (1, 0, 8),
|
"version": (1, 0, 9),
|
||||||
"blender": (2, 91, 0),
|
"blender": (2, 91, 0),
|
||||||
"location": "sidebar (N menu) > Gpencil > Toolbox / Gpencil properties",
|
"location": "sidebar (N menu) > Gpencil > Toolbox / Gpencil properties",
|
||||||
"warning": "",
|
"warning": "",
|
||||||
|
@ -44,6 +44,7 @@ from . import OP_palettes
|
||||||
from . import OP_file_checker
|
from . import OP_file_checker
|
||||||
from . import OP_render
|
from . import OP_render
|
||||||
from . import OP_copy_paste
|
from . import OP_copy_paste
|
||||||
|
from . import OP_realign
|
||||||
from . import keymaps
|
from . import keymaps
|
||||||
|
|
||||||
from .OP_pseudo_tint import GPT_OT_auto_tint_gp_layers
|
from .OP_pseudo_tint import GPT_OT_auto_tint_gp_layers
|
||||||
|
@ -439,6 +440,7 @@ def register():
|
||||||
OP_cursor_snap_canvas.register()
|
OP_cursor_snap_canvas.register()
|
||||||
OP_render.register()
|
OP_render.register()
|
||||||
OP_copy_paste.register()
|
OP_copy_paste.register()
|
||||||
|
OP_realign.register()
|
||||||
UI_tools.register()
|
UI_tools.register()
|
||||||
keymaps.register()
|
keymaps.register()
|
||||||
bpy.types.Scene.gptoolprops = bpy.props.PointerProperty(type = GP_PG_ToolsSettings)
|
bpy.types.Scene.gptoolprops = bpy.props.PointerProperty(type = GP_PG_ToolsSettings)
|
||||||
|
@ -460,6 +462,7 @@ def unregister():
|
||||||
for cls in reversed(classes):
|
for cls in reversed(classes):
|
||||||
bpy.utils.unregister_class(cls)
|
bpy.utils.unregister_class(cls)
|
||||||
UI_tools.unregister()
|
UI_tools.unregister()
|
||||||
|
OP_realign.unregister()
|
||||||
OP_copy_paste.unregister()
|
OP_copy_paste.unregister()
|
||||||
OP_render.unregister()
|
OP_render.unregister()
|
||||||
OP_cursor_snap_canvas.unregister()
|
OP_cursor_snap_canvas.unregister()
|
||||||
|
|
Loading…
Reference in New Issue