2022-01-18 22:53:08 +01:00
|
|
|
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):
|
2024-11-11 15:35:39 +01:00
|
|
|
return context.object and context.object.type == 'GREASEPENCIL'
|
2022-01-18 22:53:08 +01:00
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
for o in context.selected_objects:
|
2024-11-11 15:35:39 +01:00
|
|
|
if o.type != 'GREASEPENCIL' or not o.select_get():
|
2022-01-18 22:53:08 +01:00
|
|
|
continue
|
|
|
|
batch_flat_reproject(o)
|
|
|
|
|
|
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
### -- MENU ENTRY --
|
|
|
|
|
|
|
|
def flat_reproject_clean_menu(self, context):
|
2024-11-11 15:47:33 +01:00
|
|
|
if context.mode == 'EDIT_GREASE_PENCIL':
|
2022-01-18 22:53:08 +01:00
|
|
|
self.layout.operator_context = 'INVOKE_REGION_WIN' # needed for popup (also works with 'INVOKE_DEFAULT')
|
2022-02-22 11:29:31 +01:00
|
|
|
self.layout.operator('gp.batch_flat_reproject', icon='KEYTYPE_JITTER_VEC')
|
2022-01-18 22:53:08 +01:00
|
|
|
|
|
|
|
def flat_reproject_context_menu(self, context):
|
2024-11-11 15:47:33 +01:00
|
|
|
if context.mode == 'EDIT_GREASE_PENCIL' and context.scene.tool_settings.gpencil_selectmode_edit == 'STROKE':
|
2022-01-18 22:53:08 +01:00
|
|
|
self.layout.operator_context = 'INVOKE_REGION_WIN' # needed for popup
|
2022-02-22 11:29:31 +01:00
|
|
|
self.layout.operator('gp.batch_flat_reproject', icon='KEYTYPE_JITTER_VEC')
|
2022-01-18 22:53:08 +01:00
|
|
|
|
|
|
|
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)
|