diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..58218f5 --- /dev/null +++ b/__init__.py @@ -0,0 +1,40 @@ +__addon_enabled__ = False # Default state, Blender will manage this + +bl_info = { + "name": "Virtual Painter", + "author": "Andrin Honegger", + "version": (0, 1, 0), + "blender": (4, 3, 2), + "location": "3D Viewport > Tool Shelf > Tool", + "description": "", + "warning": "", + "category": "Paint", +} + +from . import ( + paintListener, + paintModifier, + ui, + preferences, +) + +import bpy # Blender Python API + +# Register and Unregister Classes +module_tuple = ( + #paintListener, + #paintModifier, + ui, + #preferences, +) + +def register(): + for item in module_tuple: + item.register() + +def unregister(): + for item in module_tuple: + item.unregister() + +if __name__ == "__main__": + register() diff --git a/blender_manifest.toml b/blender_manifest.toml new file mode 100644 index 0000000..3a27e0e --- /dev/null +++ b/blender_manifest.toml @@ -0,0 +1,16 @@ +schema_version = "1.0.0" + +id = "virtual_painter" +version = "0.1.0" +name = "Virtual Painter" +tagline = "Painting extension for painting normals for a painted style" +maintainer = "Andrin Honegger" +type = "add-on" + +tags = ["Paint"] + +blender_version_min = "4.3.2" + +license = [ + "SPDX:GPL-3.0-or-later", +] diff --git a/paintListener.py b/paintListener.py new file mode 100644 index 0000000..dc5d831 --- /dev/null +++ b/paintListener.py @@ -0,0 +1,7 @@ +import bpy + +def register(): + pass + +def unregister(): + pass \ No newline at end of file diff --git a/paintModifier.py b/paintModifier.py new file mode 100644 index 0000000..dc5d831 --- /dev/null +++ b/paintModifier.py @@ -0,0 +1,7 @@ +import bpy + +def register(): + pass + +def unregister(): + pass \ No newline at end of file diff --git a/preferences.py b/preferences.py new file mode 100644 index 0000000..dc5d831 --- /dev/null +++ b/preferences.py @@ -0,0 +1,7 @@ +import bpy + +def register(): + pass + +def unregister(): + pass \ No newline at end of file diff --git a/ui.py b/ui.py new file mode 100644 index 0000000..86d868d --- /dev/null +++ b/ui.py @@ -0,0 +1,134 @@ +import bpy + +class vPainterSetting(bpy.types.PropertyGroup): + is_vPainter_enabled: bpy.props.BoolProperty( + name="Addon Enabled", + description="Enable / disable the virtual Painter", + default=False, + update=lambda self, context: toggle_operator(context) + ) + texture_panel_state: bpy.props.BoolProperty( + name="Textures", + default=False, + description="Textures used for painting" + ) + +def toggle_operator(context): + """Start or stop the modal operator based on the checkbox.""" + if context.scene.vPainter_settings.is_vPainter_enabled: + # Start the operator + bpy.ops.paint.virtual_painter('INVOKE_DEFAULT') + +class textureSelection(bpy.types.PropertyGroup): + texture: bpy.props.PointerProperty( + name="Texture", + type=bpy.types.Image, + description="Select a texture", + ) + color: bpy.props.FloatVectorProperty( + name="Color", + subtype='COLOR', + size=4, + min=0.0, + max=1.0, + default=(1.0, 1.0, 1.0, 1.0), + description="Select a color (RGBA)", + ) + +class VIEW3D_PT_ToolActiveToolPanel(bpy.types.Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = "Tool" + bl_label = "Virtual Painter" + bl_idname = "VIEW3D_PT_virtual_Painter" + + def draw(self, context): + layout = self.layout + tool_settings = context.scene.vPainter_settings + normal_texture = context.scene.normal_texture_settings + texture_collection = context.scene.texture_collection_settings + + # Add the checkbox to toggle the operator + layout.prop( + tool_settings, + "is_vPainter_enabled", + text="DISABLE" if tool_settings.is_vPainter_enabled else "ENABLE", + toggle=True, + invert_checkbox=True + ) + + # Collapsible section of used textures + layout.prop( + tool_settings, + "texture_panel_state", + emboss=False, + icon="DOWNARROW_HLT" if tool_settings.texture_panel_state else "RIGHTARROW", + toggle=True + ) + if tool_settings.texture_panel_state: + section = layout.box() + section.label(text="Normal Texture") + + # Normal texture selection + section.prop(normal_texture, "texture", text="", icon="TEXTURE") + + section.separator(type='LINE') + + # Custom texture selection + section.label(text="Custom Texture") + + # Display all items in the collection + for index, item in enumerate(texture_collection): + box = section.box() + box.prop(item, "texture", text="", icon="TEXTURE") + box.prop(item, "color", text="", icon="COLOR") + box.operator("custom_texture.remove_item", text="", icon="X").index = index + section.operator("custom_texture.add_item", icon="ADD") + +# Operator to add new items to the collection +class IMAGE_OT_AddItem(bpy.types.Operator): + bl_idname = "custom_texture.add_item" + bl_label = "Add New" + bl_description = "Add a new texture" + + def execute(self, context): + texture_collection = context.scene.texture_collection_settings + texture_collection.add() + return {'FINISHED'} + +# Operator to remove items from the collection +class IMAGE_OT_RemoveItem(bpy.types.Operator): + bl_idname = "custom_texture.remove_item" + bl_label = "" + bl_description = "Remove this texture" + + index: bpy.props.IntProperty() + + def execute(self, context): + texture_collection = context.scene.texture_collection_settings + if 0 <= self.index < len(texture_collection): + texture_collection.remove(self.index) + return {'FINISHED'} + + +def register(): + bpy.utils.register_class(vPainterSetting) + bpy.utils.register_class(textureSelection) + bpy.utils.register_class(VIEW3D_PT_ToolActiveToolPanel) + bpy.utils.register_class(IMAGE_OT_AddItem) + bpy.utils.register_class(IMAGE_OT_RemoveItem) + + bpy.types.Scene.vPainter_settings = bpy.props.PointerProperty(type=vPainterSetting) + bpy.types.Scene.normal_texture_settings = bpy.props.PointerProperty(type=textureSelection) + bpy.types.Scene.texture_collection_settings = bpy.props.CollectionProperty(type=textureSelection) + +def unregister(): + bpy.utils.unregister_class(vPainterSetting) + bpy.utils.unregister_class(textureSelection) + bpy.utils.unregister_class(VIEW3D_PT_ToolActiveToolPanel) + bpy.utils.unregister_class(IMAGE_OT_AddItem) + bpy.utils.unregister_class(IMAGE_OT_RemoveItem) + + del bpy.types.Scene.vPainter_settings + del bpy.types.Scene.normal_texture_settings + del bpy.types.Scene.texture_collection_settings \ No newline at end of file