From 58e6816e392d4a840c947541da75d7ed1c05461b Mon Sep 17 00:00:00 2001 From: pullusb Date: Mon, 2 Dec 2024 14:51:43 +0100 Subject: [PATCH] Fix material move (fill opacity problem comes from blender 4.3 itself) --- OP_copy_paste.py | 6 +- OP_flat_reproject.py | 2 +- OP_material_move_to_layer.py | 32 +++--- __init__.py | 6 +- utils.py | 194 ++++++++++++++++------------------- 5 files changed, 111 insertions(+), 129 deletions(-) diff --git a/OP_copy_paste.py b/OP_copy_paste.py index aca7717..ce66a0f 100644 --- a/OP_copy_paste.py +++ b/OP_copy_paste.py @@ -373,11 +373,7 @@ def add_multiple_strokes(stroke_list, layer=None, use_current_frame=True, select for s in stroke_list: add_stroke(s, target_frame, layer, obj, select=select) - ''' - for s in stroke_data: - add_stroke(s, target_frame) - ''' - + # print(len(stroke_list), 'strokes pasted') diff --git a/OP_flat_reproject.py b/OP_flat_reproject.py index 426596d..8b86b79 100644 --- a/OP_flat_reproject.py +++ b/OP_flat_reproject.py @@ -7,7 +7,7 @@ import numpy as np from time import time from .utils import (location_to_region, region_to_location) - +## DISABLED (in init, also in menu append, see register below) """ ## Do not work on multiple object def batch_flat_reproject(obj, proj_type='VIEW', all_strokes=True, restore_frame=False): diff --git a/OP_material_move_to_layer.py b/OP_material_move_to_layer.py index a82e0e6..ba5e50c 100644 --- a/OP_material_move_to_layer.py +++ b/OP_material_move_to_layer.py @@ -93,6 +93,7 @@ class GPTB_OT_move_material_to_layer(Operator) : # t = time.time() # Dbg total = 0 oct = 0 + for ob in pool: mat_index = next((i for i, ms in enumerate(ob.material_slots) if ms.material and ms.material == mat), None) if mat_index is None: @@ -110,26 +111,26 @@ class GPTB_OT_move_material_to_layer(Operator) : ### Move Strokes to a new key (or existing key if comming for yet another layer) fct = 0 sct = 0 - for l in gpl: - if l == target_layer: + for layer in gpl: + if layer == target_layer: ## ! infinite loop if target layer is included continue - for f in l.frames: + for fr in layer.frames: ## skip if no stroke has active material - if not next((s for s in f.drawing.strokes if s.material_index == mat_index), None): + if not next((s for s in fr.drawing.strokes if s.material_index == mat_index), None): continue ## Get/Create a destination frame and keep a reference to it - if not (dest_key := key_dict.get(f.frame_number)): - dest_key = target_layer.frames.new(f.frame_number) + if not (dest_key := key_dict.get(fr.frame_number)): + dest_key = target_layer.frames.new(fr.frame_number) key_dict[dest_key.frame_number] = dest_key - print(f'{ob.name} : frame {f.frame_number}') + print(f'{ob.name} : frame {fr.frame_number}') ## Replicate strokes in dest_keys stroke_to_delete = [] - for s in f.drawing.strokes: + for s_idx, s in enumerate(fr.drawing.strokes): if s.material_index == mat_index: utils.copy_stroke_to_frame(s, dest_key) - stroke_to_delete.append(s) + stroke_to_delete.append(s_idx) ## Debug # if time.time() - t > 10: @@ -138,17 +139,16 @@ class GPTB_OT_move_material_to_layer(Operator) : sct += len(stroke_to_delete) - # print('Removing frames') # Dbg - ## Remove from source frame (f) + ## Remove from source frame (fr) if not self.copy: - for s in reversed(stroke_to_delete): - f.drawing.strokes.remove(s) + # print('Removing frames') # Dbg + if stroke_to_delete: + fr.drawing.remove_strokes(indices=stroke_to_delete) - ## ? Remove frame if layer is empty ? -> probably not, will show previous frame + ## ? Remove frame if layer is empty ? -> probably not, otherwise will show previous frame fct += 1 - l.frames.update() - + if fct: oct += 1 diff --git a/__init__.py b/__init__.py index 7030933..3954083 100755 --- a/__init__.py +++ b/__init__.py @@ -4,7 +4,7 @@ bl_info = { "name": "GP toolbox", "description": "Tool set for Grease Pencil in animation production", "author": "Samuel Bernou, Christophe Seux", -"version": (4, 0, 1), +"version": (4, 0, 2), "blender": (4, 3, 0), "location": "Sidebar (N menu) > Gpencil > Toolbox / Gpencil properties", "warning": "", @@ -35,7 +35,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_flat_reproject # Disabled from . import OP_depth_move from . import OP_key_duplicate_send from . import OP_layer_manager @@ -793,7 +793,7 @@ addon_modules = ( OP_brushes, OP_cursor_snap_canvas, OP_copy_paste, - OP_flat_reproject, + # OP_flat_reproject # Disabled, OP_realign, OP_depth_move, OP_key_duplicate_send, diff --git a/utils.py b/utils.py index 6fae8c8..cdab279 100644 --- a/utils.py +++ b/utils.py @@ -10,14 +10,80 @@ from math import sqrt from mathutils import Vector from sys import platform +## constants values -""" def get_gp_parent(layer) : - if layer.parent_type == "BONE" and layer.parent_bone : - return layer.parent.pose.bones.get(layer.parent_bone) - else : - return layer.parent - """ +## Default stroke and points attributes +stroke_attr = [ +'start_cap', +'end_cap', +'softness', +'material_index', +'fill_opacity', +'fill_color', +'cyclic', +'aspect_ratio', +'time_start', +# 'curve_type', # read-only +] + +point_attr = [ +'position', +'radius', +'rotation', +'opacity', +'vertex_color', +'delta_time', +# 'select', +] + +### Attribute value, types and shape + +attribute_value_string = { + 'FLOAT': "value", + 'INT': "value", + 'FLOAT_VECTOR': "vector", + 'FLOAT_COLOR': "color", + 'BYTE_COLOR': "color", + 'STRING': "value", + 'BOOLEAN': "value", + 'FLOAT2': "value", + 'INT8': "value", + 'INT32_2D': "value", + 'QUATERNION': "value", + 'FLOAT4X4': "value", +} + +attribute_value_dtype = { + 'FLOAT': np.float32, + 'INT': np.dtype('int'), + 'FLOAT_VECTOR': np.float32, + 'FLOAT_COLOR': np.float32, + 'BYTE_COLOR': np.int8, + 'STRING': np.dtype('str'), + 'BOOLEAN': np.dtype('bool'), + 'FLOAT2': np.float32, + 'INT8': np.int8, + 'INT32_2D': np.dtype('int'), + 'QUATERNION': np.float32, + 'FLOAT4X4': np.float32, +} + +attribute_value_shape = { + 'FLOAT': (), + 'INT': (), + 'FLOAT_VECTOR': (3,), + 'FLOAT_COLOR': (4,), + 'BYTE_COLOR': (4,), + 'STRING': (), + 'BOOLEAN': (), + 'FLOAT2':(2,), + 'INT8': (), + 'INT32_2D': (2,), + 'QUATERNION': (4,), + 'FLOAT4X4': (4,4), +} + def translate_range(OldValue, OldMin, OldMax, NewMax, NewMin): return (((OldValue - OldMin) * (NewMax - NewMin)) / (OldMax - OldMin)) + NewMin @@ -557,64 +623,30 @@ def copy_stroke_to_frame(s, frame, select=True): return created stroke ''' - ns = frame.drawing.strokes.new() - - ## Set strokes attr - stroke_attr = [ - 'line_width', - 'material_index', - 'draw_cyclic', - 'use_cyclic', - 'uv_scale', - 'uv_rotation', - 'hardness', - 'uv_translation', - 'vertex_color_fill', - ] + frame.drawing.add_strokes([len(s.points)]) + ns = frame.drawing.strokes[-1] + # print(len(s.points), 'new:', len(ns.points)) + #ns.material_index + ## replicate attributes (simple loop) + ## TODO : might need to create atribute domain if does not exists in destination for attr in stroke_attr: - if not hasattr(s, attr): - continue - # print(f'transfer stroke {attr}') # Dbg setattr(ns, attr, getattr(s, attr)) - ## create points - point_count = len(s.points) - ns.points.add(len(s.points)) - - ## Set points attr - # for p, np in zip(s.points, ns.points): - flat_list = [0.0] * point_count - flat_uv_fill_list = [0.0, 0.0] * point_count - flat_vector_list = [0.0, 0.0, 0.0] * point_count - flat_color_list = [0.0, 0.0, 0.0, 0.0] * point_count - - single_attr = [ - 'pressure', - 'strength', - 'uv_factor', - 'uv_rotation', - ] + for src_p, dest_p in zip(s.points, ns.points): + for attr in point_attr: + setattr(dest_p, attr, getattr(src_p, attr)) + ## Define selection + dest_p.select=select - for attr in single_attr: - # print(f'transfer point {attr}') # Dbg - s.points.foreach_get(attr, flat_list) - ns.points.foreach_set(attr, flat_list) + ## Direcly iterate over attribute ? + # src_start = src_dr.curve_offsets[0].value + # src_end = src_start + data_size + # dst_start = dst_dr.curve_offsets[0].value + # dst_end = dst_start + data_size + # for src_idx, dest_idx in zip(range(src_start, src_end),range(dst_start, dst_end)): + # setattr(dest_attr.data[dest_idx], val_type, getattr(source_attr.data[src_idx], val_type)) - # print(f'transfer point co') # Dbg - s.points.foreach_get('co', flat_vector_list) - ns.points.foreach_set('co', flat_vector_list) - - # print(f'transfer point uv_fill') # Dbg - s.points.foreach_get('uv_fill', flat_uv_fill_list) - ns.points.foreach_set('uv_fill', flat_uv_fill_list) - - # print(f'transfer point vertex_color') # Dbg - s.points.foreach_get('vertex_color', flat_color_list) - ns.points.foreach_set('vertex_color', flat_color_list) - - ns.select = select - ns.points.update() return ns """## Works, but do not copy all attributes type (probably ok for GP though) @@ -657,52 +689,6 @@ def bulk_frame_copy_attributes(source_attr, target_attr): # setattr(dest_attr.data[dest_idx], val_type, getattr(source_attr.data[src_idx], val_type)) """ -attribute_value_string = { - 'FLOAT': "value", - 'INT': "value", - 'FLOAT_VECTOR': "vector", - 'FLOAT_COLOR': "color", - 'BYTE_COLOR': "color", - 'STRING': "value", - 'BOOLEAN': "value", - 'FLOAT2': "value", - 'INT8': "value", - 'INT32_2D': "value", - 'QUATERNION': "value", - 'FLOAT4X4': "value", -} - -attribute_value_dtype = { - 'FLOAT': np.float32, - 'INT': np.dtype('int'), - 'FLOAT_VECTOR': np.float32, - 'FLOAT_COLOR': np.float32, - 'BYTE_COLOR': np.int8, - 'STRING': np.dtype('str'), - 'BOOLEAN': np.dtype('bool'), - 'FLOAT2': np.float32, - 'INT8': np.int8, - 'INT32_2D': np.dtype('int'), - 'QUATERNION': np.float32, - 'FLOAT4X4': np.float32, -} - -attribute_value_shape = { - 'FLOAT': (), - 'INT': (), - 'FLOAT_VECTOR': (3,), - 'FLOAT_COLOR': (4,), - 'BYTE_COLOR': (4,), - 'STRING': (), - 'BOOLEAN': (), - 'FLOAT2':(2,), - 'INT8': (), - 'INT32_2D': (2,), - 'QUATERNION': (4,), - 'FLOAT4X4': (4,4), -} - - def bulk_copy_attributes(source_attr, target_attr): '''Get and apply data as flat numpy array based on attribute type''' value_string = attribute_value_string[source_attr.data_type]