diff --git a/docu/planned_features.md b/docu/planned_features.md index e138f59..84ecc16 100644 --- a/docu/planned_features.md +++ b/docu/planned_features.md @@ -1,6 +1,6 @@ ## Planned features ### ID Mask - Editor -- [ ] A way to automatically get already used colors, f.E. you already did it by hand +- [X] A way to automatically get already used colors, f.E. you already did it by hand - [X] Select by color - [ ] Update after color change \ No newline at end of file diff --git a/src/__init__.py b/src/__init__.py index a7c40e4..d5cbd19 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -3,10 +3,12 @@ import bpy from src.menu import id_mask_select_menu from src.menu.id_mask_editor_options import IDEDITOR_IDMaskEditorOptionsMenu from src.operators.create_id_mask import CreateIDMaskOperator +from src.operators.id_editor_apply_color import IDEDITOR_ColorApplyOperator from src.operators.id_editor_create_id import IDEDITOR_CreateIDOperator from src.operators.id_editor_find_used_ids import IDEDITOR_FindUsedIDsOperator from src.operators.id_editor_paint import IDEDITOR_PaintIDMaskOperator from src.operators.id_editor_remove_id import IDEDITOR_RemoveIDOperator +from src.operators.id_editor_revert_color import IDEDITOR_ColorResetOperator from src.operators.id_mask_select import IDEDITOR_SelectIDMaskOperator from src.panels.id_mask_editor_id_list import IDMaskEditorIDList from src.properties.id_mask_editor_value_properties import IDMaskEditorValueProperties @@ -45,7 +47,9 @@ classes = ( IDEDITOR_PaintIDMaskOperator, IDEDITOR_SelectIDMaskOperator, IDEDITOR_FindUsedIDsOperator, - IDEDITOR_IDMaskEditorOptionsMenu + IDEDITOR_IDMaskEditorOptionsMenu, + IDEDITOR_ColorResetOperator, + IDEDITOR_ColorApplyOperator, ) menu_additions = [ diff --git a/src/operators/id_editor_apply_color.py b/src/operators/id_editor_apply_color.py new file mode 100644 index 0000000..94fe105 --- /dev/null +++ b/src/operators/id_editor_apply_color.py @@ -0,0 +1,84 @@ +import bpy + +class IDEDITOR_ColorApplyOperator(bpy.types.Operator): + bl_idname = "id_mask_editor.apply_colors" + bl_label = "Apply changed ID-Mask colors" + bl_description = "Searches the current ID-mask for colors and adds them to the id-list" + bl_options = {'INTERNAL'} + + triggeredByList: bpy.props.BoolProperty(default=False) + listId: bpy.props.IntProperty(default=0) + + @classmethod + def poll(cls, context): + obj = context.active_object + if obj.type != 'MESH': + return False + + if not obj.data: + return False + + mesh = obj.data + properties = mesh.id_mask_editor_properties + + if not properties.target_attribute: + return False + + return True + + def execute(self, context): + obj = context.active_object + + old_mode = bpy.context.object.mode + if old_mode != "OBJECT": + bpy.ops.object.mode_set(mode='OBJECT', toggle=False) + mesh = obj.data + properties = mesh.id_mask_editor_properties + color_attribute = mesh.color_attributes.get(properties.target_attribute) + + colors = [] + + if self.triggeredByList: + list_item = properties.possible_ids[self.listId] + if list_item.color_changed: + colors.append((list_item.original_color, list_item.color)) + else: + for id in properties.possible_ids: + if not id.color_changed: + continue + + colors.append((id.original_color, id.color)) + + for polygon in mesh.polygons: + polygon_color = self.get_color_from_polygon(color_attribute, polygon) + + for (original_color, color) in colors: + if original_color[0] != polygon_color[0] or original_color[1] != polygon_color[1] or original_color[2] != polygon_color[2]: + continue + + for idx in polygon.loop_indices: + color_attribute.data[idx].color = (color.r, color.g, color.b, 1.0) + + break + + if self.triggeredByList: + list_item = properties.possible_ids[self.listId] + list_item.color_changed = False + else: + for id in properties.possible_ids: + id.color_changed = False + + if old_mode != "OBJECT": + bpy.ops.object.mode_set(mode=old_mode, toggle=False) + return {'FINISHED'} + + def reset_color(self, value): + if not value.color_changed: + return + + value.color = value.original_color + value.color_changed = False + + def get_color_from_polygon(self, attribute, polygon): + color = attribute.data[polygon.loop_indices[0]].color + return (color[0], color[1], color[2], 1.0) \ No newline at end of file diff --git a/src/operators/id_editor_revert_color.py b/src/operators/id_editor_revert_color.py new file mode 100644 index 0000000..5f2881b --- /dev/null +++ b/src/operators/id_editor_revert_color.py @@ -0,0 +1,49 @@ +import bpy + +class IDEDITOR_ColorResetOperator(bpy.types.Operator): + bl_idname = "id_mask_editor.reset_colors" + bl_label = "Resets ID-Mask colors" + bl_description = "Resets the colors to the previous values" + bl_options = {'INTERNAL'} + + triggeredByList: bpy.props.BoolProperty(default=False) + listId: bpy.props.IntProperty(default=0) + + @classmethod + def poll(cls, context): + obj = context.active_object + if obj.type != 'MESH': + return False + + if not obj.data: + return False + + mesh = obj.data + properties = mesh.id_mask_editor_properties + + if not properties.target_attribute: + return False + + return True + + def execute(self, context): + obj = context.active_object + + mesh = obj.data + properties = mesh.id_mask_editor_properties + + if self.triggeredByList: + self.reset_color(properties.possible_ids[self.listId]) + return {'FINISHED'} + + for prop in properties.possible_ids: + self.reset_color(prop) + + return {'FINISHED'} + + def reset_color(self, value): + if not value.color_changed: + return + + value.color = value.original_color + value.color_changed = False diff --git a/src/panels/id_mask_editor.py b/src/panels/id_mask_editor.py index 19afcd8..6d755ac 100644 --- a/src/panels/id_mask_editor.py +++ b/src/panels/id_mask_editor.py @@ -2,9 +2,11 @@ import bpy from src.menu.id_mask_editor_options import IDEDITOR_IDMaskEditorOptionsMenu from src.operators.create_id_mask import CreateIDMaskOperator +from src.operators.id_editor_apply_color import IDEDITOR_ColorApplyOperator from src.operators.id_editor_create_id import IDEDITOR_CreateIDOperator from src.operators.id_editor_paint import IDEDITOR_PaintIDMaskOperator from src.operators.id_editor_remove_id import IDEDITOR_RemoveIDOperator +from src.operators.id_editor_revert_color import IDEDITOR_ColorResetOperator from src.operators.id_mask_select import IDEDITOR_SelectIDMaskOperator from src.types.colors import get_color @@ -61,7 +63,6 @@ class IDMaskEditorPanel(bpy.types.Panel): button_row.operator(IDEDITOR_PaintIDMaskOperator.bl_idname, text='Paint', icon='VPAINT_HLT') button_row.operator(IDEDITOR_SelectIDMaskOperator.bl_idname, text='Select', icon='SELECT_SET').isCalledFromEditor = True - col.template_list( 'IDMaskEditorIDList', 'IDMaskEditorIDList', @@ -72,6 +73,23 @@ class IDMaskEditorPanel(bpy.types.Panel): rows=3 ) + color_button_row = col.row(align=True) + has_color_changed = False + for id in properties.possible_ids: + if not id.color_changed: + continue + has_color_changed = True + break + + color_button_row.enabled = has_color_changed + + reset_op = color_button_row.operator(IDEDITOR_ColorResetOperator.bl_idname, icon='LOOP_BACK', text='Reset Colors') + reset_op.triggeredByList = False + + apply_op = color_button_row.operator(IDEDITOR_ColorApplyOperator.bl_idname, icon='CHECKMARK', text='Apply Colors') + apply_op.triggeredByList = False + + col = row.column(align=True) col.operator(IDEDITOR_CreateIDOperator.bl_idname, icon='ADD', text="") col.operator(IDEDITOR_RemoveIDOperator.bl_idname, icon='REMOVE', text="") @@ -80,7 +98,6 @@ class IDMaskEditorPanel(bpy.types.Panel): col.menu(IDEDITOR_IDMaskEditorOptionsMenu.bl_idname, icon='DOWNARROW_HLT', text="") - def draw_options(self, context, layout, props, element): has_render_ui = 'render_ui' in dir(element) diff --git a/src/panels/id_mask_editor_id_list.py b/src/panels/id_mask_editor_id_list.py index 680403d..44042be 100644 --- a/src/panels/id_mask_editor_id_list.py +++ b/src/panels/id_mask_editor_id_list.py @@ -1,6 +1,9 @@ import bpy +from src.operators.id_editor_apply_color import IDEDITOR_ColorApplyOperator +from src.operators.id_editor_find_used_ids import IDEDITOR_FindUsedIDsOperator from src.operators.id_editor_paint import IDEDITOR_PaintIDMaskOperator +from src.operators.id_editor_revert_color import IDEDITOR_ColorResetOperator class IDMaskEditorIDList(bpy.types.UIList): @@ -11,7 +14,19 @@ class IDMaskEditorIDList(bpy.types.UIList): split.alignment = 'LEFT' split.prop(attribute, 'color', text='') - split.emboss = 'NONE' - split2 = split.split(factor=0.75) - split2.prop(attribute, "name", text="") - split2.emboss = 'NORMAL' \ No newline at end of file + row = split.row() + col = row.column() + col.alignment = 'RIGHT' + col.emboss = 'NONE' + col.prop(attribute, "name", text="") + col.emboss = 'NORMAL' + + row1 = row.row(align=True) + row1.enabled = attribute.color_changed + reset_op = row1.operator(IDEDITOR_ColorResetOperator.bl_idname, icon='LOOP_BACK', text='') + reset_op.triggeredByList = True + reset_op.listId = _index + + apply_op = row1.operator(IDEDITOR_ColorApplyOperator.bl_idname, icon='CHECKMARK', text='') + apply_op.triggeredByList = True + apply_op.listId = _index diff --git a/src/properties/id_mask_editor_value_properties.py b/src/properties/id_mask_editor_value_properties.py index f2cbd9c..29dfed5 100644 --- a/src/properties/id_mask_editor_value_properties.py +++ b/src/properties/id_mask_editor_value_properties.py @@ -1,5 +1,21 @@ from bpy.types import (PropertyGroup) -from bpy.props import (StringProperty, FloatVectorProperty) +from bpy.props import (StringProperty,BoolProperty, FloatVectorProperty) + +def get_color(self): + return self['color'] + +def set_color(self, value): + if 'color' not in self: + self['color'] = value + return + + prev_color = self['color'] + + if not self.color_changed: + self.color_changed = True + self.original_color = prev_color + + self['color'] = value class IDMaskEditorValueProperties(PropertyGroup): name: StringProperty( @@ -8,10 +24,24 @@ class IDMaskEditorValueProperties(PropertyGroup): default='ID' ) + color_changed: BoolProperty( + default=False + ) + color: FloatVectorProperty( name="Color", subtype='COLOR', default=[1.0, 1.0, 1.0], min=0, + max=1, + get=get_color, + set=set_color + ) + + original_color: FloatVectorProperty( + name="Original Color", + subtype='COLOR', + default=[1.0, 1.0, 1.0], + min=0, max=1 ) \ No newline at end of file