gpv3 world copy cut paste

master
pullusb 2024-11-13 18:58:28 +01:00
parent 5d930df06b
commit 4937aa32c0
2 changed files with 112 additions and 113 deletions

View File

@ -45,105 +45,112 @@ def convertAttr(Attr):
else:
return(Attr)
def getMatrix (layer) :
def getMatrix(layer) :
matrix = mathutils.Matrix.Identity(4)
## FIXME: not "is_parent" or parent_type anymore
if layer.is_parented:
if layer.parent_type == 'BONE':
object = layer.parent
bone = object.pose.bones[layer.parent_bone]
matrix = bone.matrix @ object.matrix_world
matrix = matrix.copy() @ layer.matrix_inverse
else :
matrix = layer.parent.matrix_world @ layer.matrix_inverse
if layer.parent:
## temp solution
matrix = layer.parent.matrix_world @ layer.matrix_inverse
### if layer.parent_type == 'BONE':
# if layer.parent.type == 'ARMATURE':
# object = layer.parent
# bone = object.pose.bones[layer.parent_bone]
# matrix = bone.matrix @ object.matrix_world
# matrix = matrix.copy() @ layer.matrix_inverse
# else :
# matrix = layer.parent.matrix_world @ layer.matrix_inverse
return matrix.copy()
default_pt_uv_fill = Vector((0.5, 0.5))
# default_pt_uv_fill = Vector((0.5, 0.5))
def dump_gp_point(p, l, obj,
pressure=True, strength=True, vertex_color=True, uv_fill=True, uv_factor=True, uv_rotation=True):
radius=True, opacity=True, vertex_color=True, fill_color=True, uv_factor=True, rotation=True):
'''add properties of a given points to a dic and return it'''
pdic = {}
#point_attr_list = ('co', 'pressure', 'select', 'strength') #select#'rna_type'
point_dict = {}
#point_attr_list = ('co', 'radius', 'select', 'opacity') #select#'rna_type'
#for att in point_attr_list:
# pdic[att] = convertAttr(getattr(p, att))
# point_dict[att] = convertAttr(getattr(p, att))
if l.parent:
mat = getMatrix(l)
pdic['co'] = convertAttr(obj.matrix_world @ mat @ getattr(p,'co'))
point_dict['position'] = convertAttr(obj.matrix_world @ mat @ getattr(p,'position'))
else:
pdic['co'] = convertAttr(obj.matrix_world @ getattr(p,'co'))
point_dict['position'] = convertAttr(obj.matrix_world @ getattr(p,'position'))
# pdic['select'] = convertAttr(getattr(p,'select')) # need selection ?
if pressure and p.pressure != 1.0:
pdic['pressure'] = convertAttr(getattr(p,'pressure'))
if strength and p.strength != 1.0:
pdic['strength'] = convertAttr(getattr(p,'strength'))
# point_dict['select'] = convertAttr(getattr(p,'select')) # need selection ?
if radius and p.radius != 1.0:
point_dict['radius'] = convertAttr(getattr(p,'radius'))
if opacity and p.opacity != 1.0:
point_dict['opacity'] = convertAttr(getattr(p,'opacity'))
## get vertex color (long...)
if vertex_color and p.vertex_color[:] != (0.0, 0.0, 0.0, 0.0):
pdic['vertex_color'] = convertAttr(p.vertex_color)
point_dict['vertex_color'] = convertAttr(p.vertex_color)
## UV attr (maybe uv fill is always (0.5,0.5) ? also exists at stroke level...)
if uv_fill and p.uv_fill != default_pt_uv_fill:
pdic['uv_fill'] = convertAttr(p.uv_fill)
if uv_factor and p.uv_factor != 0.0:
pdic['uv_factor'] = convertAttr(p.uv_factor)
if uv_rotation and p.uv_rotation != 0.0:
pdic['uv_rotation'] = convertAttr(p.uv_rotation)
if rotation and p.rotation != 0.0:
point_dict['rotation'] = convertAttr(p.rotation)
return pdic
## No time infos
# if delta_time and p.delta_time != 0.0:
# point_dict['delta_time'] = convertAttr(getattr(p,'delta_time'))
return point_dict
def dump_gp_stroke_range(s, sid, l, obj,
pressure=True, strength=True, vertex_color=True, uv_fill=True, uv_factor=True, uv_rotation=True):
radius=True, opacity=True, vertex_color=True, fill_color=True, fill_opacity=True, rotation=True):
'''Get a grease pencil stroke and return a dic with attribute
(points attribute being a dic of dics to store points and their attributes)
'''
sdic = {}
stroke_attr_list = ('line_width',) #'select'#read-only: 'triangles'
for att in stroke_attr_list:
sdic[att] = getattr(s, att)
stroke_dict = {}
# stroke_attr_list = ('line_width',)
# for att in stroke_attr_list:
# stroke_dict[att] = getattr(s, att)
## Dump following these value only if they are non default
if s.material_index != 0:
sdic['material_index'] = s.material_index
stroke_dict['material_index'] = s.material_index
if getattr(s, 'draw_cyclic', None): # pre-2.92
sdic['draw_cyclic'] = s.draw_cyclic
if s.cyclic:
stroke_dict['cyclic'] = s.cyclic
if getattr(s, 'use_cyclic', None): # from 2.92
sdic['use_cyclic'] = s.use_cyclic
if s.softness != 0.0:
stroke_dict['softness'] = s.softness
if s.uv_scale != 1.0:
sdic['uv_scale'] = s.uv_scale
if s.aspect_ratio != 1.0:
stroke_dict['aspect_ratio'] = s.aspect_ratio
if s.uv_rotation != 0.0:
sdic['uv_rotation'] = s.uv_rotation
if s.start_cap != 0:
stroke_dict['start_cap'] = s.start_cap
if s.hardness != 1.0:
sdic['hardness'] = s.hardness
if s.end_cap != 0:
stroke_dict['end_cap'] = s.end_cap
if s.uv_translation != Vector((0.0, 0.0)):
sdic['uv_translation'] = convertAttr(s.uv_translation)
if fill_color and s.fill_color[:] != (0,0,0,0):
stroke_dict['fill_color'] = convertAttr(s.fill_color)
if s.vertex_color_fill[:] != (0,0,0,0):
sdic['vertex_color_fill'] = convertAttr(s.vertex_color_fill)
if fill_opacity and s.fill_opacity != 0.0:
stroke_dict['fill_opacity'] = s.fill_opacity
## No time infos
# if s.time_start != 0.0:
# stroke_dict['time_start'] = s.time_start
points = []
if sid is None: # no ids, just full points...
for p in s.points:
points.append(dump_gp_point(p,l,obj,
pressure=pressure, strength=strength, vertex_color=vertex_color, uv_fill=uv_fill, uv_factor=uv_factor, uv_rotation=uv_rotation))
points.append(dump_gp_point(p, l, obj,
radius=radius, opacity=opacity, vertex_color=vertex_color, rotation=rotation))
else:
for pid in sid:
points.append(dump_gp_point(s.points[pid],l,obj,
pressure=pressure, strength=strength, vertex_color=vertex_color, uv_fill=uv_fill, uv_factor=uv_factor, uv_rotation=uv_rotation))
sdic['points'] = points
return sdic
points.append(dump_gp_point(s.points[pid], l, obj,
radius=radius, opacity=opacity, vertex_color=vertex_color, rotation=rotation))
stroke_dict['points'] = points
return stroke_dict
def copycut_strokes(layers=None, copy=True, keep_empty=True):
@ -177,7 +184,7 @@ def copycut_strokes(layers=None, copy=True, keep_empty=True):
staylist = [] # init part of strokes that must survive on this layer
rm_list = [] # init strokes that must be removed from this layer
for s_index, stroke in f.drawing.strokes:
for s_index, stroke in enumerate(f.drawing.strokes):
if stroke.select:
# separate in multiple stroke if parts of the strokes a selected.
sel = [i for i, p in enumerate(stroke.points) if p.select]
@ -196,8 +203,8 @@ def copycut_strokes(layers=None, copy=True, keep_empty=True):
if not copy:
maxindex = len(stroke.points)-1
if len(substrokes) == maxindex+1: # if only one substroke, then it's the full stroke
# f.drawing.strokes.remove(stroke)
rm_list.append(stroke)
# f.drawing.strokes.remove(stroke) # gpv2
rm_list.append(s_index)
else:
neg = [i for i, p in enumerate(stroke.points) if not p.select]
@ -216,21 +223,15 @@ def copycut_strokes(layers=None, copy=True, keep_empty=True):
staylist.append(dump_gp_stroke_range(stroke, ns, l, obj))
# make a negative list containing all last index
'''#full stroke version
# if stroke.colorname == color: #line for future filters
stroke_list.append(dump_gp_stroke(stroke,l))
#delete stroke on the fly
if not copy:
f.drawing.strokes.remove(stroke)
'''
if rm_list:
f.drawing.remove_strokes(indices=rm_list)
if not copy:
selected_ids = [i for i, s in enumerate(f.drawing.strokes) if s.select]
# delete all selected strokes...
f.drawing.strokes.remove_strokes(indices=selected_ids)
if selected_ids:
f.drawing.remove_strokes(indices=selected_ids)
# ...recreate these uncutted ones
#pprint(staylist)
@ -243,7 +244,6 @@ def copycut_strokes(layers=None, copy=True, keep_empty=True):
l.frames.remove(f)
print(len(stroke_list), 'strokes copied in', time()-t0, 'seconds')
#print(stroke_list)
return stroke_list
@ -289,7 +289,7 @@ def copy_all_strokes(layers=None):
"""
def copy_all_strokes_in_frame(frame=None, layers=None, obj=None,
pressure=True, strength=True, vertex_color=True, uv_fill=True, uv_factor=True, uv_rotation=True):
radius=True, opacity=True, vertex_color=True, fill_color=True, fill_opacity=True, rotation=True):
'''
copy all stroke, not affected by selection on active frame
layers can be None, a single layer object or list of layer object as filter
@ -323,7 +323,7 @@ def copy_all_strokes_in_frame(frame=None, layers=None, obj=None,
# if s.select:
# send index of all points to get the whole stroke with "range"
stroke_list.append( dump_gp_stroke_range(s, [i for i in range(len(s.points))], l, obj,
pressure=pressure,strength=strength,vertex_color=vertex_color,uv_fill=uv_fill,uv_factor=uv_factor,uv_rotation=uv_rotation))
radius=radius, opacity=opacity, vertex_color=vertex_color, fill_color=fill_color, fill_opacity=fill_opacity, rotation=rotation))
# print(len(stroke_list), 'strokes copied in', time()-t0, 'seconds')
return stroke_list
@ -331,26 +331,27 @@ def copy_all_strokes_in_frame(frame=None, layers=None, obj=None,
def add_stroke(s, frame, layer, obj, select=False):
'''add stroke on a given frame, (layer is for parentage setting)'''
# print(3*'-',s)
ns = frame.drawing.strokes.new()
pts_to_add = len(s['points'])
frame.drawing.add_strokes([pts_to_add])
ns = frame.drawing.strokes[-1]
for att, val in s.items():
if att not in ('points'):
setattr(ns, att, val)
pts_to_add = len(s['points'])
# print(pts_to_add, 'points')#dbg
ns.points.add(pts_to_add)
# ns.points.add(pts_to_add)
ob_mat_inv = obj.matrix_world.inverted()
## patch pressure 1
# pressure_flat_list = [di['pressure'] for di in s['points']] #get all pressure flatened
## patch radius 1
# radius_flat_list = [di['radius'] for di in s['points']] #get all radius flatened
if layer.is_parented:
if layer.parent:
mat = getMatrix(layer).inverted()
for i, pt in enumerate(s['points']):
for k, v in pt.items():
if k == 'co':
if k == 'position':
setattr(ns.points[i], k, v)
ns.points[i].position = ob_mat_inv @ mat @ ns.points[i].position # invert of object * invert of layer * coordinate
else:
@ -361,7 +362,7 @@ def add_stroke(s, frame, layer, obj, select=False):
else:
for i, pt in enumerate(s['points']):
for k, v in pt.items():
if k == 'co':
if k == 'position':
setattr(ns.points[i], k, v)
ns.points[i].position = ob_mat_inv @ ns.points[i].position# invert of object * coordinate
else:
@ -369,11 +370,6 @@ def add_stroke(s, frame, layer, obj, select=False):
if select:
ns.points[i].select = True
## trigger updapte (in 2.93 fix some drawing problem with fills and UVs)
ns.points.update()
## patch pressure 2
# ns.points.foreach_set('pressure', pressure_flat_list)
def add_multiple_strokes(stroke_list, layer=None, use_current_frame=True, select=False):
'''
@ -436,7 +432,7 @@ class GPCLIP_OT_copy_strokes(bpy.types.Operator):
# return {"CANCELLED"}
t0 = time()
#ct = check_pressure()
#ct = check_radius()
strokelist = copycut_strokes(copy=True, keep_empty=True)
if not strokelist:
self.report({'ERROR'}, 'Nothing to copy')
@ -465,7 +461,7 @@ class GPCLIP_OT_cut_strokes(bpy.types.Operator):
# return {"CANCELLED"}
t0 = time()
strokelist = copycut_strokes(copy=False, keep_empty=True) # ct = check_pressure()
strokelist = copycut_strokes(copy=False, keep_empty=True) # ct = check_radius()
if not strokelist:
self.report({'ERROR'},'Nothing to cut')
return {"CANCELLED"}
@ -517,18 +513,20 @@ class GPCLIP_OT_copy_multi_strokes(bpy.types.Operator):
def poll(cls, context):
return context.object and context.object.type == 'GREASEPENCIL'
pressure : bpy.props.BoolProperty(name='pressure', default=True,
description='Dump point pressure attribute (already skipped if at default value)')
strength : bpy.props.BoolProperty(name='strength', default=True,
description='Dump point strength attribute (already skipped if at default value)')
radius : bpy.props.BoolProperty(name='radius', default=True,
description='Dump point radius attribute (already skipped if at default value)')
opacity : bpy.props.BoolProperty(name='opacity', default=True,
description='Dump point opacity attribute (already skipped if at default value)')
vertex_color : bpy.props.BoolProperty(name='vertex color', default=True,
description='Dump point vertex_color attribute (already skipped if at default value)')
uv_fill : bpy.props.BoolProperty(name='uv fill', default=True,
description='Dump point uv_fill attribute (already skipped if at default value)')
fill_color : bpy.props.BoolProperty(name='fill color', default=True,
description='Dump point fill_color attribute (already skipped if at default value)')
fill_opacity : bpy.props.BoolProperty(name='fill opacity', default=True,
description='Dump point fill_opacity attribute (already skipped if at default value)')
uv_factor : bpy.props.BoolProperty(name='uv factor', default=True,
description='Dump point uv_factor attribute (already skipped if at default value)')
uv_rotation : bpy.props.BoolProperty(name='uv rotation', default=True,
description='Dump point uv_rotation attribute (already skipped if at default value)')
rotation : bpy.props.BoolProperty(name='rotation', default=True,
description='Dump point rotation attribute (already skipped if at default value)')
def invoke(self, context, event):
# self.file_dump = event.ctrl
@ -540,12 +538,12 @@ class GPCLIP_OT_copy_multi_strokes(bpy.types.Operator):
layout.use_property_split = True
col = layout.column()
col.label(text='Keep following point attributes:')
col.prop(self, 'pressure')
col.prop(self, 'strength')
col.prop(self, 'radius')
col.prop(self, 'opacity')
col.prop(self, 'vertex_color')
col.prop(self, 'uv_fill')
col.prop(self, 'uv_factor')
col.prop(self, 'uv_rotation')
col.prop(self, 'fill_color')
col.prop(self, 'fill_opacity')
col.prop(self, 'rotation')
return
def execute(self, context):
@ -556,7 +554,7 @@ class GPCLIP_OT_copy_multi_strokes(bpy.types.Operator):
obj = context.object
gpl = obj.data.layers
t0 = time()
#ct = check_pressure()
#ct = check_radius()
layerdic = {}
layerpool = [l for l in gpl if not l.hide and l.select] # and not l.lock
@ -575,8 +573,8 @@ class GPCLIP_OT_copy_multi_strokes(bpy.types.Operator):
continue
context.scene.frame_set(f.frame_number) # use matrix of this frame
strokelist = copy_all_strokes_in_frame(frame=f, layers=l, obj=obj,
pressure=self.pressure, strength=self.strength, vertex_color=self.vertex_color,
uv_fill=self.uv_fill, uv_factor=self.uv_factor, uv_rotation=self.uv_rotation)
radius=self.radius, opacity=self.opacity, vertex_color=self.vertex_color,
fill_color=self.fill_color, uv_factor=self.uv_factor, rotation=self.rotation)
frame_dic[f.frame_number] = strokelist
@ -612,8 +610,8 @@ class GPCLIP_OT_copy_multi_strokes(bpy.types.Operator):
continue
strokelist = copy_all_strokes_in_frame(frame=f, layers=l, obj=obj,
pressure=self.pressure, strength=self.strength, vertex_color=self.vertex_color,
uv_fill=self.uv_fill, uv_factor=self.uv_factor, uv_rotation=self.uv_rotation)
radius=self.radius, opacity=self.opacity, vertex_color=self.vertex_color,
fill_color=self.fill_color, uv_factor=self.uv_factor, rotation=self.rotation)
frame_dic[i] = strokelist

View File

@ -36,6 +36,7 @@ class GPTB_OT_jump_gp_keyframe(bpy.types.Operator):
('MOVING_HOLD', 'Moving Hold', '', 'KEYTYPE_MOVING_HOLD_VEC', 4),
('EXTREME', 'Extreme', '', 'KEYTYPE_EXTREME_VEC', 5),
('JITTER', 'Jitter', '', 'KEYTYPE_JITTER_VEC', 6),
('GENERATED', 'Generated', '', 'KEYTYPE_GENERATED_VEC', 7),
))
def execute(self, context):