From bdf75cb276dfd3b5266c909de4c099c00c68a659 Mon Sep 17 00:00:00 2001 From: Thomas Dinges Date: Fri, 17 Jun 2022 11:33:32 +0200 Subject: Fix T98953: Typo in property name. --- object_boolean_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/object_boolean_tools.py b/object_boolean_tools.py index f95f19cc..149433ca 100644 --- a/object_boolean_tools.py +++ b/object_boolean_tools.py @@ -1090,7 +1090,7 @@ class PREFS_BoolTool_Props(AddonPreferences): "for a custom version that can optimize the visualization of Brushes", ) use_wire: BoolProperty( - name="Display As Wirewrame", + name="Display As Wireframe", description="Display brush as wireframe instead of bounding box", ) category: StringProperty( -- cgit v1.2.3 From 46234f26a4cc8479094a315b84afedad4804fb06 Mon Sep 17 00:00:00 2001 From: Daniel Salazar Date: Fri, 17 Jun 2022 15:38:04 -0600 Subject: AnimAll: Cleanup comments and adding Damien Picard as author --- animation_animall.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/animation_animall.py b/animation_animall.py index a7d76f53..6dea5375 100644 --- a/animation_animall.py +++ b/animation_animall.py @@ -2,8 +2,8 @@ bl_info = { "name": "AnimAll", - "author": "Daniel Salazar ", - "version": (0, 9, 1), + "author": "Daniel Salazar (ZanQdo), Damien Picard (pioverfour)", + "version": (0, 9, 2), "blender": (3, 3, 0), "location": "3D View > Toolbox > Animation tab > AnimAll", "description": "Allows animation of mesh, lattice, curve and surface data", @@ -12,11 +12,6 @@ bl_info = { "category": "Animation", } -""" -Thanks to Campbell Barton and Joshua Leung for hes API additions and fixes -Daniel 'ZanQdo' Salazar -""" - import bpy from bpy.types import ( Operator, -- cgit v1.2.3 From b51c09fd5a6fd6dfc17638b0d27a1b1254fd852c Mon Sep 17 00:00:00 2001 From: Daniel Salazar Date: Fri, 17 Jun 2022 15:47:51 -0600 Subject: AnimAll: Clearer names for keyable properties --- animation_animall.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/animation_animall.py b/animation_animall.py index 6dea5375..a72ac33e 100644 --- a/animation_animall.py +++ b/animation_animall.py @@ -33,22 +33,22 @@ class AnimallProperties(bpy.types.PropertyGroup): default=True ) key_shape: BoolProperty( - name="Shape", + name="Shape Key", description="Insert keyframes on active Shape Key layer", default=False ) key_uvs: BoolProperty( - name="UVs", + name="UV Map", description="Insert keyframes on active UV coordinates", default=False ) key_ebevel: BoolProperty( - name="E-Bevel", + name="Edge Bevel", description="Insert keyframes on edge bevel weight", default=False ) key_vbevel: BoolProperty( - name="V-Bevel", + name="Vertex Bevel", description="Insert keyframes on vertex bevel weight", default=False ) @@ -58,12 +58,12 @@ class AnimallProperties(bpy.types.PropertyGroup): default=False ) key_vgroups: BoolProperty( - name="V-groups", + name="Vertex Groups", description="Insert keyframes on active Vertex group values", default=False ) key_attribute: BoolProperty( - name="Active Attribute", + name="Attribute", description="Insert keyframes on active attribute values", default=False ) -- cgit v1.2.3 From f42d5a872e7eefa07b0b74d4eb2bd880d6fb587f Mon Sep 17 00:00:00 2001 From: Daniel Salazar Date: Fri, 17 Jun 2022 15:58:04 -0600 Subject: AnimAll: Vertex Group should be singular since it only affects the active one --- animation_animall.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/animation_animall.py b/animation_animall.py index a72ac33e..775103a7 100644 --- a/animation_animall.py +++ b/animation_animall.py @@ -58,7 +58,7 @@ class AnimallProperties(bpy.types.PropertyGroup): default=False ) key_vgroups: BoolProperty( - name="Vertex Groups", + name="Vertex Group", description="Insert keyframes on active Vertex group values", default=False ) -- cgit v1.2.3 From f888aca19ed2bc3493fd998b9d1edff573f73569 Mon Sep 17 00:00:00 2001 From: Daniel Salazar Date: Fri, 17 Jun 2022 16:51:38 -0600 Subject: AnimAll: Preparation to support vertex creases when the API permits it --- animation_animall.py | 61 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/animation_animall.py b/animation_animall.py index 775103a7..ff140860 100644 --- a/animation_animall.py +++ b/animation_animall.py @@ -42,24 +42,29 @@ class AnimallProperties(bpy.types.PropertyGroup): description="Insert keyframes on active UV coordinates", default=False ) - key_ebevel: BoolProperty( - name="Edge Bevel", - description="Insert keyframes on edge bevel weight", - default=False - ) key_vbevel: BoolProperty( name="Vertex Bevel", description="Insert keyframes on vertex bevel weight", default=False ) - key_crease: BoolProperty( - name="Crease", - description="Insert keyframes on edge creases", + key_ebevel: BoolProperty( + name="Edge Bevel", + description="Insert keyframes on edge bevel weight", + default=False + ) + # key_vcrease: BoolProperty( + # name="Vertex Crease", + # description="Insert keyframes on vertex crease weight", + # default=False + # ) + key_ecrease: BoolProperty( + name="Edge Crease", + description="Insert keyframes on edge crease weight", default=False ) key_vgroups: BoolProperty( name="Vertex Group", - description="Insert keyframes on active Vertex group values", + description="Insert keyframes on active vertex group values", default=False ) key_attribute: BoolProperty( @@ -156,14 +161,16 @@ class VIEW3D_PT_animall(Panel): row.prop(animall_properties, "key_points") row.prop(animall_properties, "key_shape") row = col.row() - row.prop(animall_properties, "key_ebevel") row.prop(animall_properties, "key_vbevel") + row.prop(animall_properties, "key_ebevel") row = col.row() - row.prop(animall_properties, "key_crease") + row.prop(animall_properties, "key_ecrease") row.prop(animall_properties, "key_uvs") row = col.row() row.prop(animall_properties, "key_attribute") row.prop(animall_properties, "key_vgroups") + row = col.row() + # Vertex group update operator if (context.active_object is not None @@ -311,18 +318,23 @@ class ANIM_OT_insert_keyframe_animall(Operator): if not animall_properties.key_selected or vert.select: insert_key(vert, 'bevel_weight', group="Vertex %s" % v_i) + if animall_properties.key_ebevel: + for e_i, edge in enumerate(data.edges): + if not animall_properties.key_selected or edge.select: + insert_key(edge, 'bevel_weight', group="Edge %s" % e_i) + if animall_properties.key_vgroups: for v_i, vert in enumerate(data.vertices): if not animall_properties.key_selected or vert.select: for group in vert.groups: insert_key(group, 'weight', group="Vertex %s" % v_i) - if animall_properties.key_ebevel: - for e_i, edge in enumerate(data.edges): - if not animall_properties.key_selected or edge.select: - insert_key(edge, 'bevel_weight', group="Edge %s" % e_i) + # if animall_properties.key_vcrease: + # for v_i, vert in enumerate(data.vertices): + # if not animall_properties.key_selected or vert.select: + # insert_key(vert, 'crease', group="Vertex %s" % v_i) - if animall_properties.key_crease: + if animall_properties.key_ecrease: for e_i, edge in enumerate(data.edges): if not animall_properties.key_selected or edge.select: insert_key(edge, 'crease', group="Edge %s" % e_i) @@ -437,20 +449,25 @@ class ANIM_OT_delete_keyframe_animall(Operator): if not animall_properties.key_selected or vert.select: delete_key(vert, 'bevel_weight') + if animall_properties.key_ebevel: + for edge in data.edges: + if not animall_properties.key_selected or edge.select: + delete_key(edge, 'bevel_weight') + if animall_properties.key_vgroups: for vert in data.vertices: if not animall_properties.key_selected or vert.select: for group in vert.groups: delete_key(group, 'weight') - if animall_properties.key_ebevel: - for edge in data.edges: - if not animall_properties.key_selected or edge.select: - delete_key(edge, 'bevel_weight') + # if animall_properties.key_vcrease: + # for vert in data.vertices: + # if not animall_properties.key_selected or vert.select: + # delete_key(vert, 'crease') - if animall_properties.key_crease: + if animall_properties.key_ecrease: for edge in data.edges: - if not animall_properties.key_selected or vert.select: + if not animall_properties.key_selected or edge.select: delete_key(edge, 'crease') if animall_properties.key_shape: -- cgit v1.2.3 From 212c93689424ace4f2b5330a5aa9f74cd742c21e Mon Sep 17 00:00:00 2001 From: Daniel Salazar Date: Fri, 17 Jun 2022 20:04:33 -0600 Subject: AnimAll: Minor UI updates --- animation_animall.py | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/animation_animall.py b/animation_animall.py index ff140860..b5b36100 100644 --- a/animation_animall.py +++ b/animation_animall.py @@ -28,7 +28,7 @@ from bpy.app.handlers import persistent # Property Definitions class AnimallProperties(bpy.types.PropertyGroup): key_selected: BoolProperty( - name="Selected Only", + name="Key Selected Only", description="Insert keyframes only on selected elements", default=True ) @@ -146,12 +146,11 @@ class VIEW3D_PT_animall(Panel): animall_properties = context.window_manager.animall_properties layout = self.layout - col = layout.column(align=True) - row = col.row() - row.prop(animall_properties, "key_selected") - col.separator() - row = col.row() + layout.label (text = 'Key:') + + col = layout.column(align=True) + row = col.row(align = True) if obj.type == 'LATTICE': row.prop(animall_properties, "key_points") @@ -160,17 +159,15 @@ class VIEW3D_PT_animall(Panel): elif obj.type == 'MESH': row.prop(animall_properties, "key_points") row.prop(animall_properties, "key_shape") - row = col.row() + row = col.row(align = True) row.prop(animall_properties, "key_vbevel") row.prop(animall_properties, "key_ebevel") - row = col.row() + row = col.row(align = True) row.prop(animall_properties, "key_ecrease") row.prop(animall_properties, "key_uvs") - row = col.row() + row = col.row(align = True) row.prop(animall_properties, "key_attribute") row.prop(animall_properties, "key_vgroups") - row = col.row() - # Vertex group update operator if (context.active_object is not None @@ -189,25 +186,29 @@ class VIEW3D_PT_animall(Panel): elif obj.type == 'CURVE': row.prop(animall_properties, "key_points") row.prop(animall_properties, "key_shape") - row = col.row() + row = col.row(align = True) row.prop(animall_properties, "key_radius") row.prop(animall_properties, "key_tilt") - row = col.row() + row = col.row(align = True) row.prop(animall_properties, "key_handle_type") elif obj.type == 'SURFACE': row.prop(animall_properties, "key_points") row.prop(animall_properties, "key_shape") - row = col.row() + row = col.row(align = True) row.prop(animall_properties, "key_radius") row.prop(animall_properties, "key_tilt") - layout.separator() + col.separator() + col = layout.column(align=True) + row = col.row() + row.prop(animall_properties, "key_selected") + row = layout.row(align=True) row.operator("anim.insert_keyframe_animall", icon="KEY_HLT") row.operator("anim.delete_keyframe_animall", icon="KEY_DEHLT") row = layout.row() - row.operator("anim.clear_animation_animall", icon="X") + row.operator("anim.clear_animation_animall", icon="CANCEL") if animall_properties.key_shape: shape_key = obj.active_shape_key @@ -234,7 +235,7 @@ class VIEW3D_PT_animall(Panel): class ANIM_OT_insert_keyframe_animall(Operator): - bl_label = "Insert" + bl_label = "Insert Key" bl_idname = "anim.insert_keyframe_animall" bl_description = "Insert a Keyframe" bl_options = {'REGISTER', 'UNDO'} @@ -420,7 +421,7 @@ class ANIM_OT_insert_keyframe_animall(Operator): class ANIM_OT_delete_keyframe_animall(Operator): - bl_label = "Delete" + bl_label = "Delete Key" bl_idname = "anim.delete_keyframe_animall" bl_description = "Delete a Keyframe" bl_options = {'REGISTER', 'UNDO'} -- cgit v1.2.3 From 8f807fbd7c0ce35147d18d1ae14cb16832ab229a Mon Sep 17 00:00:00 2001 From: Daniel Salazar Date: Fri, 17 Jun 2022 20:08:58 -0600 Subject: AnimAll: make panel open by default --- animation_animall.py | 1 - 1 file changed, 1 deletion(-) diff --git a/animation_animall.py b/animation_animall.py index b5b36100..809bd31b 100644 --- a/animation_animall.py +++ b/animation_animall.py @@ -135,7 +135,6 @@ class VIEW3D_PT_animall(Panel): bl_region_type = 'UI' bl_category = "Animate" bl_label = 'AnimAll' - bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(self, context): -- cgit v1.2.3 From 57d6a1e3e82021116f770171c5da173e43b454f8 Mon Sep 17 00:00:00 2001 From: Daniel Salazar Date: Sat, 18 Jun 2022 01:04:45 -0600 Subject: AnimAll: Remove key handle type since this can lead to jumps. See T98965 --- animation_animall.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/animation_animall.py b/animation_animall.py index 809bd31b..5ea5952b 100644 --- a/animation_animall.py +++ b/animation_animall.py @@ -77,11 +77,6 @@ class AnimallProperties(bpy.types.PropertyGroup): description="Insert keyframes on point locations", default=False ) - key_handle_type: BoolProperty( - name="Handle Types", - description="Insert keyframes on Bezier point types", - default=False - ) key_radius: BoolProperty( name="Radius", description="Insert keyframes on point radius (Shrink/Fatten)", @@ -188,8 +183,6 @@ class VIEW3D_PT_animall(Panel): row = col.row(align = True) row.prop(animall_properties, "key_radius") row.prop(animall_properties, "key_tilt") - row = col.row(align = True) - row.prop(animall_properties, "key_handle_type") elif obj.type == 'SURFACE': row.prop(animall_properties, "key_points") @@ -284,10 +277,6 @@ class ANIM_OT_insert_keyframe_animall(Operator): if animall_properties.key_radius: insert_key(CV, 'radius', group="Spline %s CV %s" % (s_i, v_i)) - if animall_properties.key_handle_type: - insert_key(CV, 'handle_left_type', group="spline %s CV %s" % (s_i, v_i)) - insert_key(CV, 'handle_right_type', group="spline %s CV %s" % (s_i, v_i)) - if animall_properties.key_tilt: insert_key(CV, 'tilt', group="Spline %s CV %s" % (s_i, v_i)) @@ -533,9 +522,6 @@ class ANIM_OT_delete_keyframe_animall(Operator): delete_key(CV, 'co') delete_key(CV, 'handle_left') delete_key(CV, 'handle_right') - if animall_properties.key_handle_type: - delete_key(CV, 'handle_left_type') - delete_key(CV, 'handle_right_type') if animall_properties.key_radius: delete_key(CV, 'radius') if animall_properties.key_tilt: -- cgit v1.2.3 From ea96c101ef12dda105c3ac9d440415b4209ca923 Mon Sep 17 00:00:00 2001 From: Daniel Salazar Date: Sat, 18 Jun 2022 01:47:36 -0600 Subject: AnimAll: Make key selected only disabled by default --- animation_animall.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/animation_animall.py b/animation_animall.py index 5ea5952b..897210d8 100644 --- a/animation_animall.py +++ b/animation_animall.py @@ -3,7 +3,7 @@ bl_info = { "name": "AnimAll", "author": "Daniel Salazar (ZanQdo), Damien Picard (pioverfour)", - "version": (0, 9, 2), + "version": (0, 9, 3), "blender": (3, 3, 0), "location": "3D View > Toolbox > Animation tab > AnimAll", "description": "Allows animation of mesh, lattice, curve and surface data", @@ -30,7 +30,7 @@ class AnimallProperties(bpy.types.PropertyGroup): key_selected: BoolProperty( name="Key Selected Only", description="Insert keyframes only on selected elements", - default=True + default=False ) key_shape: BoolProperty( name="Shape Key", @@ -137,7 +137,7 @@ class VIEW3D_PT_animall(Panel): def draw(self, context): obj = context.active_object - animall_properties = context.window_manager.animall_properties + animall_properties = obj.animall_properties layout = self.layout @@ -643,7 +643,7 @@ class AnimallAddonPreferences(AddonPreferences): def register(): bpy.utils.register_class(AnimallProperties) - bpy.types.WindowManager.animall_properties = bpy.props.PointerProperty(type=AnimallProperties) + bpy.types.Object.animall_properties = bpy.props.PointerProperty(type=AnimallProperties) bpy.utils.register_class(VIEW3D_PT_animall) bpy.utils.register_class(ANIM_OT_insert_keyframe_animall) bpy.utils.register_class(ANIM_OT_delete_keyframe_animall) @@ -654,7 +654,7 @@ def register(): def unregister(): - del bpy.types.WindowManager.animall_properties + del bpy.types.Object.animall_properties bpy.utils.unregister_class(AnimallProperties) bpy.utils.unregister_class(VIEW3D_PT_animall) bpy.utils.unregister_class(ANIM_OT_insert_keyframe_animall) -- cgit v1.2.3 From 5153c7377a3cc01bdc471d9ac276f0c1f7758151 Mon Sep 17 00:00:00 2001 From: Daniel Salazar Date: Sat, 18 Jun 2022 01:57:19 -0600 Subject: AnimAll: Store properties in scene so they are saved on the blend file --- animation_animall.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/animation_animall.py b/animation_animall.py index 897210d8..06e5f7df 100644 --- a/animation_animall.py +++ b/animation_animall.py @@ -3,7 +3,7 @@ bl_info = { "name": "AnimAll", "author": "Daniel Salazar (ZanQdo), Damien Picard (pioverfour)", - "version": (0, 9, 3), + "version": (0, 9, 4), "blender": (3, 3, 0), "location": "3D View > Toolbox > Animation tab > AnimAll", "description": "Allows animation of mesh, lattice, curve and surface data", @@ -137,7 +137,7 @@ class VIEW3D_PT_animall(Panel): def draw(self, context): obj = context.active_object - animall_properties = obj.animall_properties + animall_properties = context.scene.animall_properties layout = self.layout @@ -233,7 +233,7 @@ class ANIM_OT_insert_keyframe_animall(Operator): bl_options = {'REGISTER', 'UNDO'} def execute(op, context): - animall_properties = context.window_manager.animall_properties + animall_properties = context.scene.animall_properties if context.mode == 'OBJECT': objects = context.selected_objects @@ -416,7 +416,7 @@ class ANIM_OT_delete_keyframe_animall(Operator): def execute(op, context): - animall_properties = context.window_manager.animall_properties + animall_properties = context.scene.animall_properties if context.mode == 'OBJECT': objects = context.selected_objects @@ -643,7 +643,7 @@ class AnimallAddonPreferences(AddonPreferences): def register(): bpy.utils.register_class(AnimallProperties) - bpy.types.Object.animall_properties = bpy.props.PointerProperty(type=AnimallProperties) + bpy.types.Scene.animall_properties = bpy.props.PointerProperty(type=AnimallProperties) bpy.utils.register_class(VIEW3D_PT_animall) bpy.utils.register_class(ANIM_OT_insert_keyframe_animall) bpy.utils.register_class(ANIM_OT_delete_keyframe_animall) @@ -654,7 +654,7 @@ def register(): def unregister(): - del bpy.types.Object.animall_properties + del bpy.types.Scene.animall_properties bpy.utils.unregister_class(AnimallProperties) bpy.utils.unregister_class(VIEW3D_PT_animall) bpy.utils.unregister_class(ANIM_OT_insert_keyframe_animall) -- cgit v1.2.3 From 86abd3cf487b081fee71a6bbcec77bcc0a8f2ef7 Mon Sep 17 00:00:00 2001 From: Damien Picard Date: Sat, 18 Jun 2022 08:09:12 +0200 Subject: Animall: rename properties so they're more explicit and standard - key_points -> key_point_location - key_shape -> key_shape_key - key_vgroups -> key_vertex_group - key_ebevel -> key_edge_bevel - key_vbevel -> key_vertex_bevel --- animation_animall.py | 175 +++++++++++++++++++++++++-------------------------- 1 file changed, 85 insertions(+), 90 deletions(-) diff --git a/animation_animall.py b/animation_animall.py index 06e5f7df..9a0b7ddc 100644 --- a/animation_animall.py +++ b/animation_animall.py @@ -30,63 +30,59 @@ class AnimallProperties(bpy.types.PropertyGroup): key_selected: BoolProperty( name="Key Selected Only", description="Insert keyframes only on selected elements", - default=False - ) - key_shape: BoolProperty( + default=False) + + # Generic attributes + key_point_location: BoolProperty( + name="Location", + description="Insert keyframes on point locations", + default=False) + key_shape_key: BoolProperty( name="Shape Key", description="Insert keyframes on active Shape Key layer", - default=False - ) - key_uvs: BoolProperty( - name="UV Map", - description="Insert keyframes on active UV coordinates", - default=False - ) - key_vbevel: BoolProperty( + default=False) + + # Mesh attributes + key_vertex_bevel: BoolProperty( name="Vertex Bevel", description="Insert keyframes on vertex bevel weight", - default=False - ) - key_ebevel: BoolProperty( - name="Edge Bevel", - description="Insert keyframes on edge bevel weight", - default=False - ) - # key_vcrease: BoolProperty( + default=False) + # key_vertex_crease: BoolProperty( # name="Vertex Crease", # description="Insert keyframes on vertex crease weight", - # default=False - # ) - key_ecrease: BoolProperty( - name="Edge Crease", - description="Insert keyframes on edge crease weight", - default=False - ) - key_vgroups: BoolProperty( + # default=False) + key_vertex_group: BoolProperty( name="Vertex Group", description="Insert keyframes on active vertex group values", - default=False - ) + default=False) + + key_edge_bevel: BoolProperty( + name="Edge Bevel", + description="Insert keyframes on edge bevel weight", + default=False) + key_edge_crease: BoolProperty( + name="Edge Crease", + description="Insert keyframes on edge creases", + default=False) + key_attribute: BoolProperty( name="Attribute", description="Insert keyframes on active attribute values", - default=False - ) - key_points: BoolProperty( - name="Points", - description="Insert keyframes on point locations", - default=False - ) + default=False) + key_uvs: BoolProperty( + name="UVs", + description="Insert keyframes on active UV coordinates", + default=False) + + # Curve and surface attributes key_radius: BoolProperty( name="Radius", description="Insert keyframes on point radius (Shrink/Fatten)", - default=False - ) + default=False) key_tilt: BoolProperty( name="Tilt", description="Insert keyframes on point tilt", - default=False - ) + default=False) # Utility functions @@ -147,21 +143,21 @@ class VIEW3D_PT_animall(Panel): row = col.row(align = True) if obj.type == 'LATTICE': - row.prop(animall_properties, "key_points") - row.prop(animall_properties, "key_shape") + row.prop(animall_properties, "key_point_location") + row.prop(animall_properties, "key_shape_key") elif obj.type == 'MESH': - row.prop(animall_properties, "key_points") - row.prop(animall_properties, "key_shape") - row = col.row(align = True) - row.prop(animall_properties, "key_vbevel") - row.prop(animall_properties, "key_ebevel") - row = col.row(align = True) - row.prop(animall_properties, "key_ecrease") + row.prop(animall_properties, "key_point_location") + row.prop(animall_properties, "key_shape_key") + row = col.row() + row.prop(animall_properties, "key_vertex_bevel") + row.prop(animall_properties, "key_edge_bevel") + row = col.row() + row.prop(animall_properties, "key_edge_crease") row.prop(animall_properties, "key_uvs") row = col.row(align = True) row.prop(animall_properties, "key_attribute") - row.prop(animall_properties, "key_vgroups") + row.prop(animall_properties, "key_vertex_group") # Vertex group update operator if (context.active_object is not None @@ -178,15 +174,15 @@ class VIEW3D_PT_animall(Panel): break elif obj.type == 'CURVE': - row.prop(animall_properties, "key_points") - row.prop(animall_properties, "key_shape") + row.prop(animall_properties, "key_point_location") + row.prop(animall_properties, "key_shape_key") row = col.row(align = True) row.prop(animall_properties, "key_radius") row.prop(animall_properties, "key_tilt") elif obj.type == 'SURFACE': - row.prop(animall_properties, "key_points") - row.prop(animall_properties, "key_shape") + row.prop(animall_properties, "key_point_location") + row.prop(animall_properties, "key_shape_key") row = col.row(align = True) row.prop(animall_properties, "key_radius") row.prop(animall_properties, "key_tilt") @@ -202,7 +198,7 @@ class VIEW3D_PT_animall(Panel): row = layout.row() row.operator("anim.clear_animation_animall", icon="CANCEL") - if animall_properties.key_shape: + if animall_properties.key_shape_key: shape_key = obj.active_shape_key shape_key_index = obj.active_shape_key_index @@ -221,7 +217,7 @@ class VIEW3D_PT_animall(Panel): else: row.label(text="No active Shape Key", icon="ERROR") - if animall_properties.key_points and animall_properties.key_shape: + if animall_properties.key_point_location and animall_properties.key_shape_key: row = layout.row() row.label(text='"Points" and "Shape" are redundant?', icon="INFO") @@ -249,14 +245,14 @@ class ANIM_OT_insert_keyframe_animall(Operator): data = obj.data if obj.type == 'LATTICE': - if animall_properties.key_shape: + if animall_properties.key_shape_key: if obj.active_shape_key_index > 0: sk_name = obj.active_shape_key.name for p_i, point in enumerate(obj.active_shape_key.data): if not animall_properties.key_selected or data.points[p_i].select: insert_key(point, 'co', group="%s Point %s" % (sk_name, p_i)) - if animall_properties.key_points: + if animall_properties.key_point_location: for p_i, point in enumerate(data.points): if not animall_properties.key_selected or point.select: insert_key(point, 'co_deform', group="Point %s" % p_i) @@ -269,7 +265,7 @@ class ANIM_OT_insert_keyframe_animall(Operator): or CV.select_control_point or CV.select_left_handle or CV.select_right_handle): - if animall_properties.key_points: + if animall_properties.key_point_location: insert_key(CV, 'co', group="Spline %s CV %s" % (s_i, v_i)) insert_key(CV, 'handle_left', group="Spline %s CV %s" % (s_i, v_i)) insert_key(CV, 'handle_right', group="Spline %s CV %s" % (s_i, v_i)) @@ -283,7 +279,7 @@ class ANIM_OT_insert_keyframe_animall(Operator): elif spline.type in ('POLY', 'NURBS'): for v_i, CV in enumerate(spline.points): if not animall_properties.key_selected or CV.select: - if animall_properties.key_points: + if animall_properties.key_point_location: insert_key(CV, 'co', group="Spline %s CV %s" % (s_i, v_i)) if animall_properties.key_radius: @@ -297,38 +293,37 @@ class ANIM_OT_insert_keyframe_animall(Operator): for obj in [o for o in objects if o.type in {'MESH', 'CURVE', 'SURFACE'}]: data = obj.data if obj.type == 'MESH': - if animall_properties.key_points: + if animall_properties.key_point_location: for v_i, vert in enumerate(data.vertices): if not animall_properties.key_selected or vert.select: insert_key(vert, 'co', group="Vertex %s" % v_i) - if animall_properties.key_vbevel: + if animall_properties.key_vertex_bevel: for v_i, vert in enumerate(data.vertices): if not animall_properties.key_selected or vert.select: insert_key(vert, 'bevel_weight', group="Vertex %s" % v_i) + # if animall_properties.key_vertex_crease: + # for v_i, vert in enumerate(data.vertices): + # if not animall_properties.key_selected or vert.select: + # insert_key(vert, 'crease', group="Vertex %s" % v_i) - if animall_properties.key_ebevel: - for e_i, edge in enumerate(data.edges): - if not animall_properties.key_selected or edge.select: - insert_key(edge, 'bevel_weight', group="Edge %s" % e_i) - - if animall_properties.key_vgroups: + if animall_properties.key_vertex_group: for v_i, vert in enumerate(data.vertices): if not animall_properties.key_selected or vert.select: for group in vert.groups: insert_key(group, 'weight', group="Vertex %s" % v_i) - # if animall_properties.key_vcrease: - # for v_i, vert in enumerate(data.vertices): - # if not animall_properties.key_selected or vert.select: - # insert_key(vert, 'crease', group="Vertex %s" % v_i) + if animall_properties.key_edge_bevel: + for e_i, edge in enumerate(data.edges): + if not animall_properties.key_selected or edge.select: + insert_key(edge, 'bevel_weight', group="Edge %s" % e_i) - if animall_properties.key_ecrease: + if animall_properties.key_edge_crease: for e_i, edge in enumerate(data.edges): if not animall_properties.key_selected or edge.select: insert_key(edge, 'crease', group="Edge %s" % e_i) - if animall_properties.key_shape: + if animall_properties.key_shape_key: if obj.active_shape_key_index > 0: sk_name = obj.active_shape_key.name for v_i, vert in enumerate(obj.active_shape_key.data): @@ -373,7 +368,7 @@ class ANIM_OT_insert_keyframe_animall(Operator): elif obj.type in {'CURVE', 'SURFACE'}: # Shape key keys have to be inserted in object mode for curves... - if animall_properties.key_shape: + if animall_properties.key_shape_key: sk_name = obj.active_shape_key.name global_spline_index = 0 # numbering for shape keys, which have flattened indices for s_i, spline in enumerate(data.splines): @@ -428,22 +423,17 @@ class ANIM_OT_delete_keyframe_animall(Operator): for obj in objects: data = obj.data if obj.type == 'MESH': - if animall_properties.key_points: + if animall_properties.key_point_location: for vert in data.vertices: if not animall_properties.key_selected or vert.select: delete_key(vert, 'co') - if animall_properties.key_vbevel: + if animall_properties.key_vertex_bevel: for vert in data.vertices: if not animall_properties.key_selected or vert.select: delete_key(vert, 'bevel_weight') - if animall_properties.key_ebevel: - for edge in data.edges: - if not animall_properties.key_selected or edge.select: - delete_key(edge, 'bevel_weight') - - if animall_properties.key_vgroups: + if animall_properties.key_vertex_group: for vert in data.vertices: if not animall_properties.key_selected or vert.select: for group in vert.groups: @@ -454,12 +444,17 @@ class ANIM_OT_delete_keyframe_animall(Operator): # if not animall_properties.key_selected or vert.select: # delete_key(vert, 'crease') - if animall_properties.key_ecrease: + if animall_properties.key_edge_bevel: for edge in data.edges: if not animall_properties.key_selected or edge.select: + delete_key(edge, 'bevel_weight') + + if animall_properties.key_edge_crease: + for edge in data.edges: + if not animall_properties.key_selected or vert.select: delete_key(edge, 'crease') - if animall_properties.key_shape: + if animall_properties.key_shape_key: if obj.active_shape_key: for v_i, vert in enumerate(obj.active_shape_key.data): if not animall_properties.key_selected or data.vertices[v_i].select: @@ -492,19 +487,19 @@ class ANIM_OT_delete_keyframe_animall(Operator): delete_key(data, f'attributes["{attribute.name}"].data[{e_i}].{attribute_key}') elif obj.type == 'LATTICE': - if animall_properties.key_shape: + if animall_properties.key_shape_key: if obj.active_shape_key: for point in obj.active_shape_key.data: delete_key(point, 'co') - if animall_properties.key_points: + if animall_properties.key_point_location: for point in data.points: if not animall_properties.key_selected or point.select: delete_key(point, 'co_deform') elif obj.type in {'CURVE', 'SURFACE'}: # run this outside the splines loop (only once) - if animall_properties.key_shape: + if animall_properties.key_shape_key: if obj.active_shape_key_index > 0: for CV in obj.active_shape_key.data: delete_key(CV, 'co') @@ -518,7 +513,7 @@ class ANIM_OT_delete_keyframe_animall(Operator): or CV.select_control_point or CV.select_left_handle or CV.select_right_handle): - if animall_properties.key_points: + if animall_properties.key_point_location: delete_key(CV, 'co') delete_key(CV, 'handle_left') delete_key(CV, 'handle_right') @@ -530,7 +525,7 @@ class ANIM_OT_delete_keyframe_animall(Operator): elif spline.type in ('POLY', 'NURBS'): for CV in spline.points: if not animall_properties.key_selected or CV.select: - if animall_properties.key_points: + if animall_properties.key_point_location: delete_key(CV, 'co') if animall_properties.key_radius: delete_key(CV, 'radius') -- cgit v1.2.3 From 8ea5c49499dcc171c400c88e7b478f46585e35d2 Mon Sep 17 00:00:00 2001 From: Damien Picard Date: Sat, 18 Jun 2022 08:14:56 +0200 Subject: AnimAll: add material index for faces and curves --- animation_animall.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/animation_animall.py b/animation_animall.py index 9a0b7ddc..e7a12a2a 100644 --- a/animation_animall.py +++ b/animation_animall.py @@ -3,7 +3,7 @@ bl_info = { "name": "AnimAll", "author": "Daniel Salazar (ZanQdo), Damien Picard (pioverfour)", - "version": (0, 9, 4), + "version": (0, 9, 5), "blender": (3, 3, 0), "location": "3D View > Toolbox > Animation tab > AnimAll", "description": "Allows animation of mesh, lattice, curve and surface data", @@ -41,6 +41,10 @@ class AnimallProperties(bpy.types.PropertyGroup): name="Shape Key", description="Insert keyframes on active Shape Key layer", default=False) + key_material_index: BoolProperty( + name="Material Index", + description="Insert keyframes on face material indices", + default=False) # Mesh attributes key_vertex_bevel: BoolProperty( @@ -158,6 +162,8 @@ class VIEW3D_PT_animall(Panel): row = col.row(align = True) row.prop(animall_properties, "key_attribute") row.prop(animall_properties, "key_vertex_group") + row = col.row() + row.prop(animall_properties, "key_material_index") # Vertex group update operator if (context.active_object is not None @@ -179,6 +185,8 @@ class VIEW3D_PT_animall(Panel): row = col.row(align = True) row.prop(animall_properties, "key_radius") row.prop(animall_properties, "key_tilt") + row = col.row() + row.prop(animall_properties, "key_material_index") elif obj.type == 'SURFACE': row.prop(animall_properties, "key_point_location") @@ -186,6 +194,8 @@ class VIEW3D_PT_animall(Panel): row = col.row(align = True) row.prop(animall_properties, "key_radius") row.prop(animall_properties, "key_tilt") + row = col.row() + row.prop(animall_properties, "key_material_index") col.separator() col = layout.column(align=True) @@ -258,6 +268,13 @@ class ANIM_OT_insert_keyframe_animall(Operator): insert_key(point, 'co_deform', group="Point %s" % p_i) else: + if animall_properties.key_material_index: + for s_i, spline in enumerate(data.splines): + if (not animall_properties.key_selected + or any(point.select for point in spline.points) + or any(point.select_control_point for point in spline.bezier_points)): + insert_key(spline, 'material_index', group="Spline %s" % s_i) + for s_i, spline in enumerate(data.splines): if spline.type == 'BEZIER': for v_i, CV in enumerate(spline.bezier_points): @@ -335,6 +352,10 @@ class ANIM_OT_insert_keyframe_animall(Operator): for uv_i, uv in enumerate(data.uv_layers.active.data): if not animall_properties.key_selected or uv.select: insert_key(uv, 'uv', group="UV layer %s" % uv_i) + if animall_properties.key_material_index: + for p_i, polygon in enumerate(data.polygons): + if not animall_properties.key_selected or polygon.select: + insert_key(polygon, 'material_index', group="Face %s" % p_i) if animall_properties.key_attribute: if data.attributes.active is not None: -- cgit v1.2.3 From a784b1fbd00ee96a6bf0669f6b611caf382a7934 Mon Sep 17 00:00:00 2001 From: Damien Picard Date: Sat, 18 Jun 2022 08:24:47 +0200 Subject: Animall: make UI more aligned to Blender's conventions --- animation_animall.py | 163 ++++++++++++++++++++++++--------------------------- 1 file changed, 76 insertions(+), 87 deletions(-) diff --git a/animation_animall.py b/animation_animall.py index e7a12a2a..f13c8b12 100644 --- a/animation_animall.py +++ b/animation_animall.py @@ -3,7 +3,7 @@ bl_info = { "name": "AnimAll", "author": "Daniel Salazar (ZanQdo), Damien Picard (pioverfour)", - "version": (0, 9, 5), + "version": (0, 9, 6), "blender": (3, 3, 0), "location": "3D View > Toolbox > Animation tab > AnimAll", "description": "Allows animation of mesh, lattice, curve and surface data", @@ -141,95 +141,83 @@ class VIEW3D_PT_animall(Panel): layout = self.layout - layout.label (text = 'Key:') + layout.use_property_split = True + layout.use_property_decorate = False - col = layout.column(align=True) - row = col.row(align = True) + split = layout.split(factor=0.4, align=True) + split.label(text='') + split.label(text=' Key:') if obj.type == 'LATTICE': - row.prop(animall_properties, "key_point_location") - row.prop(animall_properties, "key_shape_key") + col = layout.column(align=True) + col.prop(animall_properties, "key_point_location") + col.prop(animall_properties, "key_shape_key") elif obj.type == 'MESH': - row.prop(animall_properties, "key_point_location") - row.prop(animall_properties, "key_shape_key") - row = col.row() - row.prop(animall_properties, "key_vertex_bevel") - row.prop(animall_properties, "key_edge_bevel") - row = col.row() - row.prop(animall_properties, "key_edge_crease") - row.prop(animall_properties, "key_uvs") - row = col.row(align = True) - row.prop(animall_properties, "key_attribute") - row.prop(animall_properties, "key_vertex_group") - row = col.row() - row.prop(animall_properties, "key_material_index") - - # Vertex group update operator - if (context.active_object is not None - and context.active_object.type == 'MESH' - and context.active_object.data.animation_data is not None - and context.active_object.data.animation_data.action is not None): - for fcurve in context.active_object.data.animation_data.action.fcurves: - if fcurve.data_path.startswith("vertex_colors"): - layout.separator() - row = layout.row() - row.label(text="Object includes old-style vertex colors. Consider updating them.", icon="ERROR") - row = layout.row() - row.operator("anim.update_vertex_color_animation_animall", icon="FILE_REFRESH") - break - - elif obj.type == 'CURVE': - row.prop(animall_properties, "key_point_location") - row.prop(animall_properties, "key_shape_key") - row = col.row(align = True) - row.prop(animall_properties, "key_radius") - row.prop(animall_properties, "key_tilt") - row = col.row() - row.prop(animall_properties, "key_material_index") - - elif obj.type == 'SURFACE': - row.prop(animall_properties, "key_point_location") - row.prop(animall_properties, "key_shape_key") - row = col.row(align = True) - row.prop(animall_properties, "key_radius") - row.prop(animall_properties, "key_tilt") - row = col.row() - row.prop(animall_properties, "key_material_index") - - col.separator() - col = layout.column(align=True) - row = col.row() - row.prop(animall_properties, "key_selected") - - row = layout.row(align=True) - row.operator("anim.insert_keyframe_animall", icon="KEY_HLT") - row.operator("anim.delete_keyframe_animall", icon="KEY_DEHLT") - row = layout.row() - row.operator("anim.clear_animation_animall", icon="CANCEL") + col = layout.column(heading="Points", align=True) + col.prop(animall_properties, "key_point_location") + col.prop(animall_properties, "key_vertex_bevel", text="Bevel") + col.prop(animall_properties, "key_vertex_group", text="Vertex Groups") + + col = layout.column(heading="Edges", align=True) + col.prop(animall_properties, "key_edge_bevel", text="Bevel") + col.prop(animall_properties, "key_edge_crease", text="Crease") + + col = layout.column(heading="Faces", align=True) + col.prop(animall_properties, "key_material_index") + + col = layout.column(heading="Others", align=True) + col.prop(animall_properties, "key_attribute") + col.prop(animall_properties, "key_uvs") + col.prop(animall_properties, "key_shape_key") + + # Vertex group update operator + if (obj.data.animation_data is not None + and obj.data.animation_data.action is not None): + for fcurve in context.active_object.data.animation_data.action.fcurves: + if fcurve.data_path.startswith("vertex_colors"): + col = layout.column(align=True) + col.label(text="Object includes old-style vertex colors. Consider updating them.", icon="ERROR") + col.operator("anim.update_vertex_color_animation_animall", icon="FILE_REFRESH") + break + + elif obj.type in {'CURVE', 'SURFACE'}: + col = layout.column(align=True) + col.prop(animall_properties, "key_point_location") + col.prop(animall_properties, "key_radius") + col.prop(animall_properties, "key_tilt") + col.prop(animall_properties, "key_material_index") + col.prop(animall_properties, "key_shape_key") if animall_properties.key_shape_key: shape_key = obj.active_shape_key shape_key_index = obj.active_shape_key_index - split = layout.split() - row = split.row() - if shape_key_index > 0: - row.label(text=shape_key.name, icon="SHAPEKEY_DATA") - row.prop(shape_key, "value", text="") + col = layout.column(align=True) + row = col.row(align=True) + row.prop(shape_key, "value", text=shape_key.name, icon="SHAPEKEY_DATA") row.prop(obj, "show_only_shape_key", text="") if shape_key.value < 1: - row = layout.row() - row.label(text='Maybe set "%s" to 1.0?' % shape_key.name, icon="INFO") - elif shape_key: - row.label(text="Cannot key on Basis Shape", icon="ERROR") + col.label(text='Maybe set "%s" to 1.0?' % shape_key.name, icon="INFO") + elif shape_key is not None: + col = layout.column(align=True) + col.label(text="Cannot key on Basis Shape", icon="ERROR") else: - row.label(text="No active Shape Key", icon="ERROR") + col = layout.column(align=True) + col.label(text="No active Shape Key", icon="ERROR") - if animall_properties.key_point_location and animall_properties.key_shape_key: - row = layout.row() - row.label(text='"Points" and "Shape" are redundant?', icon="INFO") + if animall_properties.key_point_location: + col.label(text='"Location" and "Shape Key" are redundant?', icon="INFO") + + col = layout.column(align=True) + col.prop(animall_properties, "key_selected") + + row = layout.row(align=True) + row.operator("anim.insert_keyframe_animall", icon="KEY_HLT") + row.operator("anim.delete_keyframe_animall", icon="KEY_DEHLT") + row = layout.row() + row.operator("anim.clear_animation_animall", icon="CANCEL") class ANIM_OT_insert_keyframe_animall(Operator): @@ -340,18 +328,6 @@ class ANIM_OT_insert_keyframe_animall(Operator): if not animall_properties.key_selected or edge.select: insert_key(edge, 'crease', group="Edge %s" % e_i) - if animall_properties.key_shape_key: - if obj.active_shape_key_index > 0: - sk_name = obj.active_shape_key.name - for v_i, vert in enumerate(obj.active_shape_key.data): - if not animall_properties.key_selected or data.vertices[v_i].select: - insert_key(vert, 'co', group="%s Vertex %s" % (sk_name, v_i)) - - if animall_properties.key_uvs: - if data.uv_layers.active is not None: - for uv_i, uv in enumerate(data.uv_layers.active.data): - if not animall_properties.key_selected or uv.select: - insert_key(uv, 'uv', group="UV layer %s" % uv_i) if animall_properties.key_material_index: for p_i, polygon in enumerate(data.polygons): if not animall_properties.key_selected or polygon.select: @@ -387,6 +363,19 @@ class ANIM_OT_insert_keyframe_animall(Operator): insert_key(data, f'attributes["{attribute.name}"].data[{e_i}].{attribute_key}', group=group % e_i) + if animall_properties.key_uvs: + if data.uv_layers.active is not None: + for uv_i, uv in enumerate(data.uv_layers.active.data): + if not animall_properties.key_selected or uv.select: + insert_key(uv, 'uv', group="UV layer %s" % uv_i) + + if animall_properties.key_shape_key: + if obj.active_shape_key_index > 0: + sk_name = obj.active_shape_key.name + for v_i, vert in enumerate(obj.active_shape_key.data): + if not animall_properties.key_selected or data.vertices[v_i].select: + insert_key(vert, 'co', group="%s Vertex %s" % (sk_name, v_i)) + elif obj.type in {'CURVE', 'SURFACE'}: # Shape key keys have to be inserted in object mode for curves... if animall_properties.key_shape_key: -- cgit v1.2.3 From 5902265f1e5aaeca3781ea15f994bdf0b3cb054d Mon Sep 17 00:00:00 2001 From: Damien Picard Date: Sat, 18 Jun 2022 16:40:28 +0200 Subject: AnimAll: Rename UVMap and Vertex Group settings again They were lost in a rebase, somewhere. --- animation_animall.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/animation_animall.py b/animation_animall.py index f13c8b12..79e953f9 100644 --- a/animation_animall.py +++ b/animation_animall.py @@ -74,7 +74,7 @@ class AnimallProperties(bpy.types.PropertyGroup): description="Insert keyframes on active attribute values", default=False) key_uvs: BoolProperty( - name="UVs", + name="UV Map", description="Insert keyframes on active UV coordinates", default=False) @@ -157,7 +157,7 @@ class VIEW3D_PT_animall(Panel): col = layout.column(heading="Points", align=True) col.prop(animall_properties, "key_point_location") col.prop(animall_properties, "key_vertex_bevel", text="Bevel") - col.prop(animall_properties, "key_vertex_group", text="Vertex Groups") + col.prop(animall_properties, "key_vertex_group") col = layout.column(heading="Edges", align=True) col.prop(animall_properties, "key_edge_bevel", text="Bevel") @@ -449,7 +449,7 @@ class ANIM_OT_delete_keyframe_animall(Operator): for group in vert.groups: delete_key(group, 'weight') - # if animall_properties.key_vcrease: + # if animall_properties.key_vertex_crease: # for vert in data.vertices: # if not animall_properties.key_selected or vert.select: # delete_key(vert, 'crease') @@ -508,7 +508,7 @@ class ANIM_OT_delete_keyframe_animall(Operator): delete_key(point, 'co_deform') elif obj.type in {'CURVE', 'SURFACE'}: - # run this outside the splines loop (only once) + # Run this outside the splines loop (only once) if animall_properties.key_shape_key: if obj.active_shape_key_index > 0: for CV in obj.active_shape_key.data: -- cgit v1.2.3 From b228db8ba00baada4959f9ae552c61979942805f Mon Sep 17 00:00:00 2001 From: Daniel Salazar Date: Sat, 18 Jun 2022 17:21:29 -0600 Subject: AnimAll: More UI tweaks --- animation_animall.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/animation_animall.py b/animation_animall.py index 79e953f9..920f3177 100644 --- a/animation_animall.py +++ b/animation_animall.py @@ -129,25 +129,29 @@ class VIEW3D_PT_animall(Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = "Animate" - bl_label = 'AnimAll' + bl_label = '' @classmethod def poll(self, context): return context.active_object and context.active_object.type in {'MESH', 'LATTICE', 'CURVE', 'SURFACE'} + def draw_header(self, context): + + layout = self.layout + row = layout.row() + row.label (text = 'AnimAll', icon = 'ARMATURE_DATA') + def draw(self, context): obj = context.active_object animall_properties = context.scene.animall_properties layout = self.layout + layout.label(text='Key:') + layout.use_property_split = True layout.use_property_decorate = False - split = layout.split(factor=0.4, align=True) - split.label(text='') - split.label(text=' Key:') - if obj.type == 'LATTICE': col = layout.column(align=True) col.prop(animall_properties, "key_point_location") @@ -210,8 +214,10 @@ class VIEW3D_PT_animall(Panel): if animall_properties.key_point_location: col.label(text='"Location" and "Shape Key" are redundant?', icon="INFO") - col = layout.column(align=True) - col.prop(animall_properties, "key_selected") + layout.use_property_split = False + layout.separator() + row = layout.row() + row.prop(animall_properties, "key_selected") row = layout.row(align=True) row.operator("anim.insert_keyframe_animall", icon="KEY_HLT") -- cgit v1.2.3 From 038db9d607b352f6a7391c5a6b3ab906692c9421 Mon Sep 17 00:00:00 2001 From: Damien Picard Date: Sun, 19 Jun 2022 11:26:40 +0200 Subject: AnimAll: add column heading for missing object types Only mesh add headings for attributes to key; now curves, surfaces and lattices do, too. --- animation_animall.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/animation_animall.py b/animation_animall.py index 920f3177..944f4a2f 100644 --- a/animation_animall.py +++ b/animation_animall.py @@ -153,8 +153,10 @@ class VIEW3D_PT_animall(Panel): layout.use_property_decorate = False if obj.type == 'LATTICE': - col = layout.column(align=True) + col = layout.column(heading="Points", align=True) col.prop(animall_properties, "key_point_location") + + col = layout.column(heading="Others", align=True) col.prop(animall_properties, "key_shape_key") elif obj.type == 'MESH': @@ -187,10 +189,15 @@ class VIEW3D_PT_animall(Panel): elif obj.type in {'CURVE', 'SURFACE'}: col = layout.column(align=True) + col = layout.column(heading="Points", align=True) col.prop(animall_properties, "key_point_location") col.prop(animall_properties, "key_radius") col.prop(animall_properties, "key_tilt") + + col = layout.column(heading="Splines", align=True) col.prop(animall_properties, "key_material_index") + + col = layout.column(heading="Others", align=True) col.prop(animall_properties, "key_shape_key") if animall_properties.key_shape_key: -- cgit v1.2.3 From a2650c0158eeb6488e21204887c98d376d881f72 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sat, 25 Jun 2022 06:40:58 +0200 Subject: glTF importer: Add missing image name --- io_scene_gltf2/__init__.py | 2 +- io_scene_gltf2/blender/imp/gltf2_blender_image.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index ce48f293..e9e6660e 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -4,7 +4,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (3, 3, 5), + "version": (3, 3, 6), 'blender': (3, 3, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_image.py b/io_scene_gltf2/blender/imp/gltf2_blender_image.py index 4f9af799..abce0354 100755 --- a/io_scene_gltf2/blender/imp/gltf2_blender_image.py +++ b/io_scene_gltf2/blender/imp/gltf2_blender_image.py @@ -83,7 +83,7 @@ def create_from_data(gltf, img_idx): img_data = BinaryData.get_image_data(gltf, img_idx) if img_data is None: return - img_name = 'Image_%d' % img_idx + img_name = gltf.data.images[img_idx].name or 'Image_%d' % img_idx # Create image, width and height are dummy values blender_image = bpy.data.images.new(img_name, 8, 8) -- cgit v1.2.3 From 501c97628f1597bab7571951fc090771b88c824f Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sat, 25 Jun 2022 06:45:38 +0200 Subject: glTF exporter: Allow custom name for merged animation --- io_scene_gltf2/__init__.py | 13 ++++++++++++- io_scene_gltf2/blender/exp/gltf2_blender_gather.py | 7 +++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index e9e6660e..616ba2f2 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -4,7 +4,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (3, 3, 6), + "version": (3, 3, 7), 'blender': (3, 3, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', @@ -372,6 +372,14 @@ class ExportGLTF2_Base: default=True ) + export_nla_strips_merged_animation_name: StringProperty( + name='Merged Animation Name', + description=( + "Name of single glTF animation to be exported" + ), + default='Animation' + ) + export_def_bones: BoolProperty( name='Export Deformation Bones Only', description='Export Deformation bones only (and needed bones for hierarchy)', @@ -568,6 +576,7 @@ class ExportGLTF2_Base: else: export_settings['gltf_def_bones'] = False export_settings['gltf_nla_strips'] = self.export_nla_strips + export_settings['gltf_nla_strips_merged_animation_name'] = self.export_nla_strips_merged_animation_name export_settings['gltf_optimize_animation'] = self.optimize_animation_size else: export_settings['gltf_frame_range'] = False @@ -863,6 +872,8 @@ class GLTF_PT_export_animation_export(bpy.types.Panel): layout.prop(operator, 'export_frame_step') layout.prop(operator, 'export_force_sampling') layout.prop(operator, 'export_nla_strips') + if operator.export_nla_strips is False: + layout.prop(operator, 'export_nla_strips_merged_animation_name') layout.prop(operator, 'optimize_animation_size') row = layout.row() diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather.py index 6153bc33..4eb8baed 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather.py @@ -94,9 +94,12 @@ def __gather_animations(blender_scene, export_settings): if export_settings['gltf_nla_strips'] is False: # Fake an animation with all animations of the scene merged_tracks = {} - merged_tracks['Animation'] = [] + merged_tracks_name = 'Animation' + if(len(export_settings['gltf_nla_strips_merged_animation_name']) > 0): + merged_tracks_name = export_settings['gltf_nla_strips_merged_animation_name'] + merged_tracks[merged_tracks_name] = [] for idx, animation in enumerate(animations): - merged_tracks['Animation'].append(idx) + merged_tracks[merged_tracks_name].append(idx) to_delete_idx = [] -- cgit v1.2.3 From 807a64cdfc50de1cfb263f2eb68680feddb66ec7 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sat, 25 Jun 2022 06:50:58 +0200 Subject: glTF exporter: Export DEF bones only, without animation, is now possible --- io_scene_gltf2/__init__.py | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 616ba2f2..1cd299a3 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -4,7 +4,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (3, 3, 7), + "version": (3, 3, 8), 'blender': (3, 3, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', @@ -382,7 +382,7 @@ class ExportGLTF2_Base: export_def_bones: BoolProperty( name='Export Deformation Bones Only', - description='Export Deformation bones only (and needed bones for hierarchy)', + description='Export Deformation bones only', default=False ) @@ -568,12 +568,11 @@ class ExportGLTF2_Base: export_settings['gltf_apply'] = self.export_apply export_settings['gltf_current_frame'] = self.export_current_frame export_settings['gltf_animations'] = self.export_animations + export_settings['gltf_def_bones'] = self.export_def_bones if self.export_animations: export_settings['gltf_frame_range'] = self.export_frame_range export_settings['gltf_force_sampling'] = self.export_force_sampling - if self.export_force_sampling: - export_settings['gltf_def_bones'] = self.export_def_bones - else: + if not self.export_force_sampling: export_settings['gltf_def_bones'] = False export_settings['gltf_nla_strips'] = self.export_nla_strips export_settings['gltf_nla_strips_merged_animation_name'] = self.export_nla_strips_merged_animation_name @@ -582,13 +581,13 @@ class ExportGLTF2_Base: export_settings['gltf_frame_range'] = False export_settings['gltf_move_keyframes'] = False export_settings['gltf_force_sampling'] = False - export_settings['gltf_def_bones'] = False export_settings['gltf_optimize_animation'] = False export_settings['gltf_skins'] = self.export_skins if self.export_skins: export_settings['gltf_all_vertex_influences'] = self.export_all_influences else: export_settings['gltf_all_vertex_influences'] = False + export_settings['gltf_def_bones'] = False export_settings['gltf_frame_step'] = self.export_frame_step export_settings['gltf_morph'] = self.export_morph if self.export_morph: @@ -876,12 +875,6 @@ class GLTF_PT_export_animation_export(bpy.types.Panel): layout.prop(operator, 'export_nla_strips_merged_animation_name') layout.prop(operator, 'optimize_animation_size') - row = layout.row() - row.active = operator.export_force_sampling - row.prop(operator, 'export_def_bones') - if operator.export_force_sampling is False and operator.export_def_bones is True: - layout.label(text="Export only deformation bones is not possible when not sampling animation") - class GLTF_PT_export_animation_shapekeys(bpy.types.Panel): bl_space_type = 'FILE_BROWSER' @@ -948,6 +941,12 @@ class GLTF_PT_export_animation_skinning(bpy.types.Panel): layout.active = operator.export_skins layout.prop(operator, 'export_all_influences') + row = layout.row() + row.active = operator.export_force_sampling + row.prop(operator, 'export_def_bones') + if operator.export_force_sampling is False and operator.export_def_bones is True: + layout.label(text="Export only deformation bones is not possible when not sampling animation") + class GLTF_PT_export_user_extensions(bpy.types.Panel): bl_space_type = 'FILE_BROWSER' bl_region_type = 'TOOL_PROPS' -- cgit v1.2.3 From 0e480c8fc8f5e3948fe7578e1d0081624587c7c0 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Wed, 29 Jun 2022 08:02:43 +0200 Subject: glTF exporter: Make sure to not modify virtual tree --- io_scene_gltf2/__init__.py | 2 +- io_scene_gltf2/blender/exp/gltf2_blender_gather_drivers.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 1cd299a3..8bea4aae 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -4,7 +4,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (3, 3, 8), + "version": (3, 3, 9), 'blender': (3, 3, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_drivers.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_drivers.py index b0e538c8..4b24599a 100644 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_drivers.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_drivers.py @@ -13,7 +13,7 @@ def get_sk_drivers(blender_armature_uuid, export_settings): drivers = [] # Take into account skinned mesh, and mesh parented to a bone of the armature - children_list = export_settings['vtree'].nodes[blender_armature_uuid].children + children_list = export_settings['vtree'].nodes[blender_armature_uuid].children.copy() for bone in export_settings['vtree'].get_all_bones(blender_armature_uuid): children_list.extend(export_settings['vtree'].nodes[bone].children) -- cgit v1.2.3 From 11bc851e16ae792fa7738827de114f5ace628260 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Wed, 29 Jun 2022 18:02:03 +0200 Subject: glTF: Draco: Directly look at path specified by environment variable --- io_scene_gltf2/__init__.py | 2 +- .../io/com/gltf2_io_draco_compression_extension.py | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 8bea4aae..7c6b128e 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -4,7 +4,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (3, 3, 9), + "version": (3, 3, 10), 'blender': (3, 3, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/io/com/gltf2_io_draco_compression_extension.py b/io_scene_gltf2/io/com/gltf2_io_draco_compression_extension.py index 28f06e51..b9f9ccec 100644 --- a/io_scene_gltf2/io/com/gltf2_io_draco_compression_extension.py +++ b/io_scene_gltf2/io/com/gltf2_io_draco_compression_extension.py @@ -27,7 +27,7 @@ def dll_path() -> Path: 'darwin': blender_root.parent / 'Resources' / python_lib / python_version / 'site-packages' }.get(sys.platform) else: - path = Path(path) + return Path(path) library_name = { 'win32': '{}.dll'.format(lib_name), @@ -46,12 +46,11 @@ def dll_exists(quiet=False) -> bool: Checks whether the DLL path exists. :return: True if the DLL exists. """ - exists = dll_path().exists() + path = dll_path() + exists = path.exists() and path.is_file() if quiet is False: - print("'{}' ".format(dll_path().absolute()) + ("exists, draco mesh compression is available" if exists else - "{} {} {}".format( - "does not exist, draco mesh compression not available,", - "please add it or create environment variable BLENDER_EXTERN_DRACO_LIBRARY_PATH", - "pointing to the folder" - ))) + if exists: + print_console('INFO', 'Draco mesh compression is available, use library at %s' % dll_path().absolute()) + else: + print_console('ERROR', 'Draco mesh compression is not available because library could not be found at %s' % dll_path().absolute()) return exists -- cgit v1.2.3 From 870f9775d7cd57b2d51c6c964842b96e5ba542c0 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 29 Jun 2022 20:06:29 +0300 Subject: Depsgraph Debug: add an operator to generate an SVG and open in browser. Rather than converting the dependency graph to a bitmap image, it is more advantageous to use the vector SVG format and open it in a web browser (any modern browser supports the format), because it allows text searches within the image. This patch adds a new operator and button that does just that. Differential Revision: https://developer.blender.org/D15325 --- depsgraph_debug.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/depsgraph_debug.py b/depsgraph_debug.py index 4405eb8e..7c8784a2 100644 --- a/depsgraph_debug.py +++ b/depsgraph_debug.py @@ -211,6 +211,35 @@ class SCENE_OT_depsgraph_stats_image(Operator, return True +class SCENE_OT_depsgraph_relations_svg(Operator, + SCENE_OT_depsgraph_image_common): + bl_idname = "scene.depsgraph_relations_svg" + bl_label = "Depsgraph as SVG in Browser" + bl_description = "Create an SVG image from the dependency graph and open it in the web browser" + + def performSave(self, context, depsgraph): + import os + import subprocess + import webbrowser + # Create temporary file. + dot_filepath = self._createTempFile(suffix=".dot") + # Save dependency graph to graphviz file. + depsgraph.debug_relations_graphviz(dot_filepath) + # Convert graphviz to SVG image. + svg_filepath = os.path.join(bpy.app.tempdir, "depsgraph.svg") + command = ("dot", "-Tsvg", dot_filepath, "-o", svg_filepath) + try: + subprocess.run(command) + webbrowser.open_new_tab("file://" + os.path.abspath(svg_filepath)) + except: + self.report({'ERROR'}, "Error invoking dot command") + return False + finally: + # Remove graphviz file. + os.remove(dot_filepath) + return True + + ############################################################################### # Interface. @@ -224,6 +253,7 @@ class SCENE_PT_depsgraph_common: row = col.row() row.operator("scene.depsgraph_relations_graphviz") row.operator("scene.depsgraph_relations_image") + col.operator("scene.depsgraph_relations_svg") # Everything related on evaluaiton statistics. col.label(text="Statistics:") row = col.row() @@ -264,6 +294,7 @@ class RENDERLAYER_PT_depsgraph(bpy.types.Panel, SCENE_PT_depsgraph_common): def register(): bpy.utils.register_class(SCENE_OT_depsgraph_relations_graphviz) bpy.utils.register_class(SCENE_OT_depsgraph_relations_image) + bpy.utils.register_class(SCENE_OT_depsgraph_relations_svg) bpy.utils.register_class(SCENE_OT_depsgraph_stats_gnuplot) bpy.utils.register_class(SCENE_OT_depsgraph_stats_image) bpy.utils.register_class(SCENE_PT_depsgraph) @@ -273,6 +304,7 @@ def register(): def unregister(): bpy.utils.unregister_class(SCENE_OT_depsgraph_relations_graphviz) bpy.utils.unregister_class(SCENE_OT_depsgraph_relations_image) + bpy.utils.unregister_class(SCENE_OT_depsgraph_relations_svg) bpy.utils.unregister_class(SCENE_OT_depsgraph_stats_gnuplot) bpy.utils.unregister_class(SCENE_OT_depsgraph_stats_image) bpy.utils.unregister_class(SCENE_PT_depsgraph) -- cgit v1.2.3 From 6d7056ab6e342fba9a62ecfa2efd132ffe2e403a Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 30 Jun 2022 17:20:39 +0200 Subject: Cleanup: fix various typos Contributed by luzpaz. Differential Revision: https://developer.blender.org/D15328 --- io_scene_gltf2/__init__.py | 2 +- io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_channels.py | 2 +- io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py | 2 +- io_scene_obj/import_obj.py | 4 ++-- io_scene_x3d/import_x3d.py | 4 ++-- magic_uv/utils/graph.py | 2 +- mesh_tissue/utils.py | 4 ++-- object_fracture_cell/__init__.py | 2 +- object_fracture_cell/fracture_cell_setup.py | 2 +- object_print3d_utils/export.py | 2 +- render_povray/model_meta_topology.py | 4 ++-- render_povray/model_poly_topology.py | 2 +- render_povray/render_gui.py | 2 +- space_view3d_math_vis/draw.py | 2 +- system_demo_mode/__init__.py | 2 +- system_demo_mode/config.py | 2 +- system_demo_mode/demo_mode.py | 2 +- 17 files changed, 21 insertions(+), 21 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 7c6b128e..5fb93dba 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -1175,7 +1175,7 @@ class GLTF_AddonPreferences(bpy.types.AddonPreferences): settings_node_ui : bpy.props.BoolProperty( default= False, - description="Displays glTF Settings node in Shader Editor (Menu Add > Ouput)" + description="Displays glTF Settings node in Shader Editor (Menu Add > Output)" ) diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_channels.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_channels.py index 98ae8b82..3e67f1f7 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_channels.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_channels.py @@ -37,7 +37,7 @@ def gather_animation_channels(obj_uuid: int, if blender_action.use_frame_range is True: bake_range_start = blender_action.frame_start bake_range_end = blender_action.frame_end - force_range = True # keyframe_points is read-only, we cant restrict here + force_range = True # keyframe_points is read-only, we can't restrict here else: groups = __get_channel_groups(blender_action, blender_object, export_settings) # Note: channels has some None items only for SK if some SK are not animated diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py index 20a919dc..a3a7a4c2 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py @@ -64,7 +64,7 @@ def gather_animations( obj_uuid: int, # No TRS animation are found for this object. # But we need to bake, in case we export selection # (Only when force sampling is ON) - # If force sampling is OFF, can lead to inconsistant export anyway + # If force sampling is OFF, can lead to inconsistent export anyway if export_settings['gltf_selected'] is True and blender_object.type != "ARMATURE" and export_settings['gltf_force_sampling'] is True: channels = __gather_channels_baked(obj_uuid, export_settings) if channels is not None: diff --git a/io_scene_obj/import_obj.py b/io_scene_obj/import_obj.py index d41e72f8..63c4d0e1 100644 --- a/io_scene_obj/import_obj.py +++ b/io_scene_obj/import_obj.py @@ -584,7 +584,7 @@ def create_mesh(new_objects, len_face_vert_loc_indices = len(face_vert_loc_indices) if len_face_vert_loc_indices == 1: - faces.pop(f_idx) # cant add single vert faces + faces.pop(f_idx) # can't add single vert faces # Face with a single item in face_vert_nor_indices is actually a polyline! elif face_is_edge(face): @@ -979,7 +979,7 @@ def load(context, # when there are faces that end with \ # it means they are multiline- - # since we use xreadline we cant skip to the next line + # since we use xreadline we can't skip to the next line # so we need to know whether context_multi_line = b'' diff --git a/io_scene_x3d/import_x3d.py b/io_scene_x3d/import_x3d.py index 6d13bb79..228e0507 100644 --- a/io_scene_x3d/import_x3d.py +++ b/io_scene_x3d/import_x3d.py @@ -1003,13 +1003,13 @@ class vrmlNode(object): print('\tWarning: Inline URL could not be found:', url) else: if url == self.getFilename(): - print('\tWarning: cant Inline yourself recursively:', url) + print('\tWarning: can\'t Inline yourself recursively:', url) else: try: data = gzipOpen(url) except: - print('\tWarning: cant open the file:', url) + print('\tWarning: can\'t open the file:', url) data = None if data: diff --git a/magic_uv/utils/graph.py b/magic_uv/utils/graph.py index bebabf63..277800bc 100644 --- a/magic_uv/utils/graph.py +++ b/magic_uv/utils/graph.py @@ -29,7 +29,7 @@ class Edge: raise RuntimeError("Loop edge in {} is not supported." .format(node.key)) if node not in (self.node_1, self.node_2): - raise RuntimeError("Node {} does not belog this edge." + raise RuntimeError("Node {} does not belong to this edge." .format(node.key)) if self.node_1 == node: return self.node_2 diff --git a/mesh_tissue/utils.py b/mesh_tissue/utils.py index b617ac93..b4331084 100644 --- a/mesh_tissue/utils.py +++ b/mesh_tissue/utils.py @@ -1251,7 +1251,7 @@ def get_weight(vertex_group, n_verts): :type vertex_group: :class:'bpy.types.VertexGroup' :arg n_verts: Number of Vertices (output list size). :type n_verts: int - :return: Readed weight values. + :return: Read weight values. :rtype: list """ weight = [0]*n_verts @@ -1267,7 +1267,7 @@ def get_weight_numpy(vertex_group, n_verts): :type vertex_group: :class:'bpy.types.VertexGroup' :arg n_verts: Number of Vertices (output list size). :type n_verts: int - :return: Readed weight values as numpy array. + :return: Read weight values as numpy array. :rtype: :class:'numpy.ndarray' """ weight = [0]*n_verts diff --git a/object_fracture_cell/__init__.py b/object_fracture_cell/__init__.py index e9f70a44..543f86f1 100644 --- a/object_fracture_cell/__init__.py +++ b/object_fracture_cell/__init__.py @@ -482,7 +482,7 @@ class FractureCell(Operator): rowsub.prop(self, "use_data_match") rowsub = col.row() - # on same row for even layout but infact are not all that related + # on same row for even layout but in fact are not all that related rowsub.prop(self, "material_index") rowsub.prop(self, "use_interior_vgroup") diff --git a/object_fracture_cell/fracture_cell_setup.py b/object_fracture_cell/fracture_cell_setup.py index 8171e44e..2e735495 100644 --- a/object_fracture_cell/fracture_cell_setup.py +++ b/object_fracture_cell/fracture_cell_setup.py @@ -145,7 +145,7 @@ def cell_fracture_objects( random.shuffle(points) points[source_limit:] = [] - # saddly we cant be sure there are no doubles + # sadly we can't be sure there are no doubles from mathutils import Vector to_tuple = Vector.to_tuple points = list({to_tuple(p, 4): p for p in points}.values()) diff --git a/object_print3d_utils/export.py b/object_print3d_utils/export.py index aec19732..d17cd45f 100644 --- a/object_print3d_utils/export.py +++ b/object_print3d_utils/export.py @@ -79,7 +79,7 @@ def write_mesh(context, report_cb): # first ensure the path is created if export_path: # this can fail with strange errors, - # if the dir cant be made then we get an error later. + # if the dir can't be made then we get an error later. try: os.makedirs(export_path, exist_ok=True) except: diff --git a/render_povray/model_meta_topology.py b/render_povray/model_meta_topology.py index 33a335fa..47e874c5 100644 --- a/render_povray/model_meta_topology.py +++ b/render_povray/model_meta_topology.py @@ -180,7 +180,7 @@ def export_meta(file, metas, tab_write, DEF_MAT_NAME): try: one_material = elems[1].data.materials[ 0 - ] # lame! - blender cant do enything else. + ] # lame! - blender can't do enything else. except BaseException as e: print(e.__doc__) print('An exception occurred: {}'.format(e)) @@ -236,7 +236,7 @@ def export_meta(file, metas, tab_write, DEF_MAT_NAME): importance = ob.pov.importance_value try: - material = meta.materials[0] # lame! - blender cant do enything else. + material = meta.materials[0] # lame! - blender can't do anything else. except: material = None diff --git a/render_povray/model_poly_topology.py b/render_povray/model_poly_topology.py index c8a990ea..b4d200ec 100644 --- a/render_povray/model_poly_topology.py +++ b/render_povray/model_poly_topology.py @@ -52,7 +52,7 @@ def export_mesh(file, except BaseException as e: print(e.__doc__) print("An exception occurred: {}".format(e)) - # also happens when curves cant be made into meshes because of no-data + # also happens when curves can't be made into meshes because of no-data return False # To continue object loop if me: me.calc_loop_triangles() diff --git a/render_povray/render_gui.py b/render_povray/render_gui.py index 7baa7aff..e00e3705 100755 --- a/render_povray/render_gui.py +++ b/render_povray/render_gui.py @@ -242,7 +242,7 @@ class RENDER_PT_POV_hues(RenderButtonsPanel, Panel): class RENDER_PT_POV_pattern_rules(RenderButtonsPanel, Panel): - """Use this class to change pov sets of texture generating algorythms.""" + """Use this class to change pov sets of texture generating algorithms.""" bl_label = "Pattern Rules" bl_parent_id = "RENDER_PT_POV_render_settings" diff --git a/space_view3d_math_vis/draw.py b/space_view3d_math_vis/draw.py index 28722a99..de7ad946 100644 --- a/space_view3d_math_vis/draw.py +++ b/space_view3d_math_vis/draw.py @@ -29,7 +29,7 @@ COLOR_BOUNDING_BOX_ACTIVE = (1.0, 0.5, 0.0, 1.0) def tag_redraw_areas(): context = bpy.context - # Py cant access notifers + # Py can't access notifers for window in context.window_manager.windows: for area in window.screen.areas: if area.type in ['VIEW_3D', 'PROPERTIES']: diff --git a/system_demo_mode/__init__.py b/system_demo_mode/__init__.py index 9af8b217..71870532 100644 --- a/system_demo_mode/__init__.py +++ b/system_demo_mode/__init__.py @@ -190,7 +190,7 @@ class DemoModeRun(bpy.types.Operator): if extern_demo_mode_run(): return {'FINISHED'} else: - self.report({'ERROR'}, "Cant load demo.py config, run: File -> Demo Mode (Setup)") + self.report({'ERROR'}, "Can't load demo.py config, run: File -> Demo Mode (Setup)") return {'CANCELLED'} diff --git a/system_demo_mode/config.py b/system_demo_mode/config.py index 3c0550ee..5d939959 100644 --- a/system_demo_mode/config.py +++ b/system_demo_mode/config.py @@ -40,7 +40,7 @@ def as_string(dirpath, random_order, exit, **kwargs): "# generated file\n", "\n", "# edit the search path so other systems may find the files below\n", - "# based on name only if the absolute paths cant be found\n", + "# based on name only if the absolute paths cannot be found\n", "# Use '//' for current blend file path.\n", "\n", "search_path = %r\n" % dirpath, diff --git a/system_demo_mode/demo_mode.py b/system_demo_mode/demo_mode.py index 4def3031..f412869b 100644 --- a/system_demo_mode/demo_mode.py +++ b/system_demo_mode/demo_mode.py @@ -523,7 +523,7 @@ def load_config(cfg_name=DEMO_CFG): if not os.path.exists(filepath_test): filepath_test = lookup_file(filepath_test) # attempt to get from searchpath if not os.path.exists(filepath_test): - print("Cant find %r or %r, skipping!") + print("Can't find %r or %r, skipping!") continue filecfg["file"] = os.path.normpath(filepath_test) -- cgit v1.2.3 From 849e7196eb4ee7bc5ca8a5644da49ffbd3ff3c97 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Fri, 1 Jul 2022 06:30:32 +0200 Subject: glTF exporter: Fix T99306 : Fix camera & light export when Yup is off --- io_scene_gltf2/__init__.py | 2 +- io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 5fb93dba..39763f8d 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -4,7 +4,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (3, 3, 10), + "version": (3, 3, 11), 'blender': (3, 3, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py index ba63e049..0377a6b7 100644 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py @@ -163,10 +163,16 @@ class VExportTree: # So real world matrix is collection world_matrix @ "world_matrix" of object node.matrix_world = parent_coll_matrix_world @ blender_object.matrix_world.copy() if node.blender_type == VExportNode.CAMERA and self.export_settings[gltf2_blender_export_keys.CAMERAS]: - correction = Quaternion((2**0.5/2, -2**0.5/2, 0.0, 0.0)) + if self.export_settings[gltf2_blender_export_keys.YUP]: + correction = Quaternion((2**0.5/2, -2**0.5/2, 0.0, 0.0)) + else: + correction = Matrix.Identity(4).to_quaternion() node.matrix_world @= correction.to_matrix().to_4x4() elif node.blender_type == VExportNode.LIGHT and self.export_settings[gltf2_blender_export_keys.LIGHTS]: - correction = Quaternion((2**0.5/2, -2**0.5/2, 0.0, 0.0)) + if self.export_settings[gltf2_blender_export_keys.YUP]: + correction = Quaternion((2**0.5/2, -2**0.5/2, 0.0, 0.0)) + else: + correction = Matrix.Identity(4).to_quaternion() node.matrix_world @= correction.to_matrix().to_4x4() elif node.blender_type == VExportNode.BONE: if self.export_settings['gltf_current_frame'] is True: -- cgit v1.2.3 From 403b95ef6ff38918de966ed2a5843cfa3274a58b Mon Sep 17 00:00:00 2001 From: Aras Pranckevicius Date: Mon, 4 Jul 2022 19:09:11 +0300 Subject: OBJ: remove "experimental" from C++ based importer/exporter, mark Python legacy By now I'm not aware of any serious regressions or missing functionality in the C++ based OBJ importer/exporter. They have more features (vertex colors support), and are way faster than the Python based importer/exporter. Reviewed By: Thomas Dinges, Howard Trickey Differential Revision: https://developer.blender.org/D15360 --- io_scene_obj/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/io_scene_obj/__init__.py b/io_scene_obj/__init__.py index 0ec62410..519b0c85 100644 --- a/io_scene_obj/__init__.py +++ b/io_scene_obj/__init__.py @@ -459,11 +459,11 @@ class OBJ_PT_export_geometry(bpy.types.Panel): def menu_func_import(self, context): - self.layout.operator(ImportOBJ.bl_idname, text="Wavefront (.obj)") + self.layout.operator(ImportOBJ.bl_idname, text="Wavefront (.obj) (legacy)") def menu_func_export(self, context): - self.layout.operator(ExportOBJ.bl_idname, text="Wavefront (.obj)") + self.layout.operator(ExportOBJ.bl_idname, text="Wavefront (.obj) (legacy)") classes = ( -- cgit v1.2.3 From aa5b859302f233bd99469188ad57e61c62977678 Mon Sep 17 00:00:00 2001 From: Aras Pranckevicius Date: Tue, 5 Jul 2022 11:30:50 +0300 Subject: OBJ: mark Python IO legacy in the name too --- io_scene_obj/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_scene_obj/__init__.py b/io_scene_obj/__init__.py index 519b0c85..d49f8367 100644 --- a/io_scene_obj/__init__.py +++ b/io_scene_obj/__init__.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later bl_info = { - "name": "Wavefront OBJ format", + "name": "Wavefront OBJ format (legacy)", "author": "Campbell Barton, Bastien Montagne", "version": (3, 9, 0), "blender": (3, 0, 0), -- cgit v1.2.3 From b13fa85ef34979ccd57ad8bb3e6212feb92bd9ff Mon Sep 17 00:00:00 2001 From: Pratik Borhade Date: Tue, 5 Jul 2022 11:13:21 +0200 Subject: Fix T98902: Btracer Particle Trace is broken Problem has been introduced after python 3.10 upgrade This patch explicitly casts input parameters to int Maniphest Tasks: T98902 Differential Revision: https://developer.blender.org/D15225 --- btrace/__init__.py | 2 +- btrace/bTrace.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/btrace/__init__.py b/btrace/__init__.py index fd4bf57c..527ceb3f 100644 --- a/btrace/__init__.py +++ b/btrace/__init__.py @@ -4,7 +4,7 @@ bl_info = { "name": "BTracer", "author": "liero, crazycourier, Atom, Meta-Androcto, MacKracken", - "version": (1, 2, 3), + "version": (1, 2, 4), "blender": (2, 80, 0), "location": "View3D > Sidebar > Create Tab", "description": "Tools for converting/animating objects/particles into curves", diff --git a/btrace/bTrace.py b/btrace/bTrace.py index a17c3dd7..2ec92d72 100644 --- a/btrace/bTrace.py +++ b/btrace/bTrace.py @@ -303,9 +303,9 @@ class OBJECT_OT_particletrace(Operator): spline = tracer[0].splines.new('BEZIER') # add point to spline based on step size - spline.bezier_points.add((x.lifetime - 1) // particle_step) + spline.bezier_points.add(int((x.lifetime - 1) // particle_step)) for t in list(range(int(x.lifetime))): - bpy.context.scene.frame_set(t + x.birth_time) + bpy.context.scene.frame_set(int(t + x.birth_time)) if not t % particle_step: p = spline.bezier_points[t // particle_step] -- cgit v1.2.3 From 0d1a3cc2430efe46cd5f4b224a29f17133947283 Mon Sep 17 00:00:00 2001 From: Pratik Borhade Date: Tue, 5 Jul 2022 11:20:26 +0200 Subject: Fix T98658: Bsurfaces error when cyclic cross and follow is checked in redo panel explicitly cast segment value to int for avoiding TypeError This problem has begun to happen after python 3.10 upgrade Maniphest Tasks: T98658 Differential Revision: https://developer.blender.org/D15224 --- mesh_bsurfaces.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesh_bsurfaces.py b/mesh_bsurfaces.py index 58ddd7aa..c980ed04 100644 --- a/mesh_bsurfaces.py +++ b/mesh_bsurfaces.py @@ -4,7 +4,7 @@ bl_info = { "name": "Bsurfaces GPL Edition", "author": "Eclectiel, Vladimir Spivak (cwolf3d)", - "version": (1, 8, 0), + "version": (1, 8, 1), "blender": (2, 80, 0), "location": "View3D EditMode > Sidebar > Edit Tab", "description": "Modeling and retopology tool", @@ -2517,7 +2517,7 @@ class MESH_OT_SURFSK_add_surface(Operator): self.average_gp_segment_length ) for t in range(2): - bpy.ops.curve.subdivide('INVOKE_REGION_WIN', number_cuts=segments) + bpy.ops.curve.subdivide('INVOKE_REGION_WIN', number_cuts=int(segments)) # Delete the other vertices and make it non-cyclic to # keep only the needed verts of the "closing segment" -- cgit v1.2.3 From b2d470058ebffa59c5fdd87ff4f129bd3f17120c Mon Sep 17 00:00:00 2001 From: Wannes Malfait Date: Tue, 5 Jul 2022 16:20:31 +0200 Subject: Fix T94290: node wrangler misconnects some nodes Differential Revision: https://developer.blender.org/D15373 --- node_wrangler.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/node_wrangler.py b/node_wrangler.py index 1a815dc6..206399cb 100644 --- a/node_wrangler.py +++ b/node_wrangler.py @@ -4417,12 +4417,10 @@ class NWConnectionListOutputs(Menu, NWBase): nodes, links = get_nodes_links(context) n1 = nodes[context.scene.NWLazySource] - index=0 - for o in n1.outputs: + for index, output in enumerate(n1.outputs): # Only show sockets that are exposed. - if o.enabled: - layout.operator(NWCallInputsMenu.bl_idname, text=o.name, icon="RADIOBUT_OFF").from_socket=index - index+=1 + if output.enabled: + layout.operator(NWCallInputsMenu.bl_idname, text=output.name, icon="RADIOBUT_OFF").from_socket=index class NWConnectionListInputs(Menu, NWBase): @@ -4435,17 +4433,15 @@ class NWConnectionListInputs(Menu, NWBase): n2 = nodes[context.scene.NWLazyTarget] - index = 0 - for i in n2.inputs: + for index, input in enumerate(n2.inputs): # Only show sockets that are exposed. # This prevents, for example, the scale value socket # of the vector math node being added to the list when # the mode is not 'SCALE'. - if i.enabled: - op = layout.operator(NWMakeLink.bl_idname, text=i.name, icon="FORWARD") + if input.enabled: + op = layout.operator(NWMakeLink.bl_idname, text=input.name, icon="FORWARD") op.from_socket = context.scene.NWSourceSocket op.to_socket = index - index+=1 class NWMergeMathMenu(Menu, NWBase): -- cgit v1.2.3 From e69954a9bdf901be01bf09c8aa5033b0a93cc12d Mon Sep 17 00:00:00 2001 From: Benni Merz Date: Tue, 5 Jul 2022 16:27:37 +0200 Subject: Node Wrangler: expose Custom Color property in the Frame Selected operator Differential Revision: https://developer.blender.org/D15320 --- node_wrangler.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/node_wrangler.py b/node_wrangler.py index 206399cb..a22d402c 100644 --- a/node_wrangler.py +++ b/node_wrangler.py @@ -2094,14 +2094,27 @@ class NWFrameSelected(Operator, NWBase): description='The visual name of the frame node', default=' ' ) + use_custom_color_prop: BoolProperty( + name="Custom Color", + description="Use custom color for the frame node", + default=False + ) color_prop: FloatVectorProperty( name="Color", description="The color of the frame node", - default=(0.6, 0.6, 0.6), + default=(0.604, 0.604, 0.604), min=0, max=1, step=1, precision=3, subtype='COLOR_GAMMA', size=3 ) + def draw(self, context): + layout = self.layout + layout.prop(self, 'label_prop') + layout.prop(self, 'use_custom_color_prop') + col = layout.column() + col.active = self.use_custom_color_prop + col.prop(self, 'color_prop', text="") + def execute(self, context): nodes, links = get_nodes_links(context) selected = [] @@ -2112,7 +2125,7 @@ class NWFrameSelected(Operator, NWBase): bpy.ops.node.add_node(type='NodeFrame') frm = nodes.active frm.label = self.label_prop - frm.use_custom_color = True + frm.use_custom_color = self.use_custom_color_prop frm.color = self.color_prop for node in selected: -- cgit v1.2.3 From 4bf15a06f3349a94dce06f6ed8691ed103862a10 Mon Sep 17 00:00:00 2001 From: Benni Merz Date: Tue, 5 Jul 2022 16:32:07 +0200 Subject: Node Wrangler: remove obsolete Emission Viewer code Differential Revision: https://developer.blender.org/D15350 --- node_wrangler.py | 93 +++++++------------------------------------------------- 1 file changed, 11 insertions(+), 82 deletions(-) diff --git a/node_wrangler.py b/node_wrangler.py index a22d402c..6d586e80 100644 --- a/node_wrangler.py +++ b/node_wrangler.py @@ -1051,7 +1051,7 @@ def get_internal_socket(socket): return iterator[i] def is_viewer_link(link, output_node): - if "Emission Viewer" in link.to_node.name or link.to_node == output_node and link.to_socket == output_node.inputs[0]: + if link.to_node == output_node and link.to_socket == output_node.inputs[0]: return True if link.to_node.type == 'GROUP_OUTPUT': socket = get_internal_socket(link.to_socket) @@ -1068,8 +1068,6 @@ def get_output_location(tree): # get right-most location sorted_by_xloc = (sorted(tree.nodes, key=lambda x: x.location.x)) max_xloc_node = sorted_by_xloc[-1] - if max_xloc_node.name == 'Emission Viewer': - max_xloc_node = sorted_by_xloc[-2] # get average y location sum_yloc = 0 @@ -1686,7 +1684,7 @@ class NWAddAttrNode(Operator, NWBase): class NWPreviewNode(Operator, NWBase): bl_idname = "node.nw_preview_node" bl_label = "Preview Node" - bl_description = "Connect active node to Emission Shader for shadeless previews, or to the geometry node tree's output" + bl_description = "Connect active node to the Node Group output or the Material Output" bl_options = {'REGISTER', 'UNDO'} # If false, the operator is not executed if the current node group happens to be a geometry nodes group. @@ -1697,7 +1695,6 @@ class NWPreviewNode(Operator, NWBase): def __init__(self): self.shader_output_type = "" self.shader_output_ident = "" - self.shader_viewer_ident = "" @classmethod def poll(cls, context): @@ -1748,16 +1745,13 @@ class NWPreviewNode(Operator, NWBase): if space.id not in [light for light in bpy.data.lights]: # cannot use bpy.data.lights directly as iterable self.shader_output_type = "OUTPUT_MATERIAL" self.shader_output_ident = "ShaderNodeOutputMaterial" - self.shader_viewer_ident = "ShaderNodeEmission" else: self.shader_output_type = "OUTPUT_LIGHT" self.shader_output_ident = "ShaderNodeOutputLight" - self.shader_viewer_ident = "ShaderNodeEmission" elif shader_type == 'WORLD': self.shader_output_type = "OUTPUT_WORLD" self.shader_output_ident = "ShaderNodeOutputWorld" - self.shader_viewer_ident = "ShaderNodeBackground" def get_shader_output_node(self, tree): for node in tree.nodes: @@ -1820,8 +1814,7 @@ class NWPreviewNode(Operator, NWBase): self.used_viewer_sockets_active_mat = [] materialout = self.get_shader_output_node(bpy.context.space_data.node_tree) if materialout: - emission = self.get_viewer_node(materialout) - self.search_sockets((emission if emission else materialout), self.used_viewer_sockets_active_mat) + self.search_sockets(materialout, self.used_viewer_sockets_active_mat) return socket in self.used_viewer_sockets_active_mat def is_socket_used_other_mats(self, socket): @@ -1834,18 +1827,9 @@ class NWPreviewNode(Operator, NWBase): # get viewer node materialout = self.get_shader_output_node(mat.node_tree) if materialout: - emission = self.get_viewer_node(materialout) - self.search_sockets((emission if emission else materialout), self.used_viewer_sockets_other_mats) + self.search_sockets(materialout, self.used_viewer_sockets_other_mats) return socket in self.used_viewer_sockets_other_mats - @staticmethod - def get_viewer_node(materialout): - input_socket = materialout.inputs[0] - if len(input_socket.links) > 0: - node = input_socket.links[0].from_node - if node.type == 'EMISSION' and node.name == "Emission Viewer": - return node - def invoke(self, context, event): space = context.space_data # Ignore operator when running in wrong context. @@ -1864,8 +1848,7 @@ class NWPreviewNode(Operator, NWBase): base_node_tree = space.node_tree active = nodes.active - # For geometry node trees we just connect to the group output, - # because there is no "viewer node" yet. + # For geometry node trees we just connect to the group output if space.tree_type == "GeometryNodeTree": valid = False if active: @@ -1903,7 +1886,6 @@ class NWPreviewNode(Operator, NWBase): out_i = valid_outputs[0] make_links = [] # store sockets for new links - delete_nodes = [] # store unused nodes to delete in the end if active.outputs: # If there is no 'GEOMETRY' output type - We can't preview the node if out_i is None: @@ -1944,10 +1926,6 @@ class NWPreviewNode(Operator, NWBase): tree = socket.id_data tree.outputs.remove(socket) - # Delete nodes - for tree, node in delete_nodes: - tree.nodes.remove(node) - nodes.active = active active.select = True force_update(context) @@ -1958,7 +1936,7 @@ class NWPreviewNode(Operator, NWBase): output_types = [x[1] for x in shaders_output_nodes_props] valid = False if active: - if (active.name != "Emission Viewer") and (active.type not in output_types): + if active.type not in output_types: for out in active.outputs: if is_visible_socket(out): valid = True @@ -1976,7 +1954,7 @@ class NWPreviewNode(Operator, NWBase): materialout = base_node_tree.nodes.new(self.shader_output_ident) materialout.location = get_output_location(base_node_tree) materialout.select = False - # Analyze outputs, add "Emission Viewer" if needed, make links + # Analyze outputs out_i = None valid_outputs = [] for i, out in enumerate(active.outputs): @@ -1994,56 +1972,11 @@ class NWPreviewNode(Operator, NWBase): out_i = valid_outputs[0] make_links = [] # store sockets for new links - delete_nodes = [] # store unused nodes to delete in the end if active.outputs: - # If output type not 'SHADER' - "Emission Viewer" needed - if active.outputs[out_i].type != 'SHADER': - socket_type = 'NodeSocketColor' - # get Emission Viewer node - emission_exists = False - emission_placeholder = base_node_tree.nodes[0] - for node in base_node_tree.nodes: - if "Emission Viewer" in node.name: - emission_exists = True - emission_placeholder = node - if not emission_exists: - emission = base_node_tree.nodes.new(self.shader_viewer_ident) - emission.hide = True - emission.location = [materialout.location.x, (materialout.location.y + 40)] - emission.label = "Viewer" - emission.name = "Emission Viewer" - emission.use_custom_color = True - emission.color = (0.6, 0.5, 0.4) - emission.select = False - else: - emission = emission_placeholder - output_socket = emission.inputs[0] - - # If Viewer is connected to output by user, don't change those connections (patch by gandalf3) - if emission.outputs[0].links.__len__() > 0: - if not emission.outputs[0].links[0].to_node == materialout: - make_links.append((emission.outputs[0], materialout.inputs[0])) - else: - make_links.append((emission.outputs[0], materialout.inputs[0])) - - # Set brightness of viewer to compensate for Film and CM exposure - if context.scene.render.engine == 'CYCLES' and hasattr(context.scene, 'cycles'): - intensity = 1/context.scene.cycles.film_exposure # Film exposure is a multiplier - else: - intensity = 1 - - intensity /= pow(2, (context.scene.view_settings.exposure)) # CM exposure is measured in stops/EVs (2^x) - emission.inputs[1].default_value = intensity - - else: - # Output type is 'SHADER', no Viewer needed. Delete Viewer if exists. - socket_type = 'NodeSocketShader' - materialout_index = 1 if active.outputs[out_i].name == "Volume" else 0 - make_links.append((active.outputs[out_i], materialout.inputs[materialout_index])) - output_socket = materialout.inputs[materialout_index] - for node in base_node_tree.nodes: - if node.name == 'Emission Viewer': - delete_nodes.append((base_node_tree, node)) + socket_type = 'NodeSocketShader' + materialout_index = 1 if active.outputs[out_i].name == "Volume" else 0 + make_links.append((active.outputs[out_i], materialout.inputs[materialout_index])) + output_socket = materialout.inputs[materialout_index] for li_from, li_to in make_links: base_node_tree.links.new(li_from, li_to) @@ -2069,10 +2002,6 @@ class NWPreviewNode(Operator, NWBase): tree = socket.id_data tree.outputs.remove(socket) - # Delete nodes - for tree, node in delete_nodes: - tree.nodes.remove(node) - nodes.active = active active.select = True -- cgit v1.2.3 From 09d752e8453415daff11e94f87d8692a04b4eeff Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Wed, 6 Jul 2022 09:23:43 +0200 Subject: glTF exporter: export all actions of a single armature is now under an export option --- io_scene_gltf2/__init__.py | 14 ++++++++++- .../blender/exp/gltf2_blender_gather_animations.py | 29 +++++++++++----------- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 39763f8d..bf23d017 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -4,7 +4,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (3, 3, 11), + "version": (3, 3, 12), 'blender': (3, 3, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', @@ -395,6 +395,15 @@ class ExportGLTF2_Base: default=False ) + export_anim_single_armature: BoolProperty( + name='Export all Armature Actions', + description=( + "Export all actions of a single armature. " + "WARNING: works only if you exports a single armature" + ), + default=True + ) + export_current_frame: BoolProperty( name='Use Current Frame', description='Export the scene in the current animation frame', @@ -577,11 +586,13 @@ class ExportGLTF2_Base: export_settings['gltf_nla_strips'] = self.export_nla_strips export_settings['gltf_nla_strips_merged_animation_name'] = self.export_nla_strips_merged_animation_name export_settings['gltf_optimize_animation'] = self.optimize_animation_size + export_settings['gltf_export_anim_single_armature'] = self.export_anim_single_armature else: export_settings['gltf_frame_range'] = False export_settings['gltf_move_keyframes'] = False export_settings['gltf_force_sampling'] = False export_settings['gltf_optimize_animation'] = False + export_settings['gltf_export_anim_single_armature'] = False export_settings['gltf_skins'] = self.export_skins if self.export_skins: export_settings['gltf_all_vertex_influences'] = self.export_all_influences @@ -874,6 +885,7 @@ class GLTF_PT_export_animation_export(bpy.types.Panel): if operator.export_nla_strips is False: layout.prop(operator, 'export_nla_strips_merged_animation_name') layout.prop(operator, 'optimize_animation_size') + layout.prop(operator, 'export_anim_single_armature') class GLTF_PT_export_animation_shapekeys(bpy.types.Panel): diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py index a3a7a4c2..2d579d5b 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py @@ -319,20 +319,21 @@ def __get_blender_actions(blender_object: bpy.types.Object, action_on_type[strip.action.name] = "SHAPEKEY" # If there are only 1 armature, include all animations, even if not in NLA - if blender_object.type == "ARMATURE": - if len(export_settings['vtree'].get_all_node_of_type(VExportNode.ARMATURE)) == 1: - # Keep all actions on objects (no Shapekey animation) - for act in [a for a in bpy.data.actions if a.id_root == "OBJECT"]: - # We need to check this is an armature action - # Checking that at least 1 bone is animated - if not __is_armature_action(act): - continue - # Check if this action is already taken into account - if act.name in blender_tracks.keys(): - continue - blender_actions.append(act) - blender_tracks[act.name] = None - action_on_type[act.name] = "OBJECT" + if export_settings['gltf_export_anim_single_armature'] is True: + if blender_object.type == "ARMATURE": + if len(export_settings['vtree'].get_all_node_of_type(VExportNode.ARMATURE)) == 1: + # Keep all actions on objects (no Shapekey animation) + for act in [a for a in bpy.data.actions if a.id_root == "OBJECT"]: + # We need to check this is an armature action + # Checking that at least 1 bone is animated + if not __is_armature_action(act): + continue + # Check if this action is already taken into account + if act.name in blender_tracks.keys(): + continue + blender_actions.append(act) + blender_tracks[act.name] = None + action_on_type[act.name] = "OBJECT" export_user_extensions('gather_actions_hook', export_settings, blender_object, blender_actions, blender_tracks, action_on_type) -- cgit v1.2.3 From 042fbefac686666190915d206600a5dab8e03066 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Thu, 7 Jul 2022 08:03:39 +0200 Subject: glTF importer/exporter: Manage some official Khronos Extensions about Materials KHR_materials_ior KHR_materials_sheen KHR_materials_specular KHR_materials_transmission KHR_materials_variants KHR_materials_emissive_strength KHR_materials_volume Documentation update is still in progress --- io_scene_gltf2/__init__.py | 106 ++++- .../blender/com/gltf2_blender_default.py | 6 + .../blender/com/gltf2_blender_material_helpers.py | 20 +- io_scene_gltf2/blender/com/gltf2_blender_ui.py | 472 +++++++++++++++++++++ .../blender/exp/gltf2_blender_export_keys.py | 1 - io_scene_gltf2/blender/exp/gltf2_blender_gather.py | 2 + .../blender/exp/gltf2_blender_gather_image.py | 95 ++++- .../blender/exp/gltf2_blender_gather_materials.py | 260 ++++-------- .../gltf2_blender_gather_materials_clearcoat.py | 81 ++++ .../exp/gltf2_blender_gather_materials_emission.py | 61 +++ .../exp/gltf2_blender_gather_materials_ior.py | 35 ++ ...nder_gather_materials_pbr_metallic_roughness.py | 8 +- .../exp/gltf2_blender_gather_materials_sheen.py | 68 +++ .../exp/gltf2_blender_gather_materials_specular.py | 168 ++++++++ .../gltf2_blender_gather_materials_transmission.py | 47 ++ .../exp/gltf2_blender_gather_materials_unlit.py | 6 +- .../exp/gltf2_blender_gather_materials_variants.py | 18 + .../exp/gltf2_blender_gather_materials_volume.py | 75 ++++ .../blender/exp/gltf2_blender_gather_primitives.py | 56 ++- .../blender/exp/gltf2_blender_gather_texture.py | 17 +- .../exp/gltf2_blender_gather_texture_info.py | 44 +- .../blender/exp/gltf2_blender_gather_tree.py | 17 + io_scene_gltf2/blender/exp/gltf2_blender_get.py | 41 +- io_scene_gltf2/blender/exp/gltf2_blender_image.py | 46 +- .../blender/exp/gltf2_blender_texture_specular.py | 94 ++++ .../blender/imp/gltf2_blender_KHR_materials_ior.py | 12 + ..._blender_KHR_materials_pbrSpecularGlossiness.py | 17 +- .../imp/gltf2_blender_KHR_materials_sheen.py | 88 ++++ .../imp/gltf2_blender_KHR_materials_specular.py | 350 +++++++++++++++ .../gltf2_blender_KHR_materials_transmission.py | 64 +++ .../imp/gltf2_blender_KHR_materials_unlit.py | 5 +- .../imp/gltf2_blender_KHR_materials_volume.py | 82 ++++ io_scene_gltf2/blender/imp/gltf2_blender_gltf.py | 28 ++ .../blender/imp/gltf2_blender_material.py | 5 + io_scene_gltf2/blender/imp/gltf2_blender_mesh.py | 56 ++- .../imp/gltf2_blender_pbrMetallicRoughness.py | 238 ++++++++++- .../blender/imp/gltf2_blender_texture.py | 19 +- io_scene_gltf2/io/com/gltf2_io_constants.py | 2 + io_scene_gltf2/io/com/gltf2_io_variants.py | 29 ++ io_scene_gltf2/io/imp/gltf2_io_gltf.py | 9 +- 40 files changed, 2558 insertions(+), 290 deletions(-) create mode 100644 io_scene_gltf2/blender/com/gltf2_blender_default.py create mode 100644 io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_clearcoat.py create mode 100644 io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_emission.py create mode 100644 io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_ior.py create mode 100644 io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_sheen.py create mode 100644 io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_specular.py create mode 100644 io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_transmission.py create mode 100644 io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_variants.py create mode 100644 io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_volume.py create mode 100644 io_scene_gltf2/blender/exp/gltf2_blender_texture_specular.py create mode 100644 io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_ior.py create mode 100644 io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_sheen.py create mode 100644 io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_specular.py create mode 100644 io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_transmission.py create mode 100644 io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_volume.py create mode 100644 io_scene_gltf2/io/com/gltf2_io_variants.py diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index bf23d017..358759fa 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -4,7 +4,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (3, 3, 12), + "version": (3, 3, 13), 'blender': (3, 3, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', @@ -259,6 +259,14 @@ class ExportGLTF2_Base: default='EXPORT' ) + export_original_specular: BoolProperty( + name='Export original PBR Specular', + description=( + 'Export original glTF PBR Specular, instead of Blender Principled Shader Specular' + ), + default=False, + ) + export_colors: BoolProperty( name='Vertex Colors', description='Export vertex colors with meshes', @@ -447,13 +455,6 @@ class ExportGLTF2_Base: default=False ) - export_displacement: BoolProperty( - name='Displacement Textures (EXPERIMENTAL)', - description='EXPERIMENTAL: Export displacement textures. ' - 'Uses incomplete "KHR_materials_displacement" glTF extension', - default=False - ) - will_save_settings: BoolProperty( name='Remember Export Settings', description='Store glTF export settings in the Blender project', @@ -564,6 +565,7 @@ class ExportGLTF2_Base: export_settings['gltf_colors'] = self.export_colors export_settings['gltf_cameras'] = self.export_cameras + export_settings['gltf_original_specular'] = self.export_original_specular export_settings['gltf_visible'] = self.use_visible export_settings['gltf_renderable'] = self.use_renderable @@ -611,7 +613,6 @@ class ExportGLTF2_Base: export_settings['gltf_morph_tangent'] = False export_settings['gltf_lights'] = self.export_lights - export_settings['gltf_displacement'] = self.export_displacement export_settings['gltf_binary'] = bytearray() export_settings['gltf_binaryfilename'] = ( @@ -756,6 +757,22 @@ class GLTF_PT_export_geometry(bpy.types.Panel): return operator.bl_idname == "EXPORT_SCENE_OT_gltf" + def draw(self, context): + pass + +class GLTF_PT_export_geometry_mesh(bpy.types.Panel): + bl_space_type = 'FILE_BROWSER' + bl_region_type = 'TOOL_PROPS' + bl_label = "Mesh" + bl_parent_id = "GLTF_PT_export_geometry" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + sfile = context.space_data + operator = sfile.active_operator + return operator.bl_idname == "EXPORT_SCENE_OT_gltf" + def draw(self, context): layout = self.layout layout.use_property_split = True @@ -776,11 +793,56 @@ class GLTF_PT_export_geometry(bpy.types.Panel): col.prop(operator, 'use_mesh_edges') col.prop(operator, 'use_mesh_vertices') + +class GLTF_PT_export_geometry_material(bpy.types.Panel): + bl_space_type = 'FILE_BROWSER' + bl_region_type = 'TOOL_PROPS' + bl_label = "Material" + bl_parent_id = "GLTF_PT_export_geometry" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + sfile = context.space_data + operator = sfile.active_operator + return operator.bl_idname == "EXPORT_SCENE_OT_gltf" + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + sfile = context.space_data + operator = sfile.active_operator + layout.prop(operator, 'export_materials') col = layout.column() col.active = operator.export_materials == "EXPORT" col.prop(operator, 'export_image_format') +class GLTF_PT_export_geometry_original_pbr(bpy.types.Panel): + bl_space_type = 'FILE_BROWSER' + bl_region_type = 'TOOL_PROPS' + bl_label = "PBR Extensions" + bl_parent_id = "GLTF_PT_export_geometry_material" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + sfile = context.space_data + operator = sfile.active_operator + return operator.bl_idname == "EXPORT_SCENE_OT_gltf" + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + sfile = context.space_data + operator = sfile.active_operator + + layout.prop(operator, 'export_original_specular') + class GLTF_PT_export_geometry_compression(bpy.types.Panel): bl_space_type = 'FILE_BROWSER' @@ -1182,6 +1244,14 @@ class ImportGLTF2(Operator, ImportHelper): self.loglevel = logging.NOTSET +def gltf_variant_ui_update(self, context): + from .blender.com.gltf2_blender_ui import variant_register, variant_unregister + if self.KHR_materials_variants_ui is True: + # register all needed types + variant_register() + else: + variant_unregister() + class GLTF_AddonPreferences(bpy.types.AddonPreferences): bl_idname = __package__ @@ -1190,11 +1260,18 @@ class GLTF_AddonPreferences(bpy.types.AddonPreferences): description="Displays glTF Settings node in Shader Editor (Menu Add > Output)" ) + KHR_materials_variants_ui : bpy.props.BoolProperty( + default= False, + description="Displays glTF UI to manage material variants", + update=gltf_variant_ui_update + ) + def draw(self, context): layout = self.layout row = layout.row() row.prop(self, "settings_node_ui", text="Shader Editor Add-ons") + row.prop(self, "KHR_materials_variants_ui", text="Material Variants") def menu_func_import(self, context): self.layout.operator(ImportGLTF2.bl_idname, text='glTF 2.0 (.glb/.gltf)') @@ -1206,6 +1283,9 @@ classes = ( GLTF_PT_export_include, GLTF_PT_export_transform, GLTF_PT_export_geometry, + GLTF_PT_export_geometry_mesh, + GLTF_PT_export_geometry_material, + GLTF_PT_export_geometry_original_pbr, GLTF_PT_export_geometry_compression, GLTF_PT_export_animation, GLTF_PT_export_animation_export, @@ -1225,6 +1305,8 @@ def register(): # bpy.utils.register_module(__name__) blender_ui.register() + if bpy.context.preferences.addons['io_scene_gltf2'].preferences.KHR_materials_variants_ui is True: + blender_ui.variant_register() # add to the export / import menu bpy.types.TOPBAR_MT_file_export.append(menu_func_export) @@ -1233,6 +1315,10 @@ def register(): def unregister(): import io_scene_gltf2.blender.com.gltf2_blender_ui as blender_ui + blender_ui.unregister() + if bpy.context.preferences.addons['io_scene_gltf2'].preferences.KHR_materials_variants_ui is True: + blender_ui.variant_unregister() + for c in classes: bpy.utils.unregister_class(c) for f in exporter_extension_panel_unregister_functors: @@ -1243,8 +1329,6 @@ def unregister(): f() importer_extension_panel_unregister_functors.clear() - blender_ui.unregister() - # bpy.utils.unregister_module(__name__) # remove from the export / import menu diff --git a/io_scene_gltf2/blender/com/gltf2_blender_default.py b/io_scene_gltf2/blender/com/gltf2_blender_default.py new file mode 100644 index 00000000..c3951f4e --- /dev/null +++ b/io_scene_gltf2/blender/com/gltf2_blender_default.py @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2018-2022 The glTF-Blender-IO authors. + +BLENDER_IOR = 1.45 +BLENDER_SPECULAR = 0.5 +BLENDER_SPECULAR_TINT = 0.0 \ No newline at end of file diff --git a/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py b/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py index 7b90b0a3..a44562a1 100755 --- a/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py +++ b/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py @@ -9,7 +9,25 @@ def get_gltf_node_name(): def create_settings_group(name): gltf_node_group = bpy.data.node_groups.new(name, 'ShaderNodeTree') gltf_node_group.inputs.new("NodeSocketFloat", "Occlusion") + thicknessFactor = gltf_node_group.inputs.new("NodeSocketFloat", "Thickness") + thicknessFactor.default_value = 1.0 gltf_node_group.nodes.new('NodeGroupOutput') gltf_node_group_input = gltf_node_group.nodes.new('NodeGroupInput') gltf_node_group_input.location = -200, 0 - return gltf_node_group \ No newline at end of file + return gltf_node_group + +def get_gltf_pbr_non_converted_name(): + return "original glTF PBR data" + +def create_gltf_pbr_non_converted_group(name): + gltf_node_group = bpy.data.node_groups.new(name, 'ShaderNodeTree') + + specular = gltf_node_group.inputs.new("NodeSocketFloat", "specular glTF") + specular.default_value = 1.0 + specularColor = gltf_node_group.inputs.new("NodeSocketColor", "specularColor glTF") + specularColor.default_value = [1.0,1.0,1.0,1.0] + + gltf_node_group.nodes.new('NodeGroupOutput') + gltf_node_group_input = gltf_node_group.nodes.new('NodeGroupInput') + gltf_node_group_input.location = -400, 0 + return gltf_node_group \ No newline at end of file diff --git a/io_scene_gltf2/blender/com/gltf2_blender_ui.py b/io_scene_gltf2/blender/com/gltf2_blender_ui.py index 59c364fb..7895d3d4 100644 --- a/io_scene_gltf2/blender/com/gltf2_blender_ui.py +++ b/io_scene_gltf2/blender/com/gltf2_blender_ui.py @@ -3,6 +3,9 @@ import bpy from ..com.gltf2_blender_material_helpers import get_gltf_node_name, create_settings_group +from ..com.gltf2_blender_material_helpers import get_gltf_pbr_non_converted_name, create_gltf_pbr_non_converted_group + +################ glTF Settings node ########################################### def create_gltf_ao_group(operator, group_name): @@ -40,10 +43,479 @@ def add_gltf_settings_to_menu(self, context) : if bpy.context.preferences.addons['io_scene_gltf2'].preferences.settings_node_ui is True: self.layout.operator("node.gltf_settings_node_operator") +class NODE_OT_GLTF_PBR_NON_CONVERTED_EXTENSIONS(bpy.types.Operator): + bl_idname = "node.gltf_pbr_non_converted_extensions_operator" + bl_label = "glTF PBR Non Converted Extensions" + + + @classmethod + def poll(cls, context): + space = context.space_data + return space.type == "NODE_EDITOR" \ + and context.object and context.object.active_material \ + and context.object.active_material.use_nodes is True \ + and bpy.context.preferences.addons['io_scene_gltf2'].preferences.settings_node_ui is True + + def execute(self, context): + gltf_node_name = get_gltf_pbr_non_converted_name() + if gltf_node_name in bpy.data.node_groups: + my_group = bpy.data.node_groups[get_gltf_pbr_non_converted_name()] + else: + my_group = create_gltf_pbr_non_converted_group(gltf_node_name) + node_tree = context.object.active_material.node_tree + new_node = node_tree.nodes.new("ShaderNodeGroup") + new_node.node_tree = bpy.data.node_groups[my_group.name] + return {"FINISHED"} + + +def add_gltf_pbr_non_converted_extensions_to_menu(self, context) : + if bpy.context.preferences.addons['io_scene_gltf2'].preferences.settings_node_ui is True: + self.layout.operator("node.gltf_pbr_non_converted_extensions_operator") + + +################################### KHR_materials_variants #################### + +# Global UI panel + +class gltf2_KHR_materials_variants_variant(bpy.types.PropertyGroup): + variant_idx : bpy.props.IntProperty() + name : bpy.props.StringProperty(name="Variant Name") + +class SCENE_UL_gltf2_variants(bpy.types.UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + + if self.layout_type in {'DEFAULT', 'COMPACT'}: + layout.prop(item, "name", text="", emboss=False) + + elif self.layout_type in {'GRID'}: + layout.alignment = 'CENTER' + +class SCENE_PT_gltf2_variants(bpy.types.Panel): + bl_label = "glTF Material Variants" + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = "glTF Variants" + + @classmethod + def poll(self, context): + return bpy.context.preferences.addons['io_scene_gltf2'].preferences.KHR_materials_variants_ui is True + + def draw(self, context): + layout = self.layout + row = layout.row() + + if bpy.data.scenes[0].get('gltf2_KHR_materials_variants_variants') and len(bpy.data.scenes[0].gltf2_KHR_materials_variants_variants) > 0: + + row.template_list("SCENE_UL_gltf2_variants", "", bpy.data.scenes[0], "gltf2_KHR_materials_variants_variants", bpy.data.scenes[0], "gltf2_active_variant") + col = row.column() + row = col.column(align=True) + row.operator("scene.gltf2_variant_add", icon="ADD", text="") + row.operator("scene.gltf2_variant_remove", icon="REMOVE", text="") + + row = layout.row() + row.operator("scene.gltf2_display_variant", text="Display Variant") + row = layout.row() + row.operator("scene.gltf2_assign_to_variant", text="Assign To Variant") + row = layout.row() + row.operator("scene.gltf2_reset_to_original", text="Reset To Original") + row.operator("scene.gltf2_assign_as_original", text="Assign as Original") + else: + row.operator("scene.gltf2_variant_add", text="Add Material Variant") + +class SCENE_OT_gltf2_variant_add(bpy.types.Operator): + """Add a new Material Variant""" + bl_idname = "scene.gltf2_variant_add" + bl_label = "Add Material Variant" + bl_options = {'REGISTER'} + + @classmethod + def poll(self, context): + return True + + def execute(self, context): + var = bpy.data.scenes[0].gltf2_KHR_materials_variants_variants.add() + var.variant_idx = len(bpy.data.scenes[0].gltf2_KHR_materials_variants_variants) - 1 + var.name = "VariantName" + bpy.data.scenes[0].gltf2_active_variant = len(bpy.data.scenes[0].gltf2_KHR_materials_variants_variants) - 1 + return {'FINISHED'} + +class SCENE_OT_gltf2_variant_remove(bpy.types.Operator): + """Add a new Material Variant""" + bl_idname = "scene.gltf2_variant_remove" + bl_label = "Remove Variant" + bl_options = {'REGISTER'} + + @classmethod + def poll(self, context): + return len(bpy.data.scenes[0].gltf2_KHR_materials_variants_variants) > 0 + + def execute(self, context): + bpy.data.scenes[0].gltf2_KHR_materials_variants_variants.remove(bpy.data.scenes[0].gltf2_active_variant) + + # loop on all mesh + for obj in [o for o in bpy.data.objects if o.type == "MESH"]: + mesh = obj.data + remove_idx_data = [] + for idx, i in enumerate(mesh.gltf2_variant_mesh_data): + remove_idx_variants = [] + for idx_var, v in enumerate(i.variants): + if v.variant.variant_idx == bpy.data.scenes[0].gltf2_active_variant: + remove_idx_variants.append(idx_var) + elif v.variant.variant_idx > bpy.data.scenes[0].gltf2_active_variant: + v.variant.variant_idx -= 1 + + if len(remove_idx_variants) > 0: + for idx_var in remove_idx_variants: + i.variants.remove(idx_var) + + if len(i.variants) == 0: + remove_idx_data.append(idx) + + if len(remove_idx_data) > 0: + for idx_data in remove_idx_data: + mesh.gltf2_variant_mesh_data.remove(idx_data) + + return {'FINISHED'} + + +# Operator to display a variant +class SCENE_OT_gltf2_display_variant(bpy.types.Operator): + bl_idname = "scene.gltf2_display_variant" + bl_label = "Display Variant" + bl_options = {'REGISTER'} + + + @classmethod + def poll(self, context): + return len(bpy.data.scenes[0].gltf2_KHR_materials_variants_variants) > 0 + + def execute(self, context): + + gltf2_active_variant = bpy.data.scenes[0].gltf2_active_variant + + # loop on all mesh + for obj in [o for o in bpy.data.objects if o.type == "MESH"]: + mesh = obj.data + for i in mesh.gltf2_variant_mesh_data: + if i.variants and gltf2_active_variant in [v.variant.variant_idx for v in i.variants]: + mat = i.material + slot = i.material_slot_index + if slot < len(obj.material_slots): # Seems user remove some slots... + obj.material_slots[slot].material = mat + + return {'FINISHED'} + +# Operator to assign current mesh materials to a variant +class SCENE_OT_gltf2_assign_to_variant(bpy.types.Operator): + bl_idname = "scene.gltf2_assign_to_variant" + bl_label = "Assign To Variant" + bl_options = {'REGISTER'} + + @classmethod + def poll(self, context): + return len(bpy.data.scenes[0].gltf2_KHR_materials_variants_variants) > 0 \ + and bpy.context.object.type == "MESH" + + def execute(self, context): + gltf2_active_variant = bpy.data.scenes[0].gltf2_active_variant + obj = bpy.context.object + + # loop on material slots ( primitives ) + for mat_slot_idx, s in enumerate(obj.material_slots): + # Check if there is already data for this slot + found = False + for i in obj.data.gltf2_variant_mesh_data: + if i.material_slot_index == mat_slot_idx and i.material == s.material: + found = True + variant_primitive = i + + if found is False: + variant_primitive = obj.data.gltf2_variant_mesh_data.add() + variant_primitive.material_slot_index = mat_slot_idx + variant_primitive.material = s.material + + vari = variant_primitive.variants.add() + vari.variant.variant_idx = bpy.data.scenes[0].gltf2_active_variant + + return {'FINISHED'} + +# Operator to reset mesh to orignal (using default material when exists) +class SCENE_OT_gltf2_reset_to_original(bpy.types.Operator): + bl_idname = "scene.gltf2_reset_to_original" + bl_label = "Reset to Orignal" + bl_options = {'REGISTER'} + + @classmethod + def poll(self, context): + return bpy.context.object.type == "MESH" and len(context.object.data.gltf2_variant_default_materials) > 0 + + def execute(self, context): + obj = bpy.context.object + + # loop on material slots ( primitives ) + for mat_slot_idx, s in enumerate(obj.material_slots): + # Check if there is a default material for this slot + found = False + for i in obj.data.gltf2_variant_default_materials: + if i.material_slot_index == mat_slot_idx: + s.material = i.default_material + break + + return {'FINISHED'} + +# Operator to assign current materials as default materials +class SCENE_OT_gltf2_assign_as_original(bpy.types.Operator): + bl_idname = "scene.gltf2_assign_as_original" + bl_label = "Assign as Original" + bl_options = {'REGISTER'} + + @classmethod + def poll(self, context): + return bpy.context.object.type == "MESH" + + def execute(self, context): + obj = bpy.context.object + + # loop on material slots ( primitives ) + for mat_slot_idx, s in enumerate(obj.material_slots): + # Check if there is a default material for this slot + found = False + for i in obj.data.gltf2_variant_default_materials: + if i.material_slot_index == mat_slot_idx: + found = True + # Update if needed + i.default_material = s.material + break + + if found is False: + default_mat = obj.data.gltf2_variant_default_materials.add() + default_mat.material_slot_index = mat_slot_idx + default_mat.default_material = s.material + + return {'FINISHED'} + +# Mesh Panel + +class gltf2_KHR_materials_variant_pointer(bpy.types.PropertyGroup): + variant: bpy.props.PointerProperty(type=gltf2_KHR_materials_variants_variant) + +class gltf2_KHR_materials_variants_default_material(bpy.types.PropertyGroup): + material_slot_index: bpy.props.IntProperty(name="Material Slot Index") + default_material: bpy.props.PointerProperty(type=bpy.types.Material) + +class gltf2_KHR_materials_variants_primitive(bpy.types.PropertyGroup): + material_slot_index : bpy.props.IntProperty(name="Material Slot Index") + material: bpy.props.PointerProperty(type=bpy.types.Material) + variants: bpy.props.CollectionProperty(type=gltf2_KHR_materials_variant_pointer) + active_variant_idx: bpy.props.IntProperty() + +class MESH_UL_gltf2_mesh_variants(bpy.types.UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + + vari = item.variant + layout.context_pointer_set("id", vari) + + if self.layout_type in {'DEFAULT', 'COMPACT'}: + layout.prop(bpy.data.scenes[0].gltf2_KHR_materials_variants_variants[vari.variant_idx], "name", text="", emboss=False) + elif self.layout_type in {'GRID'}: + layout.alignment = 'CENTER' + +class MESH_PT_gltf2_mesh_variants(bpy.types.Panel): + bl_label = "glTF Material Variants" + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "material" + + @classmethod + def poll(self, context): + return bpy.context.preferences.addons['io_scene_gltf2'].preferences.KHR_materials_variants_ui is True \ + and len(bpy.context.object.material_slots) > 0 + + def draw(self, context): + layout = self.layout + + active_material_slots = bpy.context.object.active_material_index + + found = False + if 'gltf2_variant_mesh_data' in bpy.context.object.data.keys(): + for idx, prim in enumerate(bpy.context.object.data.gltf2_variant_mesh_data): + if prim.material_slot_index == active_material_slots and id(prim.material) == id(bpy.context.object.material_slots[active_material_slots].material): + found = True + break + + row = layout.row() + if found is True: + row.template_list("MESH_UL_gltf2_mesh_variants", "", prim, "variants", prim, "active_variant_idx") + col = row.column() + row = col.column(align=True) + row.operator("scene.gltf2_variants_slot_add", icon="ADD", text="") + row.operator("scene.gltf2_remove_material_variant", icon="REMOVE", text="") + + row = layout.row() + if 'gltf2_KHR_materials_variants_variants' in bpy.data.scenes[0].keys() and len(bpy.data.scenes[0].gltf2_KHR_materials_variants_variants) > 0: + row.prop_search(context.object.data, "gltf2_variant_pointer", bpy.data.scenes[0], "gltf2_KHR_materials_variants_variants", text="Variant") + row = layout.row() + row.operator("scene.gltf2_material_to_variant", text="Assign To Variant") + else: + row.label(text="Please Create a Variant First") + else: + if 'gltf2_KHR_materials_variants_variants' in bpy.data.scenes[0].keys() and len(bpy.data.scenes[0].gltf2_KHR_materials_variants_variants) > 0: + row.operator("scene.gltf2_variants_slot_add", text="Add a new Variant Slot") + else: + row.label(text="Please Create a Variant First") + + +class SCENE_OT_gltf2_variant_slot_add(bpy.types.Operator): + """Add a new Slot""" + bl_idname = "scene.gltf2_variants_slot_add" + bl_label = "Add new Slot" + bl_options = {'REGISTER'} + + @classmethod + def poll(self, context): + return len(bpy.context.object.material_slots) > 0 + + def execute(self, context): + mesh = context.object.data + # Check if there is already a data for this slot_idx + material + + found = False + for i in mesh.gltf2_variant_mesh_data: + if i.material_slot_index == context.object.active_material_index and i.material == context.object.material_slots[context.object.active_material_index].material: + found = True + variant_primitive = i + + if found is False: + variant_primitive = mesh.gltf2_variant_mesh_data.add() + variant_primitive.material_slot_index = context.object.active_material_index + variant_primitive.material = context.object.material_slots[context.object.active_material_index].material + + vari = variant_primitive.variants.add() + vari.variant.variant_idx = bpy.data.scenes[0].gltf2_active_variant + + return {'FINISHED'} + +class SCENE_OT_gltf2_material_to_variant(bpy.types.Operator): + """Assign Variant to Slot""" + bl_idname = "scene.gltf2_material_to_variant" + bl_label = "Assign Material To Variant" + bl_options = {'REGISTER'} + + @classmethod + def poll(self, context): + return len(bpy.context.object.material_slots) > 0 and context.object.data.gltf2_variant_pointer != "" + + def execute(self, context): + mesh = context.object.data + + found = False + for i in mesh.gltf2_variant_mesh_data: + if i.material_slot_index == context.object.active_material_index and i.material == context.object.material_slots[context.object.active_material_index].material: + found = True + variant_primitive = i + + if found is False: + return {'CANCELLED'} + + vari = variant_primitive.variants[variant_primitive.active_variant_idx] + + # Retrieve variant idx + found = False + for v in bpy.data.scenes[0].gltf2_KHR_materials_variants_variants: + if v.name == context.object.data.gltf2_variant_pointer: + found = True + break + + if found is False: + return {'CANCELLED'} + + vari.variant.variant_idx = v.variant_idx + + return {'FINISHED'} + +class SCENE_OT_gltf2_remove_material_variant(bpy.types.Operator): + """Remove a variant Slot""" + bl_idname = "scene.gltf2_remove_material_variant" + bl_label = "Remove a variant Slot" + bl_options = {'REGISTER'} + + @classmethod + def poll(self, context): + return len(bpy.context.object.material_slots) > 0 and len(bpy.context.object.data.gltf2_variant_mesh_data) > 0 + + def execute(self, context): + mesh = context.object.data + + found = False + found_idx = -1 + for idx, i in enumerate(mesh.gltf2_variant_mesh_data): + if i.material_slot_index == context.object.active_material_index and i.material == context.object.material_slots[context.object.active_material_index].material: + found = True + variant_primitive = i + found_idx = idx + + if found is False: + return {'CANCELLED'} + + variant_primitive.variants.remove(variant_primitive.active_variant_idx) + + if len(variant_primitive.variants) == 0: + mesh.gltf2_variant_mesh_data.remove(found_idx) + + return {'FINISHED'} + + +############################################################################### def register(): bpy.utils.register_class(NODE_OT_GLTF_SETTINGS) + bpy.utils.register_class(NODE_OT_GLTF_PBR_NON_CONVERTED_EXTENSIONS) bpy.types.NODE_MT_category_SH_NEW_OUTPUT.append(add_gltf_settings_to_menu) + bpy.types.NODE_MT_category_SH_NEW_OUTPUT.append(add_gltf_pbr_non_converted_extensions_to_menu) + +def variant_register(): + bpy.utils.register_class(SCENE_OT_gltf2_display_variant) + bpy.utils.register_class(SCENE_OT_gltf2_assign_to_variant) + bpy.utils.register_class(SCENE_OT_gltf2_reset_to_original) + bpy.utils.register_class(SCENE_OT_gltf2_assign_as_original) + bpy.utils.register_class(SCENE_OT_gltf2_remove_material_variant) + bpy.utils.register_class(gltf2_KHR_materials_variants_variant) + bpy.utils.register_class(gltf2_KHR_materials_variant_pointer) + bpy.utils.register_class(gltf2_KHR_materials_variants_primitive) + bpy.utils.register_class(gltf2_KHR_materials_variants_default_material) + bpy.utils.register_class(SCENE_UL_gltf2_variants) + bpy.utils.register_class(SCENE_PT_gltf2_variants) + bpy.utils.register_class(MESH_UL_gltf2_mesh_variants) + bpy.utils.register_class(MESH_PT_gltf2_mesh_variants) + bpy.utils.register_class(SCENE_OT_gltf2_variant_add) + bpy.utils.register_class(SCENE_OT_gltf2_variant_remove) + bpy.utils.register_class(SCENE_OT_gltf2_material_to_variant) + bpy.utils.register_class(SCENE_OT_gltf2_variant_slot_add) + bpy.types.Mesh.gltf2_variant_mesh_data = bpy.props.CollectionProperty(type=gltf2_KHR_materials_variants_primitive) + bpy.types.Mesh.gltf2_variant_default_materials = bpy.props.CollectionProperty(type=gltf2_KHR_materials_variants_default_material) + bpy.types.Mesh.gltf2_variant_pointer = bpy.props.StringProperty() + bpy.types.Scene.gltf2_KHR_materials_variants_variants = bpy.props.CollectionProperty(type=gltf2_KHR_materials_variants_variant) + bpy.types.Scene.gltf2_active_variant = bpy.props.IntProperty() def unregister(): bpy.utils.unregister_class(NODE_OT_GLTF_SETTINGS) + bpy.utils.unregister_class(NODE_OT_GLTF_PBR_NON_CONVERTED_EXTENSIONS) + +def variant_unregister(): + bpy.utils.unregister_class(SCENE_OT_gltf2_variant_add) + bpy.utils.unregister_class(SCENE_OT_gltf2_variant_remove) + bpy.utils.unregister_class(SCENE_OT_gltf2_material_to_variant) + bpy.utils.unregister_class(SCENE_OT_gltf2_variant_slot_add) + bpy.utils.unregister_class(SCENE_OT_gltf2_display_variant) + bpy.utils.unregister_class(SCENE_OT_gltf2_assign_to_variant) + bpy.utils.unregister_class(SCENE_OT_gltf2_reset_to_original) + bpy.utils.unregister_class(SCENE_OT_gltf2_assign_as_original) + bpy.utils.unregister_class(SCENE_OT_gltf2_remove_material_variant) + bpy.utils.unregister_class(SCENE_PT_gltf2_variants) + bpy.utils.unregister_class(SCENE_UL_gltf2_variants) + bpy.utils.unregister_class(MESH_PT_gltf2_mesh_variants) + bpy.utils.unregister_class(MESH_UL_gltf2_mesh_variants) + bpy.utils.unregister_class(gltf2_KHR_materials_variants_default_material) + bpy.utils.unregister_class(gltf2_KHR_materials_variants_primitive) + bpy.utils.unregister_class(gltf2_KHR_materials_variants_variant) + bpy.utils.unregister_class(gltf2_KHR_materials_variant_pointer) diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_export_keys.py b/io_scene_gltf2/blender/exp/gltf2_blender_export_keys.py index 812db3f9..96f97af1 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_export_keys.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_export_keys.py @@ -20,7 +20,6 @@ RENDERABLE = 'gltf_renderable' ACTIVE_COLLECTION = 'gltf_active_collection' SKINS = 'gltf_skins' DEF_BONES_ONLY = 'gltf_def_bones' -DISPLACEMENT = 'gltf_displacement' FORCE_SAMPLING = 'gltf_force_sampling' FRAME_RANGE = 'gltf_frame_range' FRAME_STEP = 'gltf_frame_step' diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather.py index 4eb8baed..721ef115 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather.py @@ -59,6 +59,8 @@ def __gather_scene(blender_scene, export_settings): # Now, we can filter tree if needed vtree.filter() + vtree.variants_reset_to_original() + export_user_extensions('vtree_after_filter_hook', export_settings, vtree) export_settings['vtree'] = vtree diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py index 0dfce9f9..69900c1b 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py @@ -11,7 +11,7 @@ from io_scene_gltf2.blender.exp import gltf2_blender_search_node_tree from io_scene_gltf2.io.exp import gltf2_io_binary_data from io_scene_gltf2.io.exp import gltf2_io_image_data from io_scene_gltf2.io.com import gltf2_io_debug -from io_scene_gltf2.blender.exp.gltf2_blender_image import Channel, ExportImage, FillImage +from io_scene_gltf2.blender.exp.gltf2_blender_image import Channel, ExportImage, FillImage, StoreImage, StoreData from io_scene_gltf2.blender.exp.gltf2_blender_gather_cache import cached from io_scene_gltf2.io.exp.gltf2_io_user_extensions import export_user_extensions @@ -21,26 +21,31 @@ def gather_image( blender_shader_sockets: typing.Tuple[bpy.types.NodeSocket], export_settings): if not __filter_image(blender_shader_sockets, export_settings): - return None + return None, None image_data = __get_image_data(blender_shader_sockets, export_settings) if image_data.empty(): # The export image has no data - return None + return None, None mime_type = __gather_mime_type(blender_shader_sockets, image_data, export_settings) name = __gather_name(image_data, export_settings) + factor = None + if image_data.original is None: - uri = __gather_uri(image_data, mime_type, name, export_settings) + uri, factor_uri = __gather_uri(image_data, mime_type, name, export_settings) else: # Retrieve URI relative to exported glTF files uri = __gather_original_uri(image_data.original.filepath, export_settings) # In case we can't retrieve image (for example packed images, with original moved) # We don't create invalid image without uri + factor_uri = None if uri is None: return None - buffer_view = __gather_buffer_view(image_data, mime_type, name, export_settings) + buffer_view, factor_buffer_view = __gather_buffer_view(image_data, mime_type, name, export_settings) + + factor = factor_uri if uri is not None else factor_buffer_view image = __make_image( buffer_view, @@ -54,7 +59,7 @@ def gather_image( export_user_extensions('gather_image_hook', export_settings, image, blender_shader_sockets) - return image + return image, factor def __gather_original_uri(original_uri, export_settings): @@ -98,8 +103,9 @@ def __filter_image(sockets, export_settings): @cached def __gather_buffer_view(image_data, mime_type, name, export_settings): if export_settings[gltf2_blender_export_keys.FORMAT] != 'GLTF_SEPARATE': - return gltf2_io_binary_data.BinaryData(data=image_data.encode(mime_type)) - return None + data, factor = image_data.encode(mime_type) + return gltf2_io_binary_data.BinaryData(data=data), factor + return None, None def __gather_extensions(sockets, export_settings): @@ -165,13 +171,14 @@ def __gather_name(export_image, export_settings): def __gather_uri(image_data, mime_type, name, export_settings): if export_settings[gltf2_blender_export_keys.FORMAT] == 'GLTF_SEPARATE': # as usual we just store the data in place instead of already resolving the references + data, factor = image_data.encode(mime_type=mime_type) return gltf2_io_image_data.ImageData( - data=image_data.encode(mime_type=mime_type), + data=data, mime_type=mime_type, name=name - ) + ), factor - return None + return None, None def __get_image_data(sockets, export_settings) -> ExportImage: @@ -179,6 +186,18 @@ def __get_image_data(sockets, export_settings) -> ExportImage: # in a helper class. During generation of the glTF in the exporter these will then be combined to actual binary # resources. results = [__get_tex_from_socket(socket, export_settings) for socket in sockets] + + # Check if we need a simple mapping or more complex calculation + if any([socket.name == "Specular" for socket in sockets]): + return __get_image_data_specular(sockets, results, export_settings) + else: + return __get_image_data_mapping(sockets, results, export_settings) + +def __get_image_data_mapping(sockets, results, export_settings) -> ExportImage: + """ + Simple mapping + Will fit for most of exported textures : RoughnessMetallic, Basecolor, normal, ... + """ composed_image = ExportImage() for result, socket in zip(results, sockets): # Assume that user know what he does, and that channels/images are already combined correctly for pbr @@ -217,6 +236,12 @@ def __get_image_data(sockets, export_settings) -> ExportImage: dst_chan = Channel.R elif socket.name == 'Clearcoat Roughness': dst_chan = Channel.G + elif socket.name == 'Thickness': # For KHR_materials_volume + dst_chan = Channel.G + elif socket.name == "specular glTF": # For original KHR_material_specular + dst_chan = Channel.A + elif socket.name == "Sigma": # For KHR_materials_sheen + dst_chan = Channel.A if dst_chan is not None: composed_image.fill_image(result.shader_node.image, dst_chan, src_chan) @@ -243,6 +268,54 @@ def __get_image_data(sockets, export_settings) -> ExportImage: return composed_image +def __get_image_data_specular(sockets, results, export_settings) -> ExportImage: + """ + calculating Specular Texture, settings needed data + """ + from io_scene_gltf2.blender.exp.gltf2_blender_texture_specular import specular_calculation + composed_image = ExportImage() + composed_image.set_calc(specular_calculation) + + composed_image.store_data("ior", sockets[4].default_value, type="Data") + + results = [__get_tex_from_socket(socket, export_settings) for socket in sockets[:-1]] #Do not retrieve IOR --> No texture allowed + + mapping = { + 0: "specular", + 1: "specular_tint", + 2: "base_color", + 3: "transmission" + } + + for idx, result in enumerate(results): + if __get_tex_from_socket(sockets[idx], export_settings): + + composed_image.store_data(mapping[idx], result.shader_node.image, type="Image") + + # rudimentarily try follow the node tree to find the correct image data. + src_chan = None if idx == 2 else Channel.R + for elem in result.path: + if isinstance(elem.from_node, bpy.types.ShaderNodeSeparateColor): + src_chan = { + 'Red': Channel.R, + 'Green': Channel.G, + 'Blue': Channel.B, + }[elem.from_socket.name] + if elem.from_socket.name == 'Alpha': + src_chan = Channel.A + # For base_color, keep all channels, as this is a Vec, not scalar + if idx != 2: + composed_image.store_data(mapping[idx] + "_channel", src_chan, type="Data") + else: + if src_chan is not None: + composed_image.store_data(mapping[idx] + "_channel", src_chan, type="Data") + + else: + composed_image.store_data(mapping[idx], sockets[idx].default_value, type="Data") + + return composed_image + +# TODOExt deduplicate @cached def __get_tex_from_socket(blender_shader_socket: bpy.types.NodeSocket, export_settings): result = gltf2_blender_search_node_tree.from_socket( diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py index 56c3acff..c0d17fd3 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py @@ -1,21 +1,27 @@ # SPDX-License-Identifier: Apache-2.0 -# Copyright 2018-2021 The glTF-Blender-IO authors. +# Copyright 2018-2022 The glTF-Blender-IO authors. from copy import deepcopy import bpy from io_scene_gltf2.blender.exp.gltf2_blender_gather_cache import cached, cached_by_key from io_scene_gltf2.io.com import gltf2_io -from io_scene_gltf2.io.com.gltf2_io_extensions import Extension +from io_scene_gltf2.blender.exp import gltf2_blender_gather_materials_unlit from io_scene_gltf2.blender.exp import gltf2_blender_gather_texture_info, gltf2_blender_export_keys -from io_scene_gltf2.blender.exp import gltf2_blender_search_node_tree - from io_scene_gltf2.blender.exp import gltf2_blender_gather_materials_pbr_metallic_roughness -from io_scene_gltf2.blender.exp import gltf2_blender_gather_materials_unlit from ..com.gltf2_blender_extras import generate_extras from io_scene_gltf2.blender.exp import gltf2_blender_get from io_scene_gltf2.io.exp.gltf2_io_user_extensions import export_user_extensions from io_scene_gltf2.io.com.gltf2_io_debug import print_console +from io_scene_gltf2.blender.exp.gltf2_blender_gather_materials_volume import export_volume +from io_scene_gltf2.blender.exp.gltf2_blender_gather_materials_emission import export_emission_factor, \ + export_emission_texture, export_emission_strength_extension +from io_scene_gltf2.blender.exp.gltf2_blender_gather_materials_sheen import export_sheen +from io_scene_gltf2.blender.exp.gltf2_blender_gather_materials_specular import export_specular +from io_scene_gltf2.blender.exp.gltf2_blender_gather_materials_transmission import export_transmission +from io_scene_gltf2.blender.exp.gltf2_blender_gather_materials_clearcoat import export_clearcoat +from io_scene_gltf2.blender.exp.gltf2_blender_gather_materials_ior import export_ior +from io_scene_gltf2.io.com.gltf2_io_extensions import Extension @cached def get_material_cache_key(blender_material, active_uvmap_index, export_settings): @@ -39,24 +45,31 @@ def gather_material(blender_material, active_uvmap_index, export_settings): if not __filter_material(blender_material, export_settings): return None - mat_unlit = __gather_material_unlit(blender_material, active_uvmap_index, export_settings) + mat_unlit = __export_unlit(blender_material, active_uvmap_index, export_settings) if mat_unlit is not None: export_user_extensions('gather_material_hook', export_settings, mat_unlit, blender_material) return mat_unlit orm_texture = __gather_orm_texture(blender_material, export_settings) + emissive_factor = __gather_emissive_factor(blender_material, export_settings) emissive_texture, uvmap_actives_emissive_texture = __gather_emissive_texture(blender_material, export_settings) - extensions, uvmap_actives_extensions = __gather_extensions(blender_material, export_settings) + extensions, uvmap_actives_extensions = __gather_extensions(blender_material, emissive_factor, export_settings) normal_texture, uvmap_actives_normal_texture = __gather_normal_texture(blender_material, export_settings) occlusion_texture, uvmap_actives_occlusion_texture = __gather_occlusion_texture(blender_material, orm_texture, export_settings) pbr_metallic_roughness, uvmap_actives_pbr_metallic_roughness = __gather_pbr_metallic_roughness(blender_material, orm_texture, export_settings) + if any([i>1.0 for i in emissive_factor or []]) is True: + # Strength is set on extension + emission_strength = max(emissive_factor) + emissive_factor = [f / emission_strength for f in emissive_factor] + + base_material = gltf2_io.Material( alpha_cutoff=__gather_alpha_cutoff(blender_material, export_settings), alpha_mode=__gather_alpha_mode(blender_material, export_settings), double_sided=__gather_double_sided(blender_material, export_settings), - emissive_factor=__gather_emissive_factor(blender_material, export_settings), + emissive_factor=emissive_factor, emissive_texture=emissive_texture, extensions=extensions, extras=__gather_extras(blender_material, export_settings), @@ -106,6 +119,14 @@ def gather_material(blender_material, active_uvmap_index, export_settings): material.extensions["KHR_materials_clearcoat"].extension['clearcoatNormalTexture'].tex_coord = active_uvmap_index elif tex == "transmissionTexture": #TODO not tested yet material.extensions["KHR_materials_transmission"].extension['transmissionTexture'].tex_coord = active_uvmap_index + elif tex == "specularTexture": + material.extensions["KHR_materials_specular"].extension['specularTexture'].tex_coord = active_uvmap_index + elif tex == "specularColorTexture": + material.extensions["KHR_materials_specular"].extension['specularColorTexture'].tex_coord = active_uvmap_index + elif tex == "sheenColorTexture": + material.extensions["KHR_materials_sheen"].extension['sheenColorTexture'].tex_coord = active_uvmap_index + elif tex == "sheenRoughnessTexture": + material.extensions["KHR_materials_sheen"].extension['sheenRoughnessTexture'].tex_coord = active_uvmap_index # If material is not using active UVMap, we need to return the same material, # Even if multiples meshes are using different active UVMap @@ -188,72 +209,61 @@ def __gather_double_sided(blender_material, export_settings): def __gather_emissive_factor(blender_material, export_settings): - emissive_socket = gltf2_blender_get.get_socket(blender_material, "Emissive") - if emissive_socket is None: - emissive_socket = gltf2_blender_get.get_socket_old(blender_material, "EmissiveFactor") - if isinstance(emissive_socket, bpy.types.NodeSocket): - if export_settings['gltf_image_format'] != "NONE": - factor = gltf2_blender_get.get_factor_from_socket(emissive_socket, kind='RGB') - else: - factor = gltf2_blender_get.get_const_from_default_value_socket(emissive_socket, kind='RGB') - - if factor is None and emissive_socket.is_linked: - # In glTF, the default emissiveFactor is all zeros, so if an emission texture is connected, - # we have to manually set it to all ones. - factor = [1.0, 1.0, 1.0] - - if factor is None: factor = [0.0, 0.0, 0.0] - - # Handle Emission Strength - strength_socket = None - if emissive_socket.node.type == 'EMISSION': - strength_socket = emissive_socket.node.inputs['Strength'] - elif 'Emission Strength' in emissive_socket.node.inputs: - strength_socket = emissive_socket.node.inputs['Emission Strength'] - strength = ( - gltf2_blender_get.get_const_from_socket(strength_socket, kind='VALUE') - if strength_socket is not None - else None - ) - if strength is not None: - factor = [f * strength for f in factor] - - # Clamp to range [0,1] - factor = [min(1.0, f) for f in factor] - - if factor == [0, 0, 0]: factor = None - - return factor - - return None - + return export_emission_factor(blender_material, export_settings) def __gather_emissive_texture(blender_material, export_settings): - emissive = gltf2_blender_get.get_socket(blender_material, "Emissive") - if emissive is None: - emissive = gltf2_blender_get.get_socket_old(blender_material, "Emissive") - emissive_texture, use_actives_uvmap_emissive = gltf2_blender_gather_texture_info.gather_texture_info(emissive, (emissive,), export_settings) - return emissive_texture, ["emissiveTexture"] if use_actives_uvmap_emissive else None + return export_emission_texture(blender_material, export_settings) -def __gather_extensions(blender_material, export_settings): +def __gather_extensions(blender_material, emissive_factor, export_settings): extensions = {} # KHR_materials_clearcoat actives_uvmaps = [] - clearcoat_extension, use_actives_uvmap_clearcoat = __gather_clearcoat_extension(blender_material, export_settings) + clearcoat_extension, use_actives_uvmap_clearcoat = export_clearcoat(blender_material, export_settings) if clearcoat_extension: extensions["KHR_materials_clearcoat"] = clearcoat_extension actives_uvmaps.extend(use_actives_uvmap_clearcoat) # KHR_materials_transmission - transmission_extension, use_actives_uvmap_transmission = __gather_transmission_extension(blender_material, export_settings) + transmission_extension, use_actives_uvmap_transmission = export_transmission(blender_material, export_settings) if transmission_extension: extensions["KHR_materials_transmission"] = transmission_extension actives_uvmaps.extend(use_actives_uvmap_transmission) + # KHR_materials_emissive_strength + if any([i>1.0 for i in emissive_factor or []]): + emissive_strength_extension = export_emission_strength_extension(emissive_factor, export_settings) + if emissive_strength_extension: + extensions["KHR_materials_emissive_strength"] = emissive_strength_extension + + # KHR_materials_volume + + volume_extension, use_actives_uvmap_volume_thickness = export_volume(blender_material, export_settings) + if volume_extension: + extensions["KHR_materials_volume"] = volume_extension + actives_uvmaps.extend(use_actives_uvmap_volume_thickness) + + # KHR_materials_specular + specular_extension, use_actives_uvmap_specular = export_specular(blender_material, export_settings) + if specular_extension: + extensions["KHR_materials_specular"] = specular_extension + actives_uvmaps.extend(use_actives_uvmap_specular) + + # KHR_materials_sheen + sheen_extension, use_actives_uvmap_sheen = export_sheen(blender_material, export_settings) + if sheen_extension: + extensions["KHR_materials_sheen"] = sheen_extension + actives_uvmaps.extend(use_actives_uvmap_sheen) + + # KHR_materials_ior + # Keep this extension at the end, because we export it only if some others are exported + ior_extension = export_ior(blender_material, extensions, export_settings) + if ior_extension: + extensions["KHR_materials_ior"] = ior_extension + return extensions, actives_uvmaps if extensions else None @@ -271,7 +281,7 @@ def __gather_normal_texture(blender_material, export_settings): normal = gltf2_blender_get.get_socket(blender_material, "Normal") if normal is None: normal = gltf2_blender_get.get_socket_old(blender_material, "Normal") - normal_texture, use_active_uvmap_normal = gltf2_blender_gather_texture_info.gather_material_normal_texture_info_class( + normal_texture, use_active_uvmap_normal, _ = gltf2_blender_gather_texture_info.gather_material_normal_texture_info_class( normal, (normal,), export_settings) @@ -283,20 +293,20 @@ def __gather_orm_texture(blender_material, export_settings): # If not fully shared, return None, so the images will be cached and processed separately. occlusion = gltf2_blender_get.get_socket(blender_material, "Occlusion") - if occlusion is None or not __has_image_node_from_socket(occlusion): + if occlusion is None or not gltf2_blender_get.has_image_node_from_socket(occlusion): occlusion = gltf2_blender_get.get_socket_old(blender_material, "Occlusion") - if occlusion is None or not __has_image_node_from_socket(occlusion): + if occlusion is None or not gltf2_blender_get.has_image_node_from_socket(occlusion): return None metallic_socket = gltf2_blender_get.get_socket(blender_material, "Metallic") roughness_socket = gltf2_blender_get.get_socket(blender_material, "Roughness") - hasMetal = metallic_socket is not None and __has_image_node_from_socket(metallic_socket) - hasRough = roughness_socket is not None and __has_image_node_from_socket(roughness_socket) + hasMetal = metallic_socket is not None and gltf2_blender_get.has_image_node_from_socket(metallic_socket) + hasRough = roughness_socket is not None and gltf2_blender_get.has_image_node_from_socket(roughness_socket) if not hasMetal and not hasRough: metallic_roughness = gltf2_blender_get.get_socket_old(blender_material, "MetallicRoughness") - if metallic_roughness is None or not __has_image_node_from_socket(metallic_roughness): + if metallic_roughness is None or not gltf2_blender_get.has_image_node_from_socket(metallic_roughness): return None result = (occlusion, metallic_roughness) elif not hasMetal: @@ -313,7 +323,7 @@ def __gather_orm_texture(blender_material, export_settings): return None # Double-check this will past the filter in texture_info - info, info_use_active_uvmap = gltf2_blender_gather_texture_info.gather_texture_info(result[0], result, export_settings) + info, info_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info(result[0], result, export_settings) if info is None: return None @@ -323,7 +333,7 @@ def __gather_occlusion_texture(blender_material, orm_texture, export_settings): occlusion = gltf2_blender_get.get_socket(blender_material, "Occlusion") if occlusion is None: occlusion = gltf2_blender_get.get_socket_old(blender_material, "Occlusion") - occlusion_texture, use_active_uvmap_occlusion = gltf2_blender_gather_texture_info.gather_material_occlusion_texture_info_class( + occlusion_texture, use_active_uvmap_occlusion, _ = gltf2_blender_gather_texture_info.gather_material_occlusion_texture_info_class( occlusion, orm_texture or (occlusion,), export_settings) @@ -336,129 +346,7 @@ def __gather_pbr_metallic_roughness(blender_material, orm_texture, export_settin orm_texture, export_settings) -def __has_image_node_from_socket(socket): - result = gltf2_blender_search_node_tree.from_socket( - socket, - gltf2_blender_search_node_tree.FilterByType(bpy.types.ShaderNodeTexImage)) - if not result: - return False - return True - -def __gather_clearcoat_extension(blender_material, export_settings): - clearcoat_enabled = False - has_clearcoat_texture = False - has_clearcoat_roughness_texture = False - - clearcoat_extension = {} - clearcoat_roughness_slots = () - - clearcoat_socket = gltf2_blender_get.get_socket(blender_material, 'Clearcoat') - clearcoat_roughness_socket = gltf2_blender_get.get_socket(blender_material, 'Clearcoat Roughness') - clearcoat_normal_socket = gltf2_blender_get.get_socket(blender_material, 'Clearcoat Normal') - - if isinstance(clearcoat_socket, bpy.types.NodeSocket) and not clearcoat_socket.is_linked: - clearcoat_extension['clearcoatFactor'] = clearcoat_socket.default_value - clearcoat_enabled = clearcoat_extension['clearcoatFactor'] > 0 - elif __has_image_node_from_socket(clearcoat_socket): - fac = gltf2_blender_get.get_factor_from_socket(clearcoat_socket, kind='VALUE') - # default value in glTF is 0.0, but if there is a texture without factor, use 1 - clearcoat_extension['clearcoatFactor'] = fac if fac != None else 1.0 - has_clearcoat_texture = True - clearcoat_enabled = True - - if not clearcoat_enabled: - return None, None - - if isinstance(clearcoat_roughness_socket, bpy.types.NodeSocket) and not clearcoat_roughness_socket.is_linked: - clearcoat_extension['clearcoatRoughnessFactor'] = clearcoat_roughness_socket.default_value - elif __has_image_node_from_socket(clearcoat_roughness_socket): - fac = gltf2_blender_get.get_factor_from_socket(clearcoat_roughness_socket, kind='VALUE') - # default value in glTF is 0.0, but if there is a texture without factor, use 1 - clearcoat_extension['clearcoatRoughnessFactor'] = fac if fac != None else 1.0 - has_clearcoat_roughness_texture = True - - # Pack clearcoat (R) and clearcoatRoughness (G) channels. - if has_clearcoat_texture and has_clearcoat_roughness_texture: - clearcoat_roughness_slots = (clearcoat_socket, clearcoat_roughness_socket,) - elif has_clearcoat_texture: - clearcoat_roughness_slots = (clearcoat_socket,) - elif has_clearcoat_roughness_texture: - clearcoat_roughness_slots = (clearcoat_roughness_socket,) - - use_actives_uvmaps = [] - - if len(clearcoat_roughness_slots) > 0: - if has_clearcoat_texture: - clearcoat_texture, clearcoat_texture_use_active_uvmap = gltf2_blender_gather_texture_info.gather_texture_info( - clearcoat_socket, - clearcoat_roughness_slots, - export_settings, - ) - clearcoat_extension['clearcoatTexture'] = clearcoat_texture - if clearcoat_texture_use_active_uvmap: - use_actives_uvmaps.append("clearcoatTexture") - if has_clearcoat_roughness_texture: - clearcoat_roughness_texture, clearcoat_roughness_texture_use_active_uvmap = gltf2_blender_gather_texture_info.gather_texture_info( - clearcoat_roughness_socket, - clearcoat_roughness_slots, - export_settings, - ) - clearcoat_extension['clearcoatRoughnessTexture'] = clearcoat_roughness_texture - if clearcoat_roughness_texture_use_active_uvmap: - use_actives_uvmaps.append("clearcoatRoughnessTexture") - if __has_image_node_from_socket(clearcoat_normal_socket): - clearcoat_normal_texture, clearcoat_normal_texture_use_active_uvmap = gltf2_blender_gather_texture_info.gather_material_normal_texture_info_class( - clearcoat_normal_socket, - (clearcoat_normal_socket,), - export_settings - ) - clearcoat_extension['clearcoatNormalTexture'] = clearcoat_normal_texture - if clearcoat_normal_texture_use_active_uvmap: - use_actives_uvmaps.append("clearcoatNormalTexture") - - return Extension('KHR_materials_clearcoat', clearcoat_extension, False), use_actives_uvmaps - -def __gather_transmission_extension(blender_material, export_settings): - transmission_enabled = False - has_transmission_texture = False - - transmission_extension = {} - transmission_slots = () - - transmission_socket = gltf2_blender_get.get_socket(blender_material, 'Transmission') - - if isinstance(transmission_socket, bpy.types.NodeSocket) and not transmission_socket.is_linked: - transmission_extension['transmissionFactor'] = transmission_socket.default_value - transmission_enabled = transmission_extension['transmissionFactor'] > 0 - elif __has_image_node_from_socket(transmission_socket): - transmission_extension['transmissionFactor'] = 1.0 - has_transmission_texture = True - transmission_enabled = True - - if not transmission_enabled: - return None, None - - # Pack transmission channel (R). - if has_transmission_texture: - transmission_slots = (transmission_socket,) - - use_actives_uvmaps = [] - - if len(transmission_slots) > 0: - combined_texture, use_active_uvmap = gltf2_blender_gather_texture_info.gather_texture_info( - transmission_socket, - transmission_slots, - export_settings, - ) - if has_transmission_texture: - transmission_extension['transmissionTexture'] = combined_texture - if use_active_uvmap: - use_actives_uvmaps.append("transmissionTexture") - - return Extension('KHR_materials_transmission', transmission_extension, False), use_actives_uvmaps - - -def __gather_material_unlit(blender_material, active_uvmap_index, export_settings): +def __export_unlit(blender_material, active_uvmap_index, export_settings): gltf2_unlit = gltf2_blender_gather_materials_unlit info = gltf2_unlit.detect_shadeless_material(blender_material, export_settings) diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_clearcoat.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_clearcoat.py new file mode 100644 index 00000000..65c164b4 --- /dev/null +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_clearcoat.py @@ -0,0 +1,81 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2018-2022 The glTF-Blender-IO authors. + +import bpy +from io_scene_gltf2.io.com.gltf2_io_extensions import Extension +from io_scene_gltf2.blender.exp import gltf2_blender_get +from io_scene_gltf2.blender.exp import gltf2_blender_gather_texture_info + +def export_clearcoat(blender_material, export_settings): + clearcoat_enabled = False + has_clearcoat_texture = False + has_clearcoat_roughness_texture = False + + clearcoat_extension = {} + clearcoat_roughness_slots = () + + clearcoat_socket = gltf2_blender_get.get_socket(blender_material, 'Clearcoat') + clearcoat_roughness_socket = gltf2_blender_get.get_socket(blender_material, 'Clearcoat Roughness') + clearcoat_normal_socket = gltf2_blender_get.get_socket(blender_material, 'Clearcoat Normal') + + if isinstance(clearcoat_socket, bpy.types.NodeSocket) and not clearcoat_socket.is_linked: + clearcoat_extension['clearcoatFactor'] = clearcoat_socket.default_value + clearcoat_enabled = clearcoat_extension['clearcoatFactor'] > 0 + elif gltf2_blender_get.has_image_node_from_socket(clearcoat_socket): + fac = gltf2_blender_get.get_factor_from_socket(clearcoat_socket, kind='VALUE') + # default value in glTF is 0.0, but if there is a texture without factor, use 1 + clearcoat_extension['clearcoatFactor'] = fac if fac != None else 1.0 + has_clearcoat_texture = True + clearcoat_enabled = True + + if not clearcoat_enabled: + return None, None + + if isinstance(clearcoat_roughness_socket, bpy.types.NodeSocket) and not clearcoat_roughness_socket.is_linked: + clearcoat_extension['clearcoatRoughnessFactor'] = clearcoat_roughness_socket.default_value + elif gltf2_blender_get.has_image_node_from_socket(clearcoat_roughness_socket): + fac = gltf2_blender_get.get_factor_from_socket(clearcoat_roughness_socket, kind='VALUE') + # default value in glTF is 0.0, but if there is a texture without factor, use 1 + clearcoat_extension['clearcoatRoughnessFactor'] = fac if fac != None else 1.0 + has_clearcoat_roughness_texture = True + + # Pack clearcoat (R) and clearcoatRoughness (G) channels. + if has_clearcoat_texture and has_clearcoat_roughness_texture: + clearcoat_roughness_slots = (clearcoat_socket, clearcoat_roughness_socket,) + elif has_clearcoat_texture: + clearcoat_roughness_slots = (clearcoat_socket,) + elif has_clearcoat_roughness_texture: + clearcoat_roughness_slots = (clearcoat_roughness_socket,) + + use_actives_uvmaps = [] + + if len(clearcoat_roughness_slots) > 0: + if has_clearcoat_texture: + clearcoat_texture, clearcoat_texture_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info( + clearcoat_socket, + clearcoat_roughness_slots, + export_settings, + ) + clearcoat_extension['clearcoatTexture'] = clearcoat_texture + if clearcoat_texture_use_active_uvmap: + use_actives_uvmaps.append("clearcoatTexture") + if has_clearcoat_roughness_texture: + clearcoat_roughness_texture, clearcoat_roughness_texture_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info( + clearcoat_roughness_socket, + clearcoat_roughness_slots, + export_settings, + ) + clearcoat_extension['clearcoatRoughnessTexture'] = clearcoat_roughness_texture + if clearcoat_roughness_texture_use_active_uvmap: + use_actives_uvmaps.append("clearcoatRoughnessTexture") + if gltf2_blender_get.has_image_node_from_socket(clearcoat_normal_socket): + clearcoat_normal_texture, clearcoat_normal_texture_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_material_normal_texture_info_class( + clearcoat_normal_socket, + (clearcoat_normal_socket,), + export_settings + ) + clearcoat_extension['clearcoatNormalTexture'] = clearcoat_normal_texture + if clearcoat_normal_texture_use_active_uvmap: + use_actives_uvmaps.append("clearcoatNormalTexture") + + return Extension('KHR_materials_clearcoat', clearcoat_extension, False), use_actives_uvmaps \ No newline at end of file diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_emission.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_emission.py new file mode 100644 index 00000000..562fc19d --- /dev/null +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_emission.py @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2018-2022 The glTF-Blender-IO authors. + +import bpy +from io_scene_gltf2.io.com.gltf2_io_extensions import Extension +from io_scene_gltf2.blender.exp import gltf2_blender_get +from io_scene_gltf2.blender.exp import gltf2_blender_gather_texture_info + +def export_emission_factor(blender_material, export_settings): + emissive_socket = gltf2_blender_get.get_socket(blender_material, "Emissive") + if emissive_socket is None: + emissive_socket = gltf2_blender_get.get_socket_old(blender_material, "EmissiveFactor") + if isinstance(emissive_socket, bpy.types.NodeSocket): + if export_settings['gltf_image_format'] != "NONE": + factor = gltf2_blender_get.get_factor_from_socket(emissive_socket, kind='RGB') + else: + factor = gltf2_blender_get.get_const_from_default_value_socket(emissive_socket, kind='RGB') + + if factor is None and emissive_socket.is_linked: + # In glTF, the default emissiveFactor is all zeros, so if an emission texture is connected, + # we have to manually set it to all ones. + factor = [1.0, 1.0, 1.0] + + if factor is None: factor = [0.0, 0.0, 0.0] + + # Handle Emission Strength + strength_socket = None + if emissive_socket.node.type == 'EMISSION': + strength_socket = emissive_socket.node.inputs['Strength'] + elif 'Emission Strength' in emissive_socket.node.inputs: + strength_socket = emissive_socket.node.inputs['Emission Strength'] + strength = ( + gltf2_blender_get.get_const_from_socket(strength_socket, kind='VALUE') + if strength_socket is not None + else None + ) + if strength is not None: + factor = [f * strength for f in factor] + + # Clamp to range [0,1] + # Official glTF clamp to range [0,1] + # If we are outside, we need to use extension KHR_materials_emissive_strength + + if factor == [0, 0, 0]: factor = None + + return factor + + return None + +def export_emission_texture(blender_material, export_settings): + emissive = gltf2_blender_get.get_socket(blender_material, "Emissive") + if emissive is None: + emissive = gltf2_blender_get.get_socket_old(blender_material, "Emissive") + emissive_texture, use_actives_uvmap_emissive, _ = gltf2_blender_gather_texture_info.gather_texture_info(emissive, (emissive,), export_settings) + return emissive_texture, ["emissiveTexture"] if use_actives_uvmap_emissive else None + +def export_emission_strength_extension(emissive_factor, export_settings): + emissive_strength_extension = {} + emissive_strength_extension['emissiveStrength'] = max(emissive_factor) + + return Extension('KHR_materials_emissive_strength', emissive_strength_extension, False) \ No newline at end of file diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_ior.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_ior.py new file mode 100644 index 00000000..fc219c01 --- /dev/null +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_ior.py @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2018-2022 The glTF-Blender-IO authors. + +from io_scene_gltf2.io.com.gltf2_io_extensions import Extension +from io_scene_gltf2.blender.exp import gltf2_blender_get +from io_scene_gltf2.io.com.gltf2_io_constants import GLTF_IOR + +def export_ior(blender_material, extensions, export_settings): + ior_socket = gltf2_blender_get.get_socket(blender_material, 'IOR') + + if not ior_socket: + return None + + # We don't manage case where socket is linked, always check default value + if ior_socket.is_linked: + # TODOExt: add warning? + return None + + if ior_socket.default_value == GLTF_IOR: + return None + + # Export only if the following extensions are exported: + need_to_export_ior = [ + 'KHR_materials_transmission', + 'KHR_materials_volume', + 'KHR_materials_specular' + ] + + if not any([e in extensions.keys() for e in need_to_export_ior]): + return None + + ior_extension = {} + ior_extension['ior'] = ior_socket.default_value + + return Extension('KHR_materials_ior', ior_extension, False) \ No newline at end of file diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_pbr_metallic_roughness.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_pbr_metallic_roughness.py index 0b40ffd6..a5929c05 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_pbr_metallic_roughness.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_pbr_metallic_roughness.py @@ -15,8 +15,8 @@ def gather_material_pbr_metallic_roughness(blender_material, orm_texture, export if not __filter_pbr_material(blender_material, export_settings): return None, None - base_color_texture, use_active_uvmap_base_color_texture = __gather_base_color_texture(blender_material, export_settings) - metallic_roughness_texture, use_active_uvmap_metallic_roughness_texture = __gather_metallic_roughness_texture(blender_material, orm_texture, export_settings) + base_color_texture, use_active_uvmap_base_color_texture, _ = __gather_base_color_texture(blender_material, export_settings) + metallic_roughness_texture, use_active_uvmap_metallic_roughness_texture, _ = __gather_metallic_roughness_texture(blender_material, orm_texture, export_settings) material = gltf2_io.MaterialPBRMetallicRoughness( base_color_factor=__gather_base_color_factor(blender_material, export_settings), @@ -92,7 +92,7 @@ def __gather_base_color_texture(blender_material, export_settings): if socket is not None and __has_image_node_from_socket(socket) ) if not inputs: - return None, None + return None, None, None return gltf2_blender_gather_texture_info.gather_texture_info(inputs[0], inputs, export_settings) @@ -128,7 +128,7 @@ def __gather_metallic_roughness_texture(blender_material, orm_texture, export_se if not hasMetal and not hasRough: metallic_roughness = gltf2_blender_get.get_socket_old(blender_material, "MetallicRoughness") if metallic_roughness is None or not __has_image_node_from_socket(metallic_roughness): - return None, None + return None, None, None texture_input = (metallic_roughness,) elif not hasMetal: texture_input = (roughness_socket,) diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_sheen.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_sheen.py new file mode 100644 index 00000000..03625ecb --- /dev/null +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_sheen.py @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2018-2022 The glTF-Blender-IO authors. + +import bpy +from io_scene_gltf2.io.com.gltf2_io_extensions import Extension +from io_scene_gltf2.blender.exp import gltf2_blender_get +from io_scene_gltf2.blender.exp import gltf2_blender_gather_texture_info + + +def export_sheen(blender_material, export_settings): + sheen_extension = {} + + sheenColor_socket = gltf2_blender_get.get_socket(blender_material, "sheenColor") + sheenRoughness_socket = gltf2_blender_get.get_socket(blender_material, "sheenRoughness") + + if sheenColor_socket is None or sheenRoughness_socket is None: + return None, None + + sheenColor_non_linked = isinstance(sheenColor_socket, bpy.types.NodeSocket) and not sheenColor_socket.is_linked + sheenRoughness_non_linked = isinstance(sheenRoughness_socket, bpy.types.NodeSocket) and not sheenRoughness_socket.is_linked + + + use_actives_uvmaps = [] + + if sheenColor_non_linked is True: + color = sheenColor_socket.default_value[:3] + if color != (0.0, 0.0, 0.0): + sheen_extension['sheenColorFactor'] = color + else: + # Factor + fac = gltf2_blender_get.get_factor_from_socket(sheenColor_socket, kind='RGB') + if fac is not None and fac != [0.0, 0.0, 0.0]: + sheen_extension['sheenColorFactor'] = fac + + # Texture + if gltf2_blender_get.has_image_node_from_socket(sheenColor_socket): + original_sheenColor_texture, original_sheenColor_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info( + sheenColor_socket, + (sheenColor_socket,), + export_settings, + ) + sheen_extension['sheenColorTexture'] = original_sheenColor_texture + if original_sheenColor_use_active_uvmap: + use_actives_uvmaps.append("sheenColorTexture") + + + if sheenRoughness_non_linked is True: + fac = sheenRoughness_socket.default_value + if fac != 0.0: + sheen_extension['sheenRoughnessFactor'] = fac + else: + # Factor + fac = gltf2_blender_get.get_factor_from_socket(sheenRoughness_socket, kind='VALUE') + if fac is not None and fac != 0.0: + sheen_extension['sheenRoughnessFactor'] = fac + + # Texture + if gltf2_blender_get.has_image_node_from_socket(sheenRoughness_socket): + original_sheenRoughness_texture, original_sheenRoughness_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info( + sheenRoughness_socket, + (sheenRoughness_socket,), + export_settings, + ) + sheen_extension['sheenRoughnessTexture'] = original_sheenRoughness_texture + if original_sheenRoughness_use_active_uvmap: + use_actives_uvmaps.append("sheenRoughnessTexture") + + return Extension('KHR_materials_sheen', sheen_extension, False), use_actives_uvmaps diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_specular.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_specular.py new file mode 100644 index 00000000..30b32198 --- /dev/null +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_specular.py @@ -0,0 +1,168 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2018-2022 The glTF-Blender-IO authors. + +import bpy +from io_scene_gltf2.io.com.gltf2_io_extensions import Extension +from io_scene_gltf2.blender.exp import gltf2_blender_get +from io_scene_gltf2.io.com.gltf2_io_constants import GLTF_IOR +from io_scene_gltf2.blender.com.gltf2_blender_default import BLENDER_SPECULAR, BLENDER_SPECULAR_TINT +from io_scene_gltf2.blender.exp import gltf2_blender_gather_texture_info + + + +def export_original_specular(blender_material, export_settings): + specular_extension = {} + + original_specular_socket = gltf2_blender_get.get_socket_original(blender_material, 'specular glTF') + original_specularcolor_socket = gltf2_blender_get.get_socket_original(blender_material, 'specularColor glTF') + + if original_specular_socket is None or original_specularcolor_socket is None: + return None, None + + specular_non_linked = isinstance(original_specular_socket, bpy.types.NodeSocket) and not original_specular_socket.is_linked + specularcolor_non_linked = isinstance(original_specularcolor_socket, bpy.types.NodeSocket) and not original_specularcolor_socket.is_linked + + + use_actives_uvmaps = [] + + if specular_non_linked is True: + fac = original_specular_socket.default_value + if fac != 1.0: + specular_extension['specularFactor'] = fac + else: + # Factor + fac = gltf2_blender_get.get_factor_from_socket(original_specular_socket, kind='VALUE') + if fac is not None and fac != 1.0: + specular_extension['specularFactor'] = fac + + # Texture + if gltf2_blender_get.has_image_node_from_socket(original_specular_socket): + original_specular_texture, original_specular_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info( + original_specular_socket, + (original_specular_socket,), + export_settings, + ) + specular_extension['specularTexture'] = original_specular_texture + if original_specular_use_active_uvmap: + use_actives_uvmaps.append("specularTexture") + + + if specularcolor_non_linked is True: + color = original_specularcolor_socket.default_value[:3] + if color != [1.0, 1.0, 1.0]: + specular_extension['specularColorFactor'] = color + else: + # Factor + fac = gltf2_blender_get.get_factor_from_socket(original_specularcolor_socket, kind='RGB') + if fac is not None and fac != [1.0, 1.0, 1.0]: + specular_extension['specularColorFactor'] = fac + + # Texture + if gltf2_blender_get.has_image_node_from_socket(original_specularcolor_socket): + original_specularcolor_texture, original_specularcolor_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info( + original_specularcolor_socket, + (original_specularcolor_socket,), + export_settings, + ) + specular_extension['specularColorTexture'] = original_specularcolor_texture + if original_specularcolor_use_active_uvmap: + use_actives_uvmaps.append("specularColorTexture") + + return Extension('KHR_materials_specular', specular_extension, False), use_actives_uvmaps + +def export_specular(blender_material, export_settings): + + if export_settings['gltf_original_specular'] is True: + return export_original_specular(blender_material, export_settings) + + specular_extension = {} + specular_ext_enabled = False + + specular_socket = gltf2_blender_get.get_socket(blender_material, 'Specular') + specular_tint_socket = gltf2_blender_get.get_socket(blender_material, 'Specular Tint') + base_color_socket = gltf2_blender_get.get_socket(blender_material, 'Base Color') + transmission_socket = gltf2_blender_get.get_socket(blender_material, 'Transmission') + ior_socket = gltf2_blender_get.get_socket(blender_material, 'IOR') + + if base_color_socket is None: + return None, None + + # TODOExt replace by __has_image_node_from_socket calls + specular_not_linked = isinstance(specular_socket, bpy.types.NodeSocket) and not specular_socket.is_linked + specular_tint_not_linked = isinstance(specular_tint_socket, bpy.types.NodeSocket) and not specular_tint_socket.is_linked + base_color_not_linked = isinstance(base_color_socket, bpy.types.NodeSocket) and not base_color_socket.is_linked + transmission_not_linked = isinstance(transmission_socket, bpy.types.NodeSocket) and not transmission_socket.is_linked + ior_not_linked = isinstance(ior_socket, bpy.types.NodeSocket) and not ior_socket.is_linked + + specular = specular_socket.default_value if specular_not_linked else None + specular_tint = specular_tint_socket.default_value if specular_tint_not_linked else None + transmission = transmission_socket.default_value if transmission_not_linked else None + ior = ior_socket.default_value if ior_not_linked else GLTF_IOR # textures not supported #TODOExt add warning? + base_color = base_color_socket.default_value[0:3] + + no_texture = (transmission_not_linked and specular_not_linked and specular_tint_not_linked and + (specular_tint == 0.0 or (specular_tint != 0.0 and base_color_not_linked))) + + use_actives_uvmaps = [] + + if no_texture: + if specular != BLENDER_SPECULAR or specular_tint != BLENDER_SPECULAR_TINT: + import numpy as np + # See https://gist.github.com/proog128/d627c692a6bbe584d66789a5a6437a33 + specular_ext_enabled = True + + def normalize(c): + luminance = lambda c: 0.3 * c[0] + 0.6 * c[1] + 0.1 * c[2] + assert(len(c) == 3) + l = luminance(c) + if l == 0: + return np.array(c) + return np.array([c[0] / l, c[1] / l, c[2] / l]) + + f0_from_ior = ((ior - 1)/(ior + 1))**2 + tint_strength = (1 - specular_tint) + normalize(base_color) * specular_tint + specular_color = (1 - transmission) * (1 / f0_from_ior) * 0.08 * specular * tint_strength + transmission * tint_strength + specular_extension['specularColorFactor'] = list(specular_color) + else: + if specular_not_linked and specular == BLENDER_SPECULAR and specular_tint_not_linked and specular_tint == BLENDER_SPECULAR_TINT: + return None, None + + # Trying to identify cases where exporting a texture will not be needed + if specular_not_linked and transmission_not_linked and \ + specular == 0.0 and transmission == 0.0: + + specular_extension['specularColorFactor'] = [0.0, 0.0, 0.0] + return specular_extension, [] + + + # There will be a texture, with a complex calculation (no direct channel mapping) + sockets = (specular_socket, specular_tint_socket, base_color_socket, transmission_socket, ior_socket) + # Set primary socket having a texture + primary_socket = specular_socket + if specular_not_linked: + primary_socket = specular_tint_socket + if specular_tint_not_linked: + primary_socket = base_color_socket + if base_color_not_linked: + primary_socket = transmission_socket + + specularColorTexture, use_active_uvmap, specularColorFactor = gltf2_blender_gather_texture_info.gather_texture_info( + primary_socket, + sockets, + export_settings, + filter_type='ANY') + if specularColorTexture is None: + return None, None + if use_active_uvmap: + use_actives_uvmaps.append("specularColorTexture") + + specular_ext_enabled = True + specular_extension['specularColorTexture'] = specularColorTexture + + + if specularColorFactor is not None: + specular_extension['specularColorFactor'] = specularColorFactor + + + specular_extension = Extension('KHR_materials_specular', specular_extension, False) if specular_ext_enabled else None + return specular_extension, use_actives_uvmaps \ No newline at end of file diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_transmission.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_transmission.py new file mode 100644 index 00000000..fdc5d8c7 --- /dev/null +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_transmission.py @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2018-2022 The glTF-Blender-IO authors. + +import bpy +from io_scene_gltf2.io.com.gltf2_io_extensions import Extension +from io_scene_gltf2.blender.exp import gltf2_blender_get +from io_scene_gltf2.blender.exp import gltf2_blender_gather_texture_info + +def export_transmission(blender_material, export_settings): + transmission_enabled = False + has_transmission_texture = False + + transmission_extension = {} + transmission_slots = () + + transmission_socket = gltf2_blender_get.get_socket(blender_material, 'Transmission') + + if isinstance(transmission_socket, bpy.types.NodeSocket) and not transmission_socket.is_linked: + transmission_extension['transmissionFactor'] = transmission_socket.default_value + transmission_enabled = transmission_extension['transmissionFactor'] > 0 + elif gltf2_blender_get.has_image_node_from_socket(transmission_socket): + fac = gltf2_blender_get.get_factor_from_socket(transmission_socket, kind='VALUE') + transmission_extension['transmissionFactor'] = fac if fac is not None else 1.0 + has_transmission_texture = True + transmission_enabled = True + + if not transmission_enabled: + return None, None + + # Pack transmission channel (R). + if has_transmission_texture: + transmission_slots = (transmission_socket,) + + use_actives_uvmaps = [] + + if len(transmission_slots) > 0: + combined_texture, use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info( + transmission_socket, + transmission_slots, + export_settings, + ) + if has_transmission_texture: + transmission_extension['transmissionTexture'] = combined_texture + if use_active_uvmap: + use_actives_uvmaps.append("transmissionTexture") + + return Extension('KHR_materials_transmission', transmission_extension, False), use_actives_uvmaps \ No newline at end of file diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_unlit.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_unlit.py index e104b7f1..b501f98f 100644 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_unlit.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_unlit.py @@ -1,9 +1,9 @@ # SPDX-License-Identifier: Apache-2.0 -# Copyright 2018-2021 The glTF-Blender-IO authors. +# Copyright 2018-2022 The glTF-Blender-IO authors. from io_scene_gltf2.blender.exp import gltf2_blender_gather_texture_info from io_scene_gltf2.blender.exp import gltf2_blender_get - +from io_scene_gltf2.io.com.gltf2_io_extensions import Extension def detect_shadeless_material(blender_material, export_settings): """Detect if this material is "shadeless" ie. should be exported @@ -127,7 +127,7 @@ def gather_base_color_texture(info, export_settings): # because gather_image determines how to pack images based on the # names of sockets, and the names are hard-coded to a Principled # style graph. - unlit_texture, unlit_use_active_uvmap = gltf2_blender_gather_texture_info.gather_texture_info( + unlit_texture, unlit_use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info( sockets[0], sockets, export_settings, diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_variants.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_variants.py new file mode 100644 index 00000000..4c452e6a --- /dev/null +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_variants.py @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2018-2022 The glTF-Blender-IO authors. + +import bpy +from typing import Dict, Any +from io_scene_gltf2.blender.exp.gltf2_blender_gather_cache import cached +from io_scene_gltf2.io.com import gltf2_io_variants + + +@cached +def gather_variant(variant_idx, export_settings) -> Dict[str, Any]: + + variant = gltf2_io_variants.Variant( + name=bpy.data.scenes[0].gltf2_KHR_materials_variants_variants[variant_idx].name, + extensions=None, + extras=None + ) + return variant.to_dict() diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_volume.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_volume.py new file mode 100644 index 00000000..57b00700 --- /dev/null +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_volume.py @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2018-2022 The glTF-Blender-IO authors. + +import bpy +from io_scene_gltf2.io.com.gltf2_io_extensions import Extension +from io_scene_gltf2.blender.exp import gltf2_blender_get +from io_scene_gltf2.blender.exp import gltf2_blender_gather_texture_info + + +def export_volume(blender_material, export_settings): + # Implementation based on https://github.com/KhronosGroup/glTF-Blender-IO/issues/1454#issuecomment-928319444 + + # If no transmission --> No volume + transmission_enabled = False + transmission_socket = gltf2_blender_get.get_socket(blender_material, 'Transmission') + if isinstance(transmission_socket, bpy.types.NodeSocket) and not transmission_socket.is_linked: + transmission_enabled = transmission_socket.default_value > 0 + elif gltf2_blender_get.has_image_node_from_socket(transmission_socket): + transmission_enabled = True + + if transmission_enabled is False: + return None, None + + volume_extension = {} + has_thickness_texture = False + thickness_slots = () + + thicknesss_socket = gltf2_blender_get.get_socket_old(blender_material, 'Thickness') + if thicknesss_socket is None: + volume_extension['thicknessFactor'] = 1.0 + + density_socket = gltf2_blender_get.get_socket(blender_material, 'Density', volume=True) + attenuation_color_socket = gltf2_blender_get.get_socket(blender_material, 'Color', volume=True) + if density_socket is None or attenuation_color_socket is None: + return None, None + + if isinstance(attenuation_color_socket, bpy.types.NodeSocket): + rgb = gltf2_blender_get.get_const_from_default_value_socket(attenuation_color_socket, kind='RGB') + volume_extension['attenuationColor'] = rgb + + if isinstance(density_socket, bpy.types.NodeSocket): + density = gltf2_blender_get.get_const_from_default_value_socket(density_socket, kind='VALUE') + volume_extension['attenuationDistance'] = 1.0 / density if density != 0 else None # infinity (Using None as glTF default) + + + if isinstance(thicknesss_socket, bpy.types.NodeSocket) and not thicknesss_socket.is_linked: + val = thicknesss_socket.default_value + if val == 0.0: + # If no thickness, no volume extension export + return None, None + volume_extension['thicknessFactor'] = val + elif gltf2_blender_get.has_image_node_from_socket(thicknesss_socket): + fac = gltf2_blender_get.get_factor_from_socket(thicknesss_socket, kind='VALUE') + # default value in glTF is 0.0, but if there is a texture without factor, use 1 + volume_extension['thicknessFactor'] = fac if fac != None else 1.0 + has_thickness_texture = True + + # Pack thickness channel (R). + if has_thickness_texture: + thickness_slots = (thicknesss_socket,) + + use_actives_uvmaps = [] + + if len(thickness_slots) > 0: + combined_texture, use_active_uvmap, _ = gltf2_blender_gather_texture_info.gather_texture_info( + thicknesss_socket, + thickness_slots, + export_settings, + ) + if has_thickness_texture: + volume_extension['thicknessTexture'] = combined_texture + if use_active_uvmap: + use_actives_uvmaps.append("thicknessTexture") + + return Extension('KHR_materials_volume', volume_extension, False), use_actives_uvmaps \ No newline at end of file diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitives.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitives.py index 367c30f5..b2ffb6b3 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitives.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitives.py @@ -12,10 +12,12 @@ from io_scene_gltf2.blender.exp import gltf2_blender_extract from io_scene_gltf2.blender.exp import gltf2_blender_gather_accessors from io_scene_gltf2.blender.exp import gltf2_blender_gather_primitive_attributes from io_scene_gltf2.blender.exp import gltf2_blender_gather_materials +from io_scene_gltf2.blender.exp import gltf2_blender_gather_materials_variants from io_scene_gltf2.io.com import gltf2_io from io_scene_gltf2.io.exp import gltf2_io_binary_data from io_scene_gltf2.io.com import gltf2_io_constants +from io_scene_gltf2.io.com import gltf2_io_extensions from io_scene_gltf2.io.com.gltf2_io_debug import print_console @@ -86,7 +88,7 @@ def gather_primitives( primitive = gltf2_io.MeshPrimitive( attributes=internal_primitive['attributes'], - extensions=None, + extensions=__gather_extensions(blender_mesh, material_idx, active_uvmap_idx, export_settings), extras=None, indices=internal_primitive['indices'], material=material, @@ -214,3 +216,55 @@ def __gather_targets(blender_primitive, blender_mesh, modifiers, export_settings morph_index += 1 return targets return None + +def __gather_extensions(blender_mesh, + material_idx: int, + active_uvmap_idx, + export_settings): + extensions = {} + + if bpy.context.preferences.addons['io_scene_gltf2'].preferences.KHR_materials_variants_ui is False: + return None + + if bpy.data.scenes[0].get('gltf2_KHR_materials_variants_variants') is None: + return None + if len(bpy.data.scenes[0]['gltf2_KHR_materials_variants_variants']) == 0: + return None + + # Material idx is the slot idx. Retrieve associated variant, if any + mapping = [] + for i in [v for v in blender_mesh.gltf2_variant_mesh_data if v.material_slot_index == material_idx]: + variants = [] + for idx, v in enumerate(i.variants): + if v.variant.variant_idx in [o.variant.variant_idx for o in i.variants[:idx]]: + # Avoid duplicates + continue + vari = gltf2_blender_gather_materials_variants.gather_variant(v.variant.variant_idx, export_settings) + if vari is not None: + variant_extension = gltf2_io_extensions.ChildOfRootExtension( + name="KHR_materials_variants", + path=["variants"], + extension=vari + ) + variants.append(variant_extension) + if len(variants) > 0: + if i.material: + mat = gltf2_blender_gather_materials.gather_material( + i.material, + active_uvmap_idx, + export_settings + ) + else: + # empty slot + mat = None + mapping.append({'material': mat, 'variants': variants}) + + if len(mapping) > 0: + extensions["KHR_materials_variants"] = gltf2_io_extensions.Extension( + name="KHR_materials_variants", + extension={ + "mappings": mapping + } + ) + + return extensions if extensions else None diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_texture.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_texture.py index e8c6baf1..ccfd42e5 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_texture.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_texture.py @@ -26,24 +26,26 @@ def gather_texture( """ if not __filter_texture(blender_shader_sockets, export_settings): - return None + return None, None + + source, factor = __gather_source(blender_shader_sockets, export_settings) texture = gltf2_io.Texture( extensions=__gather_extensions(blender_shader_sockets, export_settings), extras=__gather_extras(blender_shader_sockets, export_settings), name=__gather_name(blender_shader_sockets, export_settings), sampler=__gather_sampler(blender_shader_sockets, export_settings), - source=__gather_source(blender_shader_sockets, export_settings) + source= source ) # although valid, most viewers can't handle missing source properties # This can have None source for "keep original", when original can't be found if texture.source is None: - return None + return None, None export_user_extensions('gather_texture_hook', export_settings, texture, blender_shader_sockets) - return texture + return texture, factor def __filter_texture(blender_shader_sockets, export_settings): @@ -66,13 +68,14 @@ def __gather_name(blender_shader_sockets, export_settings): def __gather_sampler(blender_shader_sockets, export_settings): - shader_nodes = [__get_tex_from_socket(socket).shader_node for socket in blender_shader_sockets] + shader_nodes = [__get_tex_from_socket(socket) for socket in blender_shader_sockets] if len(shader_nodes) > 1: gltf2_io_debug.print_console("WARNING", "More than one shader node tex image used for a texture. " "The resulting glTF sampler will behave like the first shader node tex image.") + first_valid_shader_node = next(filter(lambda x: x is not None, shader_nodes)).shader_node return gltf2_blender_gather_sampler.gather_sampler( - shader_nodes[0], + first_valid_shader_node, export_settings) @@ -81,7 +84,7 @@ def __gather_source(blender_shader_sockets, export_settings): # Helpers - +# TODOExt deduplicate def __get_tex_from_socket(socket): result = gltf2_blender_search_node_tree.from_socket( socket, diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_texture_info.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_texture_info.py index 15b101ad..5fe2da32 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_texture_info.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_texture_info.py @@ -19,14 +19,14 @@ from io_scene_gltf2.io.exp.gltf2_io_user_extensions import export_user_extension # occlusion the primary_socket would be the occlusion socket, and # blender_shader_sockets would be the (O,R,M) sockets. -def gather_texture_info(primary_socket, blender_shader_sockets, export_settings): - return __gather_texture_info_helper(primary_socket, blender_shader_sockets, 'DEFAULT', export_settings) +def gather_texture_info(primary_socket, blender_shader_sockets, export_settings, filter_type='ALL'): + return __gather_texture_info_helper(primary_socket, blender_shader_sockets, 'DEFAULT', filter_type, export_settings) -def gather_material_normal_texture_info_class(primary_socket, blender_shader_sockets, export_settings): - return __gather_texture_info_helper(primary_socket, blender_shader_sockets, 'NORMAL', export_settings) +def gather_material_normal_texture_info_class(primary_socket, blender_shader_sockets, export_settings, filter_type='ALL'): + return __gather_texture_info_helper(primary_socket, blender_shader_sockets, 'NORMAL', filter_type, export_settings) -def gather_material_occlusion_texture_info_class(primary_socket, blender_shader_sockets, export_settings): - return __gather_texture_info_helper(primary_socket, blender_shader_sockets, 'OCCLUSION', export_settings) +def gather_material_occlusion_texture_info_class(primary_socket, blender_shader_sockets, export_settings, filter_type='ALL'): + return __gather_texture_info_helper(primary_socket, blender_shader_sockets, 'OCCLUSION', filter_type, export_settings) @cached @@ -34,16 +34,19 @@ def __gather_texture_info_helper( primary_socket: bpy.types.NodeSocket, blender_shader_sockets: typing.Tuple[bpy.types.NodeSocket], kind: str, + filter_type: str, export_settings): - if not __filter_texture_info(primary_socket, blender_shader_sockets, export_settings): - return None, None + if not __filter_texture_info(primary_socket, blender_shader_sockets, filter_type, export_settings): + return None, None, None tex_transform, tex_coord, use_active_uvmap = __gather_texture_transform_and_tex_coord(primary_socket, export_settings) + index, factor = __gather_index(blender_shader_sockets, export_settings) + fields = { 'extensions': __gather_extensions(tex_transform, export_settings), 'extras': __gather_extras(blender_shader_sockets, export_settings), - 'index': __gather_index(blender_shader_sockets, export_settings), + 'index': index, 'tex_coord': tex_coord } @@ -59,14 +62,14 @@ def __gather_texture_info_helper( texture_info = gltf2_io.MaterialOcclusionTextureInfoClass(**fields) if texture_info.index is None: - return None, None + return None, None, None export_user_extensions('gather_texture_info_hook', export_settings, texture_info, blender_shader_sockets) - return texture_info, use_active_uvmap + return texture_info, use_active_uvmap, factor -def __filter_texture_info(primary_socket, blender_shader_sockets, export_settings): +def __filter_texture_info(primary_socket, blender_shader_sockets, filter_type, export_settings): if primary_socket is None: return False if __get_tex_from_socket(primary_socket) is None: @@ -75,9 +78,18 @@ def __filter_texture_info(primary_socket, blender_shader_sockets, export_setting return False if not all([elem is not None for elem in blender_shader_sockets]): return False - if any([__get_tex_from_socket(socket) is None for socket in blender_shader_sockets]): - # sockets do not lead to a texture --> discard - return False + if filter_type == "ALL": + # Check that all sockets link to texture + if any([__get_tex_from_socket(socket) is None for socket in blender_shader_sockets]): + # sockets do not lead to a texture --> discard + return False + elif filter_type == "ANY": + # Check that at least one socket link to texture + if all([__get_tex_from_socket(socket) is None for socket in blender_shader_sockets]): + return False + elif filter_type == "NONE": + # No check + pass return True @@ -163,7 +175,7 @@ def __gather_texture_transform_and_tex_coord(primary_socket, export_settings): return texture_transform, texcoord_idx or None, use_active_uvmap - +# TODOExt deduplicate def __get_tex_from_socket(socket): result = gltf2_blender_search_node_tree.from_socket( socket, diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py index 0377a6b7..36772667 100644 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py @@ -468,3 +468,20 @@ class VExportTree: skin = gather_skin(n.uuid, self.export_settings) skins.append(skin) return skins + + def variants_reset_to_original(self): + # Only if Variants are displayed and exported + if bpy.context.preferences.addons['io_scene_gltf2'].preferences.KHR_materials_variants_ui is False: + return + objects = [self.nodes[o].blender_object for o in self.get_all_node_of_type(VExportNode.OBJECT) if self.nodes[o].blender_object.type == "MESH" \ + and self.nodes[o].blender_object.data.get('gltf2_variant_default_materials') is not None] + for obj in objects: + # loop on material slots ( primitives ) + for mat_slot_idx, s in enumerate(obj.material_slots): + # Check if there is a default material for this slot + for i in obj.data.gltf2_variant_default_materials: + if i.material_slot_index == mat_slot_idx: + s.material = i.default_material + break + + # If not found, keep current material as default diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_get.py b/io_scene_gltf2/blender/exp/gltf2_blender_get.py index e38906e6..17eacf86 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_get.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_get.py @@ -4,9 +4,10 @@ import bpy from mathutils import Vector, Matrix -from ..com.gltf2_blender_material_helpers import get_gltf_node_name +from ..com.gltf2_blender_material_helpers import get_gltf_node_name, get_gltf_pbr_non_converted_name from ...blender.com.gltf2_blender_conversion import texture_transform_blender_to_gltf from io_scene_gltf2.io.com import gltf2_io_debug +from io_scene_gltf2.blender.exp import gltf2_blender_search_node_tree def get_animation_target(action_group: bpy.types.ActionGroup): @@ -47,7 +48,7 @@ def get_node_socket(blender_material, type, name): return None -def get_socket(blender_material: bpy.types.Material, name: str): +def get_socket(blender_material: bpy.types.Material, name: str, volume=False): """ For a given material input name, retrieve the corresponding node tree socket. @@ -70,8 +71,15 @@ def get_socket(blender_material: bpy.types.Material, name: str): elif name == "Background": type = bpy.types.ShaderNodeBackground name = "Color" + elif name == "sheenColor": + return get_node_socket(blender_material, bpy.types.ShaderNodeBsdfVelvet, "Color") + elif name == "sheenRoughness": + return get_node_socket(blender_material, bpy.types.ShaderNodeBsdfVelvet, "Sigma") else: - type = bpy.types.ShaderNodeBsdfPrincipled + if volume is False: + type = bpy.types.ShaderNodeBsdfPrincipled + else: + type = bpy.types.ShaderNodeVolumeAbsorption return get_node_socket(blender_material, type, name) @@ -97,6 +105,24 @@ def get_socket_old(blender_material: bpy.types.Material, name: str): return None +def get_socket_original(blender_material: bpy.types.Material, name: str): + """ + For a given material input name, retrieve the corresponding node tree socket in the special glTF node group. + + :param blender_material: a blender material for which to get the socket + :param name: the name of the socket + :return: a blender NodeSocket + """ + gltf_node_group_name = get_gltf_pbr_non_converted_name().lower() + if blender_material.node_tree and blender_material.use_nodes: + nodes = [n for n in blender_material.node_tree.nodes if \ + isinstance(n, bpy.types.ShaderNodeGroup) and n.node_tree.name.lower() == gltf_node_group_name] + inputs = sum([[input for input in node.inputs if input.name == name] for node in nodes], []) + if inputs: + return inputs[0] + + return None + def check_if_is_linked_to_active_output(shader_socket): for link in shader_socket.links: if isinstance(link.to_node, bpy.types.ShaderNodeOutputMaterial) and link.to_node.is_active_output is True: @@ -297,3 +323,12 @@ def previous_node(socket): if prev_socket is not None: return prev_socket.node return None + +#TODOExt is this the same as __get_tex_from_socket from gather_image ? +def has_image_node_from_socket(socket): + result = gltf2_blender_search_node_tree.from_socket( + socket, + gltf2_blender_search_node_tree.FilterByType(bpy.types.ShaderNodeTexImage)) + if not result: + return False + return True diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_image.py b/io_scene_gltf2/blender/exp/gltf2_blender_image.py index 8b9db89a..6730f479 100644 --- a/io_scene_gltf2/blender/exp/gltf2_blender_image.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_image.py @@ -27,6 +27,18 @@ class FillWhite: """Fills a channel with all ones (1.0).""" pass +class StoreData: + def __init__(self, data): + """Store numeric data (not an image channel""" + self.data = data + +class StoreImage: + """ + Store a channel with the channel src_chan from a Blender image. + This channel will be used for numpy calculation (no direct channel mapping) + """ + def __init__(self, image: bpy.types.Image): + self.image = image class ExportImage: """Custom image class. @@ -55,9 +67,13 @@ class ExportImage: def __init__(self, original=None): self.fills = {} + self.stored = {} - # In case of keeping original texture images - self.original = original + self.original = original # In case of keeping original texture images + self.numpy_calc = None + + def set_calc(self, numpy_calc): + self.numpy_calc = numpy_calc # In case of numpy calculation (no direct channel mapping) @staticmethod def from_blender_image(image: bpy.types.Image): @@ -73,6 +89,12 @@ class ExportImage: def fill_image(self, image: bpy.types.Image, dst_chan: Channel, src_chan: Channel): self.fills[dst_chan] = FillImage(image, src_chan) + def store_data(self, identifier, data, type='Image'): + if type == "Image": # This is an image + self.stored[identifier] = StoreImage(data) + else: # This is a numeric value + self.stored[identifier] = StoreData(data) + def fill_white(self, dst_chan: Channel): self.fills[dst_chan] = FillWhite() @@ -81,7 +103,7 @@ class ExportImage: def empty(self) -> bool: if self.original is None: - return not self.fills + return not (self.fills or self.stored) else: return False @@ -103,7 +125,7 @@ class ExportImage: len(set(fill.image.name for fill in self.fills.values())) == 1 ) - def encode(self, mime_type: Optional[str]) -> bytes: + def encode(self, mime_type: Optional[str]) -> Tuple[bytes, bool]: self.file_format = { "image/jpeg": "JPEG", "image/png": "PNG" @@ -111,10 +133,14 @@ class ExportImage: # Happy path = we can just use an existing Blender image if self.__on_happy_path(): - return self.__encode_happy() + return self.__encode_happy(), None - # Unhappy path = we need to create the image self.fills describes. - return self.__encode_unhappy() + # Unhappy path = we need to create the image self.fills describes or self.stores describes + if self.numpy_calc is None: + return self.__encode_unhappy(), None + else: + pixels, width, height, factor = self.numpy_calc(self.stored) + return self.__encode_from_numpy_array(pixels, (width, height)), factor def __encode_happy(self) -> bytes: return self.__encode_from_image(self.blender_image()) @@ -147,7 +173,7 @@ class ExportImage: else: # Image is the wrong size; make a temp copy and scale it. with TmpImageGuard() as guard: - _make_temp_image_copy(guard, src_image=image) + make_temp_image_copy(guard, src_image=image) tmp_image = guard.image tmp_image.scale(width, height) tmp_image.pixels.foreach_get(tmp_buf) @@ -197,7 +223,7 @@ class ExportImage: # Copy to a temp image and save. with TmpImageGuard() as guard: - _make_temp_image_copy(guard, src_image=image) + make_temp_image_copy(guard, src_image=image) tmp_image = guard.image return _encode_temp_image(tmp_image, self.file_format) @@ -228,7 +254,7 @@ class TmpImageGuard: bpy.data.images.remove(self.image, do_unlink=True) -def _make_temp_image_copy(guard: TmpImageGuard, src_image: bpy.types.Image): +def make_temp_image_copy(guard: TmpImageGuard, src_image: bpy.types.Image): """Makes a temporary copy of src_image. Will be cleaned up with guard.""" guard.image = src_image.copy() tmp_image = guard.image diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_texture_specular.py b/io_scene_gltf2/blender/exp/gltf2_blender_texture_specular.py new file mode 100644 index 00000000..6321f128 --- /dev/null +++ b/io_scene_gltf2/blender/exp/gltf2_blender_texture_specular.py @@ -0,0 +1,94 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2018-2022 The glTF-Blender-IO authors. + +import bpy +import numpy as np +from .gltf2_blender_gather_image import StoreImage, StoreData +from .gltf2_blender_image import TmpImageGuard, make_temp_image_copy + +def specular_calculation(stored): + + # See https://gist.github.com/proog128/d627c692a6bbe584d66789a5a6437a33 + + # Find all Blender images used + images = [] + for fill in stored.values(): + if isinstance(fill, StoreImage): + if fill.image not in images: + images.append(fill.image) + + if not images: + # No ImageFills; use a 1x1 white pixel + pixels = np.array([1.0, 1.0, 1.0, 1.0], np.float32) + return pixels, 1, 1 + + width = max(image.size[0] for image in images) + height = max(image.size[1] for image in images) + + buffers = {} + + for identifier, image in [(ident, store.image) for (ident, store) in stored.items() if isinstance(store, StoreImage)]: + tmp_buf = np.empty(width * height * 4, np.float32) + + if image.size[0] == width and image.size[1] == height: + image.pixels.foreach_get(tmp_buf) + else: + # Image is the wrong size; make a temp copy and scale it. + with TmpImageGuard() as guard: + make_temp_image_copy(guard, src_image=image) + tmp_image = guard.image + tmp_image.scale(width, height) + tmp_image.pixels.foreach_get(tmp_buf) + + buffers[identifier] = np.reshape(tmp_buf, [width, height, 4]) + + # keep only needed channels + ## scalar + for i in ['specular', 'specular_tint', 'transmission']: + if i in buffers.keys(): + buffers[i] = buffers[i][:,:,stored[i + "_channel"].data] + else: + buffers[i] = np.full((width, height, 1), stored[i].data) + + # Vector 3 + for i in ['base_color']: + if i in buffers.keys(): + if i + "_channel" not in stored.keys(): + buffers[i] = buffers[i][:,:,:3] + else: + # keep only needed channel + for c in range(3): + if c != stored[i+"_channel"].data: + buffers[i][:, :, c] = 0.0 + buffers[i] = buffers[i][:,:,:3] + else: + buffers[i] = np.full((width, height, 3), stored[i].data[0:3]) + + ior = stored['ior'].data + + # calculation + stack3 = lambda v: np.dstack([v]*3) + + def normalize(c): + luminance = lambda c: 0.3 * c[:,:,0] + 0.6 * c[:,:,1] + 0.1 * c[:,:,2] + l = luminance(c) + # TODOExt Manage all 0 + return c / stack3(l) + + + f0_from_ior = ((ior - 1)/(ior + 1))**2 + tint_strength = (1 - stack3(buffers['specular_tint'])) + normalize(buffers['base_color']) * stack3(buffers['specular_tint']) + out_buf = (1 - stack3(buffers['transmission'])) * (1 / f0_from_ior) * 0.08 * stack3(buffers['specular']) * tint_strength + stack3(buffers['transmission']) * tint_strength + + # Manage values > 1.0 -> Need to apply factor + factor = None + factors = [np.amax(out_buf[:, :, i]) for i in range(3)] + + if any([f > 1.0 for f in factors]): + factor = [1.0 if f < 1.0 else f for f in factors] + out_buf /= factor + + out_buf = np.dstack((out_buf, np.ones((width, height)))) # Set alpha (glTF specular) to 1 + out_buf = np.reshape(out_buf, (width * height * 4)) + + return np.float32(out_buf), width, height, [float(f) for f in factor] if factor else None diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_ior.py b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_ior.py new file mode 100644 index 00000000..ab5b2e7e --- /dev/null +++ b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_ior.py @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2018-2021 The glTF-Blender-IO authors. + +from ...io.com.gltf2_io_constants import GLTF_IOR + +def ior(mh, ior_socket): + try: + ext = mh.pymat.extensions['KHR_materials_ior'] + except Exception: + return + ior = ext.get('ior', GLTF_IOR) + ior_socket.default_value = ior diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_pbrSpecularGlossiness.py b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_pbrSpecularGlossiness.py index bed63f7f..19a394b9 100755 --- a/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_pbrSpecularGlossiness.py +++ b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_pbrSpecularGlossiness.py @@ -22,19 +22,24 @@ def pbr_specular_glossiness(mh): mh.node_tree.links.new(add_node.inputs[0], glossy_node.outputs[0]) mh.node_tree.links.new(add_node.inputs[1], diffuse_node.outputs[0]) - emission_socket, alpha_socket = make_output_nodes( + emission_socket, alpha_socket, _, _ = make_output_nodes( mh, location=(370, 250), + additional_location=None, #No additional location needed for SpecGloss shader_socket=add_node.outputs[0], make_emission_socket=mh.needs_emissive(), make_alpha_socket=not mh.is_opaque(), + make_volume_socket=None, # No possible to have KHR_materials_volume with specular/glossiness + make_velvet_socket=None # No possible to have KHR_materials_volume with specular/glossiness ) - emission( - mh, - location=(-200, 860), - color_socket=emission_socket, - ) + if emission_socket: + emission( + mh, + location=(-200, 860), + color_socket=emission_socket, + strength_socket=emission_socket.node.inputs['Strength'] + ) base_color( mh, diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_sheen.py b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_sheen.py new file mode 100644 index 00000000..3560d094 --- /dev/null +++ b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_sheen.py @@ -0,0 +1,88 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2018-2022 The glTF-Blender-IO authors. + +from ...io.com.gltf2_io import TextureInfo +from .gltf2_blender_texture import texture +from .gltf2_blender_image import BlenderImage +from ..exp.gltf2_blender_image import TmpImageGuard, make_temp_image_copy +import numpy as np +import bpy + +def sheen( mh, + location_sheenColor, + location_sheenRoughness, + sheenColor_socket, + sheenRoughness_socket + ): + + x_sheenColor, y_sheenColor = location_sheenColor + x_sheenRoughness, y_sheenRoughness = location_sheenRoughness + + try: + ext = mh.pymat.extensions['KHR_materials_sheen'] + except Exception: + return + + sheenColorFactor = ext.get('sheenColorFactor', [0.0, 0.0, 0.0]) + tex_info_color = ext.get('sheenColorTexture') + if tex_info_color is not None: + tex_info_color = TextureInfo.from_dict(tex_info_color) + + sheenRoughnessFactor = ext.get('sheenRoughnessFactor', 0.0) + tex_info_roughness = ext.get('sheenRoughnessTexture') + if tex_info_roughness is not None: + tex_info_roughness = TextureInfo.from_dict(tex_info_roughness) + + if tex_info_color is None: + sheenColorFactor.extend([1.0]) + sheenColor_socket.default_value = sheenColorFactor + else: + # Mix sheenColor factor + sheenColorFactor = sheenColorFactor + [1.0] + if sheenColorFactor != [1.0, 1.0, 1.0, 1.0]: + node = mh.node_tree.nodes.new('ShaderNodeMixRGB') + node.label = 'sheenColor Factor' + node.location = x_sheenColor - 140, y_sheenColor + node.blend_type = 'MULTIPLY' + # Outputs + mh.node_tree.links.new(sheenColor_socket, node.outputs[0]) + # Inputs + node.inputs['Fac'].default_value = 1.0 + sheenColor_socket = node.inputs['Color1'] + node.inputs['Color2'].default_value = sheenColorFactor + x_sheenColor -= 200 + + texture( + mh, + tex_info=tex_info_color, + label='SHEEN COLOR', + location=(x_sheenColor, y_sheenColor), + color_socket=sheenColor_socket + ) + + if tex_info_roughness is None: + sheenRoughness_socket.default_value = sheenRoughnessFactor + else: + # Mix sheenRoughness factor + if sheenRoughnessFactor != 1.0: + node = mh.node_tree.nodes.new('ShaderNodeMath') + node.label = 'shennRoughness Factor' + node.location = x_sheenRoughness - 140, y_sheenRoughness + node.operation = 'MULTIPLY' + # Outputs + mh.node_tree.links.new(sheenRoughness_socket, node.outputs[0]) + # Inputs + sheenRoughness_socket = node.inputs[0] + node.inputs[1].default_value = sheenRoughnessFactor + x_sheenRoughness -= 200 + + texture( + mh, + tex_info=tex_info_roughness, + label='SHEEN ROUGHNESS', + location=(x_sheenRoughness, y_sheenRoughness), + is_data=True, + color_socket=None, + alpha_socket=sheenRoughness_socket + ) + return \ No newline at end of file diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_specular.py b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_specular.py new file mode 100644 index 00000000..e4a0c52d --- /dev/null +++ b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_specular.py @@ -0,0 +1,350 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2018-2021 The glTF-Blender-IO authors. + +import bpy +from ...io.com.gltf2_io import TextureInfo +from .gltf2_blender_texture import texture +from io_scene_gltf2.io.com.gltf2_io_constants import GLTF_IOR +from .gltf2_blender_image import BlenderImage +from ..exp.gltf2_blender_image import TmpImageGuard, make_temp_image_copy + + +def specular(mh, location_specular, + location_specular_tint, + specular_socket, + specular_tint_socket, + original_specular_socket, + original_specularcolor_socket, + location_original_specular, + location_original_specularcolor): + x_specular, y_specular = location_specular + x_tint, y_tint = location_specular_tint + + if specular_socket is None: + return + if specular_tint_socket is None: + return + + try: + ext = mh.pymat.extensions['KHR_materials_specular'] + except Exception: + return + + import numpy as np + + # Retrieve image names + try: + tex_info = mh.pymat.pbr_metallic_roughness.base_color_texture + pytexture = mh.gltf.data.textures[tex_info.index] + pyimg = mh.gltf.data.images[pytexture.source] + base_color_image_name = pyimg.blender_image_name + except: + base_color_image_name = None + + # First check if we need a texture or not -> retrieve all info needed + specular_factor = ext.get('specularFactor', 1.0) + tex_specular_info = ext.get('specularTexture') + if tex_specular_info is not None: + tex_specular_info = TextureInfo.from_dict(tex_specular_info) + + specular_color_factor = np.array(ext.get('specularColorFactor', [1.0, 1.0, 1.0])[:3]) + tex_specular_color_info = ext.get('specularColorTexture') + if tex_specular_color_info is not None: + tex_specular_color_info = TextureInfo.from_dict(tex_specular_color_info) + + base_color_not_linked = base_color_image_name is None + base_color = np.array(mh.pymat.pbr_metallic_roughness.base_color_factor or [1, 1, 1]) + tex_base_color = mh.pymat.pbr_metallic_roughness.base_color_texture + base_color = base_color[:3] + + try: + ext_transmission = mh.pymat.extensions['KHR_materials_transmission'] + transmission_factor = ext_transmission.get('transmissionFactor', 0) + tex_transmission_info = ext_transmission.get('transmissionTexture') + if tex_transmission_info is not None: + tex_transmission_info = TextureInfo.from_dict(tex_transmission_info) + pytexture = mh.gltf.data.textures[tex_transmission_info.index] + pyimg = mh.gltf.data.images[pytexture.source] + transmission_image_name = pyimg.blender_image_name + else: + transmission_image_name = None + except Exception: + transmission_factor = 0 + tex_transmission_info = None + transmission_image_name = None + + transmission_not_linked = transmission_image_name is None + + try: + ext_ior = mh.pymat.extensions['KHR_materials_ior'] + ior = ext_ior.get('ior', GLTF_IOR) + except: + ior = GLTF_IOR + + use_texture = tex_specular_info is not None or tex_specular_color_info is not None \ + or transmission_not_linked is False or base_color_not_linked is False + + + # Before creating converted textures, + # Also plug non converted data into glTF PBR Non Converted Extensions node + original_specular( mh, + specular_factor, + tex_specular_info, + specular_color_factor, + tex_specular_color_info, + original_specular_socket, + original_specularcolor_socket, + location_original_specular, + location_original_specularcolor + ) + + + if not use_texture: + + def luminance(c): + return 0.3 * c[0] + 0.6 * c[1] + 0.1 * c[2] + + def normalize(c): + assert(len(c) == 3) + l = luminance(c) + if l == 0: + return c + return np.array([c[0] / l, c[1] / l, c[2] / l]) + + f0_from_ior = ((ior - 1)/(ior + 1))**2 + lum_specular_color = luminance(specular_color_factor) + blender_specular = ((lum_specular_color - transmission_factor) / (1 - transmission_factor)) * (1 / 0.08) * f0_from_ior + blender_specular_tint = luminance((normalize(specular_color_factor) - 1) / (normalize(base_color) - 1)) + if blender_specular_tint < 0 or blender_specular_tint > 1: + # TODOExt Warning clamping + blender_specular_tint = np.maximum(np.minimum(blender_specular_tint, 1), 0) + + specular_socket.default_value = blender_specular + specular_tint_socket.default_value = blender_specular_tint + # Note: blender_specular can be greater 1. The Blender documentation permits this. + + return + else: + # Need to create a texture + # First, retrieve and create all images needed + + # Base Color is already created + # Transmission is already created + # specularTexture is just created by original specular function + specular_image_name = None + try: + pytexture = mh.gltf.data.textures[tex_specular_info.index] + pyimg = mh.gltf.data.images[pytexture.source] + specular_image_name = pyimg.blender_image_name + except: + specular_image_name = None + + + # specularColorTexture is just created by original specular function + specularcolor_image_name = None + try: + pytexture = mh.gltf.data.textures[tex_specular_color_info.index] + pyimg = mh.gltf.data.images[pytexture.source] + specularcolor_image_name = pyimg.blender_image_name + except: + specularcolor_image_name = None + + stack3 = lambda v: np.dstack([v]*3) + + texts = { + base_color_image_name : 'basecolor', + transmission_image_name : 'transmission', + specularcolor_image_name : 'speccolor', + specular_image_name: 'spec' + } + images = [(name, bpy.data.images[name]) for name in [base_color_image_name, transmission_image_name, specularcolor_image_name, specular_image_name] if name is not None] + + width = max(image[1].size[0] for image in images) + height = max(image[1].size[1] for image in images) + + buffers = {} + for name, image in images: + tmp_buf = np.empty(width * height * 4, np.float32) + + if image.size[0] == width and image.size[1] == height: + image.pixels.foreach_get(tmp_buf) + else: + # Image is the wrong size; make a temp copy and scale it. + with TmpImageGuard() as guard: + make_temp_image_copy(guard, src_image=image) + tmp_image = guard.image + tmp_image.scale(width, height) + tmp_image.pixels.foreach_get(tmp_buf) + + buffers[texts[name]] = np.reshape(tmp_buf, [width, height, 4]) + buffers[texts[name]] = buffers[texts[name]][:,:,:3] + + # Manage factors + if name == transmission_image_name: + buffers[texts[name]] = stack3(buffers[texts[name]][:,:,0]) # Transmission : keep only R channel + + buffers[texts[name]] *= stack3(transmission_factor) + + elif name == base_color_image_name: + buffers[texts[name]] *= base_color + + elif name == specularcolor_image_name: + buffers[texts[name]] *= specular_color_factor + + # Create buffer if there is no image + if 'basecolor' not in buffers.keys(): + buffers['basecolor'] = np.full((width, height, 3), base_color) + if 'transmission' not in buffers.keys(): + buffers['transmission'] = np.full((width, height, 3), transmission_factor) + if 'speccolor' not in buffers.keys(): + buffers['speccolor'] = np.full((width, height, 3), specular_color_factor) + + # Calculation + + luminance = lambda c: 0.3 * c[:,:,0] + 0.6 * c[:,:,1] + 0.1 * c[:,:,2] + def normalize(c): + l = luminance(c) + if np.all(l == 0.0): + return np.array(c) + return c / stack3(l) + + f0_from_ior = ((ior - 1)/(ior + 1))**2 + lum_specular_color = stack3(luminance(buffers['speccolor'])) + blender_specular = ((lum_specular_color - buffers['transmission']) / (1 - buffers['transmission'])) * (1 / 0.08) * f0_from_ior + blender_specular_tint = luminance((normalize(buffers['speccolor']) - 1) / (normalize(buffers['basecolor']) - 1)) + np.nan_to_num(blender_specular_tint, copy=False) + blender_specular_tint = np.clip(blender_specular_tint, 0.0, 1.0) + blender_specular_tint = stack3(blender_specular_tint) + + blender_specular = np.dstack((blender_specular, np.ones((width, height)))) # Set alpha to 1 + blender_specular_tint = np.dstack((blender_specular_tint, np.ones((width, height)))) # Set alpha to 1 + + # Check if we really need to create a texture + blender_specular_tex_not_needed = np.all(np.isclose(blender_specular, blender_specular[0][0])) + blender_specular_tint_tex_not_needed = np.all(np.isclose(blender_specular_tint, blender_specular_tint[0][0])) + + if blender_specular_tex_not_needed == True: + lum = lambda c: 0.3 * c[0] + 0.6 * c[1] + 0.1 * c[2] + specular_socket.default_value = lum(blender_specular[0][0][:3]) + else: + blender_specular = np.reshape(blender_specular, width * height * 4) + # Create images in Blender, width and height are dummy values, then set packed file data + blender_image_spec = bpy.data.images.new('Specular', width, height) + blender_image_spec.pixels.foreach_set(np.float32(blender_specular)) + blender_image_spec.pack() + + # Create Textures in Blender + tex_info = tex_specular_info + if tex_info is None: + tex_info = tex_specular_color_info + if tex_info is None: + tex_info = tex_transmission_info + if tex_info is None: + tex_info = tex_base_color + + texture( + mh, + tex_info=tex_info, + label='SPECULAR', + location=(x_specular, y_specular), + is_data=True, + color_socket=specular_socket, + forced_image=blender_image_spec + ) + + if blender_specular_tint_tex_not_needed == True: + lum = lambda c: 0.3 * c[0] + 0.6 * c[1] + 0.1 * c[2] + specular_tint_socket.default_value = lum(blender_specular_tint[0][0]) + else: + blender_specular_tint = np.reshape(blender_specular_tint, width * height * 4) + # Create images in Blender, width and height are dummy values, then set packed file data + blender_image_tint = bpy.data.images.new('Specular Tint', width, height) + blender_image_tint.pixels.foreach_set(np.float32(blender_specular_tint)) + blender_image_tint.pack() + + # Create Textures in Blender + tex_info = tex_specular_color_info + if tex_info is None: + tex_info = tex_specular_info + if tex_info is None: + tex_info = tex_transmission_info + if tex_info is None: + tex_info = tex_base_color + + texture( + mh, + tex_info=tex_info, + label='SPECULAR TINT', + location=(x_tint, y_tint), + is_data=True, + color_socket=specular_tint_socket, + forced_image=blender_image_tint + ) + +def original_specular( mh, + specular_factor, + tex_specular_info, + specular_color_factor, + tex_specular_color_info, + original_specular_socket, + original_specularcolor_socket, + location_original_specular, + location_original_specularcolor + ): + + x_specular, y_specular = location_original_specular + x_specularcolor, y_specularcolor = location_original_specularcolor + + if tex_specular_info is None: + original_specular_socket.default_value = specular_factor + else: + # Mix specular factor + if specular_factor != 1.0: + node = mh.node_tree.nodes.new('ShaderNodeMath') + node.label = 'Specular Factor' + node.location = x_specular - 140, y_specular + node.operation = 'MULTIPLY' + # Outputs + mh.node_tree.links.new(original_specular_socket, node.outputs[0]) + # Inputs + original_specular_socket = node.inputs[0] + node.inputs[1].default_value = specular_factor + x_specular -= 200 + + texture( + mh, + tex_info=tex_specular_info, + label='SPECULAR', + location=(x_specular, y_specular), + is_data=True, + color_socket=None, + alpha_socket=original_specular_socket + ) + + if tex_specular_color_info is None: + specular_color_factor = list(specular_color_factor) + specular_color_factor.extend([1.0]) + original_specularcolor_socket.default_value = specular_color_factor + else: + specular_color_factor = list(specular_color_factor) + [1.0] + if specular_color_factor != [1.0, 1.0, 1.0, 1.0]: + # Mix specularColorFactor + node = mh.node_tree.nodes.new('ShaderNodeMixRGB') + node.label = 'SpecularColor Factor' + node.location = x_specularcolor - 140, y_specularcolor + node.blend_type = 'MULTIPLY' + # Outputs + mh.node_tree.links.new(original_specularcolor_socket, node.outputs[0]) + # Inputs + node.inputs['Fac'].default_value = 1.0 + original_specularcolor_socket = node.inputs['Color1'] + node.inputs['Color2'].default_value = specular_color_factor + x_specularcolor -= 200 + + texture( + mh, + tex_info=tex_specular_color_info, + label='SPECULAR COLOR', + location=(x_specularcolor, y_specularcolor), + color_socket=original_specularcolor_socket, + ) \ No newline at end of file diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_transmission.py b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_transmission.py new file mode 100644 index 00000000..dab25d14 --- /dev/null +++ b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_transmission.py @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2018-2022 The glTF-Blender-IO authors. + + +from ...io.com.gltf2_io import TextureInfo, MaterialNormalTextureInfoClass +from .gltf2_blender_texture import texture + + +# [Texture] => [Separate R] => [Transmission Factor] => +def transmission(mh, location, transmission_socket): + x, y = location + try: + ext = mh.pymat.extensions['KHR_materials_transmission'] + except Exception: + return + transmission_factor = ext.get('transmissionFactor', 0) + + # Default value is 0, so no transmission + if transmission_factor == 0: + return + + tex_info = ext.get('transmissionTexture') + if tex_info is not None: + tex_info = TextureInfo.from_dict(tex_info) + + if transmission_socket is None: + return + + if tex_info is None: + transmission_socket.default_value = transmission_factor + return + + # Mix transmission factor + if transmission_factor != 1: + node = mh.node_tree.nodes.new('ShaderNodeMath') + node.label = 'Transmission Factor' + node.location = x - 140, y + node.operation = 'MULTIPLY' + # Outputs + mh.node_tree.links.new(transmission_socket, node.outputs[0]) + # Inputs + transmission_socket = node.inputs[0] + node.inputs[1].default_value = transmission_factor + + x -= 200 + + # Separate RGB + node = mh.node_tree.nodes.new('ShaderNodeSeparateColor') + node.location = x - 150, y - 75 + # Outputs + mh.node_tree.links.new(transmission_socket, node.outputs['Red']) + # Inputs + transmission_socket = node.inputs[0] + + x -= 200 + + texture( + mh, + tex_info=tex_info, + label='TRANSMISSION', + location=(x, y), + is_data=True, + color_socket=transmission_socket, + ) diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_unlit.py b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_unlit.py index 48ad46fd..1ffdc7e4 100644 --- a/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_unlit.py +++ b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_unlit.py @@ -24,12 +24,15 @@ def unlit(mh): mh.node_tree.links.new(mix_node.inputs[1], transparent_node.outputs[0]) mh.node_tree.links.new(mix_node.inputs[2], emission_node.outputs[0]) - _emission_socket, alpha_socket = make_output_nodes( + _emission_socket, alpha_socket, _, _ = make_output_nodes( mh, location=(420, 280) if mh.is_opaque() else (150, 130), + additional_location=None, #No additional location needed for Unlit shader_socket=mix_node.outputs[0], make_emission_socket=False, make_alpha_socket=not mh.is_opaque(), + make_volume_socket=None, # Not possible to have KHR_materials_volume with unlit + make_velvet_socket=None #Not possible to have KHR_materials_sheen with unlit ) base_color( diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_volume.py b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_volume.py new file mode 100644 index 00000000..f909c7f6 --- /dev/null +++ b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_volume.py @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2018-2021 The glTF-Blender-IO authors. + +from ...io.com.gltf2_io import TextureInfo, MaterialNormalTextureInfoClass +from .gltf2_blender_texture import texture + +def volume(mh, location, volume_socket, thickness_socket): + # implementation based on https://github.com/KhronosGroup/glTF-Blender-IO/issues/1454#issuecomment-928319444 + try: + ext = mh.pymat.extensions['KHR_materials_volume'] + except Exception: + return + + # Attenuation Color + attenuationColor = \ + mh.pymat.extensions['KHR_materials_volume'] \ + .get('attenuationColor') + # glTF is color3, Blender adds alpha + if attenuationColor is None: + attenuationColor = [1.0, 1.0, 1.0, 1.0] + else: + attenuationColor.extend([1.0]) + volume_socket.node.inputs[0].default_value = attenuationColor + + # Attenuation Distance / Density + attenuationDistance = mh.pymat.extensions['KHR_materials_volume'].get('attenuationDistance') + if attenuationDistance is None: + density = 0 + else: + density = 1.0 / attenuationDistance + volume_socket.node.inputs[1].default_value = density + + # thicknessFactor / thicknessTexture + x, y = location + try: + ext = mh.pymat.extensions['KHR_materials_volume'] + except Exception: + return + thickness_factor = ext.get('thicknessFactor', 0) + tex_info = ext.get('thicknessTexture') + if tex_info is not None: + tex_info = TextureInfo.from_dict(tex_info) + + if thickness_socket is None: + return + + if tex_info is None: + thickness_socket.default_value = thickness_factor + return + + # Mix thickness factor + if thickness_factor != 1: + node = mh.node_tree.nodes.new('ShaderNodeMath') + node.label = 'Thickness Factor' + node.location = x - 140, y + node.operation = 'MULTIPLY' + # Outputs + mh.node_tree.links.new(thickness_socket, node.outputs[0]) + # Inputs + thickness_socket = node.inputs[0] + node.inputs[1].default_value = thickness_factor + + x -= 200 + + # Separate RGB + node = mh.node_tree.nodes.new('ShaderNodeSeparateColor') + node.location = x - 150, y - 75 + # Outputs + mh.node_tree.links.new(thickness_socket, node.outputs['Green']) + # Inputs + thickness_socket = node.inputs[0] + + x -= 200 + + texture( + mh, + tex_info=tex_info, + label='THICKNESS', + location=(x, y), + is_data=True, + color_socket=thickness_socket, + ) diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py b/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py index f2556465..33713b97 100755 --- a/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py +++ b/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py @@ -4,6 +4,8 @@ import bpy from mathutils import Vector, Quaternion, Matrix from .gltf2_blender_scene import BlenderScene +from ..com.gltf2_blender_ui import gltf2_KHR_materials_variants_variant, gltf2_KHR_materials_variants_primitive, gltf2_KHR_materials_variants_default_material +from .gltf2_blender_material import BlenderMaterial class BlenderGlTF(): @@ -180,6 +182,10 @@ class BlenderGlTF(): mesh.shapekey_names.append(shapekey_name) + # Manage KHR_materials_variants + BlenderGlTF.manage_material_variants(gltf) + + @staticmethod def find_unused_name(haystack, desired_name): """Finds a name not in haystack and <= 63 UTF-8 bytes. @@ -201,3 +207,25 @@ class BlenderGlTF(): suffix = '.%03d' % cntr cntr += 1 + + + @staticmethod + def manage_material_variants(gltf): + if not (gltf.data.extensions is not None and 'KHR_materials_variants' in gltf.data.extensions.keys()): + gltf.KHR_materials_variants = False + return + + gltf.KHR_materials_variants = True + # If there is no KHR_materials_variants data in scene, create it + if bpy.context.preferences.addons['io_scene_gltf2'].preferences.KHR_materials_variants_ui is False: + bpy.context.preferences.addons['io_scene_gltf2'].preferences.KHR_materials_variants_ui = True + # Setting preferences as dirty, to be sure that option is saved + bpy.context.preferences.is_dirty = True + + if len(bpy.data.scenes[0].gltf2_KHR_materials_variants_variants) > 0: + bpy.data.scenes[0].gltf2_KHR_materials_variants_variants.clear() + + for idx_variant, variant in enumerate(gltf.data.extensions['KHR_materials_variants']['variants']): + var = bpy.data.scenes[0].gltf2_KHR_materials_variants_variants.add() + var.name = variant['name'] + var.variant_idx = idx_variant diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_material.py b/io_scene_gltf2/blender/imp/gltf2_blender_material.py index 1d18c65d..9a582f7e 100755 --- a/io_scene_gltf2/blender/imp/gltf2_blender_material.py +++ b/io_scene_gltf2/blender/imp/gltf2_blender_material.py @@ -48,6 +48,11 @@ class BlenderMaterial(): else: pbr_metallic_roughness(mh) + # Manage KHR_materials_variants + # We need to store link between material idx in glTF and Blender Material id + if gltf.KHR_materials_variants is True: + gltf.variant_mapping[str(material_idx) + str(vertex_color)] = mat + import_user_extensions('gather_import_material_after_hook', gltf, pymaterial, vertex_color, mat) @staticmethod diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_mesh.py b/io_scene_gltf2/blender/imp/gltf2_blender_mesh.py index a3c1bd34..b886dd25 100755 --- a/io_scene_gltf2/blender/imp/gltf2_blender_mesh.py +++ b/io_scene_gltf2/blender/imp/gltf2_blender_mesh.py @@ -11,6 +11,7 @@ from .gltf2_blender_material import BlenderMaterial from ...io.com.gltf2_io_debug import print_console from .gltf2_io_draco_compression_extension import decode_primitive from io_scene_gltf2.io.imp.gltf2_io_user_extensions import import_user_extensions +from ..com.gltf2_blender_ui import gltf2_KHR_materials_variants_primitive, gltf2_KHR_materials_variants_variant, gltf2_KHR_materials_variants_default_material class BlenderMesh(): @@ -343,12 +344,19 @@ def do_primitives(gltf, mesh_idx, skin_idx, mesh, ob): # ---- # Assign materials to faces has_materials = any(prim.material is not None for prim in pymesh.primitives) + # Even if no primitive have material, we need to create slots if some primitives have some variant + if has_materials is False: + has_materials = any(prim.extensions is not None and 'KHR_materials_variants' in prim.extensions.keys() for prim in pymesh.primitives) + + has_variant = prim.extensions is not None and 'KHR_materials_variants' in prim.extensions.keys() \ + and 'mappings' in prim.extensions['KHR_materials_variants'].keys() + if has_materials: material_indices = np.empty(num_faces, dtype=np.uint32) empty_material_slot_index = None f = 0 - for prim in pymesh.primitives: + for idx_prim, prim in enumerate(pymesh.primitives): if prim.material is not None: # Get the material pymaterial = gltf.data.materials[prim.material] @@ -358,19 +366,55 @@ def do_primitives(gltf, mesh_idx, skin_idx, mesh, ob): material_name = pymaterial.blender_material[vertex_color] # Put material in slot (if not there) - if material_name not in mesh.materials: + if not has_variant: + if material_name not in mesh.materials: + mesh.materials.append(bpy.data.materials[material_name]) + material_index = mesh.materials.find(material_name) + else: + # In case of variant, do not merge slots mesh.materials.append(bpy.data.materials[material_name]) - material_index = mesh.materials.find(material_name) + material_index = len(mesh.materials) - 1 else: - if empty_material_slot_index is None: + if not has_variant: + if empty_material_slot_index is None: + mesh.materials.append(None) + empty_material_slot_index = len(mesh.materials) - 1 + material_index = empty_material_slot_index + else: + # In case of variant, do not merge slots mesh.materials.append(None) - empty_material_slot_index = len(mesh.materials) - 1 - material_index = empty_material_slot_index + material_index = len(mesh.materials) - 1 material_indices[f:f + prim.num_faces].fill(material_index) f += prim.num_faces + # Manage variants + if has_variant: + + # Store default material + default_mat = mesh.gltf2_variant_default_materials.add() + default_mat.material_slot_index = material_index + default_mat.default_material = bpy.data.materials[material_name] if prim.material is not None else None + + for mapping in prim.extensions['KHR_materials_variants']['mappings']: + # Store, for each variant, the material link to this primitive + + variant_primitive = mesh.gltf2_variant_mesh_data.add() + variant_primitive.material_slot_index = material_index + if 'material' not in mapping.keys(): + # Default material + variant_primitive.material = None + else: + vertex_color = 'COLOR_0' if 'COLOR_0' in prim.attributes else None + if str(mapping['material']) + str(vertex_color) not in gltf.variant_mapping.keys(): + BlenderMaterial.create(gltf, mapping['material'], vertex_color) + variant_primitive.material = gltf.variant_mapping[str(mapping['material']) + str(vertex_color)] + + for variant in mapping['variants']: + vari = variant_primitive.variants.add() + vari.variant.variant_idx = variant + mesh.polygons.foreach_set('material_index', material_indices) # ---- diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py b/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py index 4c32c35d..6f6279dc 100755 --- a/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py +++ b/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py @@ -1,13 +1,20 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2018-2021 The glTF-Blender-IO authors. +from re import M import bpy from ...io.com.gltf2_io import TextureInfo, MaterialPBRMetallicRoughness from ..com.gltf2_blender_material_helpers import get_gltf_node_name, create_settings_group +from ..com.gltf2_blender_material_helpers import get_gltf_pbr_non_converted_name, create_gltf_pbr_non_converted_group from .gltf2_blender_texture import texture from .gltf2_blender_KHR_materials_clearcoat import \ clearcoat, clearcoat_roughness, clearcoat_normal - +from .gltf2_blender_KHR_materials_transmission import transmission +from .gltf2_blender_KHR_materials_ior import ior +from .gltf2_blender_KHR_materials_volume import volume +from .gltf2_blender_KHR_materials_specular import specular +from .gltf2_blender_KHR_materials_sheen import sheen +from ...io.com.gltf2_io_constants import GLTF_IOR class MaterialHelper: """Helper class. Stores material stuff to be passed around everywhere.""" @@ -20,6 +27,8 @@ class MaterialHelper: if pymat.pbr_metallic_roughness is None: pymat.pbr_metallic_roughness = \ MaterialPBRMetallicRoughness.from_dict({}) + self.settings_node = None + self.original_pbr_node = None def is_opaque(self): alpha_mode = self.pymat.alpha_mode @@ -36,15 +45,58 @@ def pbr_metallic_roughness(mh: MaterialHelper): """Creates node tree for pbrMetallicRoughness materials.""" pbr_node = mh.node_tree.nodes.new('ShaderNodeBsdfPrincipled') pbr_node.location = 10, 300 + additional_location = 40, -370 # For occlusion and/or volume / original PBR extensions + + # Set IOR to 1.5, this is the default in glTF + # This value may be overidden later if IOR extension is set on file + pbr_node.inputs['IOR'].default_value = GLTF_IOR - make_output_nodes( + if mh.pymat.occlusion_texture is not None: + if mh.settings_node is None: + mh.settings_node = make_settings_node(mh) + mh.settings_node.location = additional_location + mh.settings_node.width = 180 + additional_location = additional_location[0], additional_location[1] - 150 + + need_volume_node = False + if mh.pymat.extensions and 'KHR_materials_volume' in mh.pymat.extensions: + if 'thicknessFactor' in mh.pymat.extensions['KHR_materials_volume'] \ + and mh.pymat.extensions['KHR_materials_volume']['thicknessFactor'] != 0.0: + + need_volume_node = True + + # We also need glTF Settings Node, to set thicknessFactor and thicknessTexture + mh.settings_node = make_settings_node(mh) + mh.settings_node.location = additional_location + mh.settings_node.width = 180 + volume_location = additional_location + additional_location = additional_location[0], additional_location[1] - 150 + + need_velvet_node = False + if mh.pymat.extensions and 'KHR_materials_sheen' in mh.pymat.extensions: + need_velvet_node = True + + _, _, volume_socket, velvet_node = make_output_nodes( mh, location=(250, 260), + additional_location=additional_location, shader_socket=pbr_node.outputs[0], - make_emission_socket=False, - make_alpha_socket=False, + make_emission_socket=False, # is managed by Principled shader node + make_alpha_socket=False, # is managed by Principled shader node + make_volume_socket=need_volume_node, + make_velvet_socket=need_velvet_node ) + if mh.pymat.extensions and 'KHR_materials_specular' in mh.pymat.extensions: + # We need glTF PBR Non Converted Extensions Node + mh.original_pbr_node = make_pbr_non_converted_extensions_node(mh) + mh.original_pbr_node.location = additional_location + mh.original_pbr_node.width = 180 + additional_location = additional_location[0], additional_location[1] - 150 + + if mh.pymat.extensions and 'KHR_materials_sheen': + pass #TOTOEXT + locs = calc_locations(mh) emission( @@ -75,13 +127,10 @@ def pbr_metallic_roughness(mh: MaterialHelper): ) if mh.pymat.occlusion_texture is not None: - node = make_settings_node(mh) - node.location = 40, -370 - node.width = 180 occlusion( mh, location=locs['occlusion'], - occlusion_socket=node.inputs['Occlusion'], + occlusion_socket=mh.settings_node.inputs['Occlusion'], ) clearcoat( @@ -102,6 +151,46 @@ def pbr_metallic_roughness(mh: MaterialHelper): normal_socket=pbr_node.inputs['Clearcoat Normal'], ) + transmission( + mh, + location=locs['transmission'], + transmission_socket=pbr_node.inputs['Transmission'] + ) + + if need_volume_node: + volume( + mh, + location=locs['volume_thickness'], + volume_socket=volume_socket, + thickness_socket=mh.settings_node.inputs[1] if mh.settings_node else None + ) + + specular( + mh, + location_specular=locs['specularTexture'], + location_specular_tint=locs['specularColorTexture'], + specular_socket=pbr_node.inputs['Specular'], + specular_tint_socket=pbr_node.inputs['Specular Tint'], + original_specular_socket=mh.original_pbr_node.inputs[0] if mh.original_pbr_node else None, + original_specularcolor_socket=mh.original_pbr_node.inputs[1] if mh.original_pbr_node else None, + location_original_specular=locs['original_specularTexture'], + location_original_specularcolor=locs['original_specularColorTexture'] + ) + + if need_velvet_node: + sheen( + mh, + location_sheenColor=locs['sheenColorTexture'], + location_sheenRoughness=locs['sheenRoughnessTexture'], + sheenColor_socket=velvet_node.inputs[0], + sheenRoughness_socket=velvet_node.inputs[1] + ) + + ior( + mh, + ior_socket=pbr_node.inputs['IOR'] + ) + def calc_locations(mh): """Calculate locations to place each bit of the node graph at.""" @@ -116,18 +205,53 @@ def calc_locations(mh): except Exception: clearcoat_ext = {} + try: + transmission_ext = mh.pymat.exntesions['KHR_materials_transmission'] + except: + transmission_ext = {} + + try: + volume_ext = mh.pymat.extensions['KHR_materials_volume'] + except Exception: + volume_ext = {} + + try: + specular_ext = mh.pymat.extensions['KHR_materials_specular'] + except: + specular_ext = {} + + try: + sheen_ext = mh.pymat.extensions['KHR_materials_sheen'] + except: + sheen_ext = {} + + locs['sheenColorTexture'] = (x, y) + if 'sheenColorTexture' in sheen_ext: + y -= height + locs['sheenRoughnessTexture'] = (x, y) + if 'sheenRoughnessTexture' in sheen_ext: + y -= height locs['base_color'] = (x, y) if mh.pymat.pbr_metallic_roughness.base_color_texture is not None or mh.vertex_color: y -= height locs['metallic_roughness'] = (x, y) if mh.pymat.pbr_metallic_roughness.metallic_roughness_texture is not None: y -= height + locs['specularTexture'] = (x, y) + if 'specularTexture' in specular_ext: + y -= height + locs['specularColorTexture'] = (x, y) + if 'specularColorTexture' in specular_ext: + y -= height locs['clearcoat'] = (x, y) if 'clearcoatTexture' in clearcoat_ext: y -= height locs['clearcoat_roughness'] = (x, y) if 'clearcoatRoughnessTexture' in clearcoat_ext: y -= height + locs['transmission'] = (x, y) + if 'transmissionTexture' in transmission_ext: + y -= height locs['emission'] = (x, y) if mh.pymat.emissive_texture is not None: y -= height @@ -140,6 +264,22 @@ def calc_locations(mh): locs['occlusion'] = (x, y) if mh.pymat.occlusion_texture is not None: y -= height + locs['volume_thickness'] = (x, y) + if 'thicknessTexture' in volume_ext: + y -= height + locs['original_specularTexture'] = (x, y) + if 'specularTexture' in specular_ext: + y -= height + locs['original_specularColorTexture'] = (x, y) + if 'specularColorTexture' in specular_ext: + y -= height + locs['original_sheenColorTexture'] = (x, y) + if 'sheenColorTexture' in sheen_ext: + y -= height + locs['original_sheenRoughnessTexture'] = (x, y) + if 'sheenRoughnessTexture' in sheen_ext: + y -= height + # Center things total_height = -y @@ -157,21 +297,29 @@ def calc_locations(mh): # [Texture] => [Emissive Factor] => -def emission(mh: MaterialHelper, location, color_socket, strength_socket=None): +def emission(mh: MaterialHelper, location, color_socket, strength_socket): x, y = location emissive_factor = mh.pymat.emissive_factor or [0, 0, 0] + strength = 1 + try: + # Get strength from KHR_materials_emissive_strength if exists + strength = mh.pymat.extensions['KHR_materials_emissive_strength']['emissiveStrength'] + except Exception: + pass + if color_socket is None: return if mh.pymat.emissive_texture is None: color_socket.default_value = emissive_factor + [1] + strength_socket.default_value = strength return # Put grayscale emissive factors into the Emission Strength e0, e1, e2 = emissive_factor if strength_socket and e0 == e1 == e2: - strength_socket.default_value = e0 + strength_socket.default_value = e0 * strength # Otherwise, use a multiply node for it else: @@ -189,6 +337,8 @@ def emission(mh: MaterialHelper, location, color_socket, strength_socket=None): x -= 200 + strength_socket.default_value = strength + texture( mh, tex_info=mh.pymat.emissive_texture, @@ -470,13 +620,16 @@ def occlusion(mh: MaterialHelper, location, occlusion_socket): def make_output_nodes( mh: MaterialHelper, location, + additional_location, shader_socket, make_emission_socket, make_alpha_socket, + make_volume_socket, + make_velvet_socket, # For sheen ): """ Creates the Material Output node and connects shader_socket to it. - If requested, it can also create places to hookup the emission/alpha + If requested, it can also create places to hookup the emission/alpha.sheen in between shader_socket and the Output node too. :return: a pair containing the sockets you should put emission and alpha @@ -484,6 +637,7 @@ def make_output_nodes( """ x, y = location emission_socket = None + velvet_node = None alpha_socket = None # Create an Emission node and add it to the shader. @@ -512,6 +666,31 @@ def make_output_nodes( x += 380 y += 125 + # Create an Velvet node add add it to the shader + # Note that you can not have Emission & Velvet at the same time + if make_velvet_socket: + # Velvet + node = mh.node_tree.nodes.new("ShaderNodeBsdfVelvet") + node.location = x + 50, y + 250 + # Node + velvet_node = node + # Outputs + velvet_output = node.outputs[0] + + # Add + node = mh.node_tree.nodes.new('ShaderNodeAddShader') + node.location = x + 250, y + 160 + # Inputs + mh.node_tree.links.new(node.inputs[0], velvet_output) + mh.node_tree.links.new(node.inputs[1], shader_socket) + # Outputs + shader_socket = node.outputs[0] + + + x += 380 + y += 125 + + # Mix with a Transparent BSDF. Mixing factor is the alpha value. if make_alpha_socket: # Transparent BSDF @@ -535,12 +714,23 @@ def make_output_nodes( y -= 210 # Material output - node = mh.node_tree.nodes.new('ShaderNodeOutputMaterial') - node.location = x + 70, y + 10 + node_output = mh.node_tree.nodes.new('ShaderNodeOutputMaterial') + node_output.location = x + 70, y + 10 + # Outputs - mh.node_tree.links.new(node.inputs[0], shader_socket) + mh.node_tree.links.new(node_output.inputs[0], shader_socket) + + # Volume Node + volume_socket = None + if make_volume_socket: + node = mh.node_tree.nodes.new('ShaderNodeVolumeAbsorption') + node.location = additional_location + # Outputs + mh.node_tree.links.new(node_output.inputs[1], node.outputs[0]) + volume_socket = node.outputs[0] - return emission_socket, alpha_socket + + return emission_socket, alpha_socket, volume_socket, velvet_node def make_settings_node(mh): @@ -560,3 +750,21 @@ def get_settings_group(): # Create a new node group gltf_node_group = create_settings_group(gltf_node_group_name) return gltf_node_group + +def make_pbr_non_converted_extensions_node(mh): + """ + Make a Group node with a hookup for PBR Non Converted Extensions. No effect in Blender, but + used to tell the exporter what the original map(s) should be. + """ + node = mh.node_tree.nodes.new('ShaderNodeGroup') + node.node_tree = get_pbr_non_converted_extensions_group() + return node + +def get_pbr_non_converted_extensions_group(): + gltf_node_group_name = get_gltf_pbr_non_converted_name() + if gltf_node_group_name in bpy.data.node_groups: + gltf_node_group = bpy.data.node_groups[gltf_node_group_name] + else: + # Create a new node group + gltf_node_group = create_gltf_pbr_non_converted_group(gltf_node_group_name) + return gltf_node_group \ No newline at end of file diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_texture.py b/io_scene_gltf2/blender/imp/gltf2_blender_texture.py index 24c9df7c..12e6d594 100644 --- a/io_scene_gltf2/blender/imp/gltf2_blender_texture.py +++ b/io_scene_gltf2/blender/imp/gltf2_blender_texture.py @@ -17,6 +17,7 @@ def texture( color_socket, alpha_socket=None, is_data=False, + forced_image=None ): """Creates nodes for a TextureInfo and hooks up the color/alpha outputs.""" x, y = location @@ -36,12 +37,15 @@ def texture( tex_img.location = x - 240, y tex_img.label = label # Get image - if pytexture.source is not None: - BlenderImage.create(mh.gltf, pytexture.source) - pyimg = mh.gltf.data.images[pytexture.source] - blender_image_name = pyimg.blender_image_name - if blender_image_name: - tex_img.image = bpy.data.images[blender_image_name] + if forced_image is None: + if pytexture.source is not None: + BlenderImage.create(mh.gltf, pytexture.source) + pyimg = mh.gltf.data.images[pytexture.source] + blender_image_name = pyimg.blender_image_name + if blender_image_name: + tex_img.image = bpy.data.images[blender_image_name] + else: + tex_img.image = forced_image # Set colorspace for data images if is_data: if tex_img.image: @@ -49,7 +53,8 @@ def texture( # Set filtering set_filtering(tex_img, pysampler) # Outputs - mh.node_tree.links.new(color_socket, tex_img.outputs['Color']) + if color_socket is not None: + mh.node_tree.links.new(color_socket, tex_img.outputs['Color']) if alpha_socket is not None: mh.node_tree.links.new(alpha_socket, tex_img.outputs['Alpha']) # Inputs diff --git a/io_scene_gltf2/io/com/gltf2_io_constants.py b/io_scene_gltf2/io/com/gltf2_io_constants.py index 19ead516..175804a3 100755 --- a/io_scene_gltf2/io/com/gltf2_io_constants.py +++ b/io_scene_gltf2/io/com/gltf2_io_constants.py @@ -145,3 +145,5 @@ GLTF_DATA_TYPE_VEC4 = "VEC4" GLTF_DATA_TYPE_MAT2 = "MAT2" GLTF_DATA_TYPE_MAT3 = "MAT3" GLTF_DATA_TYPE_MAT4 = "MAT4" + +GLTF_IOR = 1.5 \ No newline at end of file diff --git a/io_scene_gltf2/io/com/gltf2_io_variants.py b/io_scene_gltf2/io/com/gltf2_io_variants.py new file mode 100644 index 00000000..3824fee4 --- /dev/null +++ b/io_scene_gltf2/io/com/gltf2_io_variants.py @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2018-2022 The glTF-Blender-IO authors. + +from io_scene_gltf2.io.com.gltf2_io import from_dict, from_union, from_none, from_float, from_str, from_list +from io_scene_gltf2.io.com.gltf2_io import to_float, to_class + +class Variant: + """defines variant for use with glTF 2.0.""" + def __init__(self, name, extensions, extras): + self.name = name + self.extensions = extensions + self.extras = extras + + @staticmethod + def from_dict(obj): + assert isinstance(obj, dict) + name = from_union([from_str, from_none], obj.get("name")) + extensions = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + obj.get("extensions")) + extras = obj.get("extras") + return Variant(name, extensions, extras) + + def to_dict(self): + result = {} + result["name"] = from_union([from_str, from_none], self.name) + result["extensions"] = from_union([lambda x: from_dict(lambda x: from_dict(lambda x: x, x), x), from_none], + self.extensions) + result["extras"] = self.extras + return result diff --git a/io_scene_gltf2/io/imp/gltf2_io_gltf.py b/io_scene_gltf2/io/imp/gltf2_io_gltf.py index 9f096e69..75ce7265 100755 --- a/io_scene_gltf2/io/imp/gltf2_io_gltf.py +++ b/io_scene_gltf2/io/imp/gltf2_io_gltf.py @@ -28,6 +28,7 @@ class glTFImporter(): self.accessor_cache = {} self.decode_accessor_cache = {} self.import_user_extensions = import_settings['import_user_extensions'] + self.variant_mapping = {} # Used to map between mgltf material idx and blender material, for Variants if 'loglevel' not in self.import_settings.keys(): self.import_settings['loglevel'] = logging.ERROR @@ -44,7 +45,13 @@ class glTFImporter(): 'KHR_texture_transform', 'KHR_materials_clearcoat', 'KHR_mesh_quantization', - 'KHR_draco_mesh_compression' + 'KHR_draco_mesh_compression', + 'KHR_materials_variants', + 'KHR_materials_emissive_strength', + 'KHR_materials_transmission', + 'KHR_materials_specular', + 'KHR_materials_sheen', + 'KHR_materials_ior' ] # Add extensions required supported by custom import extensions -- cgit v1.2.3 From cee9c463d8035625e61b33f45d389828038f576b Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Fri, 8 Jul 2022 15:55:21 +0200 Subject: glTF: cleanup menu --- io_scene_gltf2/__init__.py | 2 +- io_scene_gltf2/blender/com/gltf2_blender_ui.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 358759fa..c3297e1c 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -4,7 +4,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (3, 3, 13), + "version": (3, 3, 14), 'blender': (3, 3, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/blender/com/gltf2_blender_ui.py b/io_scene_gltf2/blender/com/gltf2_blender_ui.py index 7895d3d4..6c77ccda 100644 --- a/io_scene_gltf2/blender/com/gltf2_blender_ui.py +++ b/io_scene_gltf2/blender/com/gltf2_blender_ui.py @@ -45,8 +45,7 @@ def add_gltf_settings_to_menu(self, context) : class NODE_OT_GLTF_PBR_NON_CONVERTED_EXTENSIONS(bpy.types.Operator): bl_idname = "node.gltf_pbr_non_converted_extensions_operator" - bl_label = "glTF PBR Non Converted Extensions" - + bl_label = "glTF Original PBR data" @classmethod def poll(cls, context): -- cgit v1.2.3 From 1cc09e24c5163ccba2bcb6250da57dd649fb9151 Mon Sep 17 00:00:00 2001 From: Ryan Inch Date: Fri, 8 Jul 2022 23:32:32 -0400 Subject: Collection Manager: Fix T99142. Task: T69577 Fix duplicate keymap entries for QVT operators. --- object_collection_manager/__init__.py | 2 +- object_collection_manager/qcd_init.py | 46 +++++++++++++++++------------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/object_collection_manager/__init__.py b/object_collection_manager/__init__.py index fd53b291..1c0c7267 100644 --- a/object_collection_manager/__init__.py +++ b/object_collection_manager/__init__.py @@ -6,7 +6,7 @@ bl_info = { "name": "Collection Manager", "description": "Manage collections and their objects", "author": "Ryan Inch", - "version": (2, 24, 1), + "version": (2, 24, 2), "blender": (2, 80, 0), "location": "View3D - Object Mode (Shortcut - M)", "warning": '', # used for warning icon and text in addons panel diff --git a/object_collection_manager/qcd_init.py b/object_collection_manager/qcd_init.py index 1273e6e0..c2d555f5 100644 --- a/object_collection_manager/qcd_init.py +++ b/object_collection_manager/qcd_init.py @@ -135,8 +135,8 @@ def register_qcd_view_hotkeys(): ["ZERO", True, "20"], ] - for key in qcd_hotkeys: - for mode in ['Object Mode', 'Pose', 'Weight Paint']: + for mode in ['Object Mode', 'Pose', 'Weight Paint']: + for key in qcd_hotkeys: km = wm.keyconfigs.addon.keymaps.new(name=mode) kmi = km.keymap_items.new('view3d.view_qcd_slot', key[0], 'PRESS', alt=key[1]) kmi.properties.slot = key[2] @@ -149,34 +149,34 @@ def register_qcd_view_hotkeys(): kmi.properties.toggle = True addon_qcd_view_hotkey_keymaps.append((km, kmi)) - km = wm.keyconfigs.addon.keymaps.new(name=mode) - kmi = km.keymap_items.new('view3d.enable_all_qcd_slots', 'PLUS', 'PRESS', shift=True) - addon_qcd_view_hotkey_keymaps.append((km, kmi)) + km = wm.keyconfigs.addon.keymaps.new(name=mode) + kmi = km.keymap_items.new('view3d.enable_all_qcd_slots', 'PLUS', 'PRESS', shift=True) + addon_qcd_view_hotkey_keymaps.append((km, kmi)) - km = wm.keyconfigs.addon.keymaps.new(name=mode) - kmi = km.keymap_items.new('view3d.isolate_selected_objects_collections', 'EQUAL', 'PRESS') - addon_qcd_view_hotkey_keymaps.append((km, kmi)) + km = wm.keyconfigs.addon.keymaps.new(name=mode) + kmi = km.keymap_items.new('view3d.isolate_selected_objects_collections', 'EQUAL', 'PRESS') + addon_qcd_view_hotkey_keymaps.append((km, kmi)) - km = wm.keyconfigs.addon.keymaps.new(name=mode) - kmi = km.keymap_items.new('view3d.disable_selected_objects_collections', 'MINUS', 'PRESS') - addon_qcd_view_hotkey_keymaps.append((km, kmi)) + km = wm.keyconfigs.addon.keymaps.new(name=mode) + kmi = km.keymap_items.new('view3d.disable_selected_objects_collections', 'MINUS', 'PRESS') + addon_qcd_view_hotkey_keymaps.append((km, kmi)) - km = wm.keyconfigs.addon.keymaps.new(name=mode) - kmi = km.keymap_items.new('view3d.disable_all_non_qcd_slots', 'PLUS', 'PRESS', shift=True, alt=True) - addon_qcd_view_hotkey_keymaps.append((km, kmi)) + km = wm.keyconfigs.addon.keymaps.new(name=mode) + kmi = km.keymap_items.new('view3d.disable_all_non_qcd_slots', 'PLUS', 'PRESS', shift=True, alt=True) + addon_qcd_view_hotkey_keymaps.append((km, kmi)) - km = wm.keyconfigs.addon.keymaps.new(name=mode) - kmi = km.keymap_items.new('view3d.disable_all_collections', 'EQUAL', 'PRESS', alt=True, ctrl=True) - addon_qcd_view_hotkey_keymaps.append((km, kmi)) + km = wm.keyconfigs.addon.keymaps.new(name=mode) + kmi = km.keymap_items.new('view3d.disable_all_collections', 'EQUAL', 'PRESS', alt=True, ctrl=True) + addon_qcd_view_hotkey_keymaps.append((km, kmi)) - km = wm.keyconfigs.addon.keymaps.new(name=mode) - kmi = km.keymap_items.new('view3d.select_all_qcd_objects', 'PLUS', 'PRESS', shift=True, ctrl=True) - addon_qcd_view_hotkey_keymaps.append((km, kmi)) + km = wm.keyconfigs.addon.keymaps.new(name=mode) + kmi = km.keymap_items.new('view3d.select_all_qcd_objects', 'PLUS', 'PRESS', shift=True, ctrl=True) + addon_qcd_view_hotkey_keymaps.append((km, kmi)) - km = wm.keyconfigs.addon.keymaps.new(name=mode) - kmi = km.keymap_items.new('view3d.discard_qcd_history', 'EQUAL', 'PRESS', alt=True) - addon_qcd_view_hotkey_keymaps.append((km, kmi)) + km = wm.keyconfigs.addon.keymaps.new(name=mode) + kmi = km.keymap_items.new('view3d.discard_qcd_history', 'EQUAL', 'PRESS', alt=True) + addon_qcd_view_hotkey_keymaps.append((km, kmi)) def register_qcd_view_edit_mode_hotkeys(): -- cgit v1.2.3 From c43a6d4f828e021d0c48220d6ac1d02d992ee20e Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sat, 9 Jul 2022 10:03:39 +0200 Subject: glTF exporter: fix division by zero in some specular approximation --- io_scene_gltf2/__init__.py | 2 +- .../imp/gltf2_blender_KHR_materials_specular.py | 22 ++++++++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index c3297e1c..5c8872f3 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -4,7 +4,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (3, 3, 14), + "version": (3, 3, 15), 'blender': (3, 3, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_specular.py b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_specular.py index e4a0c52d..3441b5ad 100644 --- a/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_specular.py +++ b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_specular.py @@ -114,10 +114,13 @@ def specular(mh, location_specular, f0_from_ior = ((ior - 1)/(ior + 1))**2 lum_specular_color = luminance(specular_color_factor) blender_specular = ((lum_specular_color - transmission_factor) / (1 - transmission_factor)) * (1 / 0.08) * f0_from_ior - blender_specular_tint = luminance((normalize(specular_color_factor) - 1) / (normalize(base_color) - 1)) - if blender_specular_tint < 0 or blender_specular_tint > 1: - # TODOExt Warning clamping - blender_specular_tint = np.maximum(np.minimum(blender_specular_tint, 1), 0) + if not all([i == 0 for i in normalize(base_color) - 1]): + blender_specular_tint = luminance((normalize(specular_color_factor) - 1) / (normalize(base_color) - 1)) + if blender_specular_tint < 0 or blender_specular_tint > 1: + # TODOExt Warning clamping + blender_specular_tint = np.maximum(np.minimum(blender_specular_tint, 1), 0) + else: + blender_specular_tint = 1.0 specular_socket.default_value = blender_specular specular_tint_socket.default_value = blender_specular_tint @@ -211,10 +214,13 @@ def specular(mh, location_specular, f0_from_ior = ((ior - 1)/(ior + 1))**2 lum_specular_color = stack3(luminance(buffers['speccolor'])) blender_specular = ((lum_specular_color - buffers['transmission']) / (1 - buffers['transmission'])) * (1 / 0.08) * f0_from_ior - blender_specular_tint = luminance((normalize(buffers['speccolor']) - 1) / (normalize(buffers['basecolor']) - 1)) - np.nan_to_num(blender_specular_tint, copy=False) - blender_specular_tint = np.clip(blender_specular_tint, 0.0, 1.0) - blender_specular_tint = stack3(blender_specular_tint) + if not np.all(normalize(buffers['basecolor']) - 1 == 0.0): + blender_specular_tint = luminance((normalize(buffers['speccolor']) - 1) / (normalize(buffers['basecolor']) - 1)) + np.nan_to_num(blender_specular_tint, copy=False) + blender_specular_tint = np.clip(blender_specular_tint, 0.0, 1.0) + blender_specular_tint = stack3(blender_specular_tint) + else: + blender_specular_tint = stack3(np.ones((width, height))) blender_specular = np.dstack((blender_specular, np.ones((width, height)))) # Set alpha to 1 blender_specular_tint = np.dstack((blender_specular_tint, np.ones((width, height)))) # Set alpha to 1 -- cgit v1.2.3 From 7faf9ac035a7e0b0cc3f4b98e46bbd2daf3dfe7b Mon Sep 17 00:00:00 2001 From: Damien Picard Date: Thu, 30 Jun 2022 18:26:50 +0200 Subject: Sun Position: add French translation --- sun_position/__init__.py | 30 ++- sun_position/properties.py | 2 +- sun_position/translations.py | 467 +++++++++++++++++++++++++++++++++++++++++++ sun_position/ui_sun.py | 37 ++-- 4 files changed, 504 insertions(+), 32 deletions(-) create mode 100644 sun_position/translations.py diff --git a/sun_position/__init__.py b/sun_position/__init__.py index 01e22df0..9a3e5b72 100644 --- a/sun_position/__init__.py +++ b/sun_position/__init__.py @@ -29,39 +29,35 @@ if "bpy" in locals(): importlib.reload(properties) importlib.reload(ui_sun) importlib.reload(hdr) + importlib.reload(translations) else: - from . import properties, ui_sun, hdr + from . import properties, ui_sun, hdr, translations import bpy +register_classes, unregister_classes = bpy.utils.register_classes_factory( + (properties.SunPosProperties, + properties.SunPosAddonPreferences, ui_sun.SUNPOS_OT_AddPreset, + ui_sun.SUNPOS_MT_Presets, ui_sun.SUNPOS_PT_Panel, + ui_sun.SUNPOS_PT_Location, ui_sun.SUNPOS_PT_Time, hdr.SUNPOS_OT_ShowHdr)) + + def register(): - bpy.utils.register_class(properties.SunPosProperties) + register_classes() bpy.types.Scene.sun_pos_properties = ( bpy.props.PointerProperty(type=properties.SunPosProperties, name="Sun Position", description="Sun Position Settings")) - bpy.utils.register_class(properties.SunPosAddonPreferences) - bpy.utils.register_class(ui_sun.SUNPOS_OT_AddPreset) - bpy.utils.register_class(ui_sun.SUNPOS_MT_Presets) - bpy.utils.register_class(ui_sun.SUNPOS_PT_Panel) - bpy.utils.register_class(ui_sun.SUNPOS_PT_Location) - bpy.utils.register_class(ui_sun.SUNPOS_PT_Time) - bpy.utils.register_class(hdr.SUNPOS_OT_ShowHdr) + bpy.app.translations.register(__name__, translations.translations_dict) bpy.app.handlers.frame_change_post.append(sun_calc.sun_handler) def unregister(): - bpy.utils.unregister_class(hdr.SUNPOS_OT_ShowHdr) - bpy.utils.unregister_class(ui_sun.SUNPOS_PT_Panel) - bpy.utils.unregister_class(ui_sun.SUNPOS_PT_Location) - bpy.utils.unregister_class(ui_sun.SUNPOS_PT_Time) - bpy.utils.unregister_class(ui_sun.SUNPOS_MT_Presets) - bpy.utils.unregister_class(ui_sun.SUNPOS_OT_AddPreset) - bpy.utils.unregister_class(properties.SunPosAddonPreferences) del bpy.types.Scene.sun_pos_properties - bpy.utils.unregister_class(properties.SunPosProperties) + unregister_classes() + bpy.app.translations.unregister(__name__) bpy.app.handlers.frame_change_post.remove(sun_calc.sun_handler) diff --git a/sun_position/properties.py b/sun_position/properties.py index ef3a21e3..ac791d43 100644 --- a/sun_position/properties.py +++ b/sun_position/properties.py @@ -136,7 +136,7 @@ class SunPosProperties(PropertyGroup): object_collection_type: EnumProperty( name="Display type", - description="Show object group as sun motion", + description="Show object collection as sun motion", items=( ('ANALEMMA', "Analemma", ""), ('DIURNAL', "Diurnal", ""), diff --git a/sun_position/translations.py b/sun_position/translations.py new file mode 100644 index 00000000..cd247ffd --- /dev/null +++ b/sun_position/translations.py @@ -0,0 +1,467 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +# Tuple of tuples: +# ((msgctxt, msgid), (sources, gen_comments), (lang, translation, (is_fuzzy, comments)), ...) +translations_tuple = ( + (("*", ""), + ((), ()), + ("fr_FR", "Project-Id-Version: Sun Position 3.1.2 (0)\nReport-Msgid-Bugs-To: \nPOT-Creation-Date: 2022-06-30 15:02:06.261278\nPO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\nLast-Translator: FULL NAME \nLanguage-Team: LANGUAGE \nLanguage: __POT__\nMIME-Version: 1.0\nContent-Type: text/plain; charset=UTF-8\nContent-Transfer-Encoding: 8bit", + (False, + ("Blender's translation file (po format).", + "Copyright (C) 2022 The Blender Foundation.", + "This file is distributed under the same license as the Blender package.", + "Damien Picard , 2022."))), + ), + (("*", "Azimuth and elevation info"), + (("bpy.types.SunPosAddonPreferences.show_az_el",), + ()), + ("fr_FR", "Infos d’azimut et de hauteur", + (False, ())), + ), + (("*", "Show azimuth and solar elevation info"), + (("bpy.types.SunPosAddonPreferences.show_az_el",), + ()), + ("fr_FR", "Afficher les infos d’azimut et de hauteur du soleil", + (False, ())), + ), + (("*", "Daylight savings"), + (("bpy.types.SunPosAddonPreferences.show_daylight_savings", + "bpy.types.SunPosProperties.use_daylight_savings"), + ()), + ("fr_FR", "Heure d’été", + (False, ())), + ), + (("*", "Show daylight savings time choice"), + (("bpy.types.SunPosAddonPreferences.show_daylight_savings",), + ()), + ("fr_FR", "Afficher l’option de changement d’heure", + (False, ())), + ), + (("*", "D° M' S\""), + (("bpy.types.SunPosAddonPreferences.show_dms",), + ()), + ("fr_FR", "", + (False, ())), + ), + (("*", "Show lat/long degrees, minutes, seconds labels"), + (("bpy.types.SunPosAddonPreferences.show_dms",), + ()), + ("fr_FR", "Afficher les étiquettes de latitude et longitude en degrés, minutes, secondes", + (False, ())), + ), + (("*", "Show North"), + (("bpy.types.SunPosAddonPreferences.show_north", + "bpy.types.SunPosProperties.show_north"), + ()), + ("fr_FR", "Afficher le nord", + (False, ())), + ), + (("*", "Show north offset choice and slider"), + (("bpy.types.SunPosAddonPreferences.show_north",), + ()), + ("fr_FR", "Afficher l’option et le curseur de décalage du nord", + (False, ())), + ), + (("*", "Refraction"), + (("bpy.types.SunPosAddonPreferences.show_refraction",), + ()), + ("fr_FR", "Réfraction", + (False, ())), + ), + (("*", "Show sun refraction choice"), + (("bpy.types.SunPosAddonPreferences.show_refraction",), + ()), + ("fr_FR", "Afficher l’option de réfraction du soleil", + (False, ())), + ), + (("*", "Sunrise and sunset info"), + (("bpy.types.SunPosAddonPreferences.show_rise_set",), + ()), + ("fr_FR", "Infos de lever et coucher", + (False, ())), + ), + (("*", "Show sunrise and sunset labels"), + (("bpy.types.SunPosAddonPreferences.show_rise_set",), + ()), + ("fr_FR", "Afficher les informations de lever et coucher du soleil", + (False, ())), + ), + (("*", "Time and place presets"), + (("bpy.types.SunPosAddonPreferences.show_time_place",), + ()), + ("fr_FR", "Préréglages d’heure et de lieu", + (False, ())), + ), + (("*", "Show time/place presets"), + (("bpy.types.SunPosAddonPreferences.show_time_place",), + ()), + ("fr_FR", "Afficher les préréglages d’heure et de lieu", + (False, ())), + ), + (("*", "Sun Position"), + (("bpy.types.Scene.sun_pos_properties", + "bpy.types.SUNPOS_PT_Panel"), + ()), + ("fr_FR", "Position du Soleil", + (False, ())), + ), + (("*", "Sun Position Settings"), + (("bpy.types.Scene.sun_pos_properties",), + ()), + ("fr_FR", "Options de Position du Soleil", + (False, ())), + ), + (("*", "Sun Position Presets"), + (("bpy.types.SUNPOS_MT_Presets",), + ()), + ("fr_FR", "Préréglages de position du Soleil", + (False, ())), + ), + (("Operator", "Synchroniser Soleil et texture"), + (("bpy.types.WORLD_OT_sunpos_show_hdr",), + ()), + ("fr_FR", "", + (False, ())), + ), + (("*", "UTC zone"), + (("bpy.types.SunPosProperties.UTC_zone",), + ()), + ("fr_FR", "Fuseau horaire", + (False, ())), + ), + (("*", "Time zone: Difference from Greenwich, England in hours"), + (("bpy.types.SunPosProperties.UTC_zone",), + ()), + ("fr_FR", "Fuseau horaire : différence avec Greenwich, Angleterre, en heures", + (False, ())), + ), + (("*", "If true, Environment texture moves with sun"), + (("bpy.types.SunPosProperties.bind_to_sun",), + ()), + ("fr_FR", "Si actif, la texture d’environnement tourne avec le Soleil", + (False, ())), + ), + (("*", "Enter coordinates"), + (("bpy.types.SunPosProperties.co_parser",), + ()), + ("fr_FR", "Saisir coordonnées", + (False, ())), + ), + (("*", "Enter coordinates from an online map"), + (("bpy.types.SunPosProperties.co_parser",), + ()), + ("fr_FR", "Saisir des coordonnées depuis une carte", + (False, ())), + ), + (("*", "Day"), + (("bpy.types.SunPosProperties.day",), + ()), + ("fr_FR", "Jour", + (False, ())), + ), + (("*", "Day of year"), + (("bpy.types.SunPosProperties.day_of_year",), + ()), + ("fr_FR", "Jour de l’année", + (False, ())), + ), + (("*", "Rotation angle of sun and environment texture"), + (("bpy.types.SunPosProperties.hdr_azimuth",), + ()), + ("fr_FR", "Angle de rotation du Soleil et de la texture d’environnement", + (False, ())), + ), + (("*", "Elevation"), + (("bpy.types.SunPosProperties.hdr_elevation",), + ()), + ("fr_FR", "Hauteur", + (False, ())), + ), + (("*", "Elevation angle of sun"), + (("bpy.types.SunPosProperties.hdr_elevation",), + ()), + ("fr_FR", "Angle de hauteur du Soleil", + (False, ())), + ), + (("*", "Name of texture to use. World nodes must be enabled and color set to Environment Texture"), + (("bpy.types.SunPosProperties.hdr_texture",), + ()), + # TODO + ("fr_FR", "Nom de la texture à utiliser. Les nœuds de shader du monde doivent être activés, et la couleur utiliser une texture d’environnement", + (False, ())), + ), + (("*", "Latitude"), + (("bpy.types.SunPosProperties.latitude",), + ()), + ("fr_FR", "Latitude", + (False, ())), + ), + (("*", "Latitude: (+) Northern (-) Southern"), + (("bpy.types.SunPosProperties.latitude",), + ()), + ("fr_FR", "Latitude : (+) nord (-) sud", + (False, ())), + ), + (("*", "Longitude"), + (("bpy.types.SunPosProperties.longitude",), + ()), + ("fr_FR", "Longitude", + (False, ())), + ), + (("*", "Longitude: (-) West of Greenwich (+) East of Greenwich"), + (("bpy.types.SunPosProperties.longitude",), + ()), + ("fr_FR", "Longitude : (-) ouest depuis Greenwich (+) est depuis Greenwich", + (False, ())), + ), + (("*", "Month"), + (("bpy.types.SunPosProperties.month",), + ()), + ("fr_FR", "Mois", + (False, ())), + ), + (("*", "North Offset"), + (("bpy.types.SunPosProperties.north_offset",), + ()), + ("fr_FR", "Décalage du nord", + (False, ())), + ), + (("*", "Rotate the scene to choose North direction"), + (("bpy.types.SunPosProperties.north_offset",), + ()), + ("fr_FR", "Tourner la scène pour choisir la direction du nord", + (False, ())), + ), + (("*", "Collection of objects used to visualize sun motion"), + (("bpy.types.SunPosProperties.object_collection",), + ()), + ("fr_FR", "Collection d’objets utilisée pour visualiser la trajectoire du Soleil", + (False, ())), + ), + (("*", "Show object collection as sun motion"), + (("bpy.types.SunPosProperties.object_collection_type",), + ()), + ("fr_FR", "Afficher la collection en tant que", + (False, ())), + ), + (("*", "Analemma"), + (("bpy.types.SunPosProperties.object_collection_type:'ANALEMMA'",), + ()), + ("fr_FR", "Analemme", + (False, ())), + ), + (("*", "Diurnal"), + (("bpy.types.SunPosProperties.object_collection_type:'DIURNAL'",), + ()), + ("fr_FR", "Diurne", + (False, ())), + ), + (("*", "Draw line pointing north"), + (("bpy.types.SunPosProperties.show_north",), + ()), + ("fr_FR", "Afficher une ligne pointant le nord", + (False, ())), + ), + (("*", "Name of sky texture to be used"), + (("bpy.types.SunPosProperties.sky_texture",), + ()), + ("fr_FR", "Nom de la texture à utiliser", + (False, ())), + ), + (("*", "Distance to sun from origin"), + (("bpy.types.SunPosProperties.sun_distance",), + ()), + ("fr_FR", "Distance entre l’origine et le Soleil", + (False, ())), + ), + (("*", "Sun Object"), + (("bpy.types.SunPosProperties.sun_object", + "scripts/addons/sun_position/ui_sun.py:101"), + ()), + ("fr_FR", "Objet soleil", + (False, ())), + ), + (("*", "Sun object to set in the scene"), + (("bpy.types.SunPosProperties.sun_object",), + ()), + ("fr_FR", "Objet soleil à utiliser dans la scène", + (False, ())), + ), + (("*", "Day Time"), + (("bpy.types.SunPosProperties.UTC_zone",), + ()), + ("fr_FR", "Heure", + (False, ())), + ), + (("*", "Time of the day"), + (("bpy.types.SunPosProperties.time",), + ()), + ("fr_FR", "Heure du jour", + (False, ())), + ), + (("*", "Time Spread"), + (("bpy.types.SunPosProperties.time_spread",), + ()), + ("fr_FR", "Plage horaire", + (False, ())), + ), + (("*", "Time period in which to spread object collection"), + (("bpy.types.SunPosProperties.time_spread",), + ()), + ("fr_FR", "Plage horaire à visualiser par les objets de la collection", + (False, ())), + ), + (("*", "Usage mode"), + (("bpy.types.SunPosProperties.usage_mode",), + ()), + ("fr_FR", "Mode", + (False, ())), + ), + (("*", "Operate in normal mode or environment texture mode"), + (("bpy.types.SunPosProperties.usage_mode",), + ()), + ("fr_FR", "Passer en mode normal ou texture d’environnement", + (False, ())), + ), + (("*", "Sun + HDR texture"), + (("bpy.types.SunPosProperties.usage_mode:'HDR'",), + ()), + ("fr_FR", "Soleil + texture HDRI", + (False, ())), + ), + (("*", "Use day of year"), + (("bpy.types.SunPosProperties.use_day_of_year",), + ()), + ("fr_FR", "Utiliser le jour de l’année", + (False, ())), + ), + (("*", "Use a single value for day of year"), + (("bpy.types.SunPosProperties.use_day_of_year",), + ()), + ("fr_FR", "Utiliser une seule valeur pour le jour de l’année", + (False, ())), + ), + (("*", "Daylight savings time adds 1 hour to standard time"), + (("bpy.types.SunPosProperties.use_daylight_savings",), + ()), + ("fr_FR", "L’heure d’été ajoute une heure à l’heure standard", + (False, ())), + ), + (("*", "Use refraction"), + (("bpy.types.SunPosProperties.use_refraction",), + ()), + ("fr_FR", "Utiliser la réfraction", + (False, ())), + ), + (("*", "Show apparent sun position due to refraction"), + (("bpy.types.SunPosProperties.use_refraction",), + ()), + ("fr_FR", "Afficher la position apparente du Soleil due à la réfraction", + (False, ())), + ), + (("*", "Year"), + (("bpy.types.SunPosProperties.year",), + ()), + ("fr_FR", "Année", + (False, ())), + ), + (("*", "Could not find 3D View"), + (("scripts/addons/sun_position/hdr.py:262",), + ()), + ("fr_FR", "Impossible de trouver la vue 3D", + (False, ())), + ), + (("*", "Please select an Environment Texture node"), + (("scripts/addons/sun_position/hdr.py:268",), + ()), + ("fr_FR", "Veuillez utiliser un nœud de texture d’environnement", + (False, ())), + ), + (("*", "Unknown projection"), + (("scripts/addons/sun_position/hdr.py:180",), + ()), + ("fr_FR", "Projection inconnue", + (False, ())), + ), + (("*", "Show options or labels:"), + (("scripts/addons/sun_position/properties.py:241",), + ()), + ("fr_FR", "Afficher les options et étiquettes :", + (False, ())), + ), + (("*", "Usage Mode"), + (("scripts/addons/sun_position/ui_sun.py:71",), + ()), + ("fr_FR", "Mode", + (False, ())), + ), + (("*", "Environment Texture"), + (("scripts/addons/sun_position/ui_sun.py:85",), + ()), + ("fr_FR", "Texture d’environnement", + (False, ())), + ), + (("*", "Enter Coordinates"), + (("scripts/addons/sun_position/ui_sun.py:174",), + ()), + ("fr_FR", "Saisir coordonnées", + (False, ())), + ), + (("*", "Local:"), + (("scripts/addons/sun_position/ui_sun.py:269",), + ()), + ("fr_FR", "Locale :", + (False, ())), + ), + (("*", "UTC:"), + (("scripts/addons/sun_position/ui_sun.py:270",), + ()), + ("fr_FR", "UTC : ", + (False, ())), + ), + (("*", "Sunrise:"), + (("scripts/addons/sun_position/ui_sun.py:285",), + ()), + ("fr_FR", "Lever : ", + (False, ())), + ), + (("*", "Sunset:"), + (("scripts/addons/sun_position/ui_sun.py:288",), + ()), + ("fr_FR", "Coucher : ", + (False, ())), + ), + (("*", "Please select World in the World panel."), + (("scripts/addons/sun_position/ui_sun.py:95", + "scripts/addons/sun_position/ui_sun.py:153"), + ()), + ("fr_FR", "Veuillez sélecttionner le monde dans le panneau Monde", + (False, ())), + ), + (("*", "Azimuth:"), + (("scripts/addons/sun_position/ui_sun.py:206",), + ()), + ("fr_FR", "Azimut :", + (False, ())), + ), + (("*", "Elevation:"), + (("scripts/addons/sun_position/ui_sun.py:209",), + ()), + ("fr_FR", "Hauteur :", + (False, ())), + ), + (("*", "Please activate Use Nodes in the World panel."), + (("scripts/addons/sun_position/ui_sun.py:92", + "scripts/addons/sun_position/ui_sun.py:150"), + ()), + ("fr_FR", "Veuillez activer Utiliser nœuds dans le panneau Monde", + (False, ())), + ), +) + +translations_dict = {} +for msg in translations_tuple: + key = msg[0] + for lang, trans, (is_fuzzy, comments) in msg[2:]: + if trans and not is_fuzzy: + translations_dict.setdefault(lang, {})[key] = trans diff --git a/sun_position/ui_sun.py b/sun_position/ui_sun.py index 1f4f8f34..05f0dcab 100644 --- a/sun_position/ui_sun.py +++ b/sun_position/ui_sun.py @@ -201,14 +201,12 @@ class SUNPOS_PT_Location(bpy.types.Panel): if p.show_az_el: col = flow.column(align=True) - row = col.row() - row.alignment = 'RIGHT' - row.label(text="Azimuth: " + - str(round(sun.azimuth, 3)) + "°") - row = col.row() - row.alignment = 'RIGHT' - row.label(text="Elevation: " + - str(round(sun.elevation, 3)) + "°") + split = col.split(factor=0.4, align=True) + split.label(text="Azimuth:") + split.label(text=str(round(sun.azimuth, 3)) + "°") + split = col.split(factor=0.4, align=True) + split.label(text="Elevation:") + split.label(text=str(round(sun.elevation, 3)) + "°") col.separator() if p.show_refraction: @@ -266,16 +264,27 @@ class SUNPOS_PT_Time(bpy.types.Panel): sp.longitude, sp.UTC_zone) col.alignment = 'CENTER' - col.label(text="Local: " + lt, icon='TIME') - col.label(text=" UTC: " + ut, icon='PREVIEW_RANGE') + + split = col.split(factor=0.5, align=True) + split.label(text="Local:", icon='TIME') + split.label(text=lt) + split = col.split(factor=0.5, align=True) + split.label(text="UTC:", icon='PREVIEW_RANGE') + split.label(text=ut) col.separator() + col = flow.column(align=True) col.alignment = 'CENTER' if p.show_rise_set: sr = format_hms(sun.sunrise.time) ss = format_hms(sun.sunset.time) - tsr = "Sunrise: " + sr - tss = " Sunset: " + ss - col.label(text=tsr, icon='LIGHT_SUN') - col.label(text=tss, icon='SOLO_ON') + + split = col.split(factor=0.5, align=True) + split.label(text="Sunrise:", icon='LIGHT_SUN') + split.label(text=sr) + split = col.split(factor=0.5, align=True) + split.label(text="Sunset:", icon='SOLO_ON') + split.label(text=ss) + + col.separator() -- cgit v1.2.3 From 732082e3eb0517d9c428f97ce11ddb103ab0580b Mon Sep 17 00:00:00 2001 From: Damien Picard Date: Thu, 30 Jun 2022 13:35:15 +0200 Subject: Animall: move to own dir, to add translations later --- animation_animall.py | 685 ------------------------------------------ animation_animall/__init__.py | 685 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 685 insertions(+), 685 deletions(-) delete mode 100644 animation_animall.py create mode 100644 animation_animall/__init__.py diff --git a/animation_animall.py b/animation_animall.py deleted file mode 100644 index 944f4a2f..00000000 --- a/animation_animall.py +++ /dev/null @@ -1,685 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later - -bl_info = { - "name": "AnimAll", - "author": "Daniel Salazar (ZanQdo), Damien Picard (pioverfour)", - "version": (0, 9, 6), - "blender": (3, 3, 0), - "location": "3D View > Toolbox > Animation tab > AnimAll", - "description": "Allows animation of mesh, lattice, curve and surface data", - "warning": "", - "doc_url": "{BLENDER_MANUAL_URL}/addons/animation/animall.html", - "category": "Animation", -} - -import bpy -from bpy.types import ( - Operator, - Panel, - AddonPreferences, - ) -from bpy.props import ( - BoolProperty, - StringProperty, - ) -from bpy.app.handlers import persistent - - -# Property Definitions -class AnimallProperties(bpy.types.PropertyGroup): - key_selected: BoolProperty( - name="Key Selected Only", - description="Insert keyframes only on selected elements", - default=False) - - # Generic attributes - key_point_location: BoolProperty( - name="Location", - description="Insert keyframes on point locations", - default=False) - key_shape_key: BoolProperty( - name="Shape Key", - description="Insert keyframes on active Shape Key layer", - default=False) - key_material_index: BoolProperty( - name="Material Index", - description="Insert keyframes on face material indices", - default=False) - - # Mesh attributes - key_vertex_bevel: BoolProperty( - name="Vertex Bevel", - description="Insert keyframes on vertex bevel weight", - default=False) - # key_vertex_crease: BoolProperty( - # name="Vertex Crease", - # description="Insert keyframes on vertex crease weight", - # default=False) - key_vertex_group: BoolProperty( - name="Vertex Group", - description="Insert keyframes on active vertex group values", - default=False) - - key_edge_bevel: BoolProperty( - name="Edge Bevel", - description="Insert keyframes on edge bevel weight", - default=False) - key_edge_crease: BoolProperty( - name="Edge Crease", - description="Insert keyframes on edge creases", - default=False) - - key_attribute: BoolProperty( - name="Attribute", - description="Insert keyframes on active attribute values", - default=False) - key_uvs: BoolProperty( - name="UV Map", - description="Insert keyframes on active UV coordinates", - default=False) - - # Curve and surface attributes - key_radius: BoolProperty( - name="Radius", - description="Insert keyframes on point radius (Shrink/Fatten)", - default=False) - key_tilt: BoolProperty( - name="Tilt", - description="Insert keyframes on point tilt", - default=False) - - -# Utility functions - -def refresh_ui_keyframes(): - try: - for area in bpy.context.screen.areas: - if area.type in ('TIMELINE', 'GRAPH_EDITOR', 'DOPESHEET_EDITOR'): - area.tag_redraw() - except: - pass - - -def insert_key(data, key, group=None): - try: - if group is not None: - data.keyframe_insert(key, group=group) - else: - data.keyframe_insert(key) - except: - pass - - -def delete_key(data, key): - try: - data.keyframe_delete(key) - except: - pass - - -def is_selected_vert_loop(data, loop_i): - """Get selection status of vertex corresponding to a loop""" - vertex_index = data.loops[loop_i].vertex_index - return data.vertices[vertex_index].select - - -# GUI (Panel) - -class VIEW3D_PT_animall(Panel): - bl_space_type = 'VIEW_3D' - bl_region_type = 'UI' - bl_category = "Animate" - bl_label = '' - - @classmethod - def poll(self, context): - return context.active_object and context.active_object.type in {'MESH', 'LATTICE', 'CURVE', 'SURFACE'} - - def draw_header(self, context): - - layout = self.layout - row = layout.row() - row.label (text = 'AnimAll', icon = 'ARMATURE_DATA') - - def draw(self, context): - obj = context.active_object - animall_properties = context.scene.animall_properties - - layout = self.layout - - layout.label(text='Key:') - - layout.use_property_split = True - layout.use_property_decorate = False - - if obj.type == 'LATTICE': - col = layout.column(heading="Points", align=True) - col.prop(animall_properties, "key_point_location") - - col = layout.column(heading="Others", align=True) - col.prop(animall_properties, "key_shape_key") - - elif obj.type == 'MESH': - col = layout.column(heading="Points", align=True) - col.prop(animall_properties, "key_point_location") - col.prop(animall_properties, "key_vertex_bevel", text="Bevel") - col.prop(animall_properties, "key_vertex_group") - - col = layout.column(heading="Edges", align=True) - col.prop(animall_properties, "key_edge_bevel", text="Bevel") - col.prop(animall_properties, "key_edge_crease", text="Crease") - - col = layout.column(heading="Faces", align=True) - col.prop(animall_properties, "key_material_index") - - col = layout.column(heading="Others", align=True) - col.prop(animall_properties, "key_attribute") - col.prop(animall_properties, "key_uvs") - col.prop(animall_properties, "key_shape_key") - - # Vertex group update operator - if (obj.data.animation_data is not None - and obj.data.animation_data.action is not None): - for fcurve in context.active_object.data.animation_data.action.fcurves: - if fcurve.data_path.startswith("vertex_colors"): - col = layout.column(align=True) - col.label(text="Object includes old-style vertex colors. Consider updating them.", icon="ERROR") - col.operator("anim.update_vertex_color_animation_animall", icon="FILE_REFRESH") - break - - elif obj.type in {'CURVE', 'SURFACE'}: - col = layout.column(align=True) - col = layout.column(heading="Points", align=True) - col.prop(animall_properties, "key_point_location") - col.prop(animall_properties, "key_radius") - col.prop(animall_properties, "key_tilt") - - col = layout.column(heading="Splines", align=True) - col.prop(animall_properties, "key_material_index") - - col = layout.column(heading="Others", align=True) - col.prop(animall_properties, "key_shape_key") - - if animall_properties.key_shape_key: - shape_key = obj.active_shape_key - shape_key_index = obj.active_shape_key_index - - if shape_key_index > 0: - col = layout.column(align=True) - row = col.row(align=True) - row.prop(shape_key, "value", text=shape_key.name, icon="SHAPEKEY_DATA") - row.prop(obj, "show_only_shape_key", text="") - if shape_key.value < 1: - col.label(text='Maybe set "%s" to 1.0?' % shape_key.name, icon="INFO") - elif shape_key is not None: - col = layout.column(align=True) - col.label(text="Cannot key on Basis Shape", icon="ERROR") - else: - col = layout.column(align=True) - col.label(text="No active Shape Key", icon="ERROR") - - if animall_properties.key_point_location: - col.label(text='"Location" and "Shape Key" are redundant?', icon="INFO") - - layout.use_property_split = False - layout.separator() - row = layout.row() - row.prop(animall_properties, "key_selected") - - row = layout.row(align=True) - row.operator("anim.insert_keyframe_animall", icon="KEY_HLT") - row.operator("anim.delete_keyframe_animall", icon="KEY_DEHLT") - row = layout.row() - row.operator("anim.clear_animation_animall", icon="CANCEL") - - -class ANIM_OT_insert_keyframe_animall(Operator): - bl_label = "Insert Key" - bl_idname = "anim.insert_keyframe_animall" - bl_description = "Insert a Keyframe" - bl_options = {'REGISTER', 'UNDO'} - - def execute(op, context): - animall_properties = context.scene.animall_properties - - if context.mode == 'OBJECT': - objects = context.selected_objects - else: - objects = context.objects_in_mode_unique_data[:] - - mode = context.object.mode - - # Separate loop for lattices, curves and surfaces, since keyframe insertion - # has to happen in Edit Mode, otherwise points move back upon mode switch... - # (except for curve shape keys) - for obj in [o for o in objects if o.type in {'CURVE', 'SURFACE', 'LATTICE'}]: - data = obj.data - - if obj.type == 'LATTICE': - if animall_properties.key_shape_key: - if obj.active_shape_key_index > 0: - sk_name = obj.active_shape_key.name - for p_i, point in enumerate(obj.active_shape_key.data): - if not animall_properties.key_selected or data.points[p_i].select: - insert_key(point, 'co', group="%s Point %s" % (sk_name, p_i)) - - if animall_properties.key_point_location: - for p_i, point in enumerate(data.points): - if not animall_properties.key_selected or point.select: - insert_key(point, 'co_deform', group="Point %s" % p_i) - - else: - if animall_properties.key_material_index: - for s_i, spline in enumerate(data.splines): - if (not animall_properties.key_selected - or any(point.select for point in spline.points) - or any(point.select_control_point for point in spline.bezier_points)): - insert_key(spline, 'material_index', group="Spline %s" % s_i) - - for s_i, spline in enumerate(data.splines): - if spline.type == 'BEZIER': - for v_i, CV in enumerate(spline.bezier_points): - if (not animall_properties.key_selected - or CV.select_control_point - or CV.select_left_handle - or CV.select_right_handle): - if animall_properties.key_point_location: - insert_key(CV, 'co', group="Spline %s CV %s" % (s_i, v_i)) - insert_key(CV, 'handle_left', group="Spline %s CV %s" % (s_i, v_i)) - insert_key(CV, 'handle_right', group="Spline %s CV %s" % (s_i, v_i)) - - if animall_properties.key_radius: - insert_key(CV, 'radius', group="Spline %s CV %s" % (s_i, v_i)) - - if animall_properties.key_tilt: - insert_key(CV, 'tilt', group="Spline %s CV %s" % (s_i, v_i)) - - elif spline.type in ('POLY', 'NURBS'): - for v_i, CV in enumerate(spline.points): - if not animall_properties.key_selected or CV.select: - if animall_properties.key_point_location: - insert_key(CV, 'co', group="Spline %s CV %s" % (s_i, v_i)) - - if animall_properties.key_radius: - insert_key(CV, 'radius', group="Spline %s CV %s" % (s_i, v_i)) - - if animall_properties.key_tilt: - insert_key(CV, 'tilt', group="Spline %s CV %s" % (s_i, v_i)) - - bpy.ops.object.mode_set(mode='OBJECT') - - for obj in [o for o in objects if o.type in {'MESH', 'CURVE', 'SURFACE'}]: - data = obj.data - if obj.type == 'MESH': - if animall_properties.key_point_location: - for v_i, vert in enumerate(data.vertices): - if not animall_properties.key_selected or vert.select: - insert_key(vert, 'co', group="Vertex %s" % v_i) - - if animall_properties.key_vertex_bevel: - for v_i, vert in enumerate(data.vertices): - if not animall_properties.key_selected or vert.select: - insert_key(vert, 'bevel_weight', group="Vertex %s" % v_i) - # if animall_properties.key_vertex_crease: - # for v_i, vert in enumerate(data.vertices): - # if not animall_properties.key_selected or vert.select: - # insert_key(vert, 'crease', group="Vertex %s" % v_i) - - if animall_properties.key_vertex_group: - for v_i, vert in enumerate(data.vertices): - if not animall_properties.key_selected or vert.select: - for group in vert.groups: - insert_key(group, 'weight', group="Vertex %s" % v_i) - - if animall_properties.key_edge_bevel: - for e_i, edge in enumerate(data.edges): - if not animall_properties.key_selected or edge.select: - insert_key(edge, 'bevel_weight', group="Edge %s" % e_i) - - if animall_properties.key_edge_crease: - for e_i, edge in enumerate(data.edges): - if not animall_properties.key_selected or edge.select: - insert_key(edge, 'crease', group="Edge %s" % e_i) - - if animall_properties.key_material_index: - for p_i, polygon in enumerate(data.polygons): - if not animall_properties.key_selected or polygon.select: - insert_key(polygon, 'material_index', group="Face %s" % p_i) - - if animall_properties.key_attribute: - if data.attributes.active is not None: - attribute = data.attributes.active - if attribute.data_type != 'STRING': - # Cannot animate string attributes? - if attribute.data_type in {'FLOAT', 'INT', 'BOOLEAN', 'INT8'}: - attribute_key = "value" - elif attribute.data_type in {'FLOAT_COLOR', 'BYTE_COLOR'}: - attribute_key = "color" - elif attribute.data_type in {'FLOAT_VECTOR', 'FLOAT2'}: - attribute_key = "vector" - - if attribute.domain == 'POINT': - group = "Vertex %s" - elif attribute.domain == 'EDGE': - group = "Edge %s" - elif attribute.domain == 'FACE': - group = "Face %s" - elif attribute.domain == 'CORNER': - group = "Loop %s" - - for e_i, _attribute_data in enumerate(attribute.data): - if (not animall_properties.key_selected - or attribute.domain == 'POINT' and data.vertices[e_i].select - or attribute.domain == 'EDGE' and data.edges[e_i].select - or attribute.domain == 'FACE' and data.polygons[e_i].select - or attribute.domain == 'CORNER' and is_selected_vert_loop(data, e_i)): - insert_key(data, f'attributes["{attribute.name}"].data[{e_i}].{attribute_key}', - group=group % e_i) - - if animall_properties.key_uvs: - if data.uv_layers.active is not None: - for uv_i, uv in enumerate(data.uv_layers.active.data): - if not animall_properties.key_selected or uv.select: - insert_key(uv, 'uv', group="UV layer %s" % uv_i) - - if animall_properties.key_shape_key: - if obj.active_shape_key_index > 0: - sk_name = obj.active_shape_key.name - for v_i, vert in enumerate(obj.active_shape_key.data): - if not animall_properties.key_selected or data.vertices[v_i].select: - insert_key(vert, 'co', group="%s Vertex %s" % (sk_name, v_i)) - - elif obj.type in {'CURVE', 'SURFACE'}: - # Shape key keys have to be inserted in object mode for curves... - if animall_properties.key_shape_key: - sk_name = obj.active_shape_key.name - global_spline_index = 0 # numbering for shape keys, which have flattened indices - for s_i, spline in enumerate(data.splines): - if spline.type == 'BEZIER': - for v_i, CV in enumerate(spline.bezier_points): - if (not animall_properties.key_selected - or CV.select_control_point - or CV.select_left_handle - or CV.select_right_handle): - if obj.active_shape_key_index > 0: - CV = obj.active_shape_key.data[global_spline_index] - insert_key(CV, 'co', group="%s Spline %s CV %s" % (sk_name, s_i, v_i)) - insert_key(CV, 'handle_left', group="%s Spline %s CV %s" % (sk_name, s_i, v_i)) - insert_key(CV, 'handle_right', group="%s Spline %s CV %s" % (sk_name, s_i, v_i)) - insert_key(CV, 'radius', group="%s Spline %s CV %s" % (sk_name, s_i, v_i)) - insert_key(CV, 'tilt', group="%s Spline %s CV %s" % (sk_name, s_i, v_i)) - global_spline_index += 1 - - elif spline.type in ('POLY', 'NURBS'): - for v_i, CV in enumerate(spline.points): - if not animall_properties.key_selected or CV.select: - if obj.active_shape_key_index > 0: - CV = obj.active_shape_key.data[global_spline_index] - insert_key(CV, 'co', group="%s Spline %s CV %s" % (sk_name, s_i, v_i)) - insert_key(CV, 'radius', group="%s Spline %s CV %s" % (sk_name, s_i, v_i)) - insert_key(CV, 'tilt', group="%s Spline %s CV %s" % (sk_name, s_i, v_i)) - global_spline_index += 1 - - bpy.ops.object.mode_set(mode=mode) - refresh_ui_keyframes() - - return {'FINISHED'} - - -class ANIM_OT_delete_keyframe_animall(Operator): - bl_label = "Delete Key" - bl_idname = "anim.delete_keyframe_animall" - bl_description = "Delete a Keyframe" - bl_options = {'REGISTER', 'UNDO'} - - - def execute(op, context): - animall_properties = context.scene.animall_properties - - if context.mode == 'OBJECT': - objects = context.selected_objects - else: - objects = context.objects_in_mode_unique_data[:] - - mode = context.object.mode - - for obj in objects: - data = obj.data - if obj.type == 'MESH': - if animall_properties.key_point_location: - for vert in data.vertices: - if not animall_properties.key_selected or vert.select: - delete_key(vert, 'co') - - if animall_properties.key_vertex_bevel: - for vert in data.vertices: - if not animall_properties.key_selected or vert.select: - delete_key(vert, 'bevel_weight') - - if animall_properties.key_vertex_group: - for vert in data.vertices: - if not animall_properties.key_selected or vert.select: - for group in vert.groups: - delete_key(group, 'weight') - - # if animall_properties.key_vertex_crease: - # for vert in data.vertices: - # if not animall_properties.key_selected or vert.select: - # delete_key(vert, 'crease') - - if animall_properties.key_edge_bevel: - for edge in data.edges: - if not animall_properties.key_selected or edge.select: - delete_key(edge, 'bevel_weight') - - if animall_properties.key_edge_crease: - for edge in data.edges: - if not animall_properties.key_selected or vert.select: - delete_key(edge, 'crease') - - if animall_properties.key_shape_key: - if obj.active_shape_key: - for v_i, vert in enumerate(obj.active_shape_key.data): - if not animall_properties.key_selected or data.vertices[v_i].select: - delete_key(vert, 'co') - - if animall_properties.key_uvs: - if data.uv_layers.active is not None: - for uv in data.uv_layers.active.data: - if not animall_properties.key_selected or uv.select: - delete_key(uv, 'uv') - - if animall_properties.key_attribute: - if data.attributes.active is not None: - attribute = data.attributes.active - if attribute.data_type != 'STRING': - # Cannot animate string attributes? - if attribute.data_type in {'FLOAT', 'INT', 'BOOLEAN', 'INT8'}: - attribute_key = "value" - elif attribute.data_type in {'FLOAT_COLOR', 'BYTE_COLOR'}: - attribute_key = "color" - elif attribute.data_type in {'FLOAT_VECTOR', 'FLOAT2'}: - attribute_key = "vector" - - for e_i, _attribute_data in enumerate(attribute.data): - if (not animall_properties.key_selected - or attribute.domain == 'POINT' and data.vertices[e_i].select - or attribute.domain == 'EDGE' and data.edges[e_i].select - or attribute.domain == 'FACE' and data.polygons[e_i].select - or attribute.domain == 'CORNER' and is_selected_vert_loop(data, e_i)): - delete_key(data, f'attributes["{attribute.name}"].data[{e_i}].{attribute_key}') - - elif obj.type == 'LATTICE': - if animall_properties.key_shape_key: - if obj.active_shape_key: - for point in obj.active_shape_key.data: - delete_key(point, 'co') - - if animall_properties.key_point_location: - for point in data.points: - if not animall_properties.key_selected or point.select: - delete_key(point, 'co_deform') - - elif obj.type in {'CURVE', 'SURFACE'}: - # Run this outside the splines loop (only once) - if animall_properties.key_shape_key: - if obj.active_shape_key_index > 0: - for CV in obj.active_shape_key.data: - delete_key(CV, 'co') - delete_key(CV, 'handle_left') - delete_key(CV, 'handle_right') - - for spline in data.splines: - if spline.type == 'BEZIER': - for CV in spline.bezier_points: - if (not animall_properties.key_selected - or CV.select_control_point - or CV.select_left_handle - or CV.select_right_handle): - if animall_properties.key_point_location: - delete_key(CV, 'co') - delete_key(CV, 'handle_left') - delete_key(CV, 'handle_right') - if animall_properties.key_radius: - delete_key(CV, 'radius') - if animall_properties.key_tilt: - delete_key(CV, 'tilt') - - elif spline.type in ('POLY', 'NURBS'): - for CV in spline.points: - if not animall_properties.key_selected or CV.select: - if animall_properties.key_point_location: - delete_key(CV, 'co') - if animall_properties.key_radius: - delete_key(CV, 'radius') - if animall_properties.key_tilt: - delete_key(CV, 'tilt') - - refresh_ui_keyframes() - - return {'FINISHED'} - - -class ANIM_OT_clear_animation_animall(Operator): - bl_label = "Clear Animation" - bl_idname = "anim.clear_animation_animall" - bl_description = ("Delete all keyframes for this object\n" - "If in a specific case it doesn't work\n" - "try to delete the keys manually") - bl_options = {'REGISTER', 'UNDO'} - - def invoke(self, context, event): - wm = context.window_manager - return wm.invoke_confirm(self, event) - - def execute(self, context): - if context.mode == 'OBJECT': - objects = context.selected_objects - else: - objects = context.objects_in_mode_unique_data - - for obj in objects: - try: - data = obj.data - data.animation_data_clear() - except: - self.report({'WARNING'}, "Clear Animation could not be performed") - return {'CANCELLED'} - - refresh_ui_keyframes() - - return {'FINISHED'} - - -class ANIM_OT_update_vertex_color_animation_animall(Operator): - bl_label = "Update Vertex Color Animation" - bl_idname = "anim.update_vertex_color_animation_animall" - bl_description = "Update old vertex color channel formats from pre-3.3 versions" - bl_options = {'REGISTER', 'UNDO'} - - @classmethod - def poll(self, context): - if (context.active_object is None - or context.active_object.type != 'MESH' - or context.active_object.data.animation_data is None - or context.active_object.data.animation_data.action is None): - return False - for fcurve in context.active_object.data.animation_data.action.fcurves: - if fcurve.data_path.startswith("vertex_colors"): - return True - - def execute(self, context): - for fcurve in context.active_object.data.animation_data.action.fcurves: - if fcurve.data_path.startswith("vertex_colors"): - fcurve.data_path = fcurve.data_path.replace("vertex_colors", "attributes") - return {'FINISHED'} - -# Add-ons Preferences Update Panel - -# Define Panel classes for updating -panels = [ - VIEW3D_PT_animall - ] - - -def update_panel(self, context): - message = "AnimAll: Updating Panel locations has failed" - try: - for panel in panels: - if "bl_rna" in panel.__dict__: - bpy.utils.unregister_class(panel) - - for panel in panels: - panel.bl_category = context.preferences.addons[__name__].preferences.category - bpy.utils.register_class(panel) - - except Exception as e: - print("\n[{}]\n{}\n\nError:\n{}".format(__name__, message, e)) - pass - - -class AnimallAddonPreferences(AddonPreferences): - # this must match the addon name, use '__package__' - # when defining this in a submodule of a python package. - bl_idname = __name__ - - category: StringProperty( - name="Tab Category", - description="Choose a name for the category of the panel", - default="Animate", - update=update_panel - ) - - def draw(self, context): - layout = self.layout - row = layout.row() - col = row.column() - - col.label(text="Tab Category:") - col.prop(self, "category", text="") - - -def register(): - bpy.utils.register_class(AnimallProperties) - bpy.types.Scene.animall_properties = bpy.props.PointerProperty(type=AnimallProperties) - bpy.utils.register_class(VIEW3D_PT_animall) - bpy.utils.register_class(ANIM_OT_insert_keyframe_animall) - bpy.utils.register_class(ANIM_OT_delete_keyframe_animall) - bpy.utils.register_class(ANIM_OT_clear_animation_animall) - bpy.utils.register_class(ANIM_OT_update_vertex_color_animation_animall) - bpy.utils.register_class(AnimallAddonPreferences) - update_panel(None, bpy.context) - - -def unregister(): - del bpy.types.Scene.animall_properties - bpy.utils.unregister_class(AnimallProperties) - bpy.utils.unregister_class(VIEW3D_PT_animall) - bpy.utils.unregister_class(ANIM_OT_insert_keyframe_animall) - bpy.utils.unregister_class(ANIM_OT_delete_keyframe_animall) - bpy.utils.unregister_class(ANIM_OT_clear_animation_animall) - bpy.utils.unregister_class(ANIM_OT_update_vertex_color_animation_animall) - bpy.utils.unregister_class(AnimallAddonPreferences) - -if __name__ == "__main__": - register() diff --git a/animation_animall/__init__.py b/animation_animall/__init__.py new file mode 100644 index 00000000..944f4a2f --- /dev/null +++ b/animation_animall/__init__.py @@ -0,0 +1,685 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +bl_info = { + "name": "AnimAll", + "author": "Daniel Salazar (ZanQdo), Damien Picard (pioverfour)", + "version": (0, 9, 6), + "blender": (3, 3, 0), + "location": "3D View > Toolbox > Animation tab > AnimAll", + "description": "Allows animation of mesh, lattice, curve and surface data", + "warning": "", + "doc_url": "{BLENDER_MANUAL_URL}/addons/animation/animall.html", + "category": "Animation", +} + +import bpy +from bpy.types import ( + Operator, + Panel, + AddonPreferences, + ) +from bpy.props import ( + BoolProperty, + StringProperty, + ) +from bpy.app.handlers import persistent + + +# Property Definitions +class AnimallProperties(bpy.types.PropertyGroup): + key_selected: BoolProperty( + name="Key Selected Only", + description="Insert keyframes only on selected elements", + default=False) + + # Generic attributes + key_point_location: BoolProperty( + name="Location", + description="Insert keyframes on point locations", + default=False) + key_shape_key: BoolProperty( + name="Shape Key", + description="Insert keyframes on active Shape Key layer", + default=False) + key_material_index: BoolProperty( + name="Material Index", + description="Insert keyframes on face material indices", + default=False) + + # Mesh attributes + key_vertex_bevel: BoolProperty( + name="Vertex Bevel", + description="Insert keyframes on vertex bevel weight", + default=False) + # key_vertex_crease: BoolProperty( + # name="Vertex Crease", + # description="Insert keyframes on vertex crease weight", + # default=False) + key_vertex_group: BoolProperty( + name="Vertex Group", + description="Insert keyframes on active vertex group values", + default=False) + + key_edge_bevel: BoolProperty( + name="Edge Bevel", + description="Insert keyframes on edge bevel weight", + default=False) + key_edge_crease: BoolProperty( + name="Edge Crease", + description="Insert keyframes on edge creases", + default=False) + + key_attribute: BoolProperty( + name="Attribute", + description="Insert keyframes on active attribute values", + default=False) + key_uvs: BoolProperty( + name="UV Map", + description="Insert keyframes on active UV coordinates", + default=False) + + # Curve and surface attributes + key_radius: BoolProperty( + name="Radius", + description="Insert keyframes on point radius (Shrink/Fatten)", + default=False) + key_tilt: BoolProperty( + name="Tilt", + description="Insert keyframes on point tilt", + default=False) + + +# Utility functions + +def refresh_ui_keyframes(): + try: + for area in bpy.context.screen.areas: + if area.type in ('TIMELINE', 'GRAPH_EDITOR', 'DOPESHEET_EDITOR'): + area.tag_redraw() + except: + pass + + +def insert_key(data, key, group=None): + try: + if group is not None: + data.keyframe_insert(key, group=group) + else: + data.keyframe_insert(key) + except: + pass + + +def delete_key(data, key): + try: + data.keyframe_delete(key) + except: + pass + + +def is_selected_vert_loop(data, loop_i): + """Get selection status of vertex corresponding to a loop""" + vertex_index = data.loops[loop_i].vertex_index + return data.vertices[vertex_index].select + + +# GUI (Panel) + +class VIEW3D_PT_animall(Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = "Animate" + bl_label = '' + + @classmethod + def poll(self, context): + return context.active_object and context.active_object.type in {'MESH', 'LATTICE', 'CURVE', 'SURFACE'} + + def draw_header(self, context): + + layout = self.layout + row = layout.row() + row.label (text = 'AnimAll', icon = 'ARMATURE_DATA') + + def draw(self, context): + obj = context.active_object + animall_properties = context.scene.animall_properties + + layout = self.layout + + layout.label(text='Key:') + + layout.use_property_split = True + layout.use_property_decorate = False + + if obj.type == 'LATTICE': + col = layout.column(heading="Points", align=True) + col.prop(animall_properties, "key_point_location") + + col = layout.column(heading="Others", align=True) + col.prop(animall_properties, "key_shape_key") + + elif obj.type == 'MESH': + col = layout.column(heading="Points", align=True) + col.prop(animall_properties, "key_point_location") + col.prop(animall_properties, "key_vertex_bevel", text="Bevel") + col.prop(animall_properties, "key_vertex_group") + + col = layout.column(heading="Edges", align=True) + col.prop(animall_properties, "key_edge_bevel", text="Bevel") + col.prop(animall_properties, "key_edge_crease", text="Crease") + + col = layout.column(heading="Faces", align=True) + col.prop(animall_properties, "key_material_index") + + col = layout.column(heading="Others", align=True) + col.prop(animall_properties, "key_attribute") + col.prop(animall_properties, "key_uvs") + col.prop(animall_properties, "key_shape_key") + + # Vertex group update operator + if (obj.data.animation_data is not None + and obj.data.animation_data.action is not None): + for fcurve in context.active_object.data.animation_data.action.fcurves: + if fcurve.data_path.startswith("vertex_colors"): + col = layout.column(align=True) + col.label(text="Object includes old-style vertex colors. Consider updating them.", icon="ERROR") + col.operator("anim.update_vertex_color_animation_animall", icon="FILE_REFRESH") + break + + elif obj.type in {'CURVE', 'SURFACE'}: + col = layout.column(align=True) + col = layout.column(heading="Points", align=True) + col.prop(animall_properties, "key_point_location") + col.prop(animall_properties, "key_radius") + col.prop(animall_properties, "key_tilt") + + col = layout.column(heading="Splines", align=True) + col.prop(animall_properties, "key_material_index") + + col = layout.column(heading="Others", align=True) + col.prop(animall_properties, "key_shape_key") + + if animall_properties.key_shape_key: + shape_key = obj.active_shape_key + shape_key_index = obj.active_shape_key_index + + if shape_key_index > 0: + col = layout.column(align=True) + row = col.row(align=True) + row.prop(shape_key, "value", text=shape_key.name, icon="SHAPEKEY_DATA") + row.prop(obj, "show_only_shape_key", text="") + if shape_key.value < 1: + col.label(text='Maybe set "%s" to 1.0?' % shape_key.name, icon="INFO") + elif shape_key is not None: + col = layout.column(align=True) + col.label(text="Cannot key on Basis Shape", icon="ERROR") + else: + col = layout.column(align=True) + col.label(text="No active Shape Key", icon="ERROR") + + if animall_properties.key_point_location: + col.label(text='"Location" and "Shape Key" are redundant?', icon="INFO") + + layout.use_property_split = False + layout.separator() + row = layout.row() + row.prop(animall_properties, "key_selected") + + row = layout.row(align=True) + row.operator("anim.insert_keyframe_animall", icon="KEY_HLT") + row.operator("anim.delete_keyframe_animall", icon="KEY_DEHLT") + row = layout.row() + row.operator("anim.clear_animation_animall", icon="CANCEL") + + +class ANIM_OT_insert_keyframe_animall(Operator): + bl_label = "Insert Key" + bl_idname = "anim.insert_keyframe_animall" + bl_description = "Insert a Keyframe" + bl_options = {'REGISTER', 'UNDO'} + + def execute(op, context): + animall_properties = context.scene.animall_properties + + if context.mode == 'OBJECT': + objects = context.selected_objects + else: + objects = context.objects_in_mode_unique_data[:] + + mode = context.object.mode + + # Separate loop for lattices, curves and surfaces, since keyframe insertion + # has to happen in Edit Mode, otherwise points move back upon mode switch... + # (except for curve shape keys) + for obj in [o for o in objects if o.type in {'CURVE', 'SURFACE', 'LATTICE'}]: + data = obj.data + + if obj.type == 'LATTICE': + if animall_properties.key_shape_key: + if obj.active_shape_key_index > 0: + sk_name = obj.active_shape_key.name + for p_i, point in enumerate(obj.active_shape_key.data): + if not animall_properties.key_selected or data.points[p_i].select: + insert_key(point, 'co', group="%s Point %s" % (sk_name, p_i)) + + if animall_properties.key_point_location: + for p_i, point in enumerate(data.points): + if not animall_properties.key_selected or point.select: + insert_key(point, 'co_deform', group="Point %s" % p_i) + + else: + if animall_properties.key_material_index: + for s_i, spline in enumerate(data.splines): + if (not animall_properties.key_selected + or any(point.select for point in spline.points) + or any(point.select_control_point for point in spline.bezier_points)): + insert_key(spline, 'material_index', group="Spline %s" % s_i) + + for s_i, spline in enumerate(data.splines): + if spline.type == 'BEZIER': + for v_i, CV in enumerate(spline.bezier_points): + if (not animall_properties.key_selected + or CV.select_control_point + or CV.select_left_handle + or CV.select_right_handle): + if animall_properties.key_point_location: + insert_key(CV, 'co', group="Spline %s CV %s" % (s_i, v_i)) + insert_key(CV, 'handle_left', group="Spline %s CV %s" % (s_i, v_i)) + insert_key(CV, 'handle_right', group="Spline %s CV %s" % (s_i, v_i)) + + if animall_properties.key_radius: + insert_key(CV, 'radius', group="Spline %s CV %s" % (s_i, v_i)) + + if animall_properties.key_tilt: + insert_key(CV, 'tilt', group="Spline %s CV %s" % (s_i, v_i)) + + elif spline.type in ('POLY', 'NURBS'): + for v_i, CV in enumerate(spline.points): + if not animall_properties.key_selected or CV.select: + if animall_properties.key_point_location: + insert_key(CV, 'co', group="Spline %s CV %s" % (s_i, v_i)) + + if animall_properties.key_radius: + insert_key(CV, 'radius', group="Spline %s CV %s" % (s_i, v_i)) + + if animall_properties.key_tilt: + insert_key(CV, 'tilt', group="Spline %s CV %s" % (s_i, v_i)) + + bpy.ops.object.mode_set(mode='OBJECT') + + for obj in [o for o in objects if o.type in {'MESH', 'CURVE', 'SURFACE'}]: + data = obj.data + if obj.type == 'MESH': + if animall_properties.key_point_location: + for v_i, vert in enumerate(data.vertices): + if not animall_properties.key_selected or vert.select: + insert_key(vert, 'co', group="Vertex %s" % v_i) + + if animall_properties.key_vertex_bevel: + for v_i, vert in enumerate(data.vertices): + if not animall_properties.key_selected or vert.select: + insert_key(vert, 'bevel_weight', group="Vertex %s" % v_i) + # if animall_properties.key_vertex_crease: + # for v_i, vert in enumerate(data.vertices): + # if not animall_properties.key_selected or vert.select: + # insert_key(vert, 'crease', group="Vertex %s" % v_i) + + if animall_properties.key_vertex_group: + for v_i, vert in enumerate(data.vertices): + if not animall_properties.key_selected or vert.select: + for group in vert.groups: + insert_key(group, 'weight', group="Vertex %s" % v_i) + + if animall_properties.key_edge_bevel: + for e_i, edge in enumerate(data.edges): + if not animall_properties.key_selected or edge.select: + insert_key(edge, 'bevel_weight', group="Edge %s" % e_i) + + if animall_properties.key_edge_crease: + for e_i, edge in enumerate(data.edges): + if not animall_properties.key_selected or edge.select: + insert_key(edge, 'crease', group="Edge %s" % e_i) + + if animall_properties.key_material_index: + for p_i, polygon in enumerate(data.polygons): + if not animall_properties.key_selected or polygon.select: + insert_key(polygon, 'material_index', group="Face %s" % p_i) + + if animall_properties.key_attribute: + if data.attributes.active is not None: + attribute = data.attributes.active + if attribute.data_type != 'STRING': + # Cannot animate string attributes? + if attribute.data_type in {'FLOAT', 'INT', 'BOOLEAN', 'INT8'}: + attribute_key = "value" + elif attribute.data_type in {'FLOAT_COLOR', 'BYTE_COLOR'}: + attribute_key = "color" + elif attribute.data_type in {'FLOAT_VECTOR', 'FLOAT2'}: + attribute_key = "vector" + + if attribute.domain == 'POINT': + group = "Vertex %s" + elif attribute.domain == 'EDGE': + group = "Edge %s" + elif attribute.domain == 'FACE': + group = "Face %s" + elif attribute.domain == 'CORNER': + group = "Loop %s" + + for e_i, _attribute_data in enumerate(attribute.data): + if (not animall_properties.key_selected + or attribute.domain == 'POINT' and data.vertices[e_i].select + or attribute.domain == 'EDGE' and data.edges[e_i].select + or attribute.domain == 'FACE' and data.polygons[e_i].select + or attribute.domain == 'CORNER' and is_selected_vert_loop(data, e_i)): + insert_key(data, f'attributes["{attribute.name}"].data[{e_i}].{attribute_key}', + group=group % e_i) + + if animall_properties.key_uvs: + if data.uv_layers.active is not None: + for uv_i, uv in enumerate(data.uv_layers.active.data): + if not animall_properties.key_selected or uv.select: + insert_key(uv, 'uv', group="UV layer %s" % uv_i) + + if animall_properties.key_shape_key: + if obj.active_shape_key_index > 0: + sk_name = obj.active_shape_key.name + for v_i, vert in enumerate(obj.active_shape_key.data): + if not animall_properties.key_selected or data.vertices[v_i].select: + insert_key(vert, 'co', group="%s Vertex %s" % (sk_name, v_i)) + + elif obj.type in {'CURVE', 'SURFACE'}: + # Shape key keys have to be inserted in object mode for curves... + if animall_properties.key_shape_key: + sk_name = obj.active_shape_key.name + global_spline_index = 0 # numbering for shape keys, which have flattened indices + for s_i, spline in enumerate(data.splines): + if spline.type == 'BEZIER': + for v_i, CV in enumerate(spline.bezier_points): + if (not animall_properties.key_selected + or CV.select_control_point + or CV.select_left_handle + or CV.select_right_handle): + if obj.active_shape_key_index > 0: + CV = obj.active_shape_key.data[global_spline_index] + insert_key(CV, 'co', group="%s Spline %s CV %s" % (sk_name, s_i, v_i)) + insert_key(CV, 'handle_left', group="%s Spline %s CV %s" % (sk_name, s_i, v_i)) + insert_key(CV, 'handle_right', group="%s Spline %s CV %s" % (sk_name, s_i, v_i)) + insert_key(CV, 'radius', group="%s Spline %s CV %s" % (sk_name, s_i, v_i)) + insert_key(CV, 'tilt', group="%s Spline %s CV %s" % (sk_name, s_i, v_i)) + global_spline_index += 1 + + elif spline.type in ('POLY', 'NURBS'): + for v_i, CV in enumerate(spline.points): + if not animall_properties.key_selected or CV.select: + if obj.active_shape_key_index > 0: + CV = obj.active_shape_key.data[global_spline_index] + insert_key(CV, 'co', group="%s Spline %s CV %s" % (sk_name, s_i, v_i)) + insert_key(CV, 'radius', group="%s Spline %s CV %s" % (sk_name, s_i, v_i)) + insert_key(CV, 'tilt', group="%s Spline %s CV %s" % (sk_name, s_i, v_i)) + global_spline_index += 1 + + bpy.ops.object.mode_set(mode=mode) + refresh_ui_keyframes() + + return {'FINISHED'} + + +class ANIM_OT_delete_keyframe_animall(Operator): + bl_label = "Delete Key" + bl_idname = "anim.delete_keyframe_animall" + bl_description = "Delete a Keyframe" + bl_options = {'REGISTER', 'UNDO'} + + + def execute(op, context): + animall_properties = context.scene.animall_properties + + if context.mode == 'OBJECT': + objects = context.selected_objects + else: + objects = context.objects_in_mode_unique_data[:] + + mode = context.object.mode + + for obj in objects: + data = obj.data + if obj.type == 'MESH': + if animall_properties.key_point_location: + for vert in data.vertices: + if not animall_properties.key_selected or vert.select: + delete_key(vert, 'co') + + if animall_properties.key_vertex_bevel: + for vert in data.vertices: + if not animall_properties.key_selected or vert.select: + delete_key(vert, 'bevel_weight') + + if animall_properties.key_vertex_group: + for vert in data.vertices: + if not animall_properties.key_selected or vert.select: + for group in vert.groups: + delete_key(group, 'weight') + + # if animall_properties.key_vertex_crease: + # for vert in data.vertices: + # if not animall_properties.key_selected or vert.select: + # delete_key(vert, 'crease') + + if animall_properties.key_edge_bevel: + for edge in data.edges: + if not animall_properties.key_selected or edge.select: + delete_key(edge, 'bevel_weight') + + if animall_properties.key_edge_crease: + for edge in data.edges: + if not animall_properties.key_selected or vert.select: + delete_key(edge, 'crease') + + if animall_properties.key_shape_key: + if obj.active_shape_key: + for v_i, vert in enumerate(obj.active_shape_key.data): + if not animall_properties.key_selected or data.vertices[v_i].select: + delete_key(vert, 'co') + + if animall_properties.key_uvs: + if data.uv_layers.active is not None: + for uv in data.uv_layers.active.data: + if not animall_properties.key_selected or uv.select: + delete_key(uv, 'uv') + + if animall_properties.key_attribute: + if data.attributes.active is not None: + attribute = data.attributes.active + if attribute.data_type != 'STRING': + # Cannot animate string attributes? + if attribute.data_type in {'FLOAT', 'INT', 'BOOLEAN', 'INT8'}: + attribute_key = "value" + elif attribute.data_type in {'FLOAT_COLOR', 'BYTE_COLOR'}: + attribute_key = "color" + elif attribute.data_type in {'FLOAT_VECTOR', 'FLOAT2'}: + attribute_key = "vector" + + for e_i, _attribute_data in enumerate(attribute.data): + if (not animall_properties.key_selected + or attribute.domain == 'POINT' and data.vertices[e_i].select + or attribute.domain == 'EDGE' and data.edges[e_i].select + or attribute.domain == 'FACE' and data.polygons[e_i].select + or attribute.domain == 'CORNER' and is_selected_vert_loop(data, e_i)): + delete_key(data, f'attributes["{attribute.name}"].data[{e_i}].{attribute_key}') + + elif obj.type == 'LATTICE': + if animall_properties.key_shape_key: + if obj.active_shape_key: + for point in obj.active_shape_key.data: + delete_key(point, 'co') + + if animall_properties.key_point_location: + for point in data.points: + if not animall_properties.key_selected or point.select: + delete_key(point, 'co_deform') + + elif obj.type in {'CURVE', 'SURFACE'}: + # Run this outside the splines loop (only once) + if animall_properties.key_shape_key: + if obj.active_shape_key_index > 0: + for CV in obj.active_shape_key.data: + delete_key(CV, 'co') + delete_key(CV, 'handle_left') + delete_key(CV, 'handle_right') + + for spline in data.splines: + if spline.type == 'BEZIER': + for CV in spline.bezier_points: + if (not animall_properties.key_selected + or CV.select_control_point + or CV.select_left_handle + or CV.select_right_handle): + if animall_properties.key_point_location: + delete_key(CV, 'co') + delete_key(CV, 'handle_left') + delete_key(CV, 'handle_right') + if animall_properties.key_radius: + delete_key(CV, 'radius') + if animall_properties.key_tilt: + delete_key(CV, 'tilt') + + elif spline.type in ('POLY', 'NURBS'): + for CV in spline.points: + if not animall_properties.key_selected or CV.select: + if animall_properties.key_point_location: + delete_key(CV, 'co') + if animall_properties.key_radius: + delete_key(CV, 'radius') + if animall_properties.key_tilt: + delete_key(CV, 'tilt') + + refresh_ui_keyframes() + + return {'FINISHED'} + + +class ANIM_OT_clear_animation_animall(Operator): + bl_label = "Clear Animation" + bl_idname = "anim.clear_animation_animall" + bl_description = ("Delete all keyframes for this object\n" + "If in a specific case it doesn't work\n" + "try to delete the keys manually") + bl_options = {'REGISTER', 'UNDO'} + + def invoke(self, context, event): + wm = context.window_manager + return wm.invoke_confirm(self, event) + + def execute(self, context): + if context.mode == 'OBJECT': + objects = context.selected_objects + else: + objects = context.objects_in_mode_unique_data + + for obj in objects: + try: + data = obj.data + data.animation_data_clear() + except: + self.report({'WARNING'}, "Clear Animation could not be performed") + return {'CANCELLED'} + + refresh_ui_keyframes() + + return {'FINISHED'} + + +class ANIM_OT_update_vertex_color_animation_animall(Operator): + bl_label = "Update Vertex Color Animation" + bl_idname = "anim.update_vertex_color_animation_animall" + bl_description = "Update old vertex color channel formats from pre-3.3 versions" + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(self, context): + if (context.active_object is None + or context.active_object.type != 'MESH' + or context.active_object.data.animation_data is None + or context.active_object.data.animation_data.action is None): + return False + for fcurve in context.active_object.data.animation_data.action.fcurves: + if fcurve.data_path.startswith("vertex_colors"): + return True + + def execute(self, context): + for fcurve in context.active_object.data.animation_data.action.fcurves: + if fcurve.data_path.startswith("vertex_colors"): + fcurve.data_path = fcurve.data_path.replace("vertex_colors", "attributes") + return {'FINISHED'} + +# Add-ons Preferences Update Panel + +# Define Panel classes for updating +panels = [ + VIEW3D_PT_animall + ] + + +def update_panel(self, context): + message = "AnimAll: Updating Panel locations has failed" + try: + for panel in panels: + if "bl_rna" in panel.__dict__: + bpy.utils.unregister_class(panel) + + for panel in panels: + panel.bl_category = context.preferences.addons[__name__].preferences.category + bpy.utils.register_class(panel) + + except Exception as e: + print("\n[{}]\n{}\n\nError:\n{}".format(__name__, message, e)) + pass + + +class AnimallAddonPreferences(AddonPreferences): + # this must match the addon name, use '__package__' + # when defining this in a submodule of a python package. + bl_idname = __name__ + + category: StringProperty( + name="Tab Category", + description="Choose a name for the category of the panel", + default="Animate", + update=update_panel + ) + + def draw(self, context): + layout = self.layout + row = layout.row() + col = row.column() + + col.label(text="Tab Category:") + col.prop(self, "category", text="") + + +def register(): + bpy.utils.register_class(AnimallProperties) + bpy.types.Scene.animall_properties = bpy.props.PointerProperty(type=AnimallProperties) + bpy.utils.register_class(VIEW3D_PT_animall) + bpy.utils.register_class(ANIM_OT_insert_keyframe_animall) + bpy.utils.register_class(ANIM_OT_delete_keyframe_animall) + bpy.utils.register_class(ANIM_OT_clear_animation_animall) + bpy.utils.register_class(ANIM_OT_update_vertex_color_animation_animall) + bpy.utils.register_class(AnimallAddonPreferences) + update_panel(None, bpy.context) + + +def unregister(): + del bpy.types.Scene.animall_properties + bpy.utils.unregister_class(AnimallProperties) + bpy.utils.unregister_class(VIEW3D_PT_animall) + bpy.utils.unregister_class(ANIM_OT_insert_keyframe_animall) + bpy.utils.unregister_class(ANIM_OT_delete_keyframe_animall) + bpy.utils.unregister_class(ANIM_OT_clear_animation_animall) + bpy.utils.unregister_class(ANIM_OT_update_vertex_color_animation_animall) + bpy.utils.unregister_class(AnimallAddonPreferences) + +if __name__ == "__main__": + register() -- cgit v1.2.3 From 6fb43c4b04b5073c6dd7b3ef88f4cee61709ce7e Mon Sep 17 00:00:00 2001 From: Damien Picard Date: Thu, 30 Jun 2022 19:06:54 +0200 Subject: Animall: add French translations --- animation_animall/__init__.py | 38 +++--- animation_animall/translations.py | 250 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 264 insertions(+), 24 deletions(-) create mode 100644 animation_animall/translations.py diff --git a/animation_animall/__init__.py b/animation_animall/__init__.py index 944f4a2f..1ff7bc69 100644 --- a/animation_animall/__init__.py +++ b/animation_animall/__init__.py @@ -13,16 +13,11 @@ bl_info = { } import bpy -from bpy.types import ( - Operator, - Panel, - AddonPreferences, - ) -from bpy.props import ( - BoolProperty, - StringProperty, - ) +from bpy.types import (Operator, Panel, AddonPreferences) +from bpy.props import (BoolProperty, StringProperty) from bpy.app.handlers import persistent +from bpy.app.translations import pgettext_iface +from . import translations # Property Definitions @@ -210,7 +205,7 @@ class VIEW3D_PT_animall(Panel): row.prop(shape_key, "value", text=shape_key.name, icon="SHAPEKEY_DATA") row.prop(obj, "show_only_shape_key", text="") if shape_key.value < 1: - col.label(text='Maybe set "%s" to 1.0?' % shape_key.name, icon="INFO") + col.label(text=pgettext_iface('Maybe set "%s" to 1.0?') % shape_key.name, icon="INFO") elif shape_key is not None: col = layout.column(align=True) col.label(text="Cannot key on Basis Shape", icon="ERROR") @@ -658,28 +653,23 @@ class AnimallAddonPreferences(AddonPreferences): col.label(text="Tab Category:") col.prop(self, "category", text="") +register_classes, unregister_classes = bpy.utils.register_classes_factory( + (AnimallProperties, VIEW3D_PT_animall, ANIM_OT_insert_keyframe_animall, + ANIM_OT_delete_keyframe_animall, ANIM_OT_clear_animation_animall, + ANIM_OT_update_vertex_color_animation_animall, AnimallAddonPreferences)) def register(): - bpy.utils.register_class(AnimallProperties) + register_classes() bpy.types.Scene.animall_properties = bpy.props.PointerProperty(type=AnimallProperties) - bpy.utils.register_class(VIEW3D_PT_animall) - bpy.utils.register_class(ANIM_OT_insert_keyframe_animall) - bpy.utils.register_class(ANIM_OT_delete_keyframe_animall) - bpy.utils.register_class(ANIM_OT_clear_animation_animall) - bpy.utils.register_class(ANIM_OT_update_vertex_color_animation_animall) - bpy.utils.register_class(AnimallAddonPreferences) update_panel(None, bpy.context) + bpy.app.translations.register(__name__, translations.translations_dict) def unregister(): del bpy.types.Scene.animall_properties - bpy.utils.unregister_class(AnimallProperties) - bpy.utils.unregister_class(VIEW3D_PT_animall) - bpy.utils.unregister_class(ANIM_OT_insert_keyframe_animall) - bpy.utils.unregister_class(ANIM_OT_delete_keyframe_animall) - bpy.utils.unregister_class(ANIM_OT_clear_animation_animall) - bpy.utils.unregister_class(ANIM_OT_update_vertex_color_animation_animall) - bpy.utils.unregister_class(AnimallAddonPreferences) + unregister_classes() + + bpy.app.translations.unregister(__name__) if __name__ == "__main__": register() diff --git a/animation_animall/translations.py b/animation_animall/translations.py new file mode 100644 index 00000000..9ced1807 --- /dev/null +++ b/animation_animall/translations.py @@ -0,0 +1,250 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +# Tuple of tuples: +# ((msgctxt, msgid), (sources, gen_comments), (lang, translation, (is_fuzzy, comments)), ...) +translations_tuple = ( + (("*", ""), + ((), ()), + ("fr_FR", "Project-Id-Version: AnimAll 0.9.6 (0)\nReport-Msgid-Bugs-To: \nPOT-Creation-Date: 2022-06-24 00:41:10.347798\nPO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\nLast-Translator: FULL NAME \nLanguage-Team: LANGUAGE \nLanguage: __POT__\nMIME-Version: 1.0\nContent-Type: text/plain; charset=UTF-8\nContent-Transfer-Encoding: 8bit", + (False, + ("Blender's translation file (po format).", + "Copyright (C) 2022 The Blender Foundation.", + "This file is distributed under the same license as the Blender package.", + "Damien Picard , 2022."))), + ), + (("*", "Tab Category"), + (("bpy.types.AnimallAddonPreferences.category",), + ()), + ("fr_FR", "Catégorie d’onglet", + (False, ())), + ), + (("*", "Choose a name for the category of the panel"), + (("bpy.types.AnimallAddonPreferences.category",), + ()), + ("fr_FR", "Choisir un nom pour la catégorie du panneau", + (False, ())), + ), + (("Operator", "Clear Animation"), + (("bpy.types.ANIM_OT_clear_animation_animall",), + ()), + ("fr_FR", "Effacer l’animation", + (False, ())), + ), + (("*", "Delete all keyframes for this object\nIf in a specific case it doesn't work\ntry to delete the keys manually"), + (("bpy.types.ANIM_OT_clear_animation_animall",), + ()), + ("fr_FR", "Supprimer toutes les images clés pour cet objet.\n" + "En cas d’échec, essayez de les supprimer manuellement", + (False, ())), + ), + (("*", "Insert a Keyframe"), + (("bpy.types.ANIM_OT_insert_keyframe_animall",), + ()), + ("fr_FR", "Insérer une image clé", + (False, ())), + ), + (("Operator", "Delete Key"), + (("bpy.types.ANIM_OT_delete_keyframe_animall",), + ()), + ("fr_FR", "Supprimer image clé", + (False, ())), + ), + (("*", "Delete a Keyframe"), + (("bpy.types.ANIM_OT_delete_keyframe_animall",), + ()), + ("fr_FR", "Supprimer une image clé", + (False, ())), + ), + (("*", "Animate"), + (("bpy.types.VIEW3D_PT_animall",), + ()), + ("fr_FR", "Animer", + (False, ())), + ), + (("*", "Insert keyframes on active attribute values"), + (("bpy.types.AnimallProperties.key_attribute",), + ()), + ("fr_FR", "Insérer des clés sur l’attribut actif", + (False, ())), + ), + (("*", "Insert keyframes on edge bevel weight"), + (("bpy.types.AnimallProperties.key_edge_bevel",), + ()), + ("fr_FR", "Insérer des clés sur les poids de biseau d’arête", + (False, ())), + ), + (("*", "Insert keyframes on edge creases"), + (("bpy.types.AnimallProperties.key_edge_crease",), + ()), + ("fr_FR", "Insérer des clés sur les plis d’arête", + (False, ())), + ), + (("*", "Insert keyframes on face material indices"), + (("bpy.types.AnimallProperties.key_material_index",), + ()), + ("fr_FR", "Insérer des clés sur les indices de matériaux", + (False, ())), + ), + (("*", "Insert keyframes on point locations"), + (("bpy.types.AnimallProperties.key_point_location",), + ()), + ("fr_FR", "Insérer des clés sur les positions des points", + (False, ())), + ), + (("*", "Insert keyframes on point radius (Shrink/Fatten)"), + (("bpy.types.AnimallProperties.key_radius",), + ()), + ("fr_FR", "Insérer des clés sur le rayon de rayon de point (épaisseur de la courbe)", + (False, ())), + ), + (("*", "Key Selected Only"), + (("bpy.types.AnimallProperties.key_selected",), + ()), + ("fr_FR", "Sélection uniquement", + (False, ())), + ), + (("*", "Insert keyframes only on selected elements"), + (("bpy.types.AnimallProperties.key_selected",), + ()), + ("fr_FR", "Insérer des images clés seulement sur les éléments sélectionnés", + (False, ())), + ), + (("*", "Insert keyframes on active Shape Key layer"), + (("bpy.types.AnimallProperties.key_shape_key",), + ()), + ("fr_FR", "Insérer des clés sur le calque de clé de forme actif", + (False, ())), + ), + (("*", "Insert keyframes on point tilt"), + (("bpy.types.AnimallProperties.key_tilt",), + ()), + ("fr_FR", "Insérer des clés sur l’inclinaison des points", + (False, ())), + ), + (("*", "Insert keyframes on active UV coordinates"), + (("bpy.types.AnimallProperties.key_uvs",), + ()), + ("fr_FR", "Insérer des clés sur les coordonnées UV actives", + (False, ())), + ), + (("*", "Insert keyframes on vertex bevel weight"), + (("bpy.types.AnimallProperties.key_vertex_bevel",), + ()), + ("fr_FR", "Insérer des clés sur les poids de biseau des sommets", + (False, ())), + ), + (("*", "Insert keyframes on active vertex group values"), + (("bpy.types.AnimallProperties.key_vertex_group",), + ()), + ("fr_FR", "Insérer des clés sur les valeurs des groupes de sommets", + (False, ())), + ), + (("*", "AnimAll"), + (("scripts/addons/animation_animall.py:142",), + ()), + ("fr_FR", "", + (False, ())), + ), + (("*", "Key:"), + (("scripts/addons/animation_animall.py:150",), + ()), + ("fr_FR", "Insérer :", + (False, ())), + ), + (("*", "Tab Category:"), + (("scripts/addons/animation_animall.py:658",), + ()), + ("fr_FR", "Catégorie d’onglet", + (False, ())), + ), + # (("*", "Points"), + # (("scripts/addons/animation_animall.py:156", + # "scripts/addons/animation_animall.py:163", + # "scripts/addons/animation_animall.py:192"), + # ()), + # ("fr_FR", "", + # (False, ())), + # ), + (("*", "Others"), + (("scripts/addons/animation_animall.py:159", + "scripts/addons/animation_animall.py:175", + "scripts/addons/animation_animall.py:200"), + ()), + ("fr_FR", "Autres", + (False, ())), + ), + # (("*", "Bevel"), + # (("scripts/addons/animation_animall.py:165", + # "scripts/addons/animation_animall.py:169"), + # ()), + # ("fr_FR", "", + # (False, ())), + # ), + # (("*", "Edges"), + # (("scripts/addons/animation_animall.py:168",), + # ()), + # ("fr_FR", "", + # (False, ())), + # ), + # (("*", "Crease"), + # (("scripts/addons/animation_animall.py:170",), + # ()), + # ("fr_FR", "", + # (False, ())), + # ), + # (("*", "Faces"), + # (("scripts/addons/animation_animall.py:172",), + # ()), + # ("fr_FR", "", + # (False, ())), + # ), + (("*", "\"Location\" and \"Shape Key\" are redundant?"), + (("scripts/addons/animation_animall.py:222",), + ()), + ("fr_FR", "\"Position\" et \"Clé de forme\" sont redondants ?", + (False, ())), + ), + (("*", "Splines"), + (("scripts/addons/animation_animall.py:197",), + ()), + ("fr_FR", "", + (False, ())), + ), + (("*", "Maybe set \"%s\" to 1.0?"), + (("scripts/addons/animation_animall.py:213",), + ()), + ("fr_FR", "Essayez de mettre « %s » à 1.0 ?", + (False, ())), + ), + (("*", "Cannot key on Basis Shape"), + (("scripts/addons/animation_animall.py:216",), + ()), + ("fr_FR", "Impossible d’ajouter une clé sur la forme de base", + (False, ())), + ), + (("*", "No active Shape Key"), + (("scripts/addons/animation_animall.py:219",), + ()), + ("fr_FR", "Pas de clé de forme active", + (False, ())), + ), + (("*", "Clear Animation could not be performed"), + (("scripts/addons/animation_animall.py:586",), + ()), + ("fr_FR", "La suppression de l’animation n’a pas pu aboutir", + (False, ())), + ), + (("*", "Object includes old-style vertex colors. Consider updating them."), + (("scripts/addons/animation_animall.py:186",), + ()), + ("fr_FR", "L’objet contient des couleurs de sommets à l’ancien format. Veuillez les mettre à jour", + (False, ())), + ), +) + +translations_dict = {} +for msg in translations_tuple: + key = msg[0] + for lang, trans, (is_fuzzy, comments) in msg[2:]: + if trans and not is_fuzzy: + translations_dict.setdefault(lang, {})[key] = trans -- cgit v1.2.3 From 7ea2e74fc41b2eabdbf639b812082e73823b09d7 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Sun, 10 Jul 2022 23:10:46 +0300 Subject: Rigify: make uniform limb scaling optional and hide the constraint. Since this feauture is likely only useful for some cartoon-style characters, make it optional. Also, move the copy scale constraint from the IK control to a new child bone; additional complexity is mitigated by being optional. --- rigify/rigs/limbs/limb_rigs.py | 50 ++++++++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/rigify/rigs/limbs/limb_rigs.py b/rigify/rigs/limbs/limb_rigs.py index fa20dd31..f60666a0 100644 --- a/rigify/rigs/limbs/limb_rigs.py +++ b/rigify/rigs/limbs/limb_rigs.py @@ -52,6 +52,7 @@ class BaseLimbRig(BaseRig): self.segments = self.params.segments self.bbone_segments = self.params.bbones self.use_ik_pivot = self.params.make_custom_pivot + self.use_uniform_scale = self.params.limb_uniform_scale rot_axis = self.params.rotation_axis @@ -144,6 +145,8 @@ class BaseLimbRig(BaseRig): # FK chain parents (or None) # ik_pivot # Custom IK pivot result (optional). + # ik_scale + # Helper bone that implements uniform scaling. # ik_swing # Bone that tracks ik_target to manually handle limb swing. # ik_target @@ -178,14 +181,15 @@ class BaseLimbRig(BaseRig): bone = self.get_bone(self.bones.ctrl.master) bone.lock_location = (True, True, True) bone.lock_rotation = (True, True, True) - bone.lock_scale = (False, False, False) + bone.lock_scale = (not self.use_uniform_scale,) * 3 bone.lock_rotation_w = True @stage.rig_bones def rig_master_control(self): mch = self.bones.mch self.make_constraint(mch.master, 'COPY_SCALE', 'root', use_make_uniform=True) - self.make_constraint(mch.master, 'COPY_SCALE', self.bones.ctrl.master, use_offset=True, space='LOCAL') + if self.use_uniform_scale: + self.make_constraint(mch.master, 'COPY_SCALE', self.bones.ctrl.master, use_offset=True, space='LOCAL') @stage.generate_widgets def make_master_control_widget(self): @@ -221,10 +225,12 @@ class BaseLimbRig(BaseRig): mch = self.bones.mch.follow self.make_constraint(mch, 'COPY_SCALE', 'root', use_make_uniform=True) - self.make_constraint( - mch, 'COPY_SCALE', self.bones.ctrl.master, - use_make_uniform=True, use_offset=True, space='LOCAL' - ) + + if self.use_uniform_scale: + self.make_constraint( + mch, 'COPY_SCALE', self.bones.ctrl.master, + use_make_uniform=True, use_offset=True, space='LOCAL' + ) con = self.make_constraint(mch, 'COPY_ROTATION', 'root') @@ -361,9 +367,12 @@ class BaseLimbRig(BaseRig): self.bones.ctrl.ik_base = self.make_ik_base_bone(orgs) self.bones.ctrl.ik_pole = self.make_ik_pole_bone(orgs) - self.bones.ctrl.ik = ik_name = self.make_ik_control_bone(orgs) + self.bones.ctrl.ik = ik_name = parent = self.make_ik_control_bone(orgs) + + if self.use_uniform_scale: + self.bones.mch.ik_scale = parent = self.make_ik_scale_bone(ik_name, orgs) - self.component_ik_pivot = self.build_ik_pivot(ik_name) + self.component_ik_pivot = self.build_ik_pivot(ik_name, parent=parent) self.build_ik_parent_switch(SwitchParentBuilder(self.generator)) def make_ik_base_bone(self, orgs): @@ -382,13 +391,18 @@ class BaseLimbRig(BaseRig): def make_ik_control_bone(self, orgs): return self.copy_bone(orgs[2], make_derived_name(orgs[2], 'ctrl', '_ik')) + def make_ik_scale_bone(self, ctrl, orgs): + return self.copy_bone(ctrl, make_derived_name(orgs[2], 'mch', '_ik_scale'), scale=1/2) + def build_ik_pivot(self, ik_name, **args): if self.use_ik_pivot: - return CustomPivotControl(self, 'ik_pivot', ik_name, parent=ik_name, **args) + return CustomPivotControl(self, 'ik_pivot', ik_name, **args) def get_ik_control_output(self): if self.component_ik_pivot: return self.component_ik_pivot.output + elif self.use_uniform_scale: + return self.bones.mch.ik_scale else: return self.bones.ctrl.ik @@ -430,6 +444,9 @@ class BaseLimbRig(BaseRig): else: self.set_bone_parent(self.bones.ctrl.ik_base, self.bones.mch.ik_swing) + if self.use_uniform_scale: + self.set_bone_parent(self.bones.mch.ik_scale, self.bones.ctrl.ik) + self.set_ik_local_location(self.bones.ctrl.ik) self.set_ik_local_location(self.bones.ctrl.ik_pole) @@ -445,11 +462,13 @@ class BaseLimbRig(BaseRig): @stage.rig_bones def rig_ik_controls(self): self.rig_hide_pole_control(self.bones.ctrl.ik_pole) - self.rig_ik_control_scale(self.bones.ctrl.ik) - def rig_ik_control_scale(self, ctrl): + if self.use_uniform_scale: + self.rig_ik_control_scale(self.bones.mch.ik_scale) + + def rig_ik_control_scale(self, mch): self.make_constraint( - ctrl, 'COPY_SCALE', self.bones.ctrl.master, + mch, 'COPY_SCALE', self.bones.ctrl.master, use_make_uniform=True, use_offset=True, space='LOCAL', ) @@ -937,6 +956,12 @@ class BaseLimbRig(BaseRig): description = "Specifies the value of the Local Location option for IK controls, which decides if the location channels are aligned to the local control orientation or world", ) + params.limb_uniform_scale = bpy.props.BoolProperty( + name = "Support Uniform Scaling", + default = False, + description = "Suport uniformly scaling the limb via the gear control at the base" + ) + # Setting up extra layers for the FK and tweak ControlLayersOption.FK.add_parameters(params) ControlLayersOption.TWEAK.add_parameters(params) @@ -958,6 +983,7 @@ class BaseLimbRig(BaseRig): r = layout.row() r.prop(params, "bbones") + layout.prop(params, 'limb_uniform_scale') layout.prop(params, 'make_custom_pivot', text="Custom IK Pivot") layout.prop(params, 'ik_local_location') -- cgit v1.2.3 From ed54c1b1c3cd3a9e42d1fc8f3eec7cb2745f15fc Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 11 Jul 2022 12:48:39 +1000 Subject: Fix T99542: 3D print toolbox thickness check causes assertion Recalculate normals when applying transformation in the 3D print toolbox. --- object_print3d_utils/mesh_helpers.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/object_print3d_utils/mesh_helpers.py b/object_print3d_utils/mesh_helpers.py index 7d23a078..444df1f1 100644 --- a/object_print3d_utils/mesh_helpers.py +++ b/object_print3d_utils/mesh_helpers.py @@ -32,7 +32,13 @@ def bmesh_copy_from_object(obj, transform=True, triangulate=True, apply_modifier # would save ram if transform: - bm.transform(obj.matrix_world) + matrix = obj.matrix_world.copy() + if not matrix.is_identity: + bm.transform(matrix) + # Update normals if the matrix has no rotation. + matrix.translation.zero() + if not matrix.is_identity: + bm.normal_update() if triangulate: bmesh.ops.triangulate(bm, faces=bm.faces) -- cgit v1.2.3 From 45f57152bb19f2d5e8f4f7edcc337fd5dcf578f8 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 11 Jul 2022 11:39:42 +0300 Subject: Rigify: fix T98490 - neck middle tweak not inheriting any scale. --- rigify/rigs/spines/super_head.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rigify/rigs/spines/super_head.py b/rigify/rigs/spines/super_head.py index e2932ad2..49f1ce3e 100644 --- a/rigify/rigs/spines/super_head.py +++ b/rigify/rigs/spines/super_head.py @@ -292,6 +292,8 @@ class Rig(BaseHeadTailRig): influence=nfactor, space='LOCAL' ) + self.make_constraint(mch, 'COPY_SCALE', ctrl.neck) + #################################################### # Tweak bones -- cgit v1.2.3 From e5d8781f544326a59c7892da2adf35a5ba815c02 Mon Sep 17 00:00:00 2001 From: Patrick Huang Date: Mon, 11 Jul 2022 12:40:09 +0300 Subject: Fix T99335: Rigify horse has wrong parents. Set parent of eye and nose bones to head instead of left ear. Differential Revision: https://developer.blender.org/D15347 --- rigify/metarigs/Animals/horse.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rigify/metarigs/Animals/horse.py b/rigify/metarigs/Animals/horse.py index cd393c5c..c9be9d54 100644 --- a/rigify/metarigs/Animals/horse.py +++ b/rigify/metarigs/Animals/horse.py @@ -618,28 +618,28 @@ def create(obj): bone.tail = 0.1990, -1.4668, 1.7420 bone.roll = 0.0000 bone.use_connect = False - bone.parent = arm.edit_bones[bones['ear.L']] + bone.parent = arm.edit_bones[bones['head']] bones['eye.L'] = bone.name bone = arm.edit_bones.new('nose.L') bone.head = 0.0450, -1.6240, 1.4228 bone.tail = 0.1039, -1.6613, 1.4269 bone.roll = 0.0000 bone.use_connect = False - bone.parent = arm.edit_bones[bones['ear.L']] + bone.parent = arm.edit_bones[bones['head']] bones['nose.L'] = bone.name bone = arm.edit_bones.new('eye.R') bone.head = -0.0988, -1.4596, 1.7351 bone.tail = -0.1990, -1.4668, 1.7420 bone.roll = -0.0000 bone.use_connect = False - bone.parent = arm.edit_bones[bones['ear.L']] + bone.parent = arm.edit_bones[bones['head']] bones['eye.R'] = bone.name bone = arm.edit_bones.new('nose.R') bone.head = -0.0450, -1.6240, 1.4228 bone.tail = -0.1039, -1.6613, 1.4269 bone.roll = -0.0000 bone.use_connect = False - bone.parent = arm.edit_bones[bones['ear.L']] + bone.parent = arm.edit_bones[bones['head']] bones['nose.R'] = bone.name bone = arm.edit_bones.new('ear.R.001') bone.head = -0.1056, -1.4118, 1.9537 -- cgit v1.2.3 From 7a8502871c34db0343cc7de52d6b49b15a84238a Mon Sep 17 00:00:00 2001 From: Samuli Raivio Date: Mon, 11 Jul 2022 11:56:49 +0200 Subject: Fix FBX IO handling of BoundaryRule enum values. Support for BoundaryRule (how to handle edges/vertices in subdivision surface) was added in rBA9a285d80167f, but the values between 'CreaseAll' and 'CreaseEdge' were inverted. Reviewed By: mont29 Differential Revision: D15035 --- io_scene_fbx/__init__.py | 2 +- io_scene_fbx/export_fbx_bin.py | 4 ++-- io_scene_fbx/import_fbx.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/io_scene_fbx/__init__.py b/io_scene_fbx/__init__.py index 1b7e646d..feea8436 100644 --- a/io_scene_fbx/__init__.py +++ b/io_scene_fbx/__init__.py @@ -3,7 +3,7 @@ bl_info = { "name": "FBX format", "author": "Campbell Barton, Bastien Montagne, Jens Restemeier", - "version": (4, 36, 2), + "version": (4, 36, 3), "blender": (3, 2, 0), "location": "File > Import-Export", "description": "FBX IO meshes, UV's, vertex colors, materials, textures, cameras, lamps and actions", diff --git a/io_scene_fbx/export_fbx_bin.py b/io_scene_fbx/export_fbx_bin.py index ae7be61c..c34a5182 100644 --- a/io_scene_fbx/export_fbx_bin.py +++ b/io_scene_fbx/export_fbx_bin.py @@ -865,9 +865,9 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes): if last_subsurf: elem_data_single_int32(geom, b"Smoothness", 2) # Display control mesh and smoothed if last_subsurf.boundary_smooth == "PRESERVE_CORNERS": - elem_data_single_int32(geom, b"BoundaryRule", 2) # CreaseAll + elem_data_single_int32(geom, b"BoundaryRule", 1) # CreaseAll else: - elem_data_single_int32(geom, b"BoundaryRule", 1) # CreaseEdge + elem_data_single_int32(geom, b"BoundaryRule", 2) # CreaseEdge elem_data_single_int32(geom, b"PreviewDivisionLevels", last_subsurf.levels) elem_data_single_int32(geom, b"RenderDivisionLevels", last_subsurf.render_levels) diff --git a/io_scene_fbx/import_fbx.py b/io_scene_fbx/import_fbx.py index 90f0c016..72d666c1 100644 --- a/io_scene_fbx/import_fbx.py +++ b/io_scene_fbx/import_fbx.py @@ -2929,7 +2929,7 @@ def load(operator, context, filepath="", mod.levels = preview_levels mod.render_levels = render_levels boundary_rule = elem_prop_first(elem_find_first(fbx_sdata, b'BoundaryRule'), default=1) - if boundary_rule == 2: + if boundary_rule == 1: mod.boundary_smooth = "PRESERVE_CORNERS" else: mod.boundary_smooth = "ALL" -- cgit v1.2.3 From c49befa43288730538d82f709b05dfd08c40e562 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 11 Jul 2022 18:36:55 +0300 Subject: Rigify: fix T95864 - inconsistent bone roll in the wolf metarig. Edit mode represents bone orientation via their head and tail locations, plus a roll value. This math has a singularity in the -Y direction, resulting in instability and discontinuity for bones exactly aligned to the Y axis (the tail flips the deform bones, so +Y is also bad). The only reliable way to avoid problems is to un-align the bones via very small offsets in the Z direction. --- rigify/metarigs/Animals/wolf.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rigify/metarigs/Animals/wolf.py b/rigify/metarigs/Animals/wolf.py index a2ade910..87d74c87 100644 --- a/rigify/metarigs/Animals/wolf.py +++ b/rigify/metarigs/Animals/wolf.py @@ -169,7 +169,7 @@ def create(obj): bone = arm.edit_bones.new('spine.004') bone.head = 0.0000, 0.4085, 0.7928 - bone.tail = 0.0000, 0.2416, 0.7928 + bone.tail = 0.0000, 0.2416, 0.7927 bone.roll = 0.0000 bone.use_connect = False bones['spine.004'] = bone.name @@ -181,7 +181,7 @@ def create(obj): bone.parent = arm.edit_bones[bones['spine.004']] bones['spine.003'] = bone.name bone = arm.edit_bones.new('spine.005') - bone.head = 0.0000, 0.2416, 0.7928 + bone.head = 0.0000, 0.2416, 0.7927 bone.tail = 0.0000, 0.1202, 0.7773 bone.roll = 0.0000 bone.use_connect = True @@ -189,14 +189,14 @@ def create(obj): bones['spine.005'] = bone.name bone = arm.edit_bones.new('spine.002') bone.head = 0.0000, 0.5555, 0.7567 - bone.tail = 0.0000, 0.7816, 0.7412 + bone.tail = 0.0000, 0.7816, 0.7411 bone.roll = 0.0000 bone.use_connect = True bone.parent = arm.edit_bones[bones['spine.003']] bones['spine.002'] = bone.name bone = arm.edit_bones.new('spine.006') bone.head = 0.0000, 0.1202, 0.7773 - bone.tail = 0.0000, 0.0096, 0.7773 + bone.tail = 0.0000, 0.0096, 0.7772 bone.roll = 0.0000 bone.use_connect = True bone.parent = arm.edit_bones[bones['spine.005']] @@ -230,14 +230,14 @@ def create(obj): bone.parent = arm.edit_bones[bones['spine.005']] bones['thigh.R'] = bone.name bone = arm.edit_bones.new('spine.001') - bone.head = 0.0000, 0.7816, 0.7412 + bone.head = 0.0000, 0.7816, 0.7411 bone.tail = 0.0000, 0.9624, 0.7412 bone.roll = 0.0000 bone.use_connect = True bone.parent = arm.edit_bones[bones['spine.002']] bones['spine.001'] = bone.name bone = arm.edit_bones.new('spine.007') - bone.head = 0.0000, 0.0096, 0.7773 + bone.head = 0.0000, 0.0096, 0.7772 bone.tail = 0.0000, -0.0980, 0.7945 bone.roll = 0.0000 bone.use_connect = True -- cgit v1.2.3 From 6af061bc26e95caf534ee60d6848967d14b97d7b Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 11 Jul 2022 19:45:29 +0300 Subject: Rigify: fix T96376 - adjust node match epsilon according to scale. --- rigify/utils/node_merger.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rigify/utils/node_merger.py b/rigify/utils/node_merger.py index 617b99df..2da48ada 100644 --- a/rigify/utils/node_merger.py +++ b/rigify/utils/node_merger.py @@ -73,7 +73,9 @@ class NodeMerger(GeneratorPlugin): while pending: added = set() for j in pending: - for co, idx, dist in tree.find_range(nodes[j].point, self.epsilon): + point = nodes[j].point + eps = max(1, point.length) * self.epsilon + for co, idx, dist in tree.find_range(point, eps): added.add(idx) pending = added.difference(merge_set) merge_set.update(added) -- cgit v1.2.3 From d2325587d73bc825986af3a1baba51cb4a9ce355 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 11 Jul 2022 20:32:12 +0300 Subject: Rigify: fix T99352 - generation issues when same named rig is linked. Only consider local datablocks when searching for the rig object, widget collection and widget objects themselves during generation. --- rigify/generate.py | 7 +++++-- rigify/utils/collections.py | 2 +- rigify/utils/widgets.py | 3 +++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/rigify/generate.py b/rigify/generate.py index 3acc2e40..53b47b00 100644 --- a/rigify/generate.py +++ b/rigify/generate.py @@ -76,7 +76,7 @@ class Generator(base_generate.BaseGenerator): target_rig.display_type = 'WIRE' # If the object is already added to the scene, switch to its collection - if target_rig.name in self.context.scene.collection.all_objects: + if target_rig in list(self.context.scene.collection.all_objects): self.__switch_to_usable_collection(target_rig) else: # Otherwise, add to the selected collection or the metarig collection if unusable @@ -117,11 +117,14 @@ class Generator(base_generate.BaseGenerator): wgts_group_name = "WGTS_" + self.obj.name old_collection = bpy.data.collections.get(wgts_group_name) + if old_collection.library: + old_collection = None + if not old_collection: # Update the old 'Widgets' collection legacy_collection = bpy.data.collections.get('Widgets') - if legacy_collection and wgts_group_name in legacy_collection.objects: + if legacy_collection and wgts_group_name in legacy_collection.objects and not legacy_collection.library: legacy_collection.name = wgts_group_name old_collection = legacy_collection diff --git a/rigify/utils/collections.py b/rigify/utils/collections.py index 8a299a9b..9eeaac51 100644 --- a/rigify/utils/collections.py +++ b/rigify/utils/collections.py @@ -53,7 +53,7 @@ def ensure_collection(context, collection_name, hidden=False) -> bpy.types.Colle active_collection = active_layer_coll.collection collection = bpy.data.collections.get(collection_name) - if not collection: + if not collection or collection.library: # Create the collection collection = bpy.data.collections.new(collection_name) collection.hide_viewport = hidden diff --git a/rigify/utils/widgets.py b/rigify/utils/widgets.py index e02f3387..ca207ddc 100644 --- a/rigify/utils/widgets.py +++ b/rigify/utils/widgets.py @@ -86,6 +86,9 @@ def create_widget(rig, bone_name, bone_transform_name=None, *, widget_name=None, if not obj: # Search the scene by name obj = scene.objects.get(obj_name) + if obj.library: + local_objs = [obj for obj in scene.objects if obj.name == obj_name and not obj.library] + obj = local_objs[0] if local_objs else None if obj: # Record the generated widget -- cgit v1.2.3 From 9f59c24f1f3b75550a8189bf4080cc7e19838837 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 12 Jul 2022 18:04:32 +0300 Subject: Rigify: fix exceptions when generating from scratch. --- rigify/generate.py | 2 +- rigify/utils/widgets.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rigify/generate.py b/rigify/generate.py index 53b47b00..d63399cc 100644 --- a/rigify/generate.py +++ b/rigify/generate.py @@ -117,7 +117,7 @@ class Generator(base_generate.BaseGenerator): wgts_group_name = "WGTS_" + self.obj.name old_collection = bpy.data.collections.get(wgts_group_name) - if old_collection.library: + if old_collection and old_collection.library: old_collection = None if not old_collection: diff --git a/rigify/utils/widgets.py b/rigify/utils/widgets.py index ca207ddc..5a16065b 100644 --- a/rigify/utils/widgets.py +++ b/rigify/utils/widgets.py @@ -86,7 +86,7 @@ def create_widget(rig, bone_name, bone_transform_name=None, *, widget_name=None, if not obj: # Search the scene by name obj = scene.objects.get(obj_name) - if obj.library: + if obj and obj.library: local_objs = [obj for obj in scene.objects if obj.name == obj_name and not obj.library] obj = local_objs[0] if local_objs else None -- cgit v1.2.3 From 144dbedd9f72b80793541703f3041daae81e3c4f Mon Sep 17 00:00:00 2001 From: Demeter Dzadik Date: Tue, 12 Jul 2022 17:28:14 +0300 Subject: Rigify: Allow entering name on first generation Requested by @dexon, see rBAece39d809ce#339214. This patch tries to improve a workflow that was hurt by D11356. One of the goals of that patch was to make the connection between metarig and generated rig stronger and more reliable, by using datablock pointers rather than text-based matching. This allows users to rename their datablocks as they would do with any other datablock in Blender, so there's much more freedom with naming when working with one metarig per generated rig per file. However, this made it hard to duplicate the metarig, make changes to it, and make a new rig out of it within the same file. Renaming several datablocks in this case is not an "option", but an unintuitive necessity. With the patch applied, when the metarig has no target rig, there is an option to input a rig name. If something is input by the user, that name will be used for the rig, the widget collection (by extension the widget objects) and the rig script, as long as they are not specified. So, while still requiring a few more clicks than before, the workflow for duplicating metarigs to make new ones is now much more reasonable: - Duplicate metarig, make changes - Remove the datablock references - Input a rig name - Generate If the a rig object with the same name already existed, it will NOT be overwritten, and the generated rig will have a .001 suffix. This matches the "new" option of the removed "new/overwrite" options. Meanwhile, the other workflow of having one metarig per generated rig per file is not affected at all: The text box for the name input can simply be ignored on the first generation. It will not show up after that, since you would never remove the target rig in this workflow. Differential Revision: https://developer.blender.org/D14757 --- rigify/__init__.py | 8 ++++++-- rigify/generate.py | 4 +++- rigify/rig_ui_template.py | 3 ++- rigify/ui.py | 8 +++++++- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/rigify/__init__.py b/rigify/__init__.py index 10e90b4c..8a45ae05 100644 --- a/rigify/__init__.py +++ b/rigify/__init__.py @@ -550,14 +550,18 @@ def register(): name="Widgets Collection", description="Defines which collection to place widget objects in. If unset, a new one will be created based on the name of the rig") + bpy.types.Armature.rigify_rig_basename = StringProperty(name="Rigify Rig Name", + description="Optional. If specified, this name will be used for the newly generated rig, widget collection and script. Otherwise, a name is generated based on the name of the metarig object by replacing 'metarig' with 'rig', 'META' with 'RIG', or prefixing with 'RIG-'. When updating an already generated rig its name is never changed", + default="") + bpy.types.Armature.rigify_target_rig = PointerProperty(type=bpy.types.Object, name="Rigify Target Rig", - description="Defines which rig to overwrite. If unset, a new one called 'rig' will be created", + description="Defines which rig to overwrite. If unset, a new one will be created with name based on the Rig Name option or the name of the metarig", poll=lambda self, obj: obj.type == 'ARMATURE' and obj.data is not self) bpy.types.Armature.rigify_rig_ui = PointerProperty(type=bpy.types.Text, name="Rigify Target Rig UI", - description="Defines the UI to overwrite. If unset, 'rig_ui.py' will be used") + description="Defines the UI to overwrite. If unset, a new one will be created and named based on the name of the rig") bpy.types.Armature.rigify_finalize_script = PointerProperty(type=bpy.types.Text, name="Finalize Script", diff --git a/rigify/generate.py b/rigify/generate.py index d63399cc..8a2aa942 100644 --- a/rigify/generate.py +++ b/rigify/generate.py @@ -65,7 +65,9 @@ class Generator(base_generate.BaseGenerator): target_rig = meta_data.rigify_target_rig if not target_rig: - if "metarig" in self.metarig.name: + if meta_data.rigify_rig_basename: + rig_new_name = meta_data.rigify_rig_basename + elif "metarig" in self.metarig.name: rig_new_name = self.metarig.name.replace("metarig", "rig") elif "META" in self.metarig.name: rig_new_name = self.metarig.name.replace("META", "RIG") diff --git a/rigify/rig_ui_template.py b/rigify/rig_ui_template.py index b98907ee..d581805f 100644 --- a/rigify/rig_ui_template.py +++ b/rigify/rig_ui_template.py @@ -1159,7 +1159,8 @@ class ScriptGenerator(base_generate.GeneratorPlugin): if script: script.clear() else: - script = bpy.data.texts.new("rig_ui.py") + script_name = self.generator.obj.name + "_ui.py" + script = bpy.data.texts.new(script_name) metarig.data.rigify_rig_ui = script for s in OrderedDict.fromkeys(self.ui_imports): diff --git a/rigify/ui.py b/rigify/ui.py index 68cfd330..3a7af546 100644 --- a/rigify/ui.py +++ b/rigify/ui.py @@ -137,10 +137,16 @@ class DATA_PT_rigify_advanced(bpy.types.Panel): armature_id_store = context.object.data col = layout.column() + + row = col.row() + row.active = not armature_id_store.rigify_target_rig + row.prop(armature_id_store, "rigify_rig_basename", text="Rig Name") + + col.separator() col.row().prop(armature_id_store, "rigify_target_rig", text="Target Rig") col.row().prop(armature_id_store, "rigify_rig_ui", text="Rig UI Script") - col.separator() col.row().prop(armature_id_store, "rigify_widgets_collection") + col.separator() col.row().prop(armature_id_store, "rigify_force_widget_update") col.row().prop(armature_id_store, "rigify_mirror_widgets") col.separator() -- cgit v1.2.3 From 4082824096f47fc358c6bd9a7e184d1ac6f38373 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 12 Jul 2022 19:15:22 +0300 Subject: Rigify: clarify the New vs Overwrite choice in the new UI. Group the three reference fields that have to be cleared to generate a completely new rig in a box. Also clarify the name and tooltip for Force Widget Update. --- rigify/__init__.py | 4 ++-- rigify/ui.py | 10 +++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/rigify/__init__.py b/rigify/__init__.py index 8a45ae05..386c8055 100644 --- a/rigify/__init__.py +++ b/rigify/__init__.py @@ -539,8 +539,8 @@ def register(): IDStore.rigify_types = CollectionProperty(type=RigifyName) IDStore.rigify_active_type = IntProperty(name="Rigify Active Type", description="The selected rig type") - bpy.types.Armature.rigify_force_widget_update = BoolProperty(name="Force Widget Update", - description="Forces Rigify to delete and rebuild all the rig widgets. if unset, only missing widgets will be created", + bpy.types.Armature.rigify_force_widget_update = BoolProperty(name="Overwrite Widget Meshes", + description="Forces Rigify to delete and rebuild all of the rig widget objects. By default, already existing widgets are reused as-is to facilitate manual editing", default=False) bpy.types.Armature.rigify_mirror_widgets = BoolProperty(name="Mirror Widgets", diff --git a/rigify/ui.py b/rigify/ui.py index 3a7af546..8b719a3f 100644 --- a/rigify/ui.py +++ b/rigify/ui.py @@ -143,9 +143,13 @@ class DATA_PT_rigify_advanced(bpy.types.Panel): row.prop(armature_id_store, "rigify_rig_basename", text="Rig Name") col.separator() - col.row().prop(armature_id_store, "rigify_target_rig", text="Target Rig") - col.row().prop(armature_id_store, "rigify_rig_ui", text="Rig UI Script") - col.row().prop(armature_id_store, "rigify_widgets_collection") + + col2 = col.box().column() + col2.label(text="Overwrite Existing:") + col2.row().prop(armature_id_store, "rigify_target_rig", text="Target Rig") + col2.row().prop(armature_id_store, "rigify_rig_ui", text="Rig UI Script") + col2.row().prop(armature_id_store, "rigify_widgets_collection") + col.separator() col.row().prop(armature_id_store, "rigify_force_widget_update") col.row().prop(armature_id_store, "rigify_mirror_widgets") -- cgit v1.2.3 From 08d13c024f60b03e7f525082db9fb12fbc576502 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Wed, 13 Jul 2022 12:10:03 +0200 Subject: glTF exporter: Fixed bezier control points to cubic spline tangents conversion Fix when animation are not baked --- io_scene_gltf2/__init__.py | 2 +- ...2_blender_gather_animation_sampler_keyframes.py | 22 +++++++++++++--------- .../exp/gltf2_blender_gather_animation_samplers.py | 13 +++++++------ 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 5c8872f3..be8aa8ed 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -4,7 +4,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (3, 3, 15), + "version": (3, 3, 16), 'blender': (3, 3, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py index e1ed19ea..ba331b74 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py @@ -398,11 +398,17 @@ def gather_keyframes(blender_obj_uuid: str, key.set_first_tangent() else: # otherwise construct an in tangent coordinate from the keyframes control points. We intermediately - # use a point at t-1 to define the tangent. This allows the tangent control point to be transformed - # normally + # use a point at t+1 to define the tangent. This allows the tangent control point to be transformed + # normally, but only works for locally linear transformation. The more non-linear a transform, the + # more imprecise this method is. + # We could use any other (v1, t1) for which (v1 - v0) / (t1 - t0) equals the tangent. By using t+1 + # for both in and out tangents, we guarantee that (even if there are errors or numerical imprecisions) + # symmetrical control points translate to symmetrical tangents. + # Note: I am not sure that linearity is never broken with quaternions and their normalization. + # Especially at sign swap it might occure that the value gets negated but the control point not. + # I have however not once encountered an issue with this. key.in_tangent = [ - c.keyframe_points[i].co[1] + ((c.keyframe_points[i].co[1] - c.keyframe_points[i].handle_left[1] - ) / (frame - frames[i - 1])) + c.keyframe_points[i].co[1] + (c.keyframe_points[i].handle_left[1] - c.keyframe_points[i].co[1]) / (c.keyframe_points[i].handle_left[0] - c.keyframe_points[i].co[0]) for c in channels if c is not None ] # Construct the out tangent @@ -410,12 +416,10 @@ def gather_keyframes(blender_obj_uuid: str, # end out-tangent should become all zero key.set_last_tangent() else: - # otherwise construct an in tangent coordinate from the keyframes control points. We intermediately - # use a point at t+1 to define the tangent. This allows the tangent control point to be transformed - # normally + # otherwise construct an in tangent coordinate from the keyframes control points. + # This happens the same way how in tangents are handled above. key.out_tangent = [ - c.keyframe_points[i].co[1] + ((c.keyframe_points[i].handle_right[1] - c.keyframe_points[i].co[1] - ) / (frames[i + 1] - frame)) + c.keyframe_points[i].co[1] + (c.keyframe_points[i].handle_right[1] - c.keyframe_points[i].co[1]) / (c.keyframe_points[i].handle_right[0] - c.keyframe_points[i].co[0]) for c in channels if c is not None ] diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_samplers.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_samplers.py index 1ee98a29..5c8011ed 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_samplers.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_samplers.py @@ -414,6 +414,7 @@ def __gather_output(channels: typing.Tuple[bpy.types.FCurve], transform = parent_inverse values = [] + fps = bpy.context.scene.render.fps for keyframe in keyframes: # Transform the data and build gltf control points value = gltf2_blender_math.transform(keyframe.value, target_datapath, transform, need_rotation_correction) @@ -426,11 +427,11 @@ def __gather_output(channels: typing.Tuple[bpy.types.FCurve], in_tangent = gltf2_blender_math.transform(keyframe.in_tangent, target_datapath, transform, need_rotation_correction) if is_yup and blender_object_if_armature is None: in_tangent = gltf2_blender_math.swizzle_yup(in_tangent, target_datapath) - # the tangent in glTF is relative to the keyframe value + # the tangent in glTF is relative to the keyframe value and uses seconds if not isinstance(value, list): - in_tangent = value - in_tangent + in_tangent = fps * (in_tangent - value) else: - in_tangent = [value[i] - in_tangent[i] for i in range(len(value))] + in_tangent = [fps * (in_tangent[i] - value[i]) for i in range(len(value))] keyframe_value = gltf2_blender_math.mathutils_to_gltf(in_tangent) + keyframe_value # append if keyframe.out_tangent is not None: @@ -438,11 +439,11 @@ def __gather_output(channels: typing.Tuple[bpy.types.FCurve], out_tangent = gltf2_blender_math.transform(keyframe.out_tangent, target_datapath, transform, need_rotation_correction) if is_yup and blender_object_if_armature is None: out_tangent = gltf2_blender_math.swizzle_yup(out_tangent, target_datapath) - # the tangent in glTF is relative to the keyframe value + # the tangent in glTF is relative to the keyframe value and uses seconds if not isinstance(value, list): - out_tangent = value - out_tangent + out_tangent = fps * (out_tangent - value) else: - out_tangent = [value[i] - out_tangent[i] for i in range(len(value))] + out_tangent = [fps * (out_tangent[i] - value[i]) for i in range(len(value))] keyframe_value = keyframe_value + gltf2_blender_math.mathutils_to_gltf(out_tangent) # append values += keyframe_value -- cgit v1.2.3 From 2fcac97522dee91d8f444055b9a996fea7d36e40 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Wed, 13 Jul 2022 12:15:28 +0200 Subject: glTF exporter: Manage all 4 types of Vertex Colors (corner/point - byte/float) --- io_scene_gltf2/__init__.py | 2 +- .../blender/exp/gltf2_blender_extract.py | 43 ++++++++++++++++------ .../gltf2_blender_gather_primitive_attributes.py | 20 ++++++---- 3 files changed, 46 insertions(+), 19 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index be8aa8ed..98210e82 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -4,7 +4,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (3, 3, 16), + "version": (3, 3, 17), 'blender': (3, 3, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_extract.py b/io_scene_gltf2/blender/exp/gltf2_blender_extract.py index d4f34126..93947acb 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_extract.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_extract.py @@ -39,6 +39,18 @@ def extract_primitives(blender_mesh, uuid_for_skined_data, blender_vertex_groups if export_settings[gltf2_blender_export_keys.COLORS]: color_max = len(blender_mesh.vertex_colors) + colors_attributes = [] + rendered_color_idx = blender_mesh.attributes.render_color_index + + if color_max > 0: + colors_attributes.append(rendered_color_idx) + # Then find other ones + colors_attributes.extend([ + i for i in range(len(blender_mesh.color_attributes)) if i != rendered_color_idx \ + and blender_mesh.vertex_colors.find(blender_mesh.color_attributes[i].name) != -1 + ]) + + armature = None skin = None if blender_vertex_groups and export_settings[gltf2_blender_export_keys.SKINS]: @@ -110,7 +122,7 @@ def extract_primitives(blender_mesh, uuid_for_skined_data, blender_vertex_groups dot_fields += [('tx', np.float32), ('ty', np.float32), ('tz', np.float32), ('tw', np.float32)] for uv_i in range(tex_coord_max): dot_fields += [('uv%dx' % uv_i, np.float32), ('uv%dy' % uv_i, np.float32)] - for col_i in range(color_max): + for col_i, _ in enumerate(colors_attributes): dot_fields += [ ('color%dr' % col_i, np.float32), ('color%dg' % col_i, np.float32), @@ -163,8 +175,12 @@ def extract_primitives(blender_mesh, uuid_for_skined_data, blender_vertex_groups dots['uv%dy' % uv_i] = uvs[:, 1] del uvs - for col_i in range(color_max): - colors = __get_colors(blender_mesh, col_i) + colors_types = [] + for col_i, blender_col_i in enumerate(colors_attributes): + colors, colors_type, domain = __get_colors(blender_mesh, col_i, blender_col_i) + if domain == "POINT": + colors = colors[dots['vertex_index']] + colors_types.append(colors_type) dots['color%dr' % col_i] = colors[:, 0] dots['color%dg' % col_i] = colors[:, 1] dots['color%db' % col_i] = colors[:, 2] @@ -251,13 +267,16 @@ def extract_primitives(blender_mesh, uuid_for_skined_data, blender_vertex_groups uvs[:, 1] = prim_dots['uv%dy' % tex_coord_i] attributes['TEXCOORD_%d' % tex_coord_i] = uvs - for color_i in range(color_max): + for color_i, _ in enumerate(colors_attributes): colors = np.empty((len(prim_dots), 4), dtype=np.float32) colors[:, 0] = prim_dots['color%dr' % color_i] colors[:, 1] = prim_dots['color%dg' % color_i] colors[:, 2] = prim_dots['color%db' % color_i] colors[:, 3] = prim_dots['color%da' % color_i] - attributes['COLOR_%d' % color_i] = colors + attributes['COLOR_%d' % color_i] = {} + attributes['COLOR_%d' % color_i]["data"] = colors + + attributes['COLOR_%d' % color_i]["norm"] = colors_types[color_i] == "BYTE_COLOR" if skin: joints = [[] for _ in range(num_joint_sets)] @@ -525,13 +544,15 @@ def __get_uvs(blender_mesh, uv_i): return uvs -def __get_colors(blender_mesh, color_i): - colors = np.empty(len(blender_mesh.loops) * 4, dtype=np.float32) - layer = blender_mesh.vertex_colors[color_i] - blender_mesh.color_attributes[layer.name].data.foreach_get('color', colors) - colors = colors.reshape(len(blender_mesh.loops), 4) +def __get_colors(blender_mesh, color_i, blender_color_i): + if blender_mesh.color_attributes[blender_color_i].domain == "POINT": + colors = np.empty(len(blender_mesh.vertices) * 4, dtype=np.float32) #POINT + else: + colors = np.empty(len(blender_mesh.loops) * 4, dtype=np.float32) #CORNER + blender_mesh.color_attributes[blender_color_i].data.foreach_get('color', colors) + colors = colors.reshape(-1, 4) # colors are already linear, no need to switch color space - return colors + return colors, blender_mesh.color_attributes[blender_color_i].data_type, blender_mesh.color_attributes[blender_color_i].domain def __get_bone_data(blender_mesh, skin, blender_vertex_groups): diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitive_attributes.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitive_attributes.py index 72f0268c..f28a1f10 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitive_attributes.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitive_attributes.py @@ -124,28 +124,34 @@ def __gather_colors(blender_primitive, export_settings): color_index = 0 color_id = 'COLOR_' + str(color_index) while blender_primitive["attributes"].get(color_id) is not None: - colors = blender_primitive["attributes"][color_id] + colors = blender_primitive["attributes"][color_id]["data"] if type(colors) is not np.ndarray: colors = np.array(colors, dtype=np.float32) colors = colors.reshape(len(colors) // 4, 4) - # Convert to normalized ushorts - colors *= 65535 - colors += 0.5 # bias for rounding - colors = colors.astype(np.uint16) + if blender_primitive["attributes"][color_id]["norm"] is True: + comp_type = gltf2_io_constants.ComponentType.UnsignedShort + + # Convert to normalized ushorts + colors *= 65535 + colors += 0.5 # bias for rounding + colors = colors.astype(np.uint16) + + else: + comp_type = gltf2_io_constants.ComponentType.Float attributes[color_id] = gltf2_io.Accessor( buffer_view=gltf2_io_binary_data.BinaryData(colors.tobytes()), byte_offset=None, - component_type=gltf2_io_constants.ComponentType.UnsignedShort, + component_type=comp_type, count=len(colors), extensions=None, extras=None, max=None, min=None, name=None, - normalized=True, + normalized=blender_primitive["attributes"][color_id]["norm"], sparse=None, type=gltf2_io_constants.DataType.Vec4, ) -- cgit v1.2.3 From 054d976114e4d3db2eb71303ccc23f6b6bd2c374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 21 Jul 2022 12:52:24 +0200 Subject: Pose Library: add documentation URL The pose library has quite nice documentation. It would be a shame if nobody would find it. --- pose_library/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pose_library/__init__.py b/pose_library/__init__.py index 33ca777f..ed22d84b 100644 --- a/pose_library/__init__.py +++ b/pose_library/__init__.py @@ -12,7 +12,7 @@ bl_info = { "blender": (3, 0, 0), "warning": "In heavily development, things may change", "location": "Asset Browser -> Animations, and 3D Viewport -> Animation panel", - # "doc_url": "{BLENDER_MANUAL_URL}/addons/animation/pose_library.html", + "doc_url": "{BLENDER_MANUAL_URL}/animation/armatures/posing/editing/pose_library.html", "support": "OFFICIAL", "category": "Animation", } -- cgit v1.2.3 From ecdb17a4018849c0b7dd345672b2c64e7b19040e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 21 Jul 2022 12:52:52 +0200 Subject: Pose Library: remove warning The warning "In heavily development, things may change" is no longer true, and can be removed. --- pose_library/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pose_library/__init__.py b/pose_library/__init__.py index ed22d84b..b13d1bf0 100644 --- a/pose_library/__init__.py +++ b/pose_library/__init__.py @@ -10,7 +10,6 @@ bl_info = { "author": "Sybren A. Stüvel", "version": (2, 0), "blender": (3, 0, 0), - "warning": "In heavily development, things may change", "location": "Asset Browser -> Animations, and 3D Viewport -> Animation panel", "doc_url": "{BLENDER_MANUAL_URL}/animation/armatures/posing/editing/pose_library.html", "support": "OFFICIAL", -- cgit v1.2.3 From f7c2fc63954c0a423fd21973f5ad95b496168fb3 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 21 Jul 2022 10:43:07 -0300 Subject: Curve Assign Shape Keys: replace deprecated bgl module Part of T80730 --- curve_assign_shapekey.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/curve_assign_shapekey.py b/curve_assign_shapekey.py index a87e89b0..75e42b1a 100644 --- a/curve_assign_shapekey.py +++ b/curve_assign_shapekey.py @@ -8,7 +8,7 @@ # # https://github.com/Shriinivas/assignshapekey/blob/master/LICENSE -import bpy, bmesh, bgl, gpu +import bpy, bmesh, gpu from gpu_extras.batch import batch_for_shader from bpy.props import BoolProperty, EnumProperty, StringProperty from collections import OrderedDict @@ -21,8 +21,8 @@ from bpy.types import Panel, Operator, AddonPreferences bl_info = { "name": "Assign Shape Keys", "author": "Shrinivas Kulkarni", - "version": (1, 0, 1), - "blender": (2, 80, 0), + "version": (1, 0, 2), + "blender": (3, 0, 0), "location": "View 3D > Sidebar > Edit Tab", "description": "Assigns one or more Bezier curves as shape keys to another Bezier curve", "doc_url": "{BLENDER_MANUAL_URL}/addons/add_curve/assign_shape_keys.html", @@ -835,7 +835,7 @@ class MarkerController: context.area.tag_redraw() def drawHandler(self): - bgl.glPointSize(MarkerController.defPointSize) + gpu.state.point_size_set(MarkerController.defPointSize) self.batch.draw(self.shader) def removeMarkers(self, context): -- cgit v1.2.3 From c383b027446f4f56daf900b264387feec41557dc Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 21 Jul 2022 10:54:15 -0300 Subject: Curve Tools: replace deprecated bgl module Part of T80730 --- curve_tools/path_finder.py | 13 ++++++------- curve_tools/show_resolution.py | 1 - curve_tools/splines_sequence.py | 3 +-- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/curve_tools/path_finder.py b/curve_tools/path_finder.py index abf53a6c..242ed27c 100644 --- a/curve_tools/path_finder.py +++ b/curve_tools/path_finder.py @@ -3,8 +3,8 @@ bl_info = { 'name': 'PathFinder', 'author': 'Spivak Vladimir (cwolf3d)', - 'version': (0, 5, 0), - 'blender': (2, 80, 0), + 'version': (0, 5, 1), + 'blender': (3, 0, 0), 'location': 'Curve Tools addon. (N) Panel', 'description': 'PathFinder - quick search, selection, removal of splines', 'warning': '', # used for warning icon and text in addons panel @@ -17,7 +17,6 @@ import time import threading import gpu -import bgl from gpu_extras.batch import batch_for_shader import bpy @@ -85,8 +84,8 @@ def draw_bezier_points(self, context, spline, matrix_world, path_color, path_thi shader.bind() shader.uniform_float("color", path_color) - bgl.glEnable(bgl.GL_BLEND) - bgl.glLineWidth(path_thickness) + gpu.state.blend_set('ALPHA') + gpu.state.line_width_set(path_thickness) batch.draw(shader) def draw_points(self, context, spline, matrix_world, path_color, path_thickness): @@ -98,8 +97,8 @@ def draw_points(self, context, spline, matrix_world, path_color, path_thickness) shader.bind() shader.uniform_float("color", path_color) - bgl.glEnable(bgl.GL_BLEND) - bgl.glLineWidth(path_thickness) + gpu.state.blend_set('ALPHA') + gpu.state.line_width_set(path_thickness) batch.draw(shader) def near(location3D, point, radius): diff --git a/curve_tools/show_resolution.py b/curve_tools/show_resolution.py index b2dbda7f..07a06540 100644 --- a/curve_tools/show_resolution.py +++ b/curve_tools/show_resolution.py @@ -6,7 +6,6 @@ import bpy from bpy import * from bpy.props import * -import bgl import blf import gpu from gpu_extras.batch import batch_for_shader diff --git a/curve_tools/splines_sequence.py b/curve_tools/splines_sequence.py index 98766058..2588b5d3 100644 --- a/curve_tools/splines_sequence.py +++ b/curve_tools/splines_sequence.py @@ -3,7 +3,6 @@ import bpy -import bgl import blf import gpu from gpu_extras.batch import batch_for_shader @@ -66,7 +65,7 @@ def draw(self, context, splines, sequence_color, font_thickness, font_size, matr batch = batch_for_shader(shader, 'LINES', {"pos": points}) shader.bind() - bgl.glLineWidth(font_thickness) + gpu.state.line_width_set(font_thickness) shader.uniform_float("color", sequence_color) batch.draw(shader) i += font_size + font_size * 0.5 -- cgit v1.2.3 From 294e899bf73cf461eee98c89a8583503cdcf58f5 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 21 Jul 2022 11:03:07 -0300 Subject: Grease Pencil Tools: replace deprecated bgl module Part of T80730 --- greasepencil_tools/__init__.py | 4 ++-- greasepencil_tools/rotate_canvas.py | 9 ++++----- greasepencil_tools/timeline_scrub.py | 13 ++++++------- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/greasepencil_tools/__init__.py b/greasepencil_tools/__init__.py index 980493ed..c75d0442 100644 --- a/greasepencil_tools/__init__.py +++ b/greasepencil_tools/__init__.py @@ -4,8 +4,8 @@ bl_info = { "name": "Grease Pencil Tools", "description": "Extra tools for Grease Pencil", "author": "Samuel Bernou, Antonio Vazquez, Daniel Martinez Lara, Matias Mendiola", -"version": (1, 6, 0), -"blender": (2, 91, 0), +"version": (1, 6, 1), +"blender": (3, 0, 0), "location": "Sidebar > Grease Pencil > Grease Pencil Tools", "warning": "", "doc_url": "{BLENDER_MANUAL_URL}/addons/object/greasepencil_tools.html", diff --git a/greasepencil_tools/rotate_canvas.py b/greasepencil_tools/rotate_canvas.py index a733eb2d..c2482fbd 100644 --- a/greasepencil_tools/rotate_canvas.py +++ b/greasepencil_tools/rotate_canvas.py @@ -10,7 +10,6 @@ from bpy.props import BoolProperty, EnumProperty from time import time ## draw utils import gpu -import bgl import blf from gpu_extras.batch import batch_for_shader from gpu_extras.presets import draw_circle_2d @@ -31,8 +30,8 @@ def draw_callback_px(self, context): if context.area != self.current_area: return shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR') - bgl.glEnable(bgl.GL_BLEND) - bgl.glLineWidth(2) + gpu.state.blend_set('ALPHA') + gpu.state.line_width_set(2.0) # init batch = batch_for_shader(shader, 'LINE_STRIP', {"pos": [self.center, self.initial_pos]})#self.vector_initial @@ -55,8 +54,8 @@ def draw_callback_px(self, context): # batch.draw(shader) # restore opengl defaults - bgl.glLineWidth(1) - bgl.glDisable(bgl.GL_BLEND) + gpu.state.line_width_set(1.0) + gpu.state.blend_set('NONE') ## text font_id = 0 diff --git a/greasepencil_tools/timeline_scrub.py b/greasepencil_tools/timeline_scrub.py index 6a1913ae..1f6c2a41 100644 --- a/greasepencil_tools/timeline_scrub.py +++ b/greasepencil_tools/timeline_scrub.py @@ -8,7 +8,6 @@ import numpy as np from time import time import bpy import gpu -import bgl import blf from gpu_extras.batch import batch_for_shader @@ -39,8 +38,8 @@ def draw_callback_px(self, context): font_id = 0 shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR') # initiate shader - bgl.glEnable(bgl.GL_BLEND) - bgl.glLineWidth(1) + gpu.state.blend_set('ALPHA') + gpu.state.line_width_set(1.0) # Draw HUD if self.use_hud_time_line: @@ -51,18 +50,18 @@ def draw_callback_px(self, context): # Display keyframes if self.use_hud_keyframes: if self.keyframe_aspect == 'LINE': - bgl.glLineWidth(3) + gpu.state.line_width_set(3.0) shader.bind() shader.uniform_float("color", self.color_timeline) self.batch_keyframes.draw(shader) else: - bgl.glLineWidth(1) + gpu.state.line_width_set(1.0) shader.bind() shader.uniform_float("color", self.color_timeline) self.batch_keyframes.draw(shader) # Show current frame line - bgl.glLineWidth(1) + gpu.state.line_width_set(1.0) if self.use_hud_playhead: playhead = [(self.cursor_x, self.my + self.playhead_size/2), (self.cursor_x, self.my - self.playhead_size/2)] @@ -72,7 +71,7 @@ def draw_callback_px(self, context): batch.draw(shader) # restore opengl defaults - bgl.glDisable(bgl.GL_BLEND) + gpu.state.blend_set('NONE') # Display current frame text blf.color(font_id, *self.color_text) -- cgit v1.2.3 From b56a6acb9f4fe1041ed377202f05ed5a5dd09d91 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 21 Jul 2022 13:03:25 -0300 Subject: UV Layout: replace deprecated bgl module Part of T80730 --- io_mesh_uv_layout/__init__.py | 4 ++-- io_mesh_uv_layout/export_uv_png.py | 26 ++++++++++---------------- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/io_mesh_uv_layout/__init__.py b/io_mesh_uv_layout/__init__.py index 324f2254..ceb5ddbc 100644 --- a/io_mesh_uv_layout/__init__.py +++ b/io_mesh_uv_layout/__init__.py @@ -3,8 +3,8 @@ bl_info = { "name": "UV Layout", "author": "Campbell Barton, Matt Ebb", - "version": (1, 1, 1), - "blender": (2, 80, 0), + "version": (1, 1, 2), + "blender": (3, 0, 0), "location": "Image-Window > UVs > Export UV Layout", "description": "Export the UV layout as a 2D graphic", "warning": "", diff --git a/io_mesh_uv_layout/export_uv_png.py b/io_mesh_uv_layout/export_uv_png.py index 958bac8e..74cd7b19 100644 --- a/io_mesh_uv_layout/export_uv_png.py +++ b/io_mesh_uv_layout/export_uv_png.py @@ -2,7 +2,6 @@ import bpy import gpu -import bgl from mathutils import Vector, Matrix from mathutils.geometry import tessellate_polygon from gpu_extras.batch import batch_for_shader @@ -12,21 +11,23 @@ def export(filepath, face_data, colors, width, height, opacity): offscreen.bind() try: - bgl.glClearColor(0.0, 0.0, 0.0, 0.0) - bgl.glClear(bgl.GL_COLOR_BUFFER_BIT) + fb = gpu.state.active_framebuffer_get() + fb.clear(color=(0.0, 0.0, 0.0, 0.0)) draw_image(face_data, opacity) - pixel_data = get_pixel_data_from_current_back_buffer(width, height) + pixel_data = fb.read_color(0, 0, width, height, 4, 0, 'UBYTE') + pixel_data.dimensions = width * height * 4 save_pixels(filepath, pixel_data, width, height) finally: offscreen.unbind() offscreen.free() def draw_image(face_data, opacity): - bgl.glLineWidth(1) - bgl.glEnable(bgl.GL_BLEND) - bgl.glEnable(bgl.GL_LINE_SMOOTH) - bgl.glHint(bgl.GL_LINE_SMOOTH_HINT, bgl.GL_NICEST) + gpu.state.line_width_set(1.0) + gpu.state.blend_set('ALPHA_PREMULT') + # TODO: Use shader that draws a smooth line (Eg. '3D_POLYLINE_UNIFORM_COLOR') + # bgl.glEnable(bgl.GL_LINE_SMOOTH) + # bgl.glHint(bgl.GL_LINE_SMOOTH_HINT, bgl.GL_NICEST) with gpu.matrix.push_pop(): gpu.matrix.load_matrix(get_normalize_uvs_matrix()) @@ -35,8 +36,7 @@ def draw_image(face_data, opacity): draw_background_colors(face_data, opacity) draw_lines(face_data) - bgl.glDisable(bgl.GL_BLEND) - bgl.glDisable(bgl.GL_LINE_SMOOTH) + gpu.state.blend_set('NONE') def get_normalize_uvs_matrix(): '''matrix maps x and y coordinates from [0, 1] to [-1, 1]''' @@ -83,12 +83,6 @@ def draw_lines(face_data): shader.uniform_float("color", (0, 0, 0, 1)) batch.draw(shader) -def get_pixel_data_from_current_back_buffer(width, height): - buffer = bgl.Buffer(bgl.GL_BYTE, width * height * 4) - bgl.glReadBuffer(bgl.GL_BACK) - bgl.glReadPixels(0, 0, width, height, bgl.GL_RGBA, bgl.GL_UNSIGNED_BYTE, buffer) - return buffer - def save_pixels(filepath, pixel_data, width, height): image = bpy.data.images.new("temp", width, height, alpha=True) image.filepath = filepath -- cgit v1.2.3 From 563ea27eb1a8843fdc05b2fe3944dfbdcd4d5a1f Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 21 Jul 2022 23:35:50 -0300 Subject: UV Layout: bring back smooth lines Feature removed in rBAb56a6acb9f4f. --- io_mesh_uv_layout/export_uv_png.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/io_mesh_uv_layout/export_uv_png.py b/io_mesh_uv_layout/export_uv_png.py index 74cd7b19..78f9f6a2 100644 --- a/io_mesh_uv_layout/export_uv_png.py +++ b/io_mesh_uv_layout/export_uv_png.py @@ -23,11 +23,7 @@ def export(filepath, face_data, colors, width, height, opacity): offscreen.free() def draw_image(face_data, opacity): - gpu.state.line_width_set(1.0) gpu.state.blend_set('ALPHA_PREMULT') - # TODO: Use shader that draws a smooth line (Eg. '3D_POLYLINE_UNIFORM_COLOR') - # bgl.glEnable(bgl.GL_LINE_SMOOTH) - # bgl.glHint(bgl.GL_LINE_SMOOTH_HINT, bgl.GL_NICEST) with gpu.matrix.push_pop(): gpu.matrix.load_matrix(get_normalize_uvs_matrix()) @@ -74,12 +70,15 @@ def draw_lines(face_data): for i in range(len(uvs)): start = uvs[i] end = uvs[(i+1) % len(uvs)] - coords.append((start[0], start[1])) - coords.append((end[0], end[1])) + coords.append((start[0], start[1], 0.0)) + coords.append((end[0], end[1], 0.0)) - shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR') + # Use '2D_UNIFORM_COLOR' if smooth lines are not required. + shader = gpu.shader.from_builtin('3D_POLYLINE_UNIFORM_COLOR') batch = batch_for_shader(shader, 'LINES', {"pos" : coords}) shader.bind() + shader.uniform_float("viewportSize", gpu.state.viewport_get()[2:]) + shader.uniform_float("lineWidth", 0.5) shader.uniform_float("color", (0, 0, 0, 1)) batch.draw(shader) -- cgit v1.2.3 From b364669bd7ec08f1c5c8b9f063f3a421a32a5f7b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 22 Jul 2022 15:58:00 +1000 Subject: system_property_chart: support custom properties Based on suggestion from D13223, but applied a different fix. --- system_property_chart.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/system_property_chart.py b/system_property_chart.py index b1fa54c2..bb6d4682 100644 --- a/system_property_chart.py +++ b/system_property_chart.py @@ -219,16 +219,26 @@ def _property_chart_copy(self, context): data_path = self.data_path - # quick & nasty method! + data_path_with_dot = data_path + if not data_path_with_dot.startswith("["): + data_path_with_dot = "." + data_path_with_dot + + code = compile( + "obj_iter%s = obj%s" % (data_path_with_dot, data_path_with_dot), + "", + 'exec', + ) + for obj_iter in selected_objects: if obj != obj_iter: try: - exec("obj_iter.%s = obj.%s" % (data_path, data_path)) + exec(code, {}, {"obj": obj, "obj_iter": obj_iter}) except: # just in case we need to know what went wrong! import traceback traceback.print_exc() + from bpy.props import StringProperty @@ -252,6 +262,7 @@ class CopyPropertyChart(Operator): # List The Classes # + classes = ( AddPresetProperties, SCENE_MT_properties_presets, @@ -260,6 +271,7 @@ classes = ( CopyPropertyChart ) + def register(): for cls in classes: bpy.utils.register_class(cls) -- cgit v1.2.3 From 4a522d353a585e6986ac7c98c1d0decadc7db916 Mon Sep 17 00:00:00 2001 From: Pablo Vazquez Date: Sun, 24 Jul 2022 04:26:46 +0200 Subject: Amaranth: Fix Debug feature to list users of image datablocks The code was looking for background images in the 3D Viewport, which have been moved to Camera objects in 2.8. Fixes T98252 --- amaranth/__init__.py | 2 +- amaranth/scene/debug.py | 34 ++++++++++++++++++---------------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/amaranth/__init__.py b/amaranth/__init__.py index 06a42c8e..e63f0cb9 100644 --- a/amaranth/__init__.py +++ b/amaranth/__init__.py @@ -74,7 +74,7 @@ from amaranth.misc import ( bl_info = { "name": "Amaranth Toolset", "author": "Pablo Vazquez, Bassam Kurdali, Sergey Sharybin, Lukas Tönne, Cesar Saez, CansecoGPC", - "version": (1, 0, 10), + "version": (1, 0, 11), "blender": (3, 2, 0), "location": "Everywhere!", "description": "A collection of tools and settings to improve productivity", diff --git a/amaranth/scene/debug.py b/amaranth/scene/debug.py index 38692743..a73d688e 100755 --- a/amaranth/scene/debug.py +++ b/amaranth/scene/debug.py @@ -65,7 +65,7 @@ class AMTH_store_data(): 'TEXTURE': [], # Textures (Psys, Brushes) 'MODIFIER': [], # Modifiers 'MESH_DATA': [], # Vertex Colors - 'VIEW3D': [], # Background Images + 'OUTLINER_OB_CAMERA': [], # Background Images 'NODETREE': [], # Compositor } libraries = [] # Libraries x type @@ -632,6 +632,7 @@ class AMTH_SCENE_OT_list_users_for_x(Operator): if name not in AMTH_store_data.users['MATERIAL']: AMTH_store_data.users['MATERIAL'].append(name) + # Check Lights for la in d.lights: # Cycles @@ -643,6 +644,7 @@ class AMTH_SCENE_OT_list_users_for_x(Operator): no.image and no.image.name == x: if la.name not in AMTH_store_data.users['LIGHT']: AMTH_store_data.users['LIGHT'].append(la.name) + # Check World for wo in d.worlds: # Cycles @@ -654,6 +656,7 @@ class AMTH_SCENE_OT_list_users_for_x(Operator): no.image and no.image.name == x: if wo.name not in AMTH_store_data.users['WORLD']: AMTH_store_data.users['WORLD'].append(wo.name) + # Check Textures for te in d.textures: if te and te.type == 'IMAGE' and te.image: @@ -662,6 +665,7 @@ class AMTH_SCENE_OT_list_users_for_x(Operator): if name == x and \ name not in AMTH_store_data.users['TEXTURE']: AMTH_store_data.users['TEXTURE'].append(te.name) + # Check Modifiers in Objects for ob in d.objects: for mo in ob.modifiers: @@ -672,21 +676,19 @@ class AMTH_SCENE_OT_list_users_for_x(Operator): name = '"{0}" modifier in {1}'.format(mo.name, ob.name) if name not in AMTH_store_data.users['MODIFIER']: AMTH_store_data.users['MODIFIER'].append(name) - # Check Background Images in Viewports - for scr in d.screens: - for ar in scr.areas: - if ar.type == 'VIEW_3D': - if ar.spaces and \ - ar.spaces.active and \ - ar.spaces.active.background_images: - for bg in ar.spaces.active.background_images: - image = bg.image - - if bg and image and image.name == x: - name = 'Background for 3D Viewport in Screen "{0}"'\ - .format(scr.name) - if name not in AMTH_store_data.users['VIEW3D']: - AMTH_store_data.users['VIEW3D'].append(name) + + # Check Background Images in Cameras + for ob in d.objects: + if ob and ob.type == 'CAMERA' and ob.data.background_images: + for bg in ob.data.background_images: + image = bg.image + + if bg and image and image.name == x: + name = 'Used as background for Camera "{0}"'\ + .format(ob.name) + if name not in AMTH_store_data.users['OUTLINER_OB_CAMERA']: + AMTH_store_data.users['OUTLINER_OB_CAMERA'].append(name) + # Check the Compositor for sce in d.scenes: if sce.node_tree and sce.node_tree.nodes: -- cgit v1.2.3 From 8d609fc7e68def4f9c43bada22d930d4b257c7e6 Mon Sep 17 00:00:00 2001 From: Pablo Vazquez Date: Sun, 24 Jul 2022 04:35:54 +0200 Subject: Amaranth: support listing images used by Empty objects in Debug tool Related to T98252 --- amaranth/__init__.py | 2 +- amaranth/scene/debug.py | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/amaranth/__init__.py b/amaranth/__init__.py index e63f0cb9..70606999 100644 --- a/amaranth/__init__.py +++ b/amaranth/__init__.py @@ -74,7 +74,7 @@ from amaranth.misc import ( bl_info = { "name": "Amaranth Toolset", "author": "Pablo Vazquez, Bassam Kurdali, Sergey Sharybin, Lukas Tönne, Cesar Saez, CansecoGPC", - "version": (1, 0, 11), + "version": (1, 0, 12, "blender": (3, 2, 0), "location": "Everywhere!", "description": "A collection of tools and settings to improve productivity", diff --git a/amaranth/scene/debug.py b/amaranth/scene/debug.py index a73d688e..4f702d08 100755 --- a/amaranth/scene/debug.py +++ b/amaranth/scene/debug.py @@ -65,7 +65,8 @@ class AMTH_store_data(): 'TEXTURE': [], # Textures (Psys, Brushes) 'MODIFIER': [], # Modifiers 'MESH_DATA': [], # Vertex Colors - 'OUTLINER_OB_CAMERA': [], # Background Images + 'OUTLINER_OB_CAMERA': [], # Background Images in Cameras + 'OUTLINER_OB_EMPTY': [], # Empty type Image 'NODETREE': [], # Compositor } libraries = [] # Libraries x type @@ -689,6 +690,18 @@ class AMTH_SCENE_OT_list_users_for_x(Operator): if name not in AMTH_store_data.users['OUTLINER_OB_CAMERA']: AMTH_store_data.users['OUTLINER_OB_CAMERA'].append(name) + # Check Empties type Image + for ob in d.objects: + if ob and ob.type == 'EMPTY' and ob.image_user: + if ob.image_user.id_data.data: + image = ob.image_user.id_data.data + + if image and image.name == x: + name = 'Used in Empty "{0}"'\ + .format(ob.name) + if name not in AMTH_store_data.users['OUTLINER_OB_EMPTY']: + AMTH_store_data.users['OUTLINER_OB_EMPTY'].append(name) + # Check the Compositor for sce in d.scenes: if sce.node_tree and sce.node_tree.nodes: -- cgit v1.2.3 From 3c6a16fcbe5eeff525a83779a896b275bc410fbe Mon Sep 17 00:00:00 2001 From: Pablo Vazquez Date: Sun, 24 Jul 2022 05:08:50 +0200 Subject: Amaranth: Add check for unsaved images in Save/Reload operator The regular Save operator does not automatically save changes in images, users become aware of this when quitting Blender or changing files. This became a problem for the Save/Reload operator since you'd lose work without noticing it. This commit adds a check and let the user now in the terminal which image files have not been saved. Fixes T73905 --- amaranth/__init__.py | 2 +- amaranth/scene/save_reload.py | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/amaranth/__init__.py b/amaranth/__init__.py index 70606999..b7e59e13 100644 --- a/amaranth/__init__.py +++ b/amaranth/__init__.py @@ -74,7 +74,7 @@ from amaranth.misc import ( bl_info = { "name": "Amaranth Toolset", "author": "Pablo Vazquez, Bassam Kurdali, Sergey Sharybin, Lukas Tönne, Cesar Saez, CansecoGPC", - "version": (1, 0, 12, + "version": (1, 0, 13), "blender": (3, 2, 0), "location": "Everywhere!", "description": "A collection of tools and settings to improve productivity", diff --git a/amaranth/scene/save_reload.py b/amaranth/scene/save_reload.py index f3bedb5e..ece61246 100644 --- a/amaranth/scene/save_reload.py +++ b/amaranth/scene/save_reload.py @@ -13,6 +13,23 @@ import bpy KEYMAPS = list() +def check_for_unsaved_images(self): + im_unsaved = [] + + for im in bpy.data.images: + if im.is_dirty: + im_unsaved.append(im.name) + + if im_unsaved: + report_text = 'There are unsaved changes in {0} image(s), check console for details.'\ + .format(len(im_unsaved)) + self.report({"WARNING"}, report_text) + + print("\nAmaranth found unsaved images when trying to save and reload.") + for im in im_unsaved: + print('* Image: "' + im + '" has unsaved changes.') + return True + return class AMTH_WM_OT_save_reload(bpy.types.Operator): """Save and Reload the current blend file""" @@ -23,6 +40,10 @@ class AMTH_WM_OT_save_reload(bpy.types.Operator): if not path: bpy.ops.wm.save_as_mainfile("INVOKE_AREA") return + + if check_for_unsaved_images(self): + return + bpy.ops.wm.save_mainfile() self.report({"INFO"}, "Saved & Reloaded") bpy.ops.wm.open_mainfile("EXEC_DEFAULT", filepath=path) -- cgit v1.2.3 From b00712001075abd383af7d88252ee199b5991231 Mon Sep 17 00:00:00 2001 From: Pablo Vazquez Date: Sun, 24 Jul 2022 05:31:12 +0200 Subject: Amaranth: Fix File Browser features showing up in Asset Browser Both the "Libraries" panel and the "Current Blend's Folder" button are meant for the File Browser editor, not the Asset Browser. Fixes T94212 --- amaranth/__init__.py | 2 +- amaranth/scene/current_blend.py | 2 +- amaranth/scene/goto_library.py | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/amaranth/__init__.py b/amaranth/__init__.py index b7e59e13..55871cc1 100644 --- a/amaranth/__init__.py +++ b/amaranth/__init__.py @@ -74,7 +74,7 @@ from amaranth.misc import ( bl_info = { "name": "Amaranth Toolset", "author": "Pablo Vazquez, Bassam Kurdali, Sergey Sharybin, Lukas Tönne, Cesar Saez, CansecoGPC", - "version": (1, 0, 13), + "version": (1, 0, 14), "blender": (3, 2, 0), "location": "Everywhere!", "description": "A collection of tools and settings to improve productivity", diff --git a/amaranth/scene/current_blend.py b/amaranth/scene/current_blend.py index 7ab855c4..11ac3679 100644 --- a/amaranth/scene/current_blend.py +++ b/amaranth/scene/current_blend.py @@ -36,7 +36,7 @@ class FILEBROWSER_PT_amaranth(bpy.types.Panel): @classmethod def poll(cls, context): - return panel_poll_is_upper_region(context.region) + return context.area.ui_type == 'FILES' and panel_poll_is_upper_region(context.region) def draw(self, context): layout = self.layout diff --git a/amaranth/scene/goto_library.py b/amaranth/scene/goto_library.py index b1ea9e5b..d38a62d3 100644 --- a/amaranth/scene/goto_library.py +++ b/amaranth/scene/goto_library.py @@ -19,6 +19,10 @@ class AMTH_FILE_PT_libraries(bpy.types.Panel): bl_category = "Bookmarks" bl_label = "Libraries" + @classmethod + def poll(cls, context): + return context.area.ui_type == 'FILES' + def draw(self, context): layout = self.layout -- cgit v1.2.3 From ad1966474ed28580aac6f43f000043596bdf37b1 Mon Sep 17 00:00:00 2001 From: Leon Schittek Date: Mon, 25 Jul 2022 20:31:14 +0200 Subject: Fix T97724: Node Wrangler: better align nodes on texture setup * The added nodes are now top aligned with the nodes they belong to. This looks nicer and works independent of screen DPI. * There is a vertical offset based on the socket the added nodes are connected to. This prevents nodes from overlapping too much, when executing the texture setup several times in a row. * Ignore disabled sockets, which would create nodes seemingly connected to nothing. Differential Revision: https://developer.blender.org/D15031 --- node_wrangler.py | 118 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 60 insertions(+), 58 deletions(-) diff --git a/node_wrangler.py b/node_wrangler.py index 6d586e80..02aa62c4 100644 --- a/node_wrangler.py +++ b/node_wrangler.py @@ -1228,13 +1228,12 @@ class NWNodeWrangler(bpy.types.AddonPreferences): def nw_check(context): space = context.space_data - valid_trees = ["ShaderNodeTree", "CompositorNodeTree", "TextureNodeTree", "GeometryNodeTree"] + editor_is_valid = space.type == 'NODE_EDITOR' - valid = False - if space.type == 'NODE_EDITOR' and space.node_tree is not None and space.tree_type in valid_trees: - valid = True + valid_trees = ["ShaderNodeTree", "CompositorNodeTree", "TextureNodeTree", "GeometryNodeTree"] + tree_is_valid = space.node_tree is not None and space.tree_type in valid_trees - return valid + return editor_is_valid and tree_is_valid class NWBase: @classmethod @@ -3014,71 +3013,74 @@ class NWAddTextureSetup(Operator, NWBase): @classmethod def poll(cls, context): - valid = False if nw_check(context): space = context.space_data if space.tree_type == 'ShaderNodeTree': - valid = True - return valid + return True + return False def execute(self, context): nodes, links = get_nodes_links(context) + shader_types = [x[1] for x in shaders_shader_nodes_props if x[1] not in {'MIX_SHADER', 'ADD_SHADER'}] texture_types = [x[1] for x in shaders_texture_nodes_props] selected_nodes = [n for n in nodes if n.select] - for t_node in selected_nodes: - valid = False + + for node in selected_nodes: + if not node.inputs: + continue + input_index = 0 - if t_node.inputs: - for index, i in enumerate(t_node.inputs): - if not i.is_linked: - valid = True - input_index = index + target_input = node.inputs[0] + for input in node.inputs: + if input.enabled: + input_index += 1 + if not input.is_linked: + target_input = input break - if valid: - locx = t_node.location.x - locy = t_node.location.y - t_node.dimensions.y/2 - - xoffset = [500, 700] - is_texture = False - if t_node.type in texture_types + ['MAPPING']: - xoffset = [290, 500] - is_texture = True - - coordout = 2 - image_type = 'ShaderNodeTexImage' - - if (t_node.type in texture_types and t_node.type != 'TEX_IMAGE') or (t_node.type == 'BACKGROUND'): - coordout = 0 # image texture uses UVs, procedural textures and Background shader use Generated - if t_node.type == 'BACKGROUND': - image_type = 'ShaderNodeTexEnvironment' - - if not is_texture: - tex = nodes.new(image_type) - tex.location = [locx - 200, locy + 112] - nodes.active = tex - links.new(tex.outputs[0], t_node.inputs[input_index]) - - t_node.select = False - if self.add_mapping or is_texture: - if t_node.type != 'MAPPING': - m = nodes.new('ShaderNodeMapping') - m.location = [locx - xoffset[0], locy + 141] - m.width = 240 - else: - m = t_node - coord = nodes.new('ShaderNodeTexCoord') - coord.location = [locx - (200 if t_node.type == 'MAPPING' else xoffset[1]), locy + 124] - - if not is_texture: - links.new(m.outputs[0], tex.inputs[0]) - links.new(coord.outputs[coordout], m.inputs[0]) - else: - nodes.active = m - links.new(m.outputs[0], t_node.inputs[input_index]) - links.new(coord.outputs[coordout], m.inputs[0]) else: - self.report({'WARNING'}, "No free inputs for node: "+t_node.name) + self.report({'WARNING'}, "No free inputs for node: " + node.name) + continue + + x_offset = 0 + padding = 40.0 + locx = node.location.x + locy = node.location.y - (input_index * padding) + + is_texture_node = node.type in texture_types + use_environment_texture = node.type == 'BACKGROUND' + + # Add an image texture before normal shader nodes. + if not is_texture_node: + image_texture_type = 'ShaderNodeTexEnvironment' if use_environment_texture else 'ShaderNodeTexImage' + image_texture_node = nodes.new(image_texture_type) + x_offset = x_offset + image_texture_node.width + padding + image_texture_node.location = [locx - x_offset, locy] + nodes.active = image_texture_node + links.new(image_texture_node.outputs[0], target_input) + + # The mapping setup following this will connect to the firrst input of this image texture. + target_input = image_texture_node.inputs[0] + + node.select = False + + if is_texture_node or self.add_mapping: + # Add Mapping node. + mapping_node = nodes.new('ShaderNodeMapping') + x_offset = x_offset + mapping_node.width + padding + mapping_node.location = [locx - x_offset, locy] + links.new(mapping_node.outputs[0], target_input) + + # Add Texture Coordinates node. + tex_coord_node = nodes.new('ShaderNodeTexCoord') + x_offset = x_offset + tex_coord_node.width + padding + tex_coord_node.location = [locx - x_offset, locy] + + is_procedural_texture = is_texture_node and node.type != 'TEX_IMAGE' + use_generated_coordinates = is_procedural_texture or use_environment_texture + tex_coord_output = tex_coord_node.outputs[0 if use_generated_coordinates else 2] + links.new(tex_coord_output, mapping_node.inputs[0]) + return {'FINISHED'} -- cgit v1.2.3 From e7ce79d8f24197e7b49ababd94612450f166d206 Mon Sep 17 00:00:00 2001 From: Leon Schittek Date: Mon, 25 Jul 2022 20:31:30 +0200 Subject: Fix T96194: Node Wrangler: use enabled sockets for lazy mix Add utility function to check for the first output that is actually enabled rather than always using the first output of a node. The previous behavior would lead to confusing situations, where a mix/math node was added but didn't seem to be connected, when the first output was currently disabled. Differential Revision: https://developer.blender.org/D14251 --- node_wrangler.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/node_wrangler.py b/node_wrangler.py index 02aa62c4..eed4c2fb 100644 --- a/node_wrangler.py +++ b/node_wrangler.py @@ -596,6 +596,13 @@ def get_nodes_from_category(category_name, context): if category.name == category_name: return sorted(category.items(context), key=lambda node: node.label) +def get_first_enabled_output(node): + for output in node.outputs: + if output.enabled: + return output + else: + return node.outputs[0] + def is_visible_socket(socket): return not socket.hide and socket.enabled and socket.type != 'CUSTOM' @@ -2452,8 +2459,8 @@ class NWMergeNodes(Operator, NWBase): mode = 'MIX' if (merge_type != 'MATH' and merge_type != 'GEOMETRY') and tree_type == 'GEOMETRY': merge_type = 'AUTO' - # The math nodes used for geometry nodes are of type 'ShaderNode' - if merge_type == 'MATH' and tree_type == 'GEOMETRY': + # The MixRGB node and math nodes used for geometry nodes are of type 'ShaderNode' + if (merge_type == 'MATH' or merge_type == 'MIX') and tree_type == 'GEOMETRY': node_type = 'ShaderNode' selected_mix = [] # entry = [index, loc] selected_shader = [] # entry = [index, loc] @@ -2473,7 +2480,8 @@ class NWMergeNodes(Operator, NWBase): ('VALUE', [t[0] for t in operations], selected_math), ('VECTOR', [], selected_vector), ): - output_type = node.outputs[0].type + output = get_first_enabled_output(node) + output_type = output.type valid_mode = mode in types_list # When mode is 'MIX' we have to cheat since the mix node is not used in # geometry nodes. @@ -2524,7 +2532,7 @@ class NWMergeNodes(Operator, NWBase): # Change the node type for math nodes in a geometry node tree. if tree_type == 'GEOMETRY': - if nodes_list is selected_math or nodes_list is selected_vector: + if nodes_list is selected_math or nodes_list is selected_vector or nodes_list is selected_mix: node_type = 'ShaderNode' if mode == 'MIX': mode = 'ADD' @@ -2649,8 +2657,9 @@ class NWMergeNodes(Operator, NWBase): # Special case: # Two nodes were selected and first selected has no output links, second selected has output links. # Then add links from last add to all links 'to_socket' of out links of second selected. + first_selected_output = get_first_enabled_output(first_selected) if len(nodes_list) == 2: - if not first_selected.outputs[0].links: + if not first_selected_output.links: second_selected = nodes[nodes_list[1][0]] for ss_link in second_selected.outputs[0].links: # Prevent cyclic dependencies when nodes to be merged are linked to one another. @@ -2658,16 +2667,16 @@ class NWMergeNodes(Operator, NWBase): if not self.link_creates_cycle(ss_link, invalid_nodes): links.new(last_add.outputs[0], ss_link.to_socket) # add links from last_add to all links 'to_socket' of out links of first selected. - for fs_link in first_selected.outputs[0].links: + for fs_link in first_selected_output.links: # Link only if "to_node" index not in invalid indexes list. if not self.link_creates_cycle(fs_link, invalid_nodes): links.new(last_add.outputs[0], fs_link.to_socket) # add link from "first" selected and "first" add node node_to = nodes[count_after - 1] - links.new(first_selected.outputs[0], node_to.inputs[first]) + links.new(first_selected_output, node_to.inputs[first]) if node_to.type == 'ZCOMBINE': for fs_out in first_selected.outputs: - if fs_out != first_selected.outputs[0] and fs_out.name in ('Z', 'Depth'): + if fs_out != first_selected_output and fs_out.name in ('Z', 'Depth'): links.new(fs_out, node_to.inputs[1]) break # add links between added ADD nodes and between selected and ADD nodes @@ -2677,20 +2686,20 @@ class NWMergeNodes(Operator, NWBase): node_to = nodes[index - 1] node_to_input_i = first node_to_z_i = 1 # if z combine - link z to first z input - links.new(node_from.outputs[0], node_to.inputs[node_to_input_i]) + links.new(get_first_enabled_output(node_from), node_to.inputs[node_to_input_i]) if node_to.type == 'ZCOMBINE': for from_out in node_from.outputs: - if from_out != node_from.outputs[0] and from_out.name in ('Z', 'Depth'): + if from_out != get_first_enabled_output(node_from) and from_out.name in ('Z', 'Depth'): links.new(from_out, node_to.inputs[node_to_z_i]) if len(nodes_list) > 1: node_from = nodes[nodes_list[i + 1][0]] node_to = nodes[index] node_to_input_i = second node_to_z_i = 3 # if z combine - link z to second z input - links.new(node_from.outputs[0], node_to.inputs[node_to_input_i]) + links.new(get_first_enabled_output(node_from), node_to.inputs[node_to_input_i]) if node_to.type == 'ZCOMBINE': for from_out in node_from.outputs: - if from_out != node_from.outputs[0] and from_out.name in ('Z', 'Depth'): + if from_out != get_first_enabled_output(node_from) and from_out.name in ('Z', 'Depth'): links.new(from_out, node_to.inputs[node_to_z_i]) index -= 1 # set "last" of added nodes as active -- cgit v1.2.3 From 3ec8a60720da4b2b4ad6d40f26b6a1aa30791973 Mon Sep 17 00:00:00 2001 From: Oliver Weissbarth Date: Mon, 25 Jul 2022 20:31:40 +0200 Subject: Fix T84451: Node Wrangler: change label from Swap Outputs to Swap Links The Operator is internally called "Swap Links" and the manual also calls it "Swap Links" as it can swap inputs and outputs. Differential Revision: https://developer.blender.org/D10031 --- node_wrangler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node_wrangler.py b/node_wrangler.py index eed4c2fb..73696ddd 100644 --- a/node_wrangler.py +++ b/node_wrangler.py @@ -5194,8 +5194,8 @@ kmi_defs = ( (NWDeleteUnused.bl_idname, 'X', 'PRESS', False, False, True, None, "Delete unused nodes"), # Frame Selected (NWFrameSelected.bl_idname, 'P', 'PRESS', False, True, False, None, "Frame selected nodes"), - # Swap Outputs - (NWSwapLinks.bl_idname, 'S', 'PRESS', False, False, True, None, "Swap Outputs"), + # Swap Links + (NWSwapLinks.bl_idname, 'S', 'PRESS', False, False, True, None, "Swap Links"), # Preview Node (NWPreviewNode.bl_idname, 'LEFTMOUSE', 'PRESS', True, True, False, (('run_in_geometry_nodes', False),), "Preview node output"), (NWPreviewNode.bl_idname, 'LEFTMOUSE', 'PRESS', False, True, True, (('run_in_geometry_nodes', True),), "Preview node output"), -- cgit v1.2.3 From a228560fcedb44811efae571a88c3dfc7985148c Mon Sep 17 00:00:00 2001 From: Vincent Blankfield Date: Mon, 25 Jul 2022 20:34:15 +0200 Subject: Fix T83719: Node Wrangler: broken node outline when fast connecting No need to subtract scroll bar widths as they are hidden by default in 2.80+. --- node_wrangler.py | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/node_wrangler.py b/node_wrangler.py index 73696ddd..4525217d 100644 --- a/node_wrangler.py +++ b/node_wrangler.py @@ -820,8 +820,7 @@ def draw_circle_2d_filled(shader, mx, my, radius, colour=(1.0, 1.0, 1.0, 0.7)): def draw_rounded_node_border(shader, node, radius=8, colour=(1.0, 1.0, 1.0, 0.7)): - area_width = bpy.context.area.width - (16*dpifac()) - 1 - bottom_bar = (16*dpifac()) + 1 + area_width = bpy.context.area.width sides = 16 radius = radius*dpifac() @@ -847,7 +846,7 @@ def draw_rounded_node_border(shader, node, radius=8, colour=(1.0, 1.0, 1.0, 0.7) vertices = [(mx,my)] for i in range(sides+1): if (4<=i<=8): - if my > bottom_bar and mx < area_width: + if mx < area_width: cosine = radius * cos(i * 2 * pi / sides) + mx sine = radius * sin(i * 2 * pi / sides) + my vertices.append((cosine,sine)) @@ -861,7 +860,7 @@ def draw_rounded_node_border(shader, node, radius=8, colour=(1.0, 1.0, 1.0, 0.7) vertices = [(mx,my)] for i in range(sides+1): if (0<=i<=4): - if my > bottom_bar and mx < area_width: + if mx < area_width: cosine = radius * cos(i * 2 * pi / sides) + mx sine = radius * sin(i * 2 * pi / sides) + my vertices.append((cosine,sine)) @@ -875,7 +874,7 @@ def draw_rounded_node_border(shader, node, radius=8, colour=(1.0, 1.0, 1.0, 0.7) vertices = [(mx,my)] for i in range(sides+1): if (8<=i<=12): - if my > bottom_bar and mx < area_width: + if mx < area_width: cosine = radius * cos(i * 2 * pi / sides) + mx sine = radius * sin(i * 2 * pi / sides) + my vertices.append((cosine,sine)) @@ -889,7 +888,7 @@ def draw_rounded_node_border(shader, node, radius=8, colour=(1.0, 1.0, 1.0, 0.7) vertices = [(mx,my)] for i in range(sides+1): if (12<=i<=16): - if my > bottom_bar and mx < area_width: + if mx < area_width: cosine = radius * cos(i * 2 * pi / sides) + mx sine = radius * sin(i * 2 * pi / sides) + my vertices.append((cosine,sine)) @@ -918,18 +917,15 @@ def draw_rounded_node_border(shader, node, radius=8, colour=(1.0, 1.0, 1.0, 0.7) m2x, m2y = bpy.context.region.view2d.view_to_region(nlocx + ndimx, nlocy, clip=False) m1x = min(m1x, area_width) m2x = min(m2x, area_width) - if m1y > bottom_bar and m2y > bottom_bar: - vertices.extend([(m1x,m1y), (m2x,m1y), - (m2x,m1y+radius), (m1x,m1y+radius)]) - indices.extend([(id_last, id_last+1, id_last+3), - (id_last+3, id_last+1, id_last+2)]) - id_last += 4 + vertices.extend([(m1x,m1y), (m2x,m1y), + (m2x,m1y+radius), (m1x,m1y+radius)]) + indices.extend([(id_last, id_last+1, id_last+3), + (id_last+3, id_last+1, id_last+2)]) + id_last += 4 # Right edge m1x, m1y = bpy.context.region.view2d.view_to_region(nlocx + ndimx, nlocy, clip=False) m2x, m2y = bpy.context.region.view2d.view_to_region(nlocx + ndimx, nlocy - ndimy, clip=False) - m1y = max(m1y, bottom_bar) - m2y = max(m2y, bottom_bar) if m1x < area_width and m2x < area_width: vertices.extend([(m1x,m2y), (m1x+radius,m2y), (m1x+radius,m1y), (m1x,m1y)]) @@ -942,11 +938,10 @@ def draw_rounded_node_border(shader, node, radius=8, colour=(1.0, 1.0, 1.0, 0.7) m2x, m2y = bpy.context.region.view2d.view_to_region(nlocx + ndimx, nlocy-ndimy, clip=False) m1x = min(m1x, area_width) m2x = min(m2x, area_width) - if m1y > bottom_bar and m2y > bottom_bar: - vertices.extend([(m1x,m2y), (m2x,m2y), - (m2x,m1y-radius), (m1x,m1y-radius)]) - indices.extend([(id_last, id_last+1, id_last+3), - (id_last+3, id_last+1, id_last+2)]) + vertices.extend([(m1x,m2y), (m2x,m2y), + (m2x,m1y-radius), (m1x,m1y-radius)]) + indices.extend([(id_last, id_last+1, id_last+3), + (id_last+3, id_last+1, id_last+2)]) # now draw all edges in one batch if len(vertices) != 0: -- cgit v1.2.3 From c27fd9ecb8aeda9c583695f7d226e4ff581d4da6 Mon Sep 17 00:00:00 2001 From: Scott Ramsay Date: Mon, 25 Jul 2022 20:59:54 +0200 Subject: Node Wrangler: use utility function to populate switch menu Currently, the nodes in the Switch menu (Shift+S) are generated from Node Wrangler's own long static list of categories and nodes, which often goes out of sync when Blender changes. This changes to using the node_categories_iter utility which uses the correct list of available nodes from nodeitems_builtins.py. The submenu classes are now procedurally generated and registered in the same fashion as the add menu, but just with a different operator in their draw function. Removing the static lists required a couple changes outside of the Switch menu. Mostly having to compare types using the verbose node.rna_type.identifier. Includes improvements on the original patch by Benni Merz. --- node_wrangler.py | 936 +++---------------------------------------------------- 1 file changed, 46 insertions(+), 890 deletions(-) diff --git a/node_wrangler.py b/node_wrangler.py index 4525217d..290ac082 100644 --- a/node_wrangler.py +++ b/node_wrangler.py @@ -3,7 +3,7 @@ bl_info = { "name": "Node Wrangler", "author": "Bartek Skorupa, Greg Zaal, Sebastian Koenig, Christian Brinkmann, Florian Meyer", - "version": (3, 40), + "version": (3, 41), "blender": (2, 93, 0), "location": "Node Editor Toolbar or Shift-W", "description": "Various tools to enhance and speed up node-based workflow", @@ -72,406 +72,6 @@ rl_outputs = ( RL_entry('use_pass_z', 'Z', 'Depth', True, True), ) -# shader nodes -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -# Keeping things in alphabetical order so we don't need to sort later. -shaders_input_nodes_props = ( - ('ShaderNodeAmbientOcclusion', 'AMBIENT_OCCLUSION', 'Ambient Occlusion'), - ('ShaderNodeAttribute', 'ATTRIBUTE', 'Attribute'), - ('ShaderNodeBevel', 'BEVEL', 'Bevel'), - ('ShaderNodeCameraData', 'CAMERA', 'Camera Data'), - ('ShaderNodeFresnel', 'FRESNEL', 'Fresnel'), - ('ShaderNodeNewGeometry', 'NEW_GEOMETRY', 'Geometry'), - ('ShaderNodeHairInfo', 'HAIR_INFO', 'Hair Info'), - ('ShaderNodeLayerWeight', 'LAYER_WEIGHT', 'Layer Weight'), - ('ShaderNodeLightPath', 'LIGHT_PATH', 'Light Path'), - ('ShaderNodeObjectInfo', 'OBJECT_INFO', 'Object Info'), - ('ShaderNodeParticleInfo', 'PARTICLE_INFO', 'Particle Info'), - ('ShaderNodeRGB', 'RGB', 'RGB'), - ('ShaderNodeTangent', 'TANGENT', 'Tangent'), - ('ShaderNodeTexCoord', 'TEX_COORD', 'Texture Coordinate'), - ('ShaderNodeUVMap', 'UVMAP', 'UV Map'), - ('ShaderNodeValue', 'VALUE', 'Value'), - ('ShaderNodeVertexColor', 'VERTEX_COLOR', 'Vertex Color'), - ('ShaderNodeVolumeInfo', 'VOLUME_INFO', 'Volume Info'), - ('ShaderNodeWireframe', 'WIREFRAME', 'Wireframe'), - -) -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -# Keeping things in alphabetical order so we don't need to sort later. -shaders_output_nodes_props = ( - ('ShaderNodeOutputAOV', 'OUTPUT_AOV', 'AOV Output'), - ('ShaderNodeOutputLight', 'OUTPUT_LIGHT', 'Light Output'), - ('ShaderNodeOutputMaterial', 'OUTPUT_MATERIAL', 'Material Output'), - ('ShaderNodeOutputWorld', 'OUTPUT_WORLD', 'World Output'), -) -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -# Keeping things in alphabetical order so we don't need to sort later. -shaders_shader_nodes_props = ( - ('ShaderNodeAddShader', 'ADD_SHADER', 'Add Shader'), - ('ShaderNodeBsdfAnisotropic', 'BSDF_ANISOTROPIC', 'Anisotropic BSDF'), - ('ShaderNodeBsdfDiffuse', 'BSDF_DIFFUSE', 'Diffuse BSDF'), - ('ShaderNodeEmission', 'EMISSION', 'Emission'), - ('ShaderNodeBsdfGlass', 'BSDF_GLASS', 'Glass BSDF'), - ('ShaderNodeBsdfGlossy', 'BSDF_GLOSSY', 'Glossy BSDF'), - ('ShaderNodeBsdfHair', 'BSDF_HAIR', 'Hair BSDF'), - ('ShaderNodeHoldout', 'HOLDOUT', 'Holdout'), - ('ShaderNodeMixShader', 'MIX_SHADER', 'Mix Shader'), - ('ShaderNodeBsdfPrincipled', 'BSDF_PRINCIPLED', 'Principled BSDF'), - ('ShaderNodeBsdfHairPrincipled', 'BSDF_HAIR_PRINCIPLED', 'Principled Hair BSDF'), - ('ShaderNodeVolumePrincipled', 'PRINCIPLED_VOLUME', 'Principled Volume'), - ('ShaderNodeBsdfRefraction', 'BSDF_REFRACTION', 'Refraction BSDF'), - ('ShaderNodeSubsurfaceScattering', 'SUBSURFACE_SCATTERING', 'Subsurface Scattering'), - ('ShaderNodeBsdfToon', 'BSDF_TOON', 'Toon BSDF'), - ('ShaderNodeBsdfTranslucent', 'BSDF_TRANSLUCENT', 'Translucent BSDF'), - ('ShaderNodeBsdfTransparent', 'BSDF_TRANSPARENT', 'Transparent BSDF'), - ('ShaderNodeBsdfVelvet', 'BSDF_VELVET', 'Velvet BSDF'), - ('ShaderNodeBackground', 'BACKGROUND', 'Background'), - ('ShaderNodeVolumeAbsorption', 'VOLUME_ABSORPTION', 'Volume Absorption'), - ('ShaderNodeVolumeScatter', 'VOLUME_SCATTER', 'Volume Scatter'), -) -# (rna_type.identifier, type, rna_type.name) -# Keeping things in alphabetical order so we don't need to sort later. -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -shaders_texture_nodes_props = ( - ('ShaderNodeTexBrick', 'TEX_BRICK', 'Brick Texture'), - ('ShaderNodeTexChecker', 'TEX_CHECKER', 'Checker Texture'), - ('ShaderNodeTexEnvironment', 'TEX_ENVIRONMENT', 'Environment Texture'), - ('ShaderNodeTexGradient', 'TEX_GRADIENT', 'Gradient Texture'), - ('ShaderNodeTexIES', 'TEX_IES', 'IES Texture'), - ('ShaderNodeTexImage', 'TEX_IMAGE', 'Image Texture'), - ('ShaderNodeTexMagic', 'TEX_MAGIC', 'Magic Texture'), - ('ShaderNodeTexMusgrave', 'TEX_MUSGRAVE', 'Musgrave Texture'), - ('ShaderNodeTexNoise', 'TEX_NOISE', 'Noise Texture'), - ('ShaderNodeTexPointDensity', 'TEX_POINTDENSITY', 'Point Density'), - ('ShaderNodeTexSky', 'TEX_SKY', 'Sky Texture'), - ('ShaderNodeTexVoronoi', 'TEX_VORONOI', 'Voronoi Texture'), - ('ShaderNodeTexWave', 'TEX_WAVE', 'Wave Texture'), - ('ShaderNodeTexWhiteNoise', 'TEX_WHITE_NOISE', 'White Noise'), -) -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -# Keeping things in alphabetical order so we don't need to sort later. -shaders_color_nodes_props = ( - ('ShaderNodeBrightContrast', 'BRIGHTCONTRAST', 'Bright Contrast'), - ('ShaderNodeGamma', 'GAMMA', 'Gamma'), - ('ShaderNodeHueSaturation', 'HUE_SAT', 'Hue Saturation Value'), - ('ShaderNodeInvert', 'INVERT', 'Invert'), - ('ShaderNodeLightFalloff', 'LIGHT_FALLOFF', 'Light Falloff'), - ('ShaderNodeMixRGB', 'MIX_RGB', 'MixRGB'), - ('ShaderNodeRGBCurve', 'CURVE_RGB', 'RGB Curves'), -) -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -# Keeping things in alphabetical order so we don't need to sort later. -shaders_vector_nodes_props = ( - ('ShaderNodeBump', 'BUMP', 'Bump'), - ('ShaderNodeDisplacement', 'DISPLACEMENT', 'Displacement'), - ('ShaderNodeMapping', 'MAPPING', 'Mapping'), - ('ShaderNodeNormal', 'NORMAL', 'Normal'), - ('ShaderNodeNormalMap', 'NORMAL_MAP', 'Normal Map'), - ('ShaderNodeVectorCurve', 'CURVE_VEC', 'Vector Curves'), - ('ShaderNodeVectorDisplacement', 'VECTOR_DISPLACEMENT', 'Vector Displacement'), - ('ShaderNodeVectorTransform', 'VECT_TRANSFORM', 'Vector Transform'), -) -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -# Keeping things in alphabetical order so we don't need to sort later. -shaders_converter_nodes_props = ( - ('ShaderNodeBlackbody', 'BLACKBODY', 'Blackbody'), - ('ShaderNodeClamp', 'CLAMP', 'Clamp'), - ('ShaderNodeValToRGB', 'VALTORGB', 'ColorRamp'), - ('ShaderNodeCombineHSV', 'COMBHSV', 'Combine HSV'), - ('ShaderNodeCombineRGB', 'COMBRGB', 'Combine RGB'), - ('ShaderNodeCombineXYZ', 'COMBXYZ', 'Combine XYZ'), - ('ShaderNodeMapRange', 'MAP_RANGE', 'Map Range'), - ('ShaderNodeMath', 'MATH', 'Math'), - ('ShaderNodeRGBToBW', 'RGBTOBW', 'RGB to BW'), - ('ShaderNodeSeparateRGB', 'SEPRGB', 'Separate RGB'), - ('ShaderNodeSeparateXYZ', 'SEPXYZ', 'Separate XYZ'), - ('ShaderNodeSeparateHSV', 'SEPHSV', 'Separate HSV'), - ('ShaderNodeVectorMath', 'VECT_MATH', 'Vector Math'), - ('ShaderNodeWavelength', 'WAVELENGTH', 'Wavelength'), -) -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -# Keeping things in alphabetical order so we don't need to sort later. -shaders_layout_nodes_props = ( - ('NodeFrame', 'FRAME', 'Frame'), - ('NodeReroute', 'REROUTE', 'Reroute'), -) - -# compositing nodes -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -# Keeping things in alphabetical order so we don't need to sort later. -compo_input_nodes_props = ( - ('CompositorNodeBokehImage', 'BOKEHIMAGE', 'Bokeh Image'), - ('CompositorNodeImage', 'IMAGE', 'Image'), - ('CompositorNodeMask', 'MASK', 'Mask'), - ('CompositorNodeMovieClip', 'MOVIECLIP', 'Movie Clip'), - ('CompositorNodeRLayers', 'R_LAYERS', 'Render Layers'), - ('CompositorNodeRGB', 'RGB', 'RGB'), - ('CompositorNodeTexture', 'TEXTURE', 'Texture'), - ('CompositorNodeTime', 'TIME', 'Time'), - ('CompositorNodeTrackPos', 'TRACKPOS', 'Track Position'), - ('CompositorNodeValue', 'VALUE', 'Value'), -) -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -# Keeping things in alphabetical order so we don't need to sort later. -compo_output_nodes_props = ( - ('CompositorNodeComposite', 'COMPOSITE', 'Composite'), - ('CompositorNodeOutputFile', 'OUTPUT_FILE', 'File Output'), - ('CompositorNodeLevels', 'LEVELS', 'Levels'), - ('CompositorNodeSplitViewer', 'SPLITVIEWER', 'Split Viewer'), - ('CompositorNodeViewer', 'VIEWER', 'Viewer'), -) -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -# Keeping things in alphabetical order so we don't need to sort later. -compo_color_nodes_props = ( - ('CompositorNodeAlphaOver', 'ALPHAOVER', 'Alpha Over'), - ('CompositorNodeBrightContrast', 'BRIGHTCONTRAST', 'Bright/Contrast'), - ('CompositorNodeColorBalance', 'COLORBALANCE', 'Color Balance'), - ('CompositorNodeColorCorrection', 'COLORCORRECTION', 'Color Correction'), - ('CompositorNodeGamma', 'GAMMA', 'Gamma'), - ('CompositorNodeHueCorrect', 'HUECORRECT', 'Hue Correct'), - ('CompositorNodeHueSat', 'HUE_SAT', 'Hue Saturation Value'), - ('CompositorNodeInvert', 'INVERT', 'Invert'), - ('CompositorNodeMixRGB', 'MIX_RGB', 'Mix'), - ('CompositorNodeCurveRGB', 'CURVE_RGB', 'RGB Curves'), - ('CompositorNodeTonemap', 'TONEMAP', 'Tonemap'), - ('CompositorNodeZcombine', 'ZCOMBINE', 'Z Combine'), -) -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -# Keeping things in alphabetical order so we don't need to sort later. -compo_converter_nodes_props = ( - ('CompositorNodePremulKey', 'PREMULKEY', 'Alpha Convert'), - ('CompositorNodeValToRGB', 'VALTORGB', 'ColorRamp'), - ('CompositorNodeCombHSVA', 'COMBHSVA', 'Combine HSVA'), - ('CompositorNodeCombRGBA', 'COMBRGBA', 'Combine RGBA'), - ('CompositorNodeCombYCCA', 'COMBYCCA', 'Combine YCbCrA'), - ('CompositorNodeCombYUVA', 'COMBYUVA', 'Combine YUVA'), - ('CompositorNodeIDMask', 'ID_MASK', 'ID Mask'), - ('CompositorNodeMath', 'MATH', 'Math'), - ('CompositorNodeRGBToBW', 'RGBTOBW', 'RGB to BW'), - ('CompositorNodeSepRGBA', 'SEPRGBA', 'Separate RGBA'), - ('CompositorNodeSepHSVA', 'SEPHSVA', 'Separate HSVA'), - ('CompositorNodeSepYUVA', 'SEPYUVA', 'Separate YUVA'), - ('CompositorNodeSepYCCA', 'SEPYCCA', 'Separate YCbCrA'), - ('CompositorNodeSetAlpha', 'SETALPHA', 'Set Alpha'), - ('CompositorNodeSwitchView', 'VIEWSWITCH', 'View Switch'), -) -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -# Keeping things in alphabetical order so we don't need to sort later. -compo_filter_nodes_props = ( - ('CompositorNodeBilateralblur', 'BILATERALBLUR', 'Bilateral Blur'), - ('CompositorNodeBlur', 'BLUR', 'Blur'), - ('CompositorNodeBokehBlur', 'BOKEHBLUR', 'Bokeh Blur'), - ('CompositorNodeDefocus', 'DEFOCUS', 'Defocus'), - ('CompositorNodeDenoise', 'DENOISE', 'Denoise'), - ('CompositorNodeDespeckle', 'DESPECKLE', 'Despeckle'), - ('CompositorNodeDilateErode', 'DILATEERODE', 'Dilate/Erode'), - ('CompositorNodeDBlur', 'DBLUR', 'Directional Blur'), - ('CompositorNodeFilter', 'FILTER', 'Filter'), - ('CompositorNodeGlare', 'GLARE', 'Glare'), - ('CompositorNodeInpaint', 'INPAINT', 'Inpaint'), - ('CompositorNodePixelate', 'PIXELATE', 'Pixelate'), - ('CompositorNodeSunBeams', 'SUNBEAMS', 'Sun Beams'), - ('CompositorNodeVecBlur', 'VECBLUR', 'Vector Blur'), -) -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -# Keeping things in alphabetical order so we don't need to sort later. -compo_vector_nodes_props = ( - ('CompositorNodeMapRange', 'MAP_RANGE', 'Map Range'), - ('CompositorNodeMapValue', 'MAP_VALUE', 'Map Value'), - ('CompositorNodeNormal', 'NORMAL', 'Normal'), - ('CompositorNodeNormalize', 'NORMALIZE', 'Normalize'), - ('CompositorNodeCurveVec', 'CURVE_VEC', 'Vector Curves'), -) -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -# Keeping things in alphabetical order so we don't need to sort later. -compo_matte_nodes_props = ( - ('CompositorNodeBoxMask', 'BOXMASK', 'Box Mask'), - ('CompositorNodeChannelMatte', 'CHANNEL_MATTE', 'Channel Key'), - ('CompositorNodeChromaMatte', 'CHROMA_MATTE', 'Chroma Key'), - ('CompositorNodeColorMatte', 'COLOR_MATTE', 'Color Key'), - ('CompositorNodeColorSpill', 'COLOR_SPILL', 'Color Spill'), - ('CompositorNodeCryptomatte', 'CRYPTOMATTE', 'Cryptomatte'), - ('CompositorNodeDiffMatte', 'DIFF_MATTE', 'Difference Key'), - ('CompositorNodeDistanceMatte', 'DISTANCE_MATTE', 'Distance Key'), - ('CompositorNodeDoubleEdgeMask', 'DOUBLEEDGEMASK', 'Double Edge Mask'), - ('CompositorNodeEllipseMask', 'ELLIPSEMASK', 'Ellipse Mask'), - ('CompositorNodeKeying', 'KEYING', 'Keying'), - ('CompositorNodeKeyingScreen', 'KEYINGSCREEN', 'Keying Screen'), - ('CompositorNodeLumaMatte', 'LUMA_MATTE', 'Luminance Key'), -) -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -# Keeping things in alphabetical order so we don't need to sort later. -compo_distort_nodes_props = ( - ('CompositorNodeCornerPin', 'CORNERPIN', 'Corner Pin'), - ('CompositorNodeCrop', 'CROP', 'Crop'), - ('CompositorNodeDisplace', 'DISPLACE', 'Displace'), - ('CompositorNodeFlip', 'FLIP', 'Flip'), - ('CompositorNodeLensdist', 'LENSDIST', 'Lens Distortion'), - ('CompositorNodeMapUV', 'MAP_UV', 'Map UV'), - ('CompositorNodeMovieDistortion', 'MOVIEDISTORTION', 'Movie Distortion'), - ('CompositorNodePlaneTrackDeform', 'PLANETRACKDEFORM', 'Plane Track Deform'), - ('CompositorNodeRotate', 'ROTATE', 'Rotate'), - ('CompositorNodeScale', 'SCALE', 'Scale'), - ('CompositorNodeStabilize', 'STABILIZE2D', 'Stabilize 2D'), - ('CompositorNodeTransform', 'TRANSFORM', 'Transform'), - ('CompositorNodeTranslate', 'TRANSLATE', 'Translate'), -) -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -# Keeping things in alphabetical order so we don't need to sort later. -compo_layout_nodes_props = ( - ('NodeFrame', 'FRAME', 'Frame'), - ('NodeReroute', 'REROUTE', 'Reroute'), - ('CompositorNodeSwitch', 'SWITCH', 'Switch'), -) -# Blender Render material nodes -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -blender_mat_input_nodes_props = ( - ('ShaderNodeMaterial', 'MATERIAL', 'Material'), - ('ShaderNodeCameraData', 'CAMERA', 'Camera Data'), - ('ShaderNodeLightData', 'LIGHT', 'Light Data'), - ('ShaderNodeValue', 'VALUE', 'Value'), - ('ShaderNodeRGB', 'RGB', 'RGB'), - ('ShaderNodeTexture', 'TEXTURE', 'Texture'), - ('ShaderNodeGeometry', 'GEOMETRY', 'Geometry'), - ('ShaderNodeExtendedMaterial', 'MATERIAL_EXT', 'Extended Material'), -) - -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -blender_mat_output_nodes_props = ( - ('ShaderNodeOutput', 'OUTPUT', 'Output'), -) - -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -blender_mat_color_nodes_props = ( - ('ShaderNodeMixRGB', 'MIX_RGB', 'MixRGB'), - ('ShaderNodeRGBCurve', 'CURVE_RGB', 'RGB Curves'), - ('ShaderNodeInvert', 'INVERT', 'Invert'), - ('ShaderNodeHueSaturation', 'HUE_SAT', 'Hue Saturation Value'), -) - -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -blender_mat_vector_nodes_props = ( - ('ShaderNodeNormal', 'NORMAL', 'Normal'), - ('ShaderNodeMapping', 'MAPPING', 'Mapping'), - ('ShaderNodeVectorCurve', 'CURVE_VEC', 'Vector Curves'), -) - -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -blender_mat_converter_nodes_props = ( - ('ShaderNodeValToRGB', 'VALTORGB', 'ColorRamp'), - ('ShaderNodeRGBToBW', 'RGBTOBW', 'RGB to BW'), - ('ShaderNodeMath', 'MATH', 'Math'), - ('ShaderNodeVectorMath', 'VECT_MATH', 'Vector Math'), - ('ShaderNodeSqueeze', 'SQUEEZE', 'Squeeze Value'), - ('ShaderNodeSeparateRGB', 'SEPRGB', 'Separate RGB'), - ('ShaderNodeCombineRGB', 'COMBRGB', 'Combine RGB'), - ('ShaderNodeSeparateHSV', 'SEPHSV', 'Separate HSV'), - ('ShaderNodeCombineHSV', 'COMBHSV', 'Combine HSV'), -) - -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -blender_mat_layout_nodes_props = ( - ('NodeReroute', 'REROUTE', 'Reroute'), -) - -# Texture Nodes -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -texture_input_nodes_props = ( - ('TextureNodeCurveTime', 'CURVE_TIME', 'Curve Time'), - ('TextureNodeCoordinates', 'COORD', 'Coordinates'), - ('TextureNodeTexture', 'TEXTURE', 'Texture'), - ('TextureNodeImage', 'IMAGE', 'Image'), -) - -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -texture_output_nodes_props = ( - ('TextureNodeOutput', 'OUTPUT', 'Output'), - ('TextureNodeViewer', 'VIEWER', 'Viewer'), -) - -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -texture_color_nodes_props = ( - ('TextureNodeMixRGB', 'MIX_RGB', 'Mix RGB'), - ('TextureNodeCurveRGB', 'CURVE_RGB', 'RGB Curves'), - ('TextureNodeInvert', 'INVERT', 'Invert'), - ('TextureNodeHueSaturation', 'HUE_SAT', 'Hue Saturation Value'), - ('TextureNodeCompose', 'COMPOSE', 'Combine RGBA'), - ('TextureNodeDecompose', 'DECOMPOSE', 'Separate RGBA'), -) - -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -texture_pattern_nodes_props = ( - ('TextureNodeChecker', 'CHECKER', 'Checker'), - ('TextureNodeBricks', 'BRICKS', 'Bricks'), -) - -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -texture_textures_nodes_props = ( - ('TextureNodeTexNoise', 'TEX_NOISE', 'Noise'), - ('TextureNodeTexDistNoise', 'TEX_DISTNOISE', 'Distorted Noise'), - ('TextureNodeTexClouds', 'TEX_CLOUDS', 'Clouds'), - ('TextureNodeTexBlend', 'TEX_BLEND', 'Blend'), - ('TextureNodeTexVoronoi', 'TEX_VORONOI', 'Voronoi'), - ('TextureNodeTexMagic', 'TEX_MAGIC', 'Magic'), - ('TextureNodeTexMarble', 'TEX_MARBLE', 'Marble'), - ('TextureNodeTexWood', 'TEX_WOOD', 'Wood'), - ('TextureNodeTexMusgrave', 'TEX_MUSGRAVE', 'Musgrave'), - ('TextureNodeTexStucci', 'TEX_STUCCI', 'Stucci'), -) - -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -texture_converter_nodes_props = ( - ('TextureNodeMath', 'MATH', 'Math'), - ('TextureNodeValToRGB', 'VALTORGB', 'ColorRamp'), - ('TextureNodeRGBToBW', 'RGBTOBW', 'RGB to BW'), - ('TextureNodeValToNor', 'VALTONOR', 'Value to Normal'), - ('TextureNodeDistance', 'DISTANCE', 'Distance'), -) - -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -texture_distort_nodes_props = ( - ('TextureNodeScale', 'SCALE', 'Scale'), - ('TextureNodeTranslate', 'TRANSLATE', 'Translate'), - ('TextureNodeRotate', 'ROTATE', 'Rotate'), - ('TextureNodeAt', 'AT', 'At'), -) - -# (rna_type.identifier, type, rna_type.name) -# Keeping mixed case to avoid having to translate entries when adding new nodes in operators. -texture_layout_nodes_props = ( - ('NodeReroute', 'REROUTE', 'Reroute'), -) - # list of blend types of "Mix" nodes in a form that can be used as 'items' for EnumProperty. # used list, not tuple for easy merging with other lists. blend_types = [ @@ -1839,7 +1439,6 @@ class NWPreviewNode(Operator, NWBase): shader_type = space.shader_type self.init_shader_variables(space, shader_type) - shader_types = [x[1] for x in shaders_shader_nodes_props] mlocx = event.mouse_region_x mlocy = event.mouse_region_y select_node = bpy.ops.node.select(location=(mlocx, mlocy), extend=False) @@ -1934,10 +1533,11 @@ class NWPreviewNode(Operator, NWBase): # What follows is code for the shader editor - output_types = [x[1] for x in shaders_output_nodes_props] + output_types = [x.nodetype for x in + get_nodes_from_category('Output', context)] valid = False if active: - if active.type not in output_types: + if active.rna_type.identifier not in output_types: for out in active.outputs: if is_visible_socket(out): valid = True @@ -2113,51 +1713,17 @@ class NWSwitchNodeType(Operator, NWBase): bl_label = "Switch Node Type" bl_options = {'REGISTER', 'UNDO'} - to_type: EnumProperty( - name="Switch to type", - items=list(shaders_input_nodes_props) + - list(shaders_output_nodes_props) + - list(shaders_shader_nodes_props) + - list(shaders_texture_nodes_props) + - list(shaders_color_nodes_props) + - list(shaders_vector_nodes_props) + - list(shaders_converter_nodes_props) + - list(shaders_layout_nodes_props) + - list(compo_input_nodes_props) + - list(compo_output_nodes_props) + - list(compo_color_nodes_props) + - list(compo_converter_nodes_props) + - list(compo_filter_nodes_props) + - list(compo_vector_nodes_props) + - list(compo_matte_nodes_props) + - list(compo_distort_nodes_props) + - list(compo_layout_nodes_props) + - list(blender_mat_input_nodes_props) + - list(blender_mat_output_nodes_props) + - list(blender_mat_color_nodes_props) + - list(blender_mat_vector_nodes_props) + - list(blender_mat_converter_nodes_props) + - list(blender_mat_layout_nodes_props) + - list(texture_input_nodes_props) + - list(texture_output_nodes_props) + - list(texture_color_nodes_props) + - list(texture_pattern_nodes_props) + - list(texture_textures_nodes_props) + - list(texture_converter_nodes_props) + - list(texture_distort_nodes_props) + - list(texture_layout_nodes_props) - ) - - geo_to_type: StringProperty( + to_type: StringProperty( name="Switch to type", default = '', ) def execute(self, context): - nodes, links = get_nodes_links(context) to_type = self.to_type - if self.geo_to_type != '': - to_type = self.geo_to_type + if len(to_type) == 0: + return {'CANCELLED'} + + nodes, links = get_nodes_links(context) # Those types of nodes will not swap. src_excludes = ('NodeFrame') # Those attributes of nodes will be copied if possible @@ -3025,11 +2591,11 @@ class NWAddTextureSetup(Operator, NWBase): def execute(self, context): nodes, links = get_nodes_links(context) - - shader_types = [x[1] for x in shaders_shader_nodes_props if x[1] not in {'MIX_SHADER', 'ADD_SHADER'}] - texture_types = [x[1] for x in shaders_texture_nodes_props] + + texture_types = [x.nodetype for x in + get_nodes_from_category('Texture', context)] selected_nodes = [n for n in nodes if n.select] - + for node in selected_nodes: if not node.inputs: continue @@ -3051,7 +2617,7 @@ class NWAddTextureSetup(Operator, NWBase): locx = node.location.x locy = node.location.y - (input_index * padding) - is_texture_node = node.type in texture_types + is_texture_node = node.rna_type.identifier in texture_types use_environment_texture = node.type == 'BACKGROUND' # Add an image texture before normal shader nodes. @@ -3707,37 +3273,32 @@ class NWLinkToOutputNode(Operator): def execute(self, context): nodes, links = get_nodes_links(context) active = nodes.active - output_node = None output_index = None tree_type = context.space_data.tree_type - if tree_type == 'ShaderNodeTree': - output_types = [x[1] for x in shaders_output_nodes_props] + ['OUTPUT'] - elif tree_type == 'CompositorNodeTree': - output_types = ['COMPOSITE'] - elif tree_type == 'TextureNodeTree': - output_types = ['OUTPUT'] - elif tree_type == 'GeometryNodeTree': - output_types = ['GROUP_OUTPUT'] + shader_outputs = {'OBJECT': 'ShaderNodeOutputMaterial', + 'WORLD': 'ShaderNodeOutputWorld', + 'LINESTYLE': 'ShaderNodeOutputLineStyle'} + output_type = { + 'ShaderNodeTree': shader_outputs[context.space_data.shader_type], + 'CompositorNodeTree': 'CompositorNodeComposite', + 'TextureNodeTree': 'TextureNodeOutput', + 'GeometryNodeTree': 'NodeGroupOutput', + }[tree_type] for node in nodes: - if node.type in output_types: + # check whether the node is an output node and, + # if supported, whether it's the active one + if node.rna_type.identifier == output_type \ + and (node.is_active_output if hasattr(node, 'is_active_output') + else True): output_node = node break - if not output_node: + else: # No output node exists bpy.ops.node.select_all(action="DESELECT") - if tree_type == 'ShaderNodeTree': - if context.space_data.shader_type == 'OBJECT': - output_node = nodes.new('ShaderNodeOutputMaterial') - elif context.space_data.shader_type == 'WORLD': - output_node = nodes.new('ShaderNodeOutputWorld') - elif tree_type == 'CompositorNodeTree': - output_node = nodes.new('CompositorNodeComposite') - elif tree_type == 'TextureNodeTree': - output_node = nodes.new('TextureNodeOutput') - elif tree_type == 'GeometryNodeTree': - output_node = nodes.new('NodeGroupOutput') + output_node = nodes.new(output_type) output_node.location.x = active.location.x + active.dimensions.x + 80 output_node.location.y = active.location.y - if (output_node and active.outputs): + + if active.outputs: for i, output in enumerate(active.outputs): if is_visible_socket(output): output_index = i @@ -3751,7 +3312,7 @@ class NWLinkToOutputNode(Operator): if tree_type == 'ShaderNodeTree': if active.outputs[output_index].name == 'Volume': out_input_index = 1 - elif active.outputs[output_index].type != 'SHADER': # connect to displacement if not a shader + elif active.outputs[output_index].name == 'Displacement': out_input_index = 2 elif tree_type == 'GeometryNodeTree': if active.outputs[output_index].type != 'GEOMETRY': @@ -4572,391 +4133,17 @@ class NWSwitchNodeTypeMenu(Menu, NWBase): def draw(self, context): layout = self.layout - tree = context.space_data.node_tree - if tree.type == 'SHADER': - layout.menu(NWSwitchShadersInputSubmenu.bl_idname) - layout.menu(NWSwitchShadersOutputSubmenu.bl_idname) - layout.menu(NWSwitchShadersShaderSubmenu.bl_idname) - layout.menu(NWSwitchShadersTextureSubmenu.bl_idname) - layout.menu(NWSwitchShadersColorSubmenu.bl_idname) - layout.menu(NWSwitchShadersVectorSubmenu.bl_idname) - layout.menu(NWSwitchShadersConverterSubmenu.bl_idname) - layout.menu(NWSwitchShadersLayoutSubmenu.bl_idname) - if tree.type == 'COMPOSITING': - layout.menu(NWSwitchCompoInputSubmenu.bl_idname) - layout.menu(NWSwitchCompoOutputSubmenu.bl_idname) - layout.menu(NWSwitchCompoColorSubmenu.bl_idname) - layout.menu(NWSwitchCompoConverterSubmenu.bl_idname) - layout.menu(NWSwitchCompoFilterSubmenu.bl_idname) - layout.menu(NWSwitchCompoVectorSubmenu.bl_idname) - layout.menu(NWSwitchCompoMatteSubmenu.bl_idname) - layout.menu(NWSwitchCompoDistortSubmenu.bl_idname) - layout.menu(NWSwitchCompoLayoutSubmenu.bl_idname) - if tree.type == 'TEXTURE': - layout.menu(NWSwitchTexInputSubmenu.bl_idname) - layout.menu(NWSwitchTexOutputSubmenu.bl_idname) - layout.menu(NWSwitchTexColorSubmenu.bl_idname) - layout.menu(NWSwitchTexPatternSubmenu.bl_idname) - layout.menu(NWSwitchTexTexturesSubmenu.bl_idname) - layout.menu(NWSwitchTexConverterSubmenu.bl_idname) - layout.menu(NWSwitchTexDistortSubmenu.bl_idname) - layout.menu(NWSwitchTexLayoutSubmenu.bl_idname) - if tree.type == 'GEOMETRY': - categories = [c for c in node_categories_iter(context) + categories = [c for c in node_categories_iter(context) if c.name not in ['Group', 'Script']] - for cat in categories: - idname = f"NODE_MT_nw_switch_{cat.identifier}_submenu" - if hasattr(bpy.types, idname): - layout.menu(idname) - else: - layout.label(text="Unable to load altered node lists.") - layout.label(text="Please re-enable Node Wrangler.") - break - - -class NWSwitchShadersInputSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_shaders_input_submenu" - bl_label = "Input" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in shaders_input_nodes_props: - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchShadersOutputSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_shaders_output_submenu" - bl_label = "Output" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in shaders_output_nodes_props: - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchShadersShaderSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_shaders_shader_submenu" - bl_label = "Shader" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in shaders_shader_nodes_props: - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchShadersTextureSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_shaders_texture_submenu" - bl_label = "Texture" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in shaders_texture_nodes_props: - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchShadersColorSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_shaders_color_submenu" - bl_label = "Color" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in shaders_color_nodes_props: - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchShadersVectorSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_shaders_vector_submenu" - bl_label = "Vector" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in shaders_vector_nodes_props: - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchShadersConverterSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_shaders_converter_submenu" - bl_label = "Converter" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in shaders_converter_nodes_props: - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchShadersLayoutSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_shaders_layout_submenu" - bl_label = "Layout" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in shaders_layout_nodes_props: - if node_type != 'FRAME': - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchCompoInputSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_compo_input_submenu" - bl_label = "Input" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in compo_input_nodes_props: - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchCompoOutputSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_compo_output_submenu" - bl_label = "Output" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in compo_output_nodes_props: - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchCompoColorSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_compo_color_submenu" - bl_label = "Color" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in compo_color_nodes_props: - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchCompoConverterSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_compo_converter_submenu" - bl_label = "Converter" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in compo_converter_nodes_props: - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchCompoFilterSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_compo_filter_submenu" - bl_label = "Filter" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in compo_filter_nodes_props: - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchCompoVectorSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_compo_vector_submenu" - bl_label = "Vector" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in compo_vector_nodes_props: - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchCompoMatteSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_compo_matte_submenu" - bl_label = "Matte" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in compo_matte_nodes_props: - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchCompoDistortSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_compo_distort_submenu" - bl_label = "Distort" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in compo_distort_nodes_props: - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchCompoLayoutSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_compo_layout_submenu" - bl_label = "Layout" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in compo_layout_nodes_props: - if node_type != 'FRAME': - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchMatInputSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_mat_input_submenu" - bl_label = "Input" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in sorted(blender_mat_input_nodes_props, key=lambda k: k[2]): - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchMatOutputSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_mat_output_submenu" - bl_label = "Output" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in sorted(blender_mat_output_nodes_props, key=lambda k: k[2]): - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchMatColorSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_mat_color_submenu" - bl_label = "Color" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in sorted(blender_mat_color_nodes_props, key=lambda k: k[2]): - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchMatVectorSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_mat_vector_submenu" - bl_label = "Vector" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in sorted(blender_mat_vector_nodes_props, key=lambda k: k[2]): - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchMatConverterSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_mat_converter_submenu" - bl_label = "Converter" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in sorted(blender_mat_converter_nodes_props, key=lambda k: k[2]): - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchMatLayoutSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_mat_layout_submenu" - bl_label = "Layout" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in sorted(blender_mat_layout_nodes_props, key=lambda k: k[2]): - if node_type != 'FRAME': - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchTexInputSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_tex_input_submenu" - bl_label = "Input" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in sorted(texture_input_nodes_props, key=lambda k: k[2]): - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchTexOutputSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_tex_output_submenu" - bl_label = "Output" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in sorted(texture_output_nodes_props, key=lambda k: k[2]): - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchTexColorSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_tex_color_submenu" - bl_label = "Color" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in sorted(texture_color_nodes_props, key=lambda k: k[2]): - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchTexPatternSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_tex_pattern_submenu" - bl_label = "Pattern" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in sorted(texture_pattern_nodes_props, key=lambda k: k[2]): - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchTexTexturesSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_tex_textures_submenu" - bl_label = "Textures" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in sorted(texture_textures_nodes_props, key=lambda k: k[2]): - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchTexConverterSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_tex_converter_submenu" - bl_label = "Converter" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in sorted(texture_converter_nodes_props, key=lambda k: k[2]): - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchTexDistortSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_tex_distort_submenu" - bl_label = "Distort" - - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in sorted(texture_distort_nodes_props, key=lambda k: k[2]): - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident - - -class NWSwitchTexLayoutSubmenu(Menu, NWBase): - bl_idname = "NODE_MT_nw_switch_tex_layout_submenu" - bl_label = "Layout" + for cat in categories: + idname = f"NODE_MT_nw_switch_{cat.identifier}_submenu" + if hasattr(bpy.types, idname): + layout.menu(idname) + else: + layout.label(text="Unable to load altered node lists.") + layout.label(text="Please re-enable Node Wrangler.") + break - def draw(self, context): - layout = self.layout - for ident, node_type, rna_name in sorted(texture_layout_nodes_props, key=lambda k: k[2]): - if node_type != 'FRAME': - props = layout.operator(NWSwitchNodeType.bl_idname, text=rna_name) - props.to_type = ident def draw_switch_category_submenu(self, context): layout = self.layout @@ -4971,7 +4158,7 @@ def draw_switch_category_submenu(self, context): node.draw(self, layout, context) continue props = layout.operator(NWSwitchNodeType.bl_idname, text=node.label) - props.geo_to_type = node.nodetype + props.to_type = node.nodetype # # APPENDAGES TO EXISTING UI @@ -5015,12 +4202,12 @@ def reset_nodes_button(self, context): node_ignore = ["FRAME","REROUTE", "GROUP"] # Check if active node is in the selection and respective type - if (len(node_selected) == 1) and node_active.select and node_active.type not in node_ignore: + if (len(node_selected) == 1) and node_active and node_active.select and node_active.type not in node_ignore: row = self.layout.row() row.operator("node.nw_reset_nodes", text="Reset Node", icon="FILE_REFRESH") self.layout.separator() - elif (len(node_selected) == 1) and node_active.select and node_active.type == "FRAME": + elif (len(node_selected) == 1) and node_active and node_active.select and node_active.type == "FRAME": row = self.layout.row() row.operator("node.nw_reset_nodes", text="Reset Nodes in Frame", icon="FILE_REFRESH") self.layout.separator() @@ -5274,37 +4461,6 @@ classes = ( NWLinkUseOutputsNamesMenu, NWAttributeMenu, NWSwitchNodeTypeMenu, - NWSwitchShadersInputSubmenu, - NWSwitchShadersOutputSubmenu, - NWSwitchShadersShaderSubmenu, - NWSwitchShadersTextureSubmenu, - NWSwitchShadersColorSubmenu, - NWSwitchShadersVectorSubmenu, - NWSwitchShadersConverterSubmenu, - NWSwitchShadersLayoutSubmenu, - NWSwitchCompoInputSubmenu, - NWSwitchCompoOutputSubmenu, - NWSwitchCompoColorSubmenu, - NWSwitchCompoConverterSubmenu, - NWSwitchCompoFilterSubmenu, - NWSwitchCompoVectorSubmenu, - NWSwitchCompoMatteSubmenu, - NWSwitchCompoDistortSubmenu, - NWSwitchCompoLayoutSubmenu, - NWSwitchMatInputSubmenu, - NWSwitchMatOutputSubmenu, - NWSwitchMatColorSubmenu, - NWSwitchMatVectorSubmenu, - NWSwitchMatConverterSubmenu, - NWSwitchMatLayoutSubmenu, - NWSwitchTexInputSubmenu, - NWSwitchTexOutputSubmenu, - NWSwitchTexColorSubmenu, - NWSwitchTexPatternSubmenu, - NWSwitchTexTexturesSubmenu, - NWSwitchTexConverterSubmenu, - NWSwitchTexDistortSubmenu, - NWSwitchTexLayoutSubmenu, ) def register(): @@ -5361,7 +4517,7 @@ def register(): # switch submenus switch_category_menus.clear() for cat in node_categories_iter(None): - if cat.name not in ['Group', 'Script'] and cat.identifier.startswith('GEO'): + if cat.name not in ['Group', 'Script']: idname = f"NODE_MT_nw_switch_{cat.identifier}_submenu" switch_category_type = type(idname, (bpy.types.Menu,), { "bl_space_type": 'NODE_EDITOR', -- cgit v1.2.3 From f2df91eae2db3a34f8b8f355d5c7a6aa1eef1437 Mon Sep 17 00:00:00 2001 From: Nathan Lovato <12694995+NathanLovato@users.noreply.github.com> Date: Wed, 27 Jul 2022 19:30:39 +0200 Subject: Update power sequencer to work with Blender 3.3 fix: update code to work with sequencer API changes in Blender 3.X fix: Error when calling grab sequence handles due to type mismatch fix: remove FAST and FASTER playback speeds, simplify playback speed code fix: rewrite select_all_left_or_right after API change causing error fix: fix error with jump_to_cut when encountering animation frames fix: fix jump_to_cut skipping some strip ends when moving forward in time fix: jump_to_cut sometimes not working when going left fix: error when trimming effect strips chore: update license text upstream to match Blender's short format --- power_sequencer/__init__.py | 9 ++-- power_sequencer/addon_preferences.py | 5 +- power_sequencer/addon_properties.py | 9 +--- power_sequencer/handlers.py | 40 ++++---------- power_sequencer/operators/__init__.py | 7 +-- power_sequencer/operators/channel_offset.py | 14 ++--- power_sequencer/operators/concatenate_strips.py | 5 +- .../operators/copy_selected_sequences.py | 5 +- power_sequencer/operators/crossfade_add.py | 7 +-- power_sequencer/operators/crossfade_edit.py | 5 +- .../operators/cut_strips_under_cursor.py | 5 +- power_sequencer/operators/delete_direct.py | 5 +- .../operators/deselect_all_left_or_right.py | 5 +- .../operators/deselect_handles_and_grab.py | 5 +- power_sequencer/operators/duplicate_move.py | 5 +- .../operators/expand_to_surrounding_cuts.py | 11 ++-- power_sequencer/operators/fade_add.py | 12 ++--- power_sequencer/operators/fade_clear.py | 11 ++-- power_sequencer/operators/gap_remove.py | 5 +- power_sequencer/operators/grab.py | 5 +- .../operators/grab_closest_handle_or_cut.py | 5 +- power_sequencer/operators/grab_sequence_handles.py | 9 ++-- power_sequencer/operators/import_local_footage.py | 5 +- power_sequencer/operators/jump_time_offset.py | 5 +- power_sequencer/operators/jump_to_cut.py | 62 ++++++++++++---------- power_sequencer/operators/make_hold_frame.py | 5 +- power_sequencer/operators/marker_delete_closest.py | 5 +- power_sequencer/operators/marker_delete_direct.py | 5 +- power_sequencer/operators/marker_snap_to_cursor.py | 5 +- power_sequencer/operators/markers_as_timecodes.py | 5 +- .../operators/markers_create_from_selected.py | 5 +- .../operators/markers_set_preview_in_between.py | 5 +- .../operators/markers_snap_matching_strips.py | 5 +- .../operators/meta_resize_to_content.py | 5 +- .../operators/meta_trim_content_to_bounds.py | 5 +- power_sequencer/operators/meta_ungroup_and_trim.py | 5 +- power_sequencer/operators/mouse_toggle_mute.py | 5 +- power_sequencer/operators/mouse_trim_instantly.py | 5 +- power_sequencer/operators/mouse_trim_modal.py | 7 +-- .../operators/open_project_directory.py | 5 +- .../operators/playback_speed_decrease.py | 52 ------------------ .../operators/playback_speed_increase.py | 52 ------------------ power_sequencer/operators/playback_speed_set.py | 17 ++---- power_sequencer/operators/preview_closest_cut.py | 5 +- power_sequencer/operators/preview_to_selection.py | 5 +- power_sequencer/operators/render_apply_preset.py | 5 +- .../operators/render_presets/twitter_720p.py | 5 +- .../operators/render_presets/youtube_1080.py | 5 +- power_sequencer/operators/ripple_delete.py | 5 +- power_sequencer/operators/save_direct.py | 5 +- .../operators/scene_create_from_selection.py | 5 +- power_sequencer/operators/scene_cycle.py | 5 +- power_sequencer/operators/scene_merge_from.py | 5 +- power_sequencer/operators/scene_open_from_strip.py | 5 +- .../operators/scene_rename_with_strip.py | 5 +- .../operators/select_all_left_or_right.py | 13 +++-- .../operators/select_closest_to_mouse.py | 5 +- power_sequencer/operators/select_linked_effect.py | 5 +- power_sequencer/operators/select_linked_strips.py | 5 +- power_sequencer/operators/select_related_strips.py | 5 +- .../operators/select_strips_under_cursor.py | 5 +- power_sequencer/operators/set_timeline_range.py | 5 +- power_sequencer/operators/snap.py | 11 ++-- power_sequencer/operators/snap_selection.py | 11 ++-- power_sequencer/operators/space_sequences.py | 5 +- power_sequencer/operators/speed_remove_effect.py | 5 +- power_sequencer/operators/speed_up_movie_strip.py | 23 +++++--- power_sequencer/operators/swap_strips.py | 5 +- power_sequencer/operators/toggle_selected_mute.py | 5 +- power_sequencer/operators/toggle_waveforms.py | 5 +- power_sequencer/operators/transitions_remove.py | 5 +- .../operators/trim_left_or_right_handles.py | 5 +- power_sequencer/operators/trim_three_point_edit.py | 5 +- .../operators/trim_to_surrounding_cuts.py | 5 +- power_sequencer/operators/utils/__init__.py | 4 +- power_sequencer/operators/utils/doc.py | 5 +- power_sequencer/operators/utils/draw.py | 5 +- power_sequencer/operators/utils/functions.py | 31 ++++++----- power_sequencer/operators/utils/global_settings.py | 5 +- .../operators/utils/info_progress_bar.py | 5 +- power_sequencer/operators/value_offset.py | 10 ++-- power_sequencer/tools/__init__.py | 7 +-- power_sequencer/tools/trim.py | 5 +- power_sequencer/ui/__init__.py | 5 +- power_sequencer/ui/menu_contextual.py | 5 +- power_sequencer/ui/menu_toolbar.py | 5 +- power_sequencer/utils/addon_auto_imports.py | 5 +- power_sequencer/utils/register_shortcuts.py | 5 +- 88 files changed, 209 insertions(+), 545 deletions(-) delete mode 100644 power_sequencer/operators/playback_speed_decrease.py delete mode 100644 power_sequencer/operators/playback_speed_increase.py diff --git a/power_sequencer/__init__.py b/power_sequencer/__init__.py index c646da7c..e8f920f7 100755 --- a/power_sequencer/__init__.py +++ b/power_sequencer/__init__.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors from typing import List, Tuple, Type import bpy @@ -27,8 +24,8 @@ bl_info = { "name": "Power Sequencer", "description": "Video editing tools for content creators", "author": "Nathan Lovato", - "version": (1, 5, 0), - "blender": (2, 81, 0), + "version": (2, 0, 1), + "blender": (2, 93, 3), "location": "Sequencer", "tracker_url": "https://github.com/GDquest/Blender-power-sequencer/issues", "wiki_url": "https://www.gdquest.com/docs/documentation/power-sequencer/", diff --git a/power_sequencer/addon_preferences.py b/power_sequencer/addon_preferences.py index 4220e89c..cfa21334 100644 --- a/power_sequencer/addon_preferences.py +++ b/power_sequencer/addon_preferences.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors """ Add-on preferences and interface in the Blender preferences window. """ diff --git a/power_sequencer/addon_properties.py b/power_sequencer/addon_properties.py index 575a3954..3e7a4df3 100644 --- a/power_sequencer/addon_properties.py +++ b/power_sequencer/addon_properties.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy @@ -10,8 +7,6 @@ class PowerSequencerProperties(bpy.types.PropertyGroup): playback_speed: bpy.props.EnumProperty( items=[ ("NORMAL", "Normal (1x)", ""), - ("FAST", "Fast (1.33x)", ""), - ("FASTER", "Faster (1.66x)", ""), ("DOUBLE", "Double (2x)", ""), ("TRIPLE", "Triple (3x)", ""), ], @@ -19,8 +14,6 @@ class PowerSequencerProperties(bpy.types.PropertyGroup): default="NORMAL", ) - frame_pre: bpy.props.IntProperty(name="Frame before frame_change", default=0, min=0) - active_tab: bpy.props.StringProperty( name="Active Tab", description="The name of the active tab in the UI", default="Sequencer" ) diff --git a/power_sequencer/handlers.py b/power_sequencer/handlers.py index d9c107da..c743d18b 100644 --- a/power_sequencer/handlers.py +++ b/power_sequencer/handlers.py @@ -1,21 +1,9 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from bpy.app.handlers import persistent -@persistent -def power_sequencer_load_file_post(arg): - """ - Called after loading the blend file - """ - for scene in bpy.data.scenes: - scene.power_sequencer.frame_pre = bpy.context.scene.frame_current - - @persistent def power_sequencer_playback_speed_post(scene): """ @@ -24,27 +12,23 @@ def power_sequencer_playback_speed_post(scene): It steps over frame rather than increase the playback speed smoothly, but it's still useful for faster editing """ + + # Calling this function triggers a callback to this function via the frame + # changed handler, causing a stack overflow. We use a property to prevent + # errors. if bpy.context.screen and not bpy.context.screen.is_animation_playing: return playback_speed = scene.power_sequencer.playback_speed - frame_start = scene.frame_current - frame_post = scene.frame_current - - if playback_speed == "FAST" and frame_start % 3 == 0: - frame_post += 1 - elif playback_speed == "FASTER" and frame_start % 2 == 0: - frame_post += 1 - elif playback_speed == "DOUBLE": - # 2.5x -> skip 5 frames for 2. 2 then 3 then 2 etc. - frame_post += 1 + target_frame = scene.frame_current + if playback_speed == "DOUBLE": + target_frame += 1 elif playback_speed == "TRIPLE": - frame_post += 2 + target_frame += 2 - if frame_start != frame_post: - bpy.ops.screen.frame_offset(delta=frame_post - frame_start) - scene.power_sequencer.frame_pre = scene.frame_current + if target_frame != scene.frame_current: + bpy.ops.screen.frame_offset(delta=target_frame - scene.frame_current) def draw_playback_speed(self, context): @@ -64,7 +48,6 @@ def register_handlers(): bpy.types.SEQUENCER_HT_header.append(draw_playback_speed) # Handlers - bpy.app.handlers.load_post.append(power_sequencer_load_file_post) bpy.app.handlers.frame_change_post.append(power_sequencer_playback_speed_post) @@ -74,5 +57,4 @@ def unregister_handlers(): bpy.types.SEQUENCER_HT_header.remove(draw_playback_speed) # Handlers - bpy.app.handlers.load_post.remove(power_sequencer_load_file_post) bpy.app.handlers.frame_change_post.remove(power_sequencer_playback_speed_post) diff --git a/power_sequencer/operators/__init__.py b/power_sequencer/operators/__init__.py index b1222a18..57dbe2bb 100755 --- a/power_sequencer/operators/__init__.py +++ b/power_sequencer/operators/__init__.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import importlib import os @@ -16,7 +13,7 @@ def get_operator_classes(): module_paths = ["." + os.path.splitext(f)[0] for f in module_files] classes = [] for path in module_paths: - module = importlib.import_module(path, package="power_sequencer.operators") + module = importlib.import_module(path, package=__package__) operator_names = [entry for entry in dir(module) if entry.startswith("POWER_SEQUENCER_OT")] classes.extend([getattr(module, name) for name in operator_names]) return classes diff --git a/power_sequencer/operators/channel_offset.py b/power_sequencer/operators/channel_offset.py index b7569afa..1e9c97f7 100644 --- a/power_sequencer/operators/channel_offset.py +++ b/power_sequencer/operators/channel_offset.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors from operator import attrgetter import bpy @@ -117,12 +114,11 @@ class POWER_SEQUENCER_OT_channel_offset(bpy.types.Operator): context, s.frame_final_start, s.frame_final_end, to_trim, to_delete ) - if not self.keep_selection_offset: - s.channel = comparison_function(limit_channel, s.channel + channel_offset) - if s.channel == limit_channel: - move_selection(context, [s], 0, 0) + s.channel = comparison_function(limit_channel, s.channel + channel_offset) + if s.channel == limit_channel: + move_selection(context, [s], 0, 0) - if self.keep_selection_offset: + if self.keep_selection_offset and not self.trim_target_channel: start_frame = head.frame_final_start x_difference = 0 while not head.channel == limit_channel: diff --git a/power_sequencer/operators/concatenate_strips.py b/power_sequencer/operators/concatenate_strips.py index 32f5f59b..17ff4ee4 100644 --- a/power_sequencer/operators/concatenate_strips.py +++ b/power_sequencer/operators/concatenate_strips.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from operator import attrgetter diff --git a/power_sequencer/operators/copy_selected_sequences.py b/power_sequencer/operators/copy_selected_sequences.py index 643ecb33..338f1cff 100644 --- a/power_sequencer/operators/copy_selected_sequences.py +++ b/power_sequencer/operators/copy_selected_sequences.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from operator import attrgetter diff --git a/power_sequencer/operators/crossfade_add.py b/power_sequencer/operators/crossfade_add.py index 73644c80..25ce7ae7 100644 --- a/power_sequencer/operators/crossfade_add.py +++ b/power_sequencer/operators/crossfade_add.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.functions import find_sequences_after @@ -94,7 +91,7 @@ class POWER_SEQUENCER_OT_crossfade_add(bpy.types.Operator): Moves the handles of the two sequences before adding the crossfade """ fade_duration = convert_duration_to_frames(context, self.crossfade_duration) - fade_offset = fade_duration / 2 + fade_offset = int(fade_duration / 2) if hasattr(sequence_1, "input_1"): sequence_1.input_1.frame_final_end -= fade_offset diff --git a/power_sequencer/operators/crossfade_edit.py b/power_sequencer/operators/crossfade_edit.py index 0e330e10..6d3be976 100644 --- a/power_sequencer/operators/crossfade_edit.py +++ b/power_sequencer/operators/crossfade_edit.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.global_settings import SequenceTypes diff --git a/power_sequencer/operators/cut_strips_under_cursor.py b/power_sequencer/operators/cut_strips_under_cursor.py index 4a66a782..51bf0789 100644 --- a/power_sequencer/operators/cut_strips_under_cursor.py +++ b/power_sequencer/operators/cut_strips_under_cursor.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.doc import doc_name, doc_idname, doc_brief, doc_description diff --git a/power_sequencer/operators/delete_direct.py b/power_sequencer/operators/delete_direct.py index bda5face..1fc8bf60 100644 --- a/power_sequencer/operators/delete_direct.py +++ b/power_sequencer/operators/delete_direct.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.functions import get_mouse_frame_and_channel diff --git a/power_sequencer/operators/deselect_all_left_or_right.py b/power_sequencer/operators/deselect_all_left_or_right.py index 6769d173..3ba79483 100644 --- a/power_sequencer/operators/deselect_all_left_or_right.py +++ b/power_sequencer/operators/deselect_all_left_or_right.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.doc import doc_name, doc_idname, doc_brief, doc_description diff --git a/power_sequencer/operators/deselect_handles_and_grab.py b/power_sequencer/operators/deselect_handles_and_grab.py index 468baf8b..36f547df 100644 --- a/power_sequencer/operators/deselect_handles_and_grab.py +++ b/power_sequencer/operators/deselect_handles_and_grab.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.doc import doc_name, doc_idname, doc_brief, doc_description diff --git a/power_sequencer/operators/duplicate_move.py b/power_sequencer/operators/duplicate_move.py index 32c62a56..cce7a6ed 100644 --- a/power_sequencer/operators/duplicate_move.py +++ b/power_sequencer/operators/duplicate_move.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.functions import get_mouse_frame_and_channel diff --git a/power_sequencer/operators/expand_to_surrounding_cuts.py b/power_sequencer/operators/expand_to_surrounding_cuts.py index 7129e42d..4b8a6f93 100644 --- a/power_sequencer/operators/expand_to_surrounding_cuts.py +++ b/power_sequencer/operators/expand_to_surrounding_cuts.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.functions import slice_selection @@ -22,7 +19,11 @@ class POWER_SEQUENCER_OT_expand_to_surrounding_cuts(bpy.types.Operator): "demo": "", "description": doc_description(__doc__), "shortcuts": [ - ({"type": "E", "value": "PRESS", "ctrl": True}, {}, "Expand to Surrounding Cuts",) + ( + {"type": "E", "value": "PRESS", "ctrl": True}, + {}, + "Expand to Surrounding Cuts", + ) ], "keymap": "Sequencer", } diff --git a/power_sequencer/operators/fade_add.py b/power_sequencer/operators/fade_add.py index 39fe18a9..ab692dba 100644 --- a/power_sequencer/operators/fade_add.py +++ b/power_sequencer/operators/fade_add.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from mathutils import Vector from math import floor @@ -16,7 +13,7 @@ class POWER_SEQUENCER_OT_fade_add(bpy.types.Operator): Fade options: - In, Out, In and Out create a fade animation of the given duration from - the start of the sequence, to the end of the sequence, or on both sides + the start of the sequence, to the end of the sequence, or on boths sides - From playhead: the fade animation goes from the start of sequences under the playhead to the playhead - To playhead: the fade animation goes from the playhead to the end of sequences under the playhead @@ -40,7 +37,10 @@ class POWER_SEQUENCER_OT_fade_add(bpy.types.Operator): bl_options = {"REGISTER", "UNDO"} duration_seconds: bpy.props.FloatProperty( - name="Fade Duration", description="Duration of the fade in seconds", default=1.0, min=0.01, + name="Fade Duration", + description="Duration of the fade in seconds", + default=1.0, + min=0.01, ) type: bpy.props.EnumProperty( items=[ diff --git a/power_sequencer/operators/fade_clear.py b/power_sequencer/operators/fade_clear.py index c74b27cf..d73c7347 100644 --- a/power_sequencer/operators/fade_clear.py +++ b/power_sequencer/operators/fade_clear.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.doc import doc_name, doc_idname, doc_brief, doc_description @@ -21,7 +18,11 @@ class POWER_SEQUENCER_OT_fade_clear(bpy.types.Operator): "demo": "", "description": doc_description(__doc__), "shortcuts": [ - ({"type": "F", "value": "PRESS", "alt": True, "ctrl": True}, {}, "Clear Fades",) + ( + {"type": "F", "value": "PRESS", "alt": True, "ctrl": True}, + {}, + "Clear Fades", + ) ], "keymap": "Sequencer", } diff --git a/power_sequencer/operators/gap_remove.py b/power_sequencer/operators/gap_remove.py index 7f2ec5ae..64070667 100644 --- a/power_sequencer/operators/gap_remove.py +++ b/power_sequencer/operators/gap_remove.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from operator import attrgetter diff --git a/power_sequencer/operators/grab.py b/power_sequencer/operators/grab.py index de53d2a4..57299d72 100644 --- a/power_sequencer/operators/grab.py +++ b/power_sequencer/operators/grab.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.functions import get_mouse_frame_and_channel diff --git a/power_sequencer/operators/grab_closest_handle_or_cut.py b/power_sequencer/operators/grab_closest_handle_or_cut.py index 55440b95..7a7a056d 100644 --- a/power_sequencer/operators/grab_closest_handle_or_cut.py +++ b/power_sequencer/operators/grab_closest_handle_or_cut.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors """ Selects and grabs the strip handle or cut closest to the mouse cursor. Hover near a cut and use this operator to slide it. diff --git a/power_sequencer/operators/grab_sequence_handles.py b/power_sequencer/operators/grab_sequence_handles.py index ecbe8c4b..d8137b0f 100644 --- a/power_sequencer/operators/grab_sequence_handles.py +++ b/power_sequencer/operators/grab_sequence_handles.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.global_settings import SequenceTypes @@ -34,8 +31,8 @@ class POWER_SEQUENCER_OT_grab_sequence_handles(bpy.types.Operator): bl_options = {"REGISTER", "UNDO"} always_find_closest: bpy.props.BoolProperty(name="Always find closest", default=False) - frame: bpy.props.IntProperty(name="Frame", default=-1, options={"HIDDEN"}) - channel: bpy.props.IntProperty(name="Channel", default=-1, options={"HIDDEN"}) + frame: bpy.props.FloatProperty(name="Frame", default=-1.0, options={"HIDDEN"}) + channel: bpy.props.FloatProperty(name="Channel", default=-1.0, options={"HIDDEN"}) @classmethod def poll(cls, context): diff --git a/power_sequencer/operators/import_local_footage.py b/power_sequencer/operators/import_local_footage.py index 8612210a..7eb7120b 100644 --- a/power_sequencer/operators/import_local_footage.py +++ b/power_sequencer/operators/import_local_footage.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import json import os import re diff --git a/power_sequencer/operators/jump_time_offset.py b/power_sequencer/operators/jump_time_offset.py index ee9b1f9b..79941827 100644 --- a/power_sequencer/operators/jump_time_offset.py +++ b/power_sequencer/operators/jump_time_offset.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.functions import convert_duration_to_frames diff --git a/power_sequencer/operators/jump_to_cut.py b/power_sequencer/operators/jump_to_cut.py index add0dab9..02a0b9f9 100644 --- a/power_sequencer/operators/jump_to_cut.py +++ b/power_sequencer/operators/jump_to_cut.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from operator import attrgetter @@ -45,8 +42,8 @@ class POWER_SEQUENCER_OT_jump_to_cut(bpy.types.Operator): name="Direction", description="Jump direction, either forward or backward", items=[ - ("RIGHT", "Forward", "Jump forward in time"), - ("LEFT", "Backward", "Jump backward in time"), + ("RIGHT", "Right", "Jump forward in time"), + ("LEFT", "Left", "Jump backward in time"), ], ) @@ -56,9 +53,6 @@ class POWER_SEQUENCER_OT_jump_to_cut(bpy.types.Operator): def execute(self, context): frame_current = context.scene.frame_current - sorted_sequences = sorted( - context.sequences, key=attrgetter("frame_final_start", "frame_final_end") - ) fcurves = [] animation_data = context.scene.animation_data @@ -66,43 +60,53 @@ class POWER_SEQUENCER_OT_jump_to_cut(bpy.types.Operator): fcurves = animation_data.action.fcurves frame_target = -1 + + # First find the closest cut, then if that sequence has an associated + # fcurve, loop through the curve's keyframes. if self.direction == "RIGHT": - sequences = [s for s in sorted_sequences if s.frame_final_end > frame_current] - for s in sequences: + frame_target = 100_000_000 + for s in context.sequences: + if s.frame_final_end <= frame_current: + continue + + candidates = [frame_target, s.frame_final_end] + if s.frame_final_start > frame_current: + candidates.append(s.frame_final_start) - frame_target = ( - s.frame_final_end - if s.frame_final_start <= frame_current - else s.frame_final_start - ) + frame_target = min(candidates) for f in fcurves: + if s.name not in f.data_path: + continue + for k in f.keyframe_points: frame = k.co[0] - if frame <= context.scene.frame_current: + if frame <= frame_current: continue frame_target = min(frame_target, frame) - break elif self.direction == "LEFT": - sequences = [ - s for s in reversed(sorted_sequences) if s.frame_final_start < frame_current - ] - for s in sequences: + for s in context.sequences: + if s.frame_final_start >= frame_current: + continue + + candidates = [frame_target, s.frame_final_start] + if s.frame_final_end < frame_current: + candidates.append(s.frame_final_end) - frame_target = ( - s.frame_final_start if s.frame_final_end >= frame_current else s.frame_final_end - ) + frame_target = max(candidates) for f in fcurves: + if s.name not in f.data_path: + continue + for k in f.keyframe_points: frame = k.co[0] - if frame >= context.scene.frame_current: + if frame >= frame_current: continue frame_target = max(frame_target, frame) - break - if frame_target != -1: - context.scene.frame_current = max(1, frame_target) + if frame_target > 0 and frame_target != 100_000_000: + context.scene.frame_current = int(frame_target) return {"FINISHED"} diff --git a/power_sequencer/operators/make_hold_frame.py b/power_sequencer/operators/make_hold_frame.py index 4c1f110d..3650a4cb 100644 --- a/power_sequencer/operators/make_hold_frame.py +++ b/power_sequencer/operators/make_hold_frame.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy import operator diff --git a/power_sequencer/operators/marker_delete_closest.py b/power_sequencer/operators/marker_delete_closest.py index a4756958..68659b51 100644 --- a/power_sequencer/operators/marker_delete_closest.py +++ b/power_sequencer/operators/marker_delete_closest.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.doc import doc_name, doc_idname, doc_brief, doc_description diff --git a/power_sequencer/operators/marker_delete_direct.py b/power_sequencer/operators/marker_delete_direct.py index efc8ba66..3b6f9297 100644 --- a/power_sequencer/operators/marker_delete_direct.py +++ b/power_sequencer/operators/marker_delete_direct.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.doc import doc_name, doc_idname, doc_brief, doc_description diff --git a/power_sequencer/operators/marker_snap_to_cursor.py b/power_sequencer/operators/marker_snap_to_cursor.py index dbb3a6c7..8fe80653 100644 --- a/power_sequencer/operators/marker_snap_to_cursor.py +++ b/power_sequencer/operators/marker_snap_to_cursor.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.doc import doc_name, doc_idname, doc_brief, doc_description diff --git a/power_sequencer/operators/markers_as_timecodes.py b/power_sequencer/operators/markers_as_timecodes.py index 52658838..74b42794 100644 --- a/power_sequencer/operators/markers_as_timecodes.py +++ b/power_sequencer/operators/markers_as_timecodes.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy import datetime as dt diff --git a/power_sequencer/operators/markers_create_from_selected.py b/power_sequencer/operators/markers_create_from_selected.py index bd346d84..620995de 100644 --- a/power_sequencer/operators/markers_create_from_selected.py +++ b/power_sequencer/operators/markers_create_from_selected.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.doc import doc_name, doc_idname, doc_brief, doc_description diff --git a/power_sequencer/operators/markers_set_preview_in_between.py b/power_sequencer/operators/markers_set_preview_in_between.py index 60724312..8ac0e9f6 100644 --- a/power_sequencer/operators/markers_set_preview_in_between.py +++ b/power_sequencer/operators/markers_set_preview_in_between.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.functions import find_neighboring_markers diff --git a/power_sequencer/operators/markers_snap_matching_strips.py b/power_sequencer/operators/markers_snap_matching_strips.py index da475eeb..b9eab7b4 100644 --- a/power_sequencer/operators/markers_snap_matching_strips.py +++ b/power_sequencer/operators/markers_snap_matching_strips.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.doc import doc_name, doc_idname, doc_brief, doc_description diff --git a/power_sequencer/operators/meta_resize_to_content.py b/power_sequencer/operators/meta_resize_to_content.py index ed54ebca..b5d0b29f 100644 --- a/power_sequencer/operators/meta_resize_to_content.py +++ b/power_sequencer/operators/meta_resize_to_content.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.functions import get_frame_range diff --git a/power_sequencer/operators/meta_trim_content_to_bounds.py b/power_sequencer/operators/meta_trim_content_to_bounds.py index 11b48369..e309f8e1 100644 --- a/power_sequencer/operators/meta_trim_content_to_bounds.py +++ b/power_sequencer/operators/meta_trim_content_to_bounds.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.global_settings import SequenceTypes diff --git a/power_sequencer/operators/meta_ungroup_and_trim.py b/power_sequencer/operators/meta_ungroup_and_trim.py index 00c79cf4..e467492d 100644 --- a/power_sequencer/operators/meta_ungroup_and_trim.py +++ b/power_sequencer/operators/meta_ungroup_and_trim.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.doc import doc_name, doc_idname, doc_brief, doc_description diff --git a/power_sequencer/operators/mouse_toggle_mute.py b/power_sequencer/operators/mouse_toggle_mute.py index 362b2e9d..cd135829 100644 --- a/power_sequencer/operators/mouse_toggle_mute.py +++ b/power_sequencer/operators/mouse_toggle_mute.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors """Toggle mute a sequence as you click on it""" import bpy from math import floor diff --git a/power_sequencer/operators/mouse_trim_instantly.py b/power_sequencer/operators/mouse_trim_instantly.py index 332053fc..3a79a5a7 100644 --- a/power_sequencer/operators/mouse_trim_instantly.py +++ b/power_sequencer/operators/mouse_trim_instantly.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from math import floor diff --git a/power_sequencer/operators/mouse_trim_modal.py b/power_sequencer/operators/mouse_trim_modal.py index a138933b..93ca1346 100644 --- a/power_sequencer/operators/mouse_trim_modal.py +++ b/power_sequencer/operators/mouse_trim_modal.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy import bgl import gpu @@ -35,7 +32,7 @@ class POWER_SEQUENCER_OT_mouse_trim(bpy.types.Operator): *brief* Cut or Trim strips quickly with the mouse cursor - Click somewhere in the Sequencer to insert a cut, click and drag to trim + Click somehwere in the Sequencer to insert a cut, click and drag to trim With this function you can quickly cut and remove a section of strips while keeping or collapsing the remaining gap. Press Ctrl to snap to cuts. diff --git a/power_sequencer/operators/open_project_directory.py b/power_sequencer/operators/open_project_directory.py index f32f2ac2..02c72de1 100644 --- a/power_sequencer/operators/open_project_directory.py +++ b/power_sequencer/operators/open_project_directory.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy import os from platform import system diff --git a/power_sequencer/operators/playback_speed_decrease.py b/power_sequencer/operators/playback_speed_decrease.py deleted file mode 100644 index a856ac4b..00000000 --- a/power_sequencer/operators/playback_speed_decrease.py +++ /dev/null @@ -1,52 +0,0 @@ -# SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - -import bpy - -from .utils.doc import doc_name, doc_idname, doc_brief, doc_description - - -class POWER_SEQUENCER_OT_playback_speed_decrease(bpy.types.Operator): - """ - *brief* Decrease playback speed incrementally down to normal - - - Playback speed may be set to any of the following speeds: - - * Normal (1x) - * Fast (1.33x) - * Faster (1.66x) - * Double (2x) - * Triple (3x) - - Activating this operator will decrease playback speed through each - of these steps until minimum speed is reached. - """ - - doc = { - "name": doc_name(__qualname__), - "demo": "", - "description": doc_description(__doc__), - "shortcuts": [({"type": "COMMA", "value": "PRESS"}, {}, "Decrease Playback Speed")], - "keymap": "Sequencer", - } - bl_idname = doc_idname(__qualname__) - bl_label = doc["name"] - bl_description = doc_brief(doc["description"]) - - @classmethod - def poll(cls, context): - return context.sequences - - def execute(self, context): - scene = context.scene - - speeds = ["NORMAL", "FAST", "FASTER", "DOUBLE", "TRIPLE"] - playback_speed = scene.power_sequencer.playback_speed - - index = max(0, speeds.index(playback_speed) - 1) - scene.power_sequencer.playback_speed = speeds[index] - - return {"FINISHED"} diff --git a/power_sequencer/operators/playback_speed_increase.py b/power_sequencer/operators/playback_speed_increase.py deleted file mode 100644 index 7c2f2ad2..00000000 --- a/power_sequencer/operators/playback_speed_increase.py +++ /dev/null @@ -1,52 +0,0 @@ -# SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - -import bpy - -from .utils.doc import doc_name, doc_idname, doc_brief, doc_description - - -class POWER_SEQUENCER_OT_playback_speed_increase(bpy.types.Operator): - """ - *brief* Increase playback speed up to triple - - - Playback speed may be set to any of the following speeds: - - * Normal (1x) - * Fast (1.33x) - * Faster (1.66x) - * Double (2x) - * Triple (3x) - - Activating this operator will increase playback speed through each - of these steps until maximum speed is reached. - """ - - doc = { - "name": doc_name(__qualname__), - "demo": "", - "description": doc_description(__doc__), - "shortcuts": [({"type": "PERIOD", "value": "PRESS"}, {}, "Increase playback speed")], - "keymap": "Sequencer", - } - bl_idname = doc_idname(__qualname__) - bl_label = doc["name"] - bl_description = doc_brief(doc["description"]) - - @classmethod - def poll(cls, context): - return context.sequences - - def execute(self, context): - scene = context.scene - - speeds = ["NORMAL", "FAST", "FASTER", "DOUBLE", "TRIPLE"] - playback_speed = scene.power_sequencer.playback_speed - - index = min(speeds.index(playback_speed) + 1, len(speeds) - 1) - scene.power_sequencer.playback_speed = speeds[index] - - return {"FINISHED"} diff --git a/power_sequencer/operators/playback_speed_set.py b/power_sequencer/operators/playback_speed_set.py index 9f576247..82c45b7a 100644 --- a/power_sequencer/operators/playback_speed_set.py +++ b/power_sequencer/operators/playback_speed_set.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.doc import doc_name, doc_idname, doc_brief, doc_description @@ -19,14 +16,8 @@ class POWER_SEQUENCER_OT_playback_speed_set(bpy.types.Operator): "description": doc_description(__doc__), "shortcuts": [ ({"type": "ONE", "ctrl": True, "value": "PRESS"}, {"speed": "NORMAL"}, "Speed to 1x"), - ({"type": "TWO", "ctrl": True, "value": "PRESS"}, {"speed": "FAST"}, "Speed to 1.33x"), - ( - {"type": "THREE", "ctrl": True, "value": "PRESS"}, - {"speed": "FASTER"}, - "Speed to 1.66x", - ), - ({"type": "FOUR", "ctrl": True, "value": "PRESS"}, {"speed": "DOUBLE"}, "Speed to 2x"), - ({"type": "FIVE", "ctrl": True, "value": "PRESS"}, {"speed": "TRIPLE"}, "Speed to 3x"), + ({"type": "TWO", "ctrl": True, "value": "PRESS"}, {"speed": "DOUBLE"}, "Speed to 2x"), + ({"type": "THREE", "ctrl": True, "value": "PRESS"}, {"speed": "TRIPLE"}, "Speed to 3x"), ], "keymap": "Sequencer", } @@ -38,8 +29,6 @@ class POWER_SEQUENCER_OT_playback_speed_set(bpy.types.Operator): speed: bpy.props.EnumProperty( items=[ ("NORMAL", "Normal (1x)", ""), - ("FAST", "Fast (1.33x)", ""), - ("FASTER", "Faster (1.66x)", ""), ("DOUBLE", "Double (2x)", ""), ("TRIPLE", "Triple (3x)", ""), ], diff --git a/power_sequencer/operators/preview_closest_cut.py b/power_sequencer/operators/preview_closest_cut.py index 312be788..2375c21a 100644 --- a/power_sequencer/operators/preview_closest_cut.py +++ b/power_sequencer/operators/preview_closest_cut.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.functions import get_frame_range diff --git a/power_sequencer/operators/preview_to_selection.py b/power_sequencer/operators/preview_to_selection.py index ea6c0ed7..7ad8e2c7 100644 --- a/power_sequencer/operators/preview_to_selection.py +++ b/power_sequencer/operators/preview_to_selection.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.functions import get_frame_range diff --git a/power_sequencer/operators/render_apply_preset.py b/power_sequencer/operators/render_apply_preset.py index dc3b0ed1..d7ad5b38 100644 --- a/power_sequencer/operators/render_apply_preset.py +++ b/power_sequencer/operators/render_apply_preset.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy import os diff --git a/power_sequencer/operators/render_presets/twitter_720p.py b/power_sequencer/operators/render_presets/twitter_720p.py index 109090cc..eadb4266 100644 --- a/power_sequencer/operators/render_presets/twitter_720p.py +++ b/power_sequencer/operators/render_presets/twitter_720p.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors if __name__ == "__main__": import bpy diff --git a/power_sequencer/operators/render_presets/youtube_1080.py b/power_sequencer/operators/render_presets/youtube_1080.py index 5bf6e826..61bab65d 100644 --- a/power_sequencer/operators/render_presets/youtube_1080.py +++ b/power_sequencer/operators/render_presets/youtube_1080.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors if __name__ == "__main__": import bpy diff --git a/power_sequencer/operators/ripple_delete.py b/power_sequencer/operators/ripple_delete.py index 939da4e0..515f318f 100644 --- a/power_sequencer/operators/ripple_delete.py +++ b/power_sequencer/operators/ripple_delete.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.doc import doc_brief, doc_description, doc_idname, doc_name diff --git a/power_sequencer/operators/save_direct.py b/power_sequencer/operators/save_direct.py index f41c3d4f..09b130a3 100644 --- a/power_sequencer/operators/save_direct.py +++ b/power_sequencer/operators/save_direct.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.doc import doc_name, doc_idname, doc_brief, doc_description diff --git a/power_sequencer/operators/scene_create_from_selection.py b/power_sequencer/operators/scene_create_from_selection.py index 206e385a..15ed0237 100644 --- a/power_sequencer/operators/scene_create_from_selection.py +++ b/power_sequencer/operators/scene_create_from_selection.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from operator import attrgetter diff --git a/power_sequencer/operators/scene_cycle.py b/power_sequencer/operators/scene_cycle.py index 1be39ab9..05b861e7 100644 --- a/power_sequencer/operators/scene_cycle.py +++ b/power_sequencer/operators/scene_cycle.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.doc import doc_name, doc_idname, doc_brief, doc_description diff --git a/power_sequencer/operators/scene_merge_from.py b/power_sequencer/operators/scene_merge_from.py index b411e039..9d790baf 100644 --- a/power_sequencer/operators/scene_merge_from.py +++ b/power_sequencer/operators/scene_merge_from.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.doc import doc_name, doc_idname, doc_brief, doc_description diff --git a/power_sequencer/operators/scene_open_from_strip.py b/power_sequencer/operators/scene_open_from_strip.py index 0b992356..8c868bc3 100644 --- a/power_sequencer/operators/scene_open_from_strip.py +++ b/power_sequencer/operators/scene_open_from_strip.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.doc import doc_name, doc_idname, doc_brief, doc_description diff --git a/power_sequencer/operators/scene_rename_with_strip.py b/power_sequencer/operators/scene_rename_with_strip.py index 8bc6432f..b5327439 100644 --- a/power_sequencer/operators/scene_rename_with_strip.py +++ b/power_sequencer/operators/scene_rename_with_strip.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.doc import doc_name, doc_idname, doc_brief, doc_description diff --git a/power_sequencer/operators/select_all_left_or_right.py b/power_sequencer/operators/select_all_left_or_right.py index 7d9087ee..794a9378 100644 --- a/power_sequencer/operators/select_all_left_or_right.py +++ b/power_sequencer/operators/select_all_left_or_right.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.doc import doc_name, doc_idname, doc_brief, doc_description @@ -51,4 +48,10 @@ class POWER_SEQUENCER_OT_select_all_left_or_right(bpy.types.Operator): return context.sequences def execute(self, context): - return bpy.ops.sequencer.select("INVOKE_DEFAULT", left_right=self.side) + if self.side == "LEFT": + for s in context.sequences: + s.select = s.frame_final_end < context.scene.frame_current + else: + for s in context.sequences: + s.select = s.frame_final_start > context.scene.frame_current + return {"FINISHED"} diff --git a/power_sequencer/operators/select_closest_to_mouse.py b/power_sequencer/operators/select_closest_to_mouse.py index f14bc309..dee7e967 100644 --- a/power_sequencer/operators/select_closest_to_mouse.py +++ b/power_sequencer/operators/select_closest_to_mouse.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.functions import find_strips_mouse diff --git a/power_sequencer/operators/select_linked_effect.py b/power_sequencer/operators/select_linked_effect.py index a63096f1..d00a7a4a 100644 --- a/power_sequencer/operators/select_linked_effect.py +++ b/power_sequencer/operators/select_linked_effect.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.functions import find_linked diff --git a/power_sequencer/operators/select_linked_strips.py b/power_sequencer/operators/select_linked_strips.py index 5716d13d..29022257 100644 --- a/power_sequencer/operators/select_linked_strips.py +++ b/power_sequencer/operators/select_linked_strips.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.doc import doc_name, doc_idname, doc_brief, doc_description diff --git a/power_sequencer/operators/select_related_strips.py b/power_sequencer/operators/select_related_strips.py index 0e442ebd..e7101fee 100644 --- a/power_sequencer/operators/select_related_strips.py +++ b/power_sequencer/operators/select_related_strips.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.global_settings import SequenceTypes diff --git a/power_sequencer/operators/select_strips_under_cursor.py b/power_sequencer/operators/select_strips_under_cursor.py index a47d6e80..0c4712b5 100644 --- a/power_sequencer/operators/select_strips_under_cursor.py +++ b/power_sequencer/operators/select_strips_under_cursor.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.functions import get_sequences_under_cursor diff --git a/power_sequencer/operators/set_timeline_range.py b/power_sequencer/operators/set_timeline_range.py index 65a90f4d..a9bbd163 100644 --- a/power_sequencer/operators/set_timeline_range.py +++ b/power_sequencer/operators/set_timeline_range.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.doc import doc_name, doc_idname, doc_brief, doc_description diff --git a/power_sequencer/operators/snap.py b/power_sequencer/operators/snap.py index 793f5bd9..24a4f52a 100644 --- a/power_sequencer/operators/snap.py +++ b/power_sequencer/operators/snap.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.functions import get_sequences_under_cursor @@ -21,7 +18,11 @@ class POWER_SEQUENCER_OT_snap(bpy.types.Operator): "demo": "", "description": doc_description(__doc__), "shortcuts": [ - ({"type": "S", "value": "PRESS", "shift": True}, {}, "Snap sequences to cursor",) + ( + {"type": "S", "value": "PRESS", "shift": True}, + {}, + "Snap sequences to cursor", + ) ], "keymap": "Sequencer", } diff --git a/power_sequencer/operators/snap_selection.py b/power_sequencer/operators/snap_selection.py index 38da4c17..b73c5561 100644 --- a/power_sequencer/operators/snap_selection.py +++ b/power_sequencer/operators/snap_selection.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.functions import get_sequences_under_cursor, move_selection from .utils.doc import doc_name, doc_idname, doc_brief, doc_description @@ -21,7 +18,11 @@ class POWER_SEQUENCER_OT_snap_selection(bpy.types.Operator): "demo": "", "description": doc_description(__doc__), "shortcuts": [ - ({"type": "S", "value": "PRESS", "alt": True}, {}, "Snap selection to cursor",) + ( + {"type": "S", "value": "PRESS", "alt": True}, + {}, + "Snap selection to cursor", + ) ], "keymap": "Sequencer", } diff --git a/power_sequencer/operators/space_sequences.py b/power_sequencer/operators/space_sequences.py index ddb28dfe..da3a0ac5 100644 --- a/power_sequencer/operators/space_sequences.py +++ b/power_sequencer/operators/space_sequences.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.functions import convert_duration_to_frames diff --git a/power_sequencer/operators/speed_remove_effect.py b/power_sequencer/operators/speed_remove_effect.py index ae2a4d35..69348461 100644 --- a/power_sequencer/operators/speed_remove_effect.py +++ b/power_sequencer/operators/speed_remove_effect.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.doc import doc_name, doc_idname, doc_brief, doc_description diff --git a/power_sequencer/operators/speed_up_movie_strip.py b/power_sequencer/operators/speed_up_movie_strip.py index c004e2fc..9e863f36 100644 --- a/power_sequencer/operators/speed_up_movie_strip.py +++ b/power_sequencer/operators/speed_up_movie_strip.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from math import ceil @@ -24,9 +21,21 @@ class POWER_SEQUENCER_OT_speed_up_movie_strip(bpy.types.Operator): "demo": "https://i.imgur.com/ZyEd0jD.gif", "description": doc_description(__doc__), "shortcuts": [ - ({"type": "TWO", "value": "PRESS", "alt": True}, {"speed_factor": 2.0}, "Speed x2",), - ({"type": "THREE", "value": "PRESS", "alt": True}, {"speed_factor": 3.0}, "Speed x3",), - ({"type": "FOUR", "value": "PRESS", "alt": True}, {"speed_factor": 4.0}, "Speed x4",), + ( + {"type": "TWO", "value": "PRESS", "alt": True}, + {"speed_factor": 2.0}, + "Speed x2", + ), + ( + {"type": "THREE", "value": "PRESS", "alt": True}, + {"speed_factor": 3.0}, + "Speed x3", + ), + ( + {"type": "FOUR", "value": "PRESS", "alt": True}, + {"speed_factor": 4.0}, + "Speed x4", + ), ], "keymap": "Sequencer", } diff --git a/power_sequencer/operators/swap_strips.py b/power_sequencer/operators/swap_strips.py index 44537c43..6b0460fa 100644 --- a/power_sequencer/operators/swap_strips.py +++ b/power_sequencer/operators/swap_strips.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from operator import attrgetter diff --git a/power_sequencer/operators/toggle_selected_mute.py b/power_sequencer/operators/toggle_selected_mute.py index 3ee1a022..1a79237f 100644 --- a/power_sequencer/operators/toggle_selected_mute.py +++ b/power_sequencer/operators/toggle_selected_mute.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.doc import doc_name, doc_idname, doc_brief, doc_description diff --git a/power_sequencer/operators/toggle_waveforms.py b/power_sequencer/operators/toggle_waveforms.py index 3e064182..12104493 100644 --- a/power_sequencer/operators/toggle_waveforms.py +++ b/power_sequencer/operators/toggle_waveforms.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.global_settings import SequenceTypes diff --git a/power_sequencer/operators/transitions_remove.py b/power_sequencer/operators/transitions_remove.py index d1db0d74..1fb1b0d0 100644 --- a/power_sequencer/operators/transitions_remove.py +++ b/power_sequencer/operators/transitions_remove.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from operator import attrgetter diff --git a/power_sequencer/operators/trim_left_or_right_handles.py b/power_sequencer/operators/trim_left_or_right_handles.py index aea81610..c02b8650 100644 --- a/power_sequencer/operators/trim_left_or_right_handles.py +++ b/power_sequencer/operators/trim_left_or_right_handles.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors from operator import attrgetter import bpy diff --git a/power_sequencer/operators/trim_three_point_edit.py b/power_sequencer/operators/trim_three_point_edit.py index f174589e..6d173258 100644 --- a/power_sequencer/operators/trim_three_point_edit.py +++ b/power_sequencer/operators/trim_three_point_edit.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.functions import get_mouse_frame_and_channel diff --git a/power_sequencer/operators/trim_to_surrounding_cuts.py b/power_sequencer/operators/trim_to_surrounding_cuts.py index ab5df939..3057dfe5 100644 --- a/power_sequencer/operators/trim_to_surrounding_cuts.py +++ b/power_sequencer/operators/trim_to_surrounding_cuts.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors """ Find the two closest cuts, trims and deletes all strips above in the range but leaves some margin. Removes the newly formed gap. diff --git a/power_sequencer/operators/utils/__init__.py b/power_sequencer/operators/utils/__init__.py index 71fca8c4..be8d8b53 100644 --- a/power_sequencer/operators/utils/__init__.py +++ b/power_sequencer/operators/utils/__init__.py @@ -1,4 +1,2 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors diff --git a/power_sequencer/operators/utils/doc.py b/power_sequencer/operators/utils/doc.py index 8bdae410..be52c7c2 100644 --- a/power_sequencer/operators/utils/doc.py +++ b/power_sequencer/operators/utils/doc.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors """ Utilities to convert operator names and docstrings to human-readable text. Used to generate names for Blender's operator search, and to generate Power Sequencer's documentation. diff --git a/power_sequencer/operators/utils/draw.py b/power_sequencer/operators/utils/draw.py index 5a156bea..3cede778 100644 --- a/power_sequencer/operators/utils/draw.py +++ b/power_sequencer/operators/utils/draw.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors """Drawing utilities. A list of functions to draw common elements""" # import bgl import blf diff --git a/power_sequencer/operators/utils/functions.py b/power_sequencer/operators/utils/functions.py index 0d5c9fcf..c4552c2d 100644 --- a/power_sequencer/operators/utils/functions.py +++ b/power_sequencer/operators/utils/functions.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors from math import floor, sqrt from operator import attrgetter @@ -279,9 +276,7 @@ def trim_strips(context, frame_start, frame_end, to_trim, to_delete=[]): elif s.frame_final_end > trim_start and s.frame_final_start < trim_start: s.frame_final_end = trim_start - for s in to_delete: - bpy.context.sequences.remove(s) - + delete_strips(to_delete) for s in initial_selection: s.select = True return {"FINISHED"} @@ -331,7 +326,8 @@ def get_sequences_under_cursor(context): def ripple_move(context, sequences, duration_frames, delete=False): - """Moves sequences in the list and ripples the change to all sequences after them, in the corresponding channels + """ + Moves sequences in the list and ripples the change to all sequences after them, in the corresponding channels The `duration_frames` can be positive or negative. If `delete` is True, deletes every sequence in `sequences`. """ @@ -344,10 +340,7 @@ def ripple_move(context, sequences, duration_frames, delete=False): ] if delete: - bpy.ops.sequencer.select_all(action="DESELECT") - for s in sequences: - s.select = True - bpy.ops.sequencer.delete() + delete_strips(sequences) else: to_ripple = set(to_ripple + sequences) @@ -390,8 +383,20 @@ def find_strips_in_range(frame_start, frame_end, sequences, find_overlapping=Tru return strips_inside_range, strips_overlapping_range +def delete_strips(to_delete): + """ + Deletes the list of sequences `to_delete` + """ + # Effect strips get deleted with their source so we skip them to avoid errors. + to_delete = [s for s in to_delete if s.type in SequenceTypes.CUTABLE] + sequences = bpy.context.scene.sequence_editor.sequences + for s in to_delete: + sequences.remove(s) + + def move_selection(context, sequences, frame_offset, channel_offset=0): - """Offsets the selected `sequences` horizontally and vertically and preserves + """ + Offsets the selected `sequences` horizontally and vertically and preserves the current selected sequences. """ if not sequences: diff --git a/power_sequencer/operators/utils/global_settings.py b/power_sequencer/operators/utils/global_settings.py index a5c9756b..6c5ed55a 100644 --- a/power_sequencer/operators/utils/global_settings.py +++ b/power_sequencer/operators/utils/global_settings.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors class ProjectSettings: RESOLUTION_X = 1920 RESOLUTION_Y = 1080 diff --git a/power_sequencer/operators/utils/info_progress_bar.py b/power_sequencer/operators/utils/info_progress_bar.py index e645c762..e479aecf 100644 --- a/power_sequencer/operators/utils/info_progress_bar.py +++ b/power_sequencer/operators/utils/info_progress_bar.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy diff --git a/power_sequencer/operators/value_offset.py b/power_sequencer/operators/value_offset.py index 919e8a43..f7ea129f 100644 --- a/power_sequencer/operators/value_offset.py +++ b/power_sequencer/operators/value_offset.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .utils.doc import doc_brief, doc_description, doc_idname, doc_name @@ -10,8 +7,9 @@ from .utils.functions import convert_duration_to_frames class POWER_SEQUENCER_OT_value_offset(bpy.types.Operator): - """Instantly offset selected strips, either using frames or seconds. """ \ - """Allows to nudge the selection quickly, using keyboard shortcuts""" + """Instantly offset selected strips, either using frames or seconds. Allows to + nudge the selection quickly, using keyboard shortcuts. + """ doc = { "name": doc_name(__qualname__), diff --git a/power_sequencer/tools/__init__.py b/power_sequencer/tools/__init__.py index e7ac0ddc..6c24a950 100644 --- a/power_sequencer/tools/__init__.py +++ b/power_sequencer/tools/__init__.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import importlib import os @@ -16,7 +13,7 @@ def get_tool_classes(): module_paths = ["." + os.path.splitext(f)[0] for f in module_files] classes = [] for path in module_paths: - module = importlib.import_module(path, package="power_sequencer.tools") + module = importlib.import_module(path, package=__package__) tool_names = [entry for entry in dir(module) if entry.startswith("POWER_SEQUENCER_TOOL")] classes.extend([getattr(module, name) for name in tool_names]) return classes diff --git a/power_sequencer/tools/trim.py b/power_sequencer/tools/trim.py index 9c60b2d0..7b740828 100644 --- a/power_sequencer/tools/trim.py +++ b/power_sequencer/tools/trim.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from bpy.types import WorkSpaceTool diff --git a/power_sequencer/ui/__init__.py b/power_sequencer/ui/__init__.py index 9b5b5c6b..b81bdea2 100644 --- a/power_sequencer/ui/__init__.py +++ b/power_sequencer/ui/__init__.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from .menu_contextual import POWER_SEQUENCER_MT_contextual from .menu_toolbar import ( diff --git a/power_sequencer/ui/menu_contextual.py b/power_sequencer/ui/menu_contextual.py index 41628448..56b1b33a 100644 --- a/power_sequencer/ui/menu_contextual.py +++ b/power_sequencer/ui/menu_contextual.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy from ..operators.utils.global_settings import SequenceTypes diff --git a/power_sequencer/ui/menu_toolbar.py b/power_sequencer/ui/menu_toolbar.py index 4feb1ea7..6372f139 100755 --- a/power_sequencer/ui/menu_toolbar.py +++ b/power_sequencer/ui/menu_toolbar.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy diff --git a/power_sequencer/utils/addon_auto_imports.py b/power_sequencer/utils/addon_auto_imports.py index 47f430b9..21a45615 100644 --- a/power_sequencer/utils/addon_auto_imports.py +++ b/power_sequencer/utils/addon_auto_imports.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import pkgutil import importlib diff --git a/power_sequencer/utils/register_shortcuts.py b/power_sequencer/utils/register_shortcuts.py index f928f7ff..29694cee 100644 --- a/power_sequencer/utils/register_shortcuts.py +++ b/power_sequencer/utils/register_shortcuts.py @@ -1,8 +1,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors - -# This file is part of Power Sequencer. - +# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy import operator as op from .. import operators -- cgit v1.2.3 From 880284a41e843ebe751e005a046586ba6718681b Mon Sep 17 00:00:00 2001 From: Damien Picard Date: Thu, 28 Jul 2022 15:32:02 +0200 Subject: Add camera rigs: use bone subtarget for DOF We no longer need the Empty workaround since rB2e70d5cb980e Also fix UI error when Track To constraint was renamed Differential Revision: https://developer.blender.org/D15471 --- add_camera_rigs/__init__.py | 4 ++-- add_camera_rigs/operators.py | 28 +++++++--------------------- add_camera_rigs/ui_panels.py | 33 +++++++++++++++++++++------------ 3 files changed, 30 insertions(+), 35 deletions(-) diff --git a/add_camera_rigs/__init__.py b/add_camera_rigs/__init__.py index 878d4555..cbf83d95 100644 --- a/add_camera_rigs/__init__.py +++ b/add_camera_rigs/__init__.py @@ -3,8 +3,8 @@ bl_info = { "name": "Add Camera Rigs", "author": "Wayne Dixon, Brian Raschko, Kris Wittig, Damien Picard, Flavio Perez", - "version": (1, 4, 4), - "blender": (2, 80, 0), + "version": (1, 5, 0), + "blender": (3, 3, 0), "location": "View3D > Add > Camera > Dolly or Crane Rig", "description": "Adds a Camera Rig with UI", "doc_url": "{BLENDER_MANUAL_URL}/addons/camera/camera_rigs.html", diff --git a/add_camera_rigs/operators.py b/add_camera_rigs/operators.py index 18300e08..29e77e3b 100644 --- a/add_camera_rigs/operators.py +++ b/add_camera_rigs/operators.py @@ -73,30 +73,16 @@ class ADD_CAMERA_RIGS_OT_add_marker_bind(Operator, CameraRigMixin): return {'FINISHED'} -class ADD_CAMERA_RIGS_OT_add_dof_object(Operator, CameraRigMixin): - bl_idname = "add_camera_rigs.add_dof_object" - bl_label = "Add DOF Object" - bl_description = "Create Empty and add as DOF Object" +class ADD_CAMERA_RIGS_OT_set_dof_bone(Operator, CameraRigMixin): + bl_idname = "add_camera_rigs.set_dof_bone" + bl_label = "Set DOF Bone" + bl_description = "Set the Aim bone as a DOF target" def execute(self, context): rig, cam = get_rig_and_cam(context.active_object) - bone = rig.data.bones['Aim_shape_rotation-MCH'] - # Add Empty - empty_obj = bpy.data.objects.new("EmptyDOF", None) - context.scene.collection.objects.link(empty_obj) - - # Parent to Aim Child bone - empty_obj.parent = rig - empty_obj.parent_type = "BONE" - empty_obj.parent_bone = "Aim_shape_rotation-MCH" - - # Move to bone head - empty_obj.location = bone.head - - # Make this new empty the dof_object - cam.data.dof.use_dof = True - cam.data.dof.focus_object = empty_obj + cam.data.dof.focus_object = rig + cam.data.dof.focus_subtarget = 'Aim_shape_rotation-MCH' return {'FINISHED'} @@ -104,7 +90,7 @@ class ADD_CAMERA_RIGS_OT_add_dof_object(Operator, CameraRigMixin): classes = ( ADD_CAMERA_RIGS_OT_set_scene_camera, ADD_CAMERA_RIGS_OT_add_marker_bind, - ADD_CAMERA_RIGS_OT_add_dof_object, + ADD_CAMERA_RIGS_OT_set_dof_bone, ) diff --git a/add_camera_rigs/ui_panels.py b/add_camera_rigs/ui_panels.py index c6066147..5d545d3a 100644 --- a/add_camera_rigs/ui_panels.py +++ b/add_camera_rigs/ui_panels.py @@ -32,20 +32,23 @@ class ADD_CAMERA_RIGS_PT_camera_rig_ui(Panel, CameraRigMixin): layout.prop(cam_data, "type") # DoF - col = layout.column(align=True) + col = layout.column(align=False) col.prop(cam_data.dof, "use_dof") if cam_data.dof.use_dof: - if rig["rig_id"].lower() in ("crane_rig", "dolly_rig"): - if cam_data.dof.focus_object is None: - col.operator("add_camera_rigs.add_dof_object", - text="Add DOF Empty", icon="OUTLINER_OB_EMPTY") - else: - col.prop(cam_data.dof, "focus_object") - row = col.row(align=True) + sub = col.column(align=True) + if cam_data.dof.focus_object is None: + sub.operator("add_camera_rigs.set_dof_bone") + sub.prop(cam_data.dof, "focus_object") + if (cam_data.dof.focus_object is not None + and cam_data.dof.focus_object.type == 'ARMATURE'): + sub.prop_search(cam_data.dof, "focus_subtarget", + cam_data.dof.focus_object.data, "bones") + sub = col.column(align=True) + row = sub.row(align=True) row.active = cam_data.dof.focus_object is None row.prop(pose_bones["Camera"], '["focus_distance"]', text="Focus Distance") - col.prop(pose_bones["Camera"], + sub.prop(pose_bones["Camera"], '["aperture_fstop"]', text="F-Stop") # Viewport display @@ -74,9 +77,15 @@ class ADD_CAMERA_RIGS_PT_camera_rig_ui(Panel, CameraRigMixin): if rig["rig_id"].lower() in ("dolly_rig", "crane_rig"): # Track to Constraint col = layout.column(align=True) - col.label(text="Tracking:") - col.prop(pose_bones["Camera"].constraints["Track To"], - 'influence', text="Aim Lock", slider=True) + track_to_constraint = None + for con in pose_bones["Camera"].constraints: + if con.type == 'TRACK_TO': + track_to_constraint = con + break + if track_to_constraint is not None: + col.label(text="Tracking:") + col.prop(track_to_constraint, 'influence', + text="Aim Lock", slider=True) # Crane arm stuff if rig["rig_id"].lower() == "crane_rig": -- cgit v1.2.3 From 547adaa4edf8a96e94613bb9345624a00c7f8632 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 28 Jul 2022 11:46:33 -0300 Subject: Archimesh: replace deprecated bgl module Part of T80730 --- archimesh/__init__.py | 4 ++-- archimesh/achm_gltools.py | 24 ++++++++++++------------ archimesh/achm_main_panel.py | 1 - 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/archimesh/__init__.py b/archimesh/__init__.py index 261397f1..d79e02f7 100644 --- a/archimesh/__init__.py +++ b/archimesh/__init__.py @@ -11,8 +11,8 @@ bl_info = { "name": "Archimesh", "author": "Antonio Vazquez (antonioya)", "location": "View3D > Add Mesh / Sidebar > Create Tab", - "version": (1, 2, 2), - "blender": (2, 80, 0), + "version": (1, 2, 3), + "blender": (3, 0, 0), "description": "Generate rooms, doors, windows, and other architecture objects", "doc_url": "{BLENDER_MANUAL_URL}/addons/add_mesh/archimesh.html", "category": "Add Mesh" diff --git a/archimesh/achm_gltools.py b/archimesh/achm_gltools.py index de11226d..00160989 100644 --- a/archimesh/achm_gltools.py +++ b/archimesh/achm_gltools.py @@ -16,7 +16,6 @@ from mathutils import Vector from bpy_extras import view3d_utils from .achm_room_maker import get_wall_points # GPU -import bgl import gpu from gpu_extras.batch import batch_for_shader @@ -39,7 +38,7 @@ def draw_main(context): measure = scene.archimesh_gl_measure dspname = scene.archimesh_gl_name - bgl.glEnable(bgl.GL_BLEND) + gpu.state.blend_set('ALPHA') # Display selected or all if scene.archimesh_gl_ghost is False: objlist = context.selected_objects @@ -81,8 +80,8 @@ def draw_main(context): # ----------------------- # restore opengl defaults # ----------------------- - bgl.glLineWidth(1) - bgl.glDisable(bgl.GL_BLEND) + gpu.state.line_width_set(1.0) + gpu.state.blend_set('NONE') # ------------------------------------------------------------- @@ -175,8 +174,8 @@ def draw_room_data(myobj, op, region, rv3d, rgba, rgbaw, fsize, wfsize, space, m screen_point_b2 = view3d_utils.location_3d_to_region_2d(region, rv3d, b2_s2) # colour + line setup - bgl.glEnable(bgl.GL_BLEND) - bgl.glLineWidth(1) + gpu.state.blend_set('ALPHA') + gpu.state.line_width_set(1.0) # -------------------------------- # Measures # -------------------------------- @@ -284,8 +283,8 @@ def draw_door_data(myobj, op, region, rv3d, rgba, fsize, space, measure): screen_point_ep3 = view3d_utils.location_3d_to_region_2d(region, rv3d, e_p3) # colour + line setup - bgl.glEnable(bgl.GL_BLEND) - bgl.glLineWidth(1) + gpu.state.blend_set('ALPHA') + gpu.state.line_width_set(1.0) # -------------------------------- # Measures @@ -368,8 +367,8 @@ def draw_window_rail_data(myobj, op, region, rv3d, rgba, fsize, space, measure): screen_point_tp3 = view3d_utils.location_3d_to_region_2d(region, rv3d, t_p3) # colour + line setup - bgl.glEnable(bgl.GL_BLEND) - bgl.glLineWidth(1) + gpu.state.blend_set('ALPHA') + gpu.state.line_width_set(1.0) # -------------------------------- # Measures @@ -473,9 +472,10 @@ def draw_window_panel_data(myobj, op, region, rv3d, rgba, fsize, space, measure) screen_point_gp3 = view3d_utils.location_3d_to_region_2d(region, rv3d, g_p3) screen_point_gp4 = view3d_utils.location_3d_to_region_2d(region, rv3d, g_p4) screen_point_gp5 = view3d_utils.location_3d_to_region_2d(region, rv3d, g_p5) + # colour + line setup - bgl.glEnable(bgl.GL_BLEND) - bgl.glLineWidth(1) + gpu.state.blend_set('ALPHA') + gpu.state.line_width_set(1.0) # -------------------------------- # Measures diff --git a/archimesh/achm_main_panel.py b/archimesh/achm_main_panel.py index 97c7d19d..fda383f4 100644 --- a/archimesh/achm_main_panel.py +++ b/archimesh/achm_main_panel.py @@ -8,7 +8,6 @@ # noinspection PyUnresolvedReferences import bpy # noinspection PyUnresolvedReferences -import bgl from bpy.types import Operator, Panel, SpaceView3D from math import sqrt, fabs, pi, asin from .achm_tools import * -- cgit v1.2.3 From a1424dc87f2caea19d089c45b975f237e6746b22 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 28 Jul 2022 12:03:49 -0300 Subject: MeasureIt: replace deprecated bgl module Part of T80730 --- measureit/__init__.py | 4 ++-- measureit/measureit_geometry.py | 7 +++---- measureit/measureit_main.py | 9 ++++----- measureit/measureit_render.py | 10 ++++------ 4 files changed, 13 insertions(+), 17 deletions(-) diff --git a/measureit/__init__.py b/measureit/__init__.py index 8d4b9576..c7f4fc7e 100644 --- a/measureit/__init__.py +++ b/measureit/__init__.py @@ -11,8 +11,8 @@ bl_info = { "name": "MeasureIt", "author": "Antonio Vazquez (antonioya)", "location": "View3D > Sidebar > View Tab", - "version": (1, 8, 1), - "blender": (2, 80, 0), + "version": (1, 8, 2), + "blender": (3, 0, 0), "description": "Tools for measuring objects.", "doc_url": "{BLENDER_MANUAL_URL}/addons/3d_view/measureit.html", "category": "3D View" diff --git a/measureit/measureit_geometry.py b/measureit/measureit_geometry.py index a1e90b9e..56efad0e 100644 --- a/measureit/measureit_geometry.py +++ b/measureit/measureit_geometry.py @@ -17,7 +17,6 @@ from bpy_extras import view3d_utils, mesh_utils import bpy_extras.object_utils as object_utils from sys import exc_info # GPU -import bgl import gpu from gpu_extras.batch import batch_for_shader @@ -477,7 +476,7 @@ def draw_segments(context, myobj, op, region, rv3d): # ------------------------------------ # Draw lines # ------------------------------------ - bgl.glEnable(bgl.GL_BLEND) + gpu.state.blend_set('ALPHA') if ms.gltype == 1: # Segment draw_line(screen_point_ap1, screen_point_v11, rgba) @@ -1175,7 +1174,7 @@ def draw_faces(context, myobj, region, rv3d): a_p2 = (a_p1[0] + normal[0] * ln, a_p1[1] + normal[1] * ln, a_p1[2] + normal[2] * ln) # line setup - bgl.glEnable(bgl.GL_BLEND) + gpu.state.blend_set('ALPHA') imm_set_line_width(th) # converting to screen coordinates txtpoint2d = get_2d_point(region, rv3d, a_p1) @@ -1185,7 +1184,7 @@ def draw_faces(context, myobj, region, rv3d): draw_text(myobj, txtpoint2d, str(f.index), rgba, fsize) # Draw Normal if scene.measureit_debug_normals is True: - bgl.glEnable(bgl.GL_BLEND) + gpu.state.blend_set('ALPHA') draw_arrow(txtpoint2d, point2, rgba, 10, "99", "1") if len(obverts) > 2 and scene.measureit_debug_normal_details is True: diff --git a/measureit/measureit_main.py b/measureit/measureit_main.py index 23e9de46..d69ba268 100644 --- a/measureit/measureit_main.py +++ b/measureit/measureit_main.py @@ -11,7 +11,6 @@ import bpy import bmesh from bmesh import from_edit_mesh # noinspection PyUnresolvedReferences -import bgl from bpy.types import PropertyGroup, Panel, Object, Operator, SpaceView3D from bpy.props import IntProperty, CollectionProperty, FloatVectorProperty, BoolProperty, StringProperty, \ FloatProperty, EnumProperty @@ -1933,8 +1932,8 @@ def draw_main(context): else: objlist = context.view_layer.objects - # Enable GL drawing - bgl.glEnable(bgl.GL_BLEND) + # Enable drawing + gpu.state.blend_set('ALPHA') # --------------------------------------- # Generate all OpenGL calls for measures # --------------------------------------- @@ -1964,9 +1963,9 @@ def draw_main(context): draw_faces(context, myobj, region, rv3d) # ----------------------- - # restore opengl defaults + # restore defaults # ----------------------- - bgl.glDisable(bgl.GL_BLEND) + gpu.state.blend_set('NONE') # ------------------------------------------------------------- diff --git a/measureit/measureit_render.py b/measureit/measureit_render.py index efc5c1b0..093149d5 100644 --- a/measureit/measureit_render.py +++ b/measureit/measureit_render.py @@ -8,7 +8,6 @@ # noinspection PyUnresolvedReferences import bpy import gpu -import bgl # noinspection PyUnresolvedReferences import blf from os import path, remove @@ -54,8 +53,8 @@ def render_main(self, context, animation=False): [0, 0, 0, 1]]) with offscreen.bind(): - bgl.glClearColor(0.0, 0.0, 0.0, 0.0) - bgl.glClear(bgl.GL_COLOR_BUFFER_BIT) + fb = gpu.state.active_framebuffer_get() + fb.clear(color=(0.0, 0.0, 0.0, 0.0)) gpu.matrix.reset() gpu.matrix.load_matrix(view_matrix) gpu.matrix.load_projection_matrix(Matrix.Identity(4)) @@ -101,9 +100,8 @@ def render_main(self, context, animation=False): y2 = height - y1 draw_rectangle((x1, y1), (x2, y2), rfcolor) - buffer = bgl.Buffer(bgl.GL_BYTE, width * height * 4) - bgl.glReadBuffer(bgl.GL_COLOR_ATTACHMENT0) - bgl.glReadPixels(0, 0, width, height, bgl.GL_RGBA, bgl.GL_UNSIGNED_BYTE, buffer) + buffer = fb.read_color(0, 0, width, height, 4, 0, 'UBYTE') + buffer.dimensions = width * height * 4 offscreen.free() -- cgit v1.2.3 From c8109966ce74ba9516d81f1bc98a64d00eeb0b67 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 28 Jul 2022 12:12:54 -0300 Subject: Scatter Objects: replace deprecated bgl module Part of T80730 --- object_scatter/__init__.py | 4 ++-- object_scatter/operator.py | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/object_scatter/__init__.py b/object_scatter/__init__.py index 12bc8ca0..8edeac71 100644 --- a/object_scatter/__init__.py +++ b/object_scatter/__init__.py @@ -3,8 +3,8 @@ bl_info = { "name": "Scatter Objects", "author": "Jacques Lucke", - "version": (0, 1), - "blender": (2, 80, 0), + "version": (0, 2), + "blender": (3, 0, 0), "location": "3D View", "description": "Distribute object instances on another object.", "warning": "", diff --git a/object_scatter/operator.py b/object_scatter/operator.py index 07bf3884..8be78672 100644 --- a/object_scatter/operator.py +++ b/object_scatter/operator.py @@ -2,7 +2,6 @@ import bpy import gpu -import bgl import blf import math import enum @@ -340,14 +339,14 @@ def draw_matrices_batches(batches): shader.bind() shader.uniform_float("color", (0.4, 0.4, 1.0, 0.3)) - bgl.glEnable(bgl.GL_BLEND) - bgl.glDepthMask(bgl.GL_FALSE) + gpu.state.blend_set('ALPHA') + gpu.state.depth_mask_set(False) for batch in batches: batch.draw(shader) - bgl.glDisable(bgl.GL_BLEND) - bgl.glDepthMask(bgl.GL_TRUE) + gpu.state.blend_set('NONE') + gpu.state.depth_mask_set(True) def create_batch_for_matrices(matrices, base_scale): coords = [] @@ -367,7 +366,7 @@ def create_batch_for_matrices(matrices, base_scale): def draw_line_strip_batch(batch, color, thickness=1): shader = get_uniform_color_shader() - bgl.glLineWidth(thickness) + gpu.state.line_width_set(thickness) shader.bind() shader.uniform_float("color", color) batch.draw(shader) -- cgit v1.2.3 From ce8c5f8d1b180051d540fb162872300bd4598e31 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 28 Jul 2022 12:17:26 -0300 Subject: Power Sequencer: replace deprecated bgl module Part of T80730 --- power_sequencer/__init__.py | 2 +- power_sequencer/operators/mouse_trim_modal.py | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/power_sequencer/__init__.py b/power_sequencer/__init__.py index e8f920f7..877de302 100755 --- a/power_sequencer/__init__.py +++ b/power_sequencer/__init__.py @@ -24,7 +24,7 @@ bl_info = { "name": "Power Sequencer", "description": "Video editing tools for content creators", "author": "Nathan Lovato", - "version": (2, 0, 1), + "version": (2, 0, 2), "blender": (2, 93, 3), "location": "Sequencer", "tracker_url": "https://github.com/GDquest/Blender-power-sequencer/issues", diff --git a/power_sequencer/operators/mouse_trim_modal.py b/power_sequencer/operators/mouse_trim_modal.py index 93ca1346..e8f751c9 100644 --- a/power_sequencer/operators/mouse_trim_modal.py +++ b/power_sequencer/operators/mouse_trim_modal.py @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-3.0-or-later # Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors import bpy -import bgl import gpu import math from mathutils import Vector @@ -358,8 +357,8 @@ def draw(self, context, frame_start=-1, frame_end=-1, target_strips=[], draw_arr rect_origin = Vector((start_x, start_y)) rect_size = Vector((end_x - start_x, abs(start_y - end_y))) - bgl.glEnable(bgl.GL_BLEND) - bgl.glLineWidth(3) + gpu.state.blend_set('ALPHA') + gpu.state.line_width_set(3.0) draw_rectangle(SHADER, rect_origin, rect_size, color_fill) # Vertical lines draw_line(SHADER, Vector((start_x, start_y)), Vector((start_x, end_y)), color_line) @@ -374,8 +373,8 @@ def draw(self, context, frame_start=-1, frame_end=-1, target_strips=[], draw_arr draw_triangle_equilateral(SHADER, center_1, radius, color=color_line) draw_triangle_equilateral(SHADER, center_2, radius, math.pi, color=color_line) - bgl.glLineWidth(1) - bgl.glDisable(bgl.GL_BLEND) + gpu.state.line_width_set(1) + gpu.state.blend_set('NONE') def get_frame_and_channel(event): -- cgit v1.2.3 From c1cdc33579fe55af354d702abcd077ddd31d8d9e Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 28 Jul 2022 12:19:44 -0300 Subject: Precision Drawing Tools: replace deprecated bgl module Part of T80730 --- precision_drawing_tools/__init__.py | 2 +- precision_drawing_tools/pdt_functions.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/precision_drawing_tools/__init__.py b/precision_drawing_tools/__init__.py index 6cb9ab52..6fdb72f2 100644 --- a/precision_drawing_tools/__init__.py +++ b/precision_drawing_tools/__init__.py @@ -12,7 +12,7 @@ bl_info = { "name": "Precision Drawing Tools (PDT)", "author": "Alan Odom (Clockmender), Rune Morling (ermo)", - "version": (1, 5, 2), + "version": (1, 5, 3), "blender": (3, 0, 0), "location": "View3D > UI > PDT", "description": "Precision Drawing Tools for Accurate Modelling", diff --git a/precision_drawing_tools/pdt_functions.py b/precision_drawing_tools/pdt_functions.py index 66bf1de2..9352b66f 100644 --- a/precision_drawing_tools/pdt_functions.py +++ b/precision_drawing_tools/pdt_functions.py @@ -8,7 +8,6 @@ import bpy import bmesh -import bgl import gpu import numpy as np from mathutils import Vector, Quaternion @@ -584,7 +583,7 @@ def draw_3d(coords, gtype, rgba, context): try: if coords is not None: - bgl.glEnable(bgl.GL_BLEND) + gpu.state.blend_set('ALPHA') SHADER.bind() SHADER.uniform_float("color", rgba) batch.draw(SHADER) -- cgit v1.2.3 From f3e1e980b6199e4733a8f255f49e6b94151f5e25 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 28 Jul 2022 12:23:35 -0300 Subject: Math Vis: replace deprecated bgl module Part of T80730 --- space_view3d_math_vis/__init__.py | 4 ++-- space_view3d_math_vis/draw.py | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/space_view3d_math_vis/__init__.py b/space_view3d_math_vis/__init__.py index b6aac3cd..ac05ffc6 100644 --- a/space_view3d_math_vis/__init__.py +++ b/space_view3d_math_vis/__init__.py @@ -3,8 +3,8 @@ bl_info = { "name": "Math Vis (Console)", "author": "Campbell Barton", - "version": (0, 2, 1), - "blender": (2, 80, 0), + "version": (0, 2, 2), + "blender": (3, 0, 0), "location": "Properties: Scene > Math Vis Console and Python Console: Menu", "description": "Display console defined mathutils variables in the 3D view", "doc_url": "{BLENDER_MANUAL_URL}/addons/3d_view/math_vis_console.html", diff --git a/space_view3d_math_vis/draw.py b/space_view3d_math_vis/draw.py index de7ad946..0b7084eb 100644 --- a/space_view3d_math_vis/draw.py +++ b/space_view3d_math_vis/draw.py @@ -3,7 +3,6 @@ import bpy import blf import gpu -import bgl from gpu_extras.batch import batch_for_shader from . import utils @@ -126,9 +125,9 @@ def draw_callback_view(): with_bounding_box = not settings.bbox_hide if settings.in_front: - bgl.glDepthFunc(bgl.GL_ALWAYS) + gpu.state.depth_test_set('ALWAYS') else: - bgl.glDepthFunc(bgl.GL_LESS) + gpu.state.depth_test_set('LESS') data_matrix, data_quat, data_euler, data_vector, data_vector_array = utils.console_math_data() if settings.index in range(0,len(prop_states)): -- cgit v1.2.3 From ec558f2a91577304a84fa378eba23ed206450bff Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 28 Jul 2022 12:26:48 -0300 Subject: VR Scene Inspection: replace deprecated bgl module Part of T80730 --- viewport_vr_preview/__init__.py | 2 +- viewport_vr_preview/operators.py | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/viewport_vr_preview/__init__.py b/viewport_vr_preview/__init__.py index eb7dca4d..cc4580ae 100644 --- a/viewport_vr_preview/__init__.py +++ b/viewport_vr_preview/__init__.py @@ -3,7 +3,7 @@ bl_info = { "name": "VR Scene Inspection", "author": "Julian Eisel (Severin), Sebastian Koenig, Peter Kim (muxed-reality)", - "version": (0, 11, 0), + "version": (0, 11, 1), "blender": (3, 2, 0), "location": "3D View > Sidebar > VR", "description": ("View the viewport with virtual reality glasses " diff --git a/viewport_vr_preview/operators.py b/viewport_vr_preview/operators.py index 3c92a44f..7981511e 100644 --- a/viewport_vr_preview/operators.py +++ b/viewport_vr_preview/operators.py @@ -12,7 +12,6 @@ from bpy.types import ( GizmoGroup, Operator, ) -import bgl import math from math import radians from mathutils import Euler, Matrix, Quaternion, Vector @@ -305,8 +304,8 @@ class VIEW3D_GT_vr_camera_cone(Gizmo): 'LINES', lines_shape_verts) # Ensure correct GL state (otherwise other gizmos might mess that up) - bgl.glLineWidth(1) - bgl.glEnable(bgl.GL_BLEND) + gpu.state.line_width_set(1.0) + gpu.state.blend_set('ALPHA') self.draw_custom_shape(self.frame_shape) self.draw_custom_shape(self.lines_shape) @@ -316,8 +315,8 @@ class VIEW3D_GT_vr_controller_grip(Gizmo): bl_idname = "VIEW_3D_GT_vr_controller_grip" def draw(self, context): - bgl.glLineWidth(1) - bgl.glEnable(bgl.GL_BLEND) + gpu.state.line_width_set(1.0) + gpu.state.blend_set('ALPHA') self.color = 0.422, 0.438, 0.446 self.draw_preset_circle(self.matrix_basis, axis='POS_X') @@ -329,8 +328,8 @@ class VIEW3D_GT_vr_controller_aim(Gizmo): bl_idname = "VIEW_3D_GT_vr_controller_aim" def draw(self, context): - bgl.glLineWidth(1) - bgl.glEnable(bgl.GL_BLEND) + gpu.state.line_width_set(1.0) + gpu.state.blend_set('ALPHA') self.color = 1.0, 0.2, 0.322 self.draw_preset_arrow(self.matrix_basis, axis='POS_X') -- cgit v1.2.3 From fdebdd681f0bee5fb1914eaa8b1c2c022b73a2f1 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 28 Jul 2022 16:12:06 -0300 Subject: UV Layout: don't convert line coordinates to 3d Although shader uses 3D attribute, it fills missing components with vec4(0,0,0,1). This was changed in rBA563ea27eb1a8 --- io_mesh_uv_layout/__init__.py | 10 +++++----- io_mesh_uv_layout/export_uv_png.py | 10 ++++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/io_mesh_uv_layout/__init__.py b/io_mesh_uv_layout/__init__.py index ceb5ddbc..40d9b501 100644 --- a/io_mesh_uv_layout/__init__.py +++ b/io_mesh_uv_layout/__init__.py @@ -3,7 +3,7 @@ bl_info = { "name": "UV Layout", "author": "Campbell Barton, Matt Ebb", - "version": (1, 1, 2), + "version": (1, 1, 3), "blender": (3, 0, 0), "location": "Image-Window > UVs > Export UV Layout", "description": "Export the UV layout as a 2D graphic", @@ -128,10 +128,10 @@ class ExportUVLayout(bpy.types.Operator): polygon_data = list(self.iter_polygon_data_to_draw(context, meshes)) different_colors = set(color for _, color in polygon_data) if self.modified: - depsgraph = context.evaluated_depsgraph_get() - for obj in self.iter_objects_to_export(context): - obj_eval = obj.evaluated_get(depsgraph) - obj_eval.to_mesh_clear() + depsgraph = context.evaluated_depsgraph_get() + for obj in self.iter_objects_to_export(context): + obj_eval = obj.evaluated_get(depsgraph) + obj_eval.to_mesh_clear() export = self.get_exporter() export(filepath, polygon_data, different_colors, self.size[0], self.size[1], self.opacity) diff --git a/io_mesh_uv_layout/export_uv_png.py b/io_mesh_uv_layout/export_uv_png.py index 78f9f6a2..b7110c73 100644 --- a/io_mesh_uv_layout/export_uv_png.py +++ b/io_mesh_uv_layout/export_uv_png.py @@ -70,12 +70,14 @@ def draw_lines(face_data): for i in range(len(uvs)): start = uvs[i] end = uvs[(i+1) % len(uvs)] - coords.append((start[0], start[1], 0.0)) - coords.append((end[0], end[1], 0.0)) + coords.append((start[0], start[1])) + coords.append((end[0], end[1])) - # Use '2D_UNIFORM_COLOR' if smooth lines are not required. + # Use '2D_UNIFORM_COLOR' in the `batch_for_shader` so we don't need to + # convert the coordinates to 3D as in the case of + # '3D_POLYLINE_UNIFORM_COLOR'. + batch = batch_for_shader(gpu.shader.from_builtin('2D_UNIFORM_COLOR'), 'LINES', {"pos" : coords}) shader = gpu.shader.from_builtin('3D_POLYLINE_UNIFORM_COLOR') - batch = batch_for_shader(shader, 'LINES', {"pos" : coords}) shader.bind() shader.uniform_float("viewportSize", gpu.state.viewport_get()[2:]) shader.uniform_float("lineWidth", 0.5) -- cgit v1.2.3 From bff8295be576ae2a2cb2c989157ec9fdcaa21c0a Mon Sep 17 00:00:00 2001 From: Pratik Borhade Date: Fri, 29 Jul 2022 15:27:53 +1000 Subject: Fix T93422: ANT Landscape Tools - Landscape Eroder not working Ref D15432 --- ant_landscape/ant_functions.py | 156 +++++++++++++---------------------------- 1 file changed, 48 insertions(+), 108 deletions(-) diff --git a/ant_landscape/ant_functions.py b/ant_landscape/ant_functions.py index f63747aa..77bb5c83 100644 --- a/ant_landscape/ant_functions.py +++ b/ant_landscape/ant_functions.py @@ -908,61 +908,36 @@ class Eroder(bpy.types.Operator): stats = Stats() counts= {} + maps = { + 'rainmap': lambda g, r, c: g.rainmap[r, c], + 'scree': lambda g, r, c: g.avalanced[r, c], + 'avalanced': lambda g, r, c: -g.avalanced[r, c], + 'water': lambda g, r, c: g.water[r, c] / g.watermax, + 'scour': lambda g, r, c: g.scour[r, c] / max(g.scourmax, -g.scourmin), + 'deposit': lambda g, r, c: g.scour[r, c] / min(-g.scourmax, g.scourmin), + 'flowrate': lambda g, r, c: g.flowrate[r, c], + 'sediment': lambda g, r, c: g.sediment[r, c], + 'sedimentpct': lambda g, r, c: g.sedimentpct[r, c], + 'capacity': lambda g, r, c: g.capacity[r, c] + } def execute(self, context): ob = context.active_object - me = ob.data + oldMesh = ob.data self.stats.reset() - try: - vgActive = ob.vertex_groups.active.name - except: - vgActive = "capacity" - print("ActiveGroup", vgActive) - try: - vg=ob.vertex_groups["rainmap"] - except: - vg=ob.vertex_groups.new(name="rainmap") - try: - vgscree=ob.vertex_groups["scree"] - except: - vgscree=ob.vertex_groups.new(name="scree") - try: - vgavalanced=ob.vertex_groups["avalanced"] - except: - vgavalanced=ob.vertex_groups.new(name="avalanced") - try: - vgw=ob.vertex_groups["water"] - except: - vgw=ob.vertex_groups.new(name="water") - try: - vgscour=ob.vertex_groups["scour"] - except: - vgscour=ob.vertex_groups.new(name="scour") - try: - vgdeposit=ob.vertex_groups["deposit"] - except: - vgdeposit=ob.vertex_groups.new(name="deposit") - try: - vgflowrate=ob.vertex_groups["flowrate"] - except: - vgflowrate=ob.vertex_groups.new(name="flowrate") - try: - vgsediment=ob.vertex_groups["sediment"] - except: - vgsediment=ob.vertex_groups.new(name="sediment") - try: - vgsedimentpct=ob.vertex_groups["sedimentpct"] - except: - vgsedimentpct=ob.vertex_groups.new(name="sedimentpct") - try: - vgcapacity=ob.vertex_groups["capacity"] - except: - vgcapacity=ob.vertex_groups.new(name="capacity") - - g = Grid.fromBlenderMesh(me, vg, self.Ef) - - me = bpy.data.meshes.new(me.name) + index_to_name = {} + + for name in self.maps: + try: + ob.vertex_groups[name] + except: + ob.vertex_groups.new(name=name) + # Save a mapping from index to name, in case, + # the next iteration is different. + index_to_name[ob.vertex_groups[name].index] = name + + g = Grid.fromBlenderMesh(oldMesh, ob.vertex_groups['rainmap'], self.Ef) self.counts['diffuse'] = 0 self.counts['avalanche'] = 0 @@ -986,64 +961,29 @@ class Eroder(bpy.types.Operator): g.fluvial_erosion(self.Kr, self.Kv, self.userainmap, self.Kc, self.Ks, self.Kz*50, self.Ka, 0,0,0,0, self.numexpr) self.counts['water']+=1 - g.toBlenderMesh(me) - ob.data = me - - if vg: - for row in range(g.rainmap.shape[0]): - for col in range(g.rainmap.shape[1]): - i = row * g.rainmap.shape[1] + col - vg.add([i],g.rainmap[row,col],'ADD') - if vgscree: - for row in range(g.rainmap.shape[0]): - for col in range(g.rainmap.shape[1]): - i = row * g.rainmap.shape[1] + col - vgscree.add([i],g.avalanced[row,col],'ADD') - if vgavalanced: - for row in range(g.rainmap.shape[0]): - for col in range(g.rainmap.shape[1]): - i = row * g.rainmap.shape[1] + col - vgavalanced.add([i],-g.avalanced[row,col],'ADD') - if vgw: - for row in range(g.rainmap.shape[0]): - for col in range(g.rainmap.shape[1]): - i = row * g.rainmap.shape[1] + col - vgw.add([i],g.water[row,col]/g.watermax,'ADD') - if vgscour: - for row in range(g.rainmap.shape[0]): - for col in range(g.rainmap.shape[1]): - i = row * g.rainmap.shape[1] + col - vgscour.add([i],g.scour[row,col]/max(g.scourmax, -g.scourmin),'ADD') - if vgdeposit: - for row in range(g.rainmap.shape[0]): - for col in range(g.rainmap.shape[1]): - i = row * g.rainmap.shape[1] + col - vgdeposit.add([i],g.scour[row,col]/min(-g.scourmax, g.scourmin),'ADD') - if vgflowrate: - for row in range(g.rainmap.shape[0]): - for col in range(g.rainmap.shape[1]): - i = row * g.rainmap.shape[1] + col - vgflowrate.add([i],g.flowrate[row,col],'ADD') - if vgsediment: - for row in range(g.rainmap.shape[0]): - for col in range(g.rainmap.shape[1]): - i = row * g.rainmap.shape[1] + col - vgsediment.add([i],g.sediment[row,col],'ADD') - if vgsedimentpct: - for row in range(g.rainmap.shape[0]): - for col in range(g.rainmap.shape[1]): - i = row * g.rainmap.shape[1] + col - vgsedimentpct.add([i],g.sedimentpct[row,col],'ADD') - if vgcapacity: - for row in range(g.rainmap.shape[0]): - for col in range(g.rainmap.shape[1]): - i = row * g.rainmap.shape[1] + col - vgcapacity.add([i],g.capacity[row,col],'ADD') - try: - vg = ob.vertex_groups["vgActive"] - except: - vg = vgcapacity - ob.vertex_groups.active = vg + newMesh = bpy.data.meshes.new(oldMesh.name) + g.toBlenderMesh(newMesh) + + # This empties ob.vertex_groups. + ob.data = newMesh + + # Copy vertex groups from the old mesh. + for name in self.maps: + ob.vertex_groups.new(name=name) + for vert in oldMesh.vertices: + for group in vert.groups: + name = index_to_name[group.group] + if name: + ob.vertex_groups[name].add([vert.index], group.weight, 'REPLACE') + + # Add the new data. + for row in range(g.rainmap.shape[0]): + for col in range(g.rainmap.shape[1]): + i = row * g.rainmap.shape[1] + col + for name, fn in self.maps.items(): + ob.vertex_groups[name].add([i], fn(g, row, col), 'ADD') + + ob.vertex_groups.active = ob.vertex_groups['capacity'] if self.smooth: bpy.ops.object.shade_smooth() -- cgit v1.2.3 From 8669c66177eab49871e9ae758c74da05cce89adf Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 29 Jul 2022 15:33:17 +1000 Subject: Cleanup: run autopep8 on io_mesh_uv_layout --- io_mesh_uv_layout/export_uv_eps.py | 6 ++++++ io_mesh_uv_layout/export_uv_png.py | 20 ++++++++++++++------ io_mesh_uv_layout/export_uv_svg.py | 6 ++++++ 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/io_mesh_uv_layout/export_uv_eps.py b/io_mesh_uv_layout/export_uv_eps.py index 04b8a38e..9e013e13 100644 --- a/io_mesh_uv_layout/export_uv_eps.py +++ b/io_mesh_uv_layout/export_uv_eps.py @@ -8,6 +8,7 @@ def export(filepath, face_data, colors, width, height, opacity): for text in get_file_parts(face_data, colors, width, height, opacity): file.write(text) + def get_file_parts(face_data, colors, width, height, opacity): yield from header(width, height) if opacity > 0.0: @@ -35,6 +36,7 @@ def header(width, height): yield "1 setlinejoin\n" yield "1 setlinecap\n" + def prepare_colors(colors, out_name_by_color): for i, color in enumerate(colors): name = f"COLOR_{i}" @@ -48,18 +50,21 @@ def prepare_colors(colors, out_name_by_color): yield "0 setgray\n" yield "} def\n" + def draw_colored_polygons(face_data, name_by_color, width, height): for uvs, color in face_data: yield from draw_polygon_path(uvs, width, height) yield "closepath\n" yield "%s\n" % name_by_color[color] + def draw_lines(face_data, width, height): for uvs, _ in face_data: yield from draw_polygon_path(uvs, width, height) yield "closepath\n" yield "stroke\n" + def draw_polygon_path(uvs, width, height): yield "newpath\n" for j, uv in enumerate(uvs): @@ -69,6 +74,7 @@ def draw_polygon_path(uvs, width, height): else: yield "%.5f %.5f lineto\n" % uv_scale + def footer(): yield "showpage\n" yield "%%EOF\n" diff --git a/io_mesh_uv_layout/export_uv_png.py b/io_mesh_uv_layout/export_uv_png.py index b7110c73..b051c980 100644 --- a/io_mesh_uv_layout/export_uv_png.py +++ b/io_mesh_uv_layout/export_uv_png.py @@ -6,6 +6,7 @@ from mathutils import Vector, Matrix from mathutils.geometry import tessellate_polygon from gpu_extras.batch import batch_for_shader + def export(filepath, face_data, colors, width, height, opacity): offscreen = gpu.types.GPUOffScreen(width, height) offscreen.bind() @@ -22,6 +23,7 @@ def export(filepath, face_data, colors, width, height, opacity): offscreen.unbind() offscreen.free() + def draw_image(face_data, opacity): gpu.state.blend_set('ALPHA_PREMULT') @@ -34,6 +36,7 @@ def draw_image(face_data, opacity): gpu.state.blend_set('NONE') + def get_normalize_uvs_matrix(): '''matrix maps x and y coordinates from [0, 1] to [-1, 1]''' matrix = Matrix.Identity(4) @@ -43,6 +46,7 @@ def get_normalize_uvs_matrix(): matrix[1][1] = 2 return matrix + def draw_background_colors(face_data, opacity): coords = [uv for uvs, _ in face_data for uv in uvs] colors = [(*color, opacity) for uvs, color in face_data for _ in range(len(uvs))] @@ -55,28 +59,31 @@ def draw_background_colors(face_data, opacity): offset += len(uvs) shader = gpu.shader.from_builtin('2D_FLAT_COLOR') - batch = batch_for_shader(shader, 'TRIS', - {"pos" : coords, - "color" : colors}, - indices=indices) + batch = batch_for_shader( + shader, 'TRIS', + {"pos": coords, "color": colors}, + indices=indices, + ) batch.draw(shader) + def tessellate_uvs(uvs): return tessellate_polygon([uvs]) + def draw_lines(face_data): coords = [] for uvs, _ in face_data: for i in range(len(uvs)): start = uvs[i] - end = uvs[(i+1) % len(uvs)] + end = uvs[(i + 1) % len(uvs)] coords.append((start[0], start[1])) coords.append((end[0], end[1])) # Use '2D_UNIFORM_COLOR' in the `batch_for_shader` so we don't need to # convert the coordinates to 3D as in the case of # '3D_POLYLINE_UNIFORM_COLOR'. - batch = batch_for_shader(gpu.shader.from_builtin('2D_UNIFORM_COLOR'), 'LINES', {"pos" : coords}) + batch = batch_for_shader(gpu.shader.from_builtin('2D_UNIFORM_COLOR'), 'LINES', {"pos": coords}) shader = gpu.shader.from_builtin('3D_POLYLINE_UNIFORM_COLOR') shader.bind() shader.uniform_float("viewportSize", gpu.state.viewport_get()[2:]) @@ -84,6 +91,7 @@ def draw_lines(face_data): shader.uniform_float("color", (0, 0, 0, 1)) batch.draw(shader) + def save_pixels(filepath, pixel_data, width, height): image = bpy.data.images.new("temp", width, height, alpha=True) image.filepath = filepath diff --git a/io_mesh_uv_layout/export_uv_svg.py b/io_mesh_uv_layout/export_uv_svg.py index d8e2b5a4..a4b6a3c3 100644 --- a/io_mesh_uv_layout/export_uv_svg.py +++ b/io_mesh_uv_layout/export_uv_svg.py @@ -4,16 +4,19 @@ import bpy from os.path import basename from xml.sax.saxutils import escape + def export(filepath, face_data, colors, width, height, opacity): with open(filepath, 'w', encoding='utf-8') as file: for text in get_file_parts(face_data, colors, width, height, opacity): file.write(text) + def get_file_parts(face_data, colors, width, height, opacity): yield from header(width, height) yield from draw_polygons(face_data, width, height, opacity) yield from footer() + def header(width, height): yield '\n' yield '{escape(desc)}\n' + def draw_polygons(face_data, width, height, opacity): for uvs, color in face_data: fill = f'fill="{get_color_string(color)}"' @@ -37,10 +41,12 @@ def draw_polygons(face_data, width, height, opacity): yield f'{x*width:.3f},{y*height:.3f} ' yield '" />\n' + def get_color_string(color): r, g, b = color return f"rgb({round(r*255)}, {round(g*255)}, {round(b*255)})" + def footer(): yield '\n' yield '\n' -- cgit v1.2.3 From 4e71e31be00e78942e5a3103911e4a3f878cb338 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 29 Jul 2022 15:38:24 +1000 Subject: Cleanup: run autopep8 on ant_landscape --- ant_landscape/__init__.py | 726 +++++++++++++-------------- ant_landscape/add_mesh_ant_landscape.py | 838 ++++++++++++++++---------------- ant_landscape/ant_functions.py | 463 +++++++++--------- ant_landscape/ant_noise.py | 187 +++---- ant_landscape/eroder.py | 411 +++++++++------- ant_landscape/mesh_ant_displace.py | 759 +++++++++++++++-------------- ant_landscape/stats.py | 7 +- ant_landscape/test.py | 2 +- ant_landscape/utils.py | 4 +- 9 files changed, 1731 insertions(+), 1666 deletions(-) diff --git a/ant_landscape/__init__.py b/ant_landscape/__init__.py index 295793b9..c330ad5e 100644 --- a/ant_landscape/__init__.py +++ b/ant_landscape/__init__.py @@ -30,23 +30,24 @@ else: import bpy from bpy.props import ( - BoolProperty, - FloatProperty, - IntProperty, - StringProperty, - PointerProperty, - EnumProperty, - ) + BoolProperty, + FloatProperty, + IntProperty, + StringProperty, + PointerProperty, + EnumProperty, +) from .ant_functions import ( - draw_ant_refresh, - draw_ant_main, - draw_ant_noise, - draw_ant_displace, - ) + draw_ant_refresh, + draw_ant_main, + draw_ant_noise, + draw_ant_displace, +) # ------------------------------------------------------------ # Menu's and panels + def menu_func_eroder(self, context): ob = bpy.context.active_object if ob and (ob.ant_landscape.keys() and not ob.ant_landscape['sphere_mesh']): @@ -129,7 +130,7 @@ class AntMainSettingsPanel(bpy.types.Panel): if ant.sphere_mesh: split.prop(ant, "remove_double", toggle=True, text="Remove Doubles", icon='MESH_DATA') box.prop(ant, "ant_terrain_name") - box.prop_search(ant, "land_material", bpy.data, "materials") + box.prop_search(ant, "land_material", bpy.data, "materials") col = box.column(align=True) col.prop(ant, "subdivision_x") col.prop(ant, "subdivision_y") @@ -398,131 +399,131 @@ class AntDisplaceSettingsPanel(bpy.types.Panel): class AntLandscapePropertiesGroup(bpy.types.PropertyGroup): ant_terrain_name: StringProperty( - name="Name", - default="Landscape" - ) + name="Name", + default="Landscape" + ) land_material: StringProperty( - name='Material', - default="", - description="Terrain material" - ) + name='Material', + default="", + description="Terrain material" + ) water_material: StringProperty( - name='Material', - default="", - description="Water plane material" - ) + name='Material', + default="", + description="Water plane material" + ) texture_block: StringProperty( - name="Texture", - default="" - ) + name="Texture", + default="" + ) at_cursor: BoolProperty( - name="Cursor", - default=True, - description="Place at cursor location", - ) + name="Cursor", + default=True, + description="Place at cursor location", + ) smooth_mesh: BoolProperty( - name="Smooth", - default=True, - description="Shade smooth" - ) + name="Smooth", + default=True, + description="Shade smooth" + ) tri_face: BoolProperty( - name="Triangulate", - default=False, - description="Triangulate faces" - ) + name="Triangulate", + default=False, + description="Triangulate faces" + ) sphere_mesh: BoolProperty( - name="Sphere", - default=False, - description="Generate uv sphere - remove doubles when ready" - ) + name="Sphere", + default=False, + description="Generate uv sphere - remove doubles when ready" + ) subdivision_x: IntProperty( - name="Subdivisions X", - default=128, - min=4, - max=6400, - description="Mesh X subdivisions" - ) + name="Subdivisions X", + default=128, + min=4, + max=6400, + description="Mesh X subdivisions" + ) subdivision_y: IntProperty( - default=128, - name="Subdivisions Y", - min=4, - max=6400, - description="Mesh Y subdivisions" - ) + default=128, + name="Subdivisions Y", + min=4, + max=6400, + description="Mesh Y subdivisions" + ) mesh_size: FloatProperty( - default=2.0, - name="Mesh Size", - min=0.01, - max=100000.0, - description="Mesh size" - ) + default=2.0, + name="Mesh Size", + min=0.01, + max=100000.0, + description="Mesh size" + ) mesh_size_x: FloatProperty( - default=2.0, - name="Mesh Size X", - min=0.01, - description="Mesh x size" - ) + default=2.0, + name="Mesh Size X", + min=0.01, + description="Mesh x size" + ) mesh_size_y: FloatProperty( - name="Mesh Size Y", - default=2.0, - min=0.01, - description="Mesh y size" - ) + name="Mesh Size Y", + default=2.0, + min=0.01, + description="Mesh y size" + ) random_seed: IntProperty( - name="Random Seed", - default=0, - min=0, - description="Randomize noise origin" - ) + name="Random Seed", + default=0, + min=0, + description="Randomize noise origin" + ) noise_offset_x: FloatProperty( - name="Offset X", - default=0.0, - description="Noise X Offset" - ) + name="Offset X", + default=0.0, + description="Noise X Offset" + ) noise_offset_y: FloatProperty( - name="Offset Y", - default=0.0, - description="Noise Y Offset" - ) + name="Offset Y", + default=0.0, + description="Noise Y Offset" + ) noise_offset_z: FloatProperty( - name="Offset Z", - default=0.0, - description="Noise Z Offset" - ) + name="Offset Z", + default=0.0, + description="Noise Z Offset" + ) noise_size_x: FloatProperty( - default=1.0, - name="Size X", - min=0.01, - max=1000.0, - description="Noise x size" - ) + default=1.0, + name="Size X", + min=0.01, + max=1000.0, + description="Noise x size" + ) noise_size_y: FloatProperty( - name="Size Y", - default=1.0, - min=0.01, - max=1000.0, - description="Noise y size" - ) + name="Size Y", + default=1.0, + min=0.01, + max=1000.0, + description="Noise y size" + ) noise_size_z: FloatProperty( - name="Size Z", - default=1.0, - min=0.01, - max=1000.0, - description="Noise Z size" - ) + name="Size Z", + default=1.0, + min=0.01, + max=1000.0, + description="Noise Z size" + ) noise_size: FloatProperty( - name="Noise Size", - default=1.0, - min=0.01, - max=1000.0, - description="Noise size" - ) + name="Noise Size", + default=1.0, + min=0.01, + max=1000.0, + description="Noise size" + ) noise_type: EnumProperty( - name="Noise Type", - default='hetero_terrain', - description="Noise type", - items = [ + name="Noise Type", + default='hetero_terrain', + description="Noise type", + items=[ ('multi_fractal', "Multi Fractal", "Blender: Multi Fractal algorithm", 0), ('ridged_multi_fractal', "Ridged MFractal", "Blender: Ridged Multi Fractal", 1), ('hybrid_multi_fractal', "Hybrid MFractal", "Blender: Hybrid Multi Fractal", 2), @@ -542,110 +543,110 @@ class AntLandscapePropertiesGroup(bpy.types.PropertyGroup): ('slick_rock', "Slick Rock", "A.N.T: slick rock", 16), ('planet_noise', "Planet Noise", "Planet Noise by: Farsthary", 17), ('blender_texture', "Blender Texture - Texture Nodes", "Blender texture data block", 18)] - ) + ) basis_type: EnumProperty( - name="Noise Basis", - default=ant_noise.noise_basis_default, - description="Noise basis algorithms", - items = ant_noise.noise_basis - ) + name="Noise Basis", + default=ant_noise.noise_basis_default, + description="Noise basis algorithms", + items=ant_noise.noise_basis + ) vl_basis_type: EnumProperty( - name="vlNoise Basis", - default=ant_noise.noise_basis_default, - description="VLNoise basis algorithms", - items = ant_noise.noise_basis - ) + name="vlNoise Basis", + default=ant_noise.noise_basis_default, + description="VLNoise basis algorithms", + items=ant_noise.noise_basis + ) distortion: FloatProperty( - name="Distortion", - default=1.0, - min=0.01, - max=100.0, - description="Distortion amount" - ) + name="Distortion", + default=1.0, + min=0.01, + max=100.0, + description="Distortion amount" + ) hard_noise: EnumProperty( - name="Soft Hard", - default="0", - description="Soft Noise, Hard noise", - items = [ + name="Soft Hard", + default="0", + description="Soft Noise, Hard noise", + items=[ ("0", "Soft", "Soft Noise", 0), ("1", "Hard", "Hard noise", 1)] - ) + ) noise_depth: IntProperty( - name="Depth", - default=8, - min=0, - max=16, - description="Noise Depth - number of frequencies in the fBm" - ) + name="Depth", + default=8, + min=0, + max=16, + description="Noise Depth - number of frequencies in the fBm" + ) amplitude: FloatProperty( - name="Amp", - default=0.5, - min=0.01, - max=1.0, - description="Amplitude" - ) + name="Amp", + default=0.5, + min=0.01, + max=1.0, + description="Amplitude" + ) frequency: FloatProperty( - name="Freq", - default=2.0, - min=0.01, - max=5.0, - description="Frequency" - ) + name="Freq", + default=2.0, + min=0.01, + max=5.0, + description="Frequency" + ) dimension: FloatProperty( - name="Dimension", - default=1.0, - min=0.01, - max=2.0, - description="H - fractal dimension of the roughest areas" - ) + name="Dimension", + default=1.0, + min=0.01, + max=2.0, + description="H - fractal dimension of the roughest areas" + ) lacunarity: FloatProperty( - name="Lacunarity", - min=0.01, - max=6.0, - default=2.0, - description="Lacunarity - gap between successive frequencies" - ) + name="Lacunarity", + min=0.01, + max=6.0, + default=2.0, + description="Lacunarity - gap between successive frequencies" + ) offset: FloatProperty( - name="Offset", - default=1.0, - min=0.01, - max=6.0, - description="Offset - raises the terrain from sea level" - ) + name="Offset", + default=1.0, + min=0.01, + max=6.0, + description="Offset - raises the terrain from sea level" + ) gain: FloatProperty( - name="Gain", - default=1.0, - min=0.01, - max=6.0, - description="Gain - scale factor" - ) + name="Gain", + default=1.0, + min=0.01, + max=6.0, + description="Gain - scale factor" + ) marble_bias: EnumProperty( - name="Bias", - default="0", - description="Marble bias", - items = [ + name="Bias", + default="0", + description="Marble bias", + items=[ ("0", "Sin", "Sin", 0), ("1", "Cos", "Cos", 1), ("2", "Tri", "Tri", 2), ("3", "Saw", "Saw", 3)] - ) + ) marble_sharp: EnumProperty( - name="Sharp", - default="0", - description="Marble sharpness", - items = [ + name="Sharp", + default="0", + description="Marble sharpness", + items=[ ("0", "Soft", "Soft", 0), ("1", "Sharp", "Sharp", 1), ("2", "Sharper", "Sharper", 2), ("3", "Soft inv.", "Soft", 3), ("4", "Sharp inv.", "Sharp", 4), ("5", "Sharper inv.", "Sharper", 5)] - ) + ) marble_shape: EnumProperty( - name="Shape", - default="0", - description="Marble shape", - items= [ + name="Shape", + default="0", + description="Marble shape", + items=[ ("0", "Default", "Default", 0), ("1", "Ring", "Ring", 1), ("2", "Swirl", "Swirl", 2), @@ -654,38 +655,38 @@ class AntLandscapePropertiesGroup(bpy.types.PropertyGroup): ("5", "Z", "Z", 5), ("6", "Y", "Y", 6), ("7", "X", "X", 7)] - ) + ) height: FloatProperty( - name="Height", - default=0.5, - min=-10000.0, - max=10000.0, - description="Noise intensity scale" - ) + name="Height", + default=0.5, + min=-10000.0, + max=10000.0, + description="Noise intensity scale" + ) height_invert: BoolProperty( - name="Invert", - default=False, - description="Height invert", - ) + name="Invert", + default=False, + description="Height invert", + ) height_offset: FloatProperty( - name="Offset", - default=0.0, - min=-10000.0, - max=10000.0, - description="Height offset" - ) + name="Offset", + default=0.0, + min=-10000.0, + max=10000.0, + description="Height offset" + ) fx_mixfactor: FloatProperty( - name="Mix Factor", - default=0.0, - min=-1.0, - max=1.0, - description="Effect mix factor: -1.0 = Noise, +1.0 = Effect" - ) + name="Mix Factor", + default=0.0, + min=-1.0, + max=1.0, + description="Effect mix factor: -1.0 = Noise, +1.0 = Effect" + ) fx_mix_mode: EnumProperty( - name="Effect Mix", - default="0", - description="Effect mix mode", - items = [ + name="Effect Mix", + default="0", + description="Effect mix mode", + items=[ ("0", "Mix", "Mix", 0), ("1", "Add", "Add", 1), ("2", "Sub", "Subtract", 2), @@ -695,13 +696,13 @@ class AntLandscapePropertiesGroup(bpy.types.PropertyGroup): ("6", "Mod", "Modulo", 6), ("7", "Min", "Minimum", 7), ("8", "Max", "Maximum", 8) - ] - ) + ] + ) fx_type: EnumProperty( - name="Effect Type", - default="0", - description="Effect type", - items = [ + name="Effect Type", + default="0", + description="Effect type", + items=[ ("0", "None", "No effect", 0), ("1", "Gradient", "Gradient", 1), ("2", "Waves", "Waves - Bumps", 2), @@ -724,183 +725,184 @@ class AntLandscapePropertiesGroup(bpy.types.PropertyGroup): ("19", "Stone", "Stone", 19), ("20", "Flat Turb", "Flat turbulence", 20), ("21", "Flat Voronoi", "Flat voronoi", 21) - ] - ) + ] + ) fx_bias: EnumProperty( - name="Effect Bias", - default="0", - description="Effect bias type", - items = [ + name="Effect Bias", + default="0", + description="Effect bias type", + items=[ ("0", "Sin", "Sin", 0), ("1", "Cos", "Cos", 1), ("2", "Tri", "Tri", 2), ("3", "Saw", "Saw", 3), ("4", "None", "None", 4)] - ) + ) fx_turb: FloatProperty( - name="Distortion", - default=0.0, - min=0.0, - max=1000.0, - description="Effect turbulence distortion" - ) + name="Distortion", + default=0.0, + min=0.0, + max=1000.0, + description="Effect turbulence distortion" + ) fx_depth: IntProperty( - name="Depth", - default=0, - min=0, - max=16, - description="Effect depth - number of frequencies" - ) + name="Depth", + default=0, + min=0, + max=16, + description="Effect depth - number of frequencies" + ) fx_amplitude: FloatProperty( - name="Amp", - default=0.5, - min=0.01, - max=1.0, - description="Amplitude" - ) + name="Amp", + default=0.5, + min=0.01, + max=1.0, + description="Amplitude" + ) fx_frequency: FloatProperty( - name="Freq", - default=2.0, - min=0.01, - max=5.0, - description="Frequency" - ) + name="Freq", + default=2.0, + min=0.01, + max=5.0, + description="Frequency" + ) fx_size: FloatProperty( - name="Effect Size", - default=1.0, - min=0.01, - max=1000.0, - description="Effect size" - ) + name="Effect Size", + default=1.0, + min=0.01, + max=1000.0, + description="Effect size" + ) fx_loc_x: FloatProperty( - name="Offset X", - default=0.0, - description="Effect x offset" - ) + name="Offset X", + default=0.0, + description="Effect x offset" + ) fx_loc_y: FloatProperty( - name="Offset Y", - default=0.0, - description="Effect y offset" - ) + name="Offset Y", + default=0.0, + description="Effect y offset" + ) fx_height: FloatProperty( - name="Intensity", - default=1.0, - min=-1000.0, - max=1000.0, - description="Effect intensity scale" - ) + name="Intensity", + default=1.0, + min=-1000.0, + max=1000.0, + description="Effect intensity scale" + ) fx_invert: BoolProperty( - name="Invert", - default=False, - description="Effect invert" - ) + name="Invert", + default=False, + description="Effect invert" + ) fx_offset: FloatProperty( - name="Offset", - default=0.0, - min=-1000.0, - max=1000.0, - description="Effect height offset" - ) + name="Offset", + default=0.0, + min=-1000.0, + max=1000.0, + description="Effect height offset" + ) edge_falloff: EnumProperty( - name="Falloff", - default="3", - description="Flatten edges", - items = [ + name="Falloff", + default="3", + description="Flatten edges", + items=[ ("0", "None", "None", 0), ("1", "Y", "Y Falloff", 1), ("2", "X", "X Falloff", 2), ("3", "X Y", "X Y Falloff", 3)] - ) + ) falloff_x: FloatProperty( - name="Falloff X", - default=4.0, - min=0.1, - max=100.0, - description="Falloff x scale" - ) + name="Falloff X", + default=4.0, + min=0.1, + max=100.0, + description="Falloff x scale" + ) falloff_y: FloatProperty( - name="Falloff Y", - default=4.0, - min=0.1, - max=100.0, - description="Falloff y scale" - ) + name="Falloff Y", + default=4.0, + min=0.1, + max=100.0, + description="Falloff y scale" + ) edge_level: FloatProperty( - name="Edge Level", - default=0.0, - min=-10000.0, - max=10000.0, - description="Edge level, sealevel offset" - ) + name="Edge Level", + default=0.0, + min=-10000.0, + max=10000.0, + description="Edge level, sealevel offset" + ) maximum: FloatProperty( - name="Maximum", - default=1.0, - min=-10000.0, - max=10000.0, - description="Maximum, flattens terrain at plateau level" - ) + name="Maximum", + default=1.0, + min=-10000.0, + max=10000.0, + description="Maximum, flattens terrain at plateau level" + ) minimum: FloatProperty( - name="Minimum", - default=-1.0, - min=-10000.0, - max=10000.0, - description="Minimum, flattens terrain at seabed level" - ) + name="Minimum", + default=-1.0, + min=-10000.0, + max=10000.0, + description="Minimum, flattens terrain at seabed level" + ) vert_group: StringProperty( - name="Vertex Group", - default="" - ) + name="Vertex Group", + default="" + ) strata: FloatProperty( - name="Amount", - default=5.0, - min=0.01, - max=1000.0, - description="Strata layers / terraces" - ) + name="Amount", + default=5.0, + min=0.01, + max=1000.0, + description="Strata layers / terraces" + ) strata_type: EnumProperty( - name="Strata", - default="0", - description="Strata types", - items = [ + name="Strata", + default="0", + description="Strata types", + items=[ ("0", "None", "No strata", 0), ("1", "Smooth", "Smooth transitions", 1), ("2", "Sharp Sub", "Sharp subtract transitions", 2), ("3", "Sharp Add", "Sharp add transitions", 3), ("4", "Quantize", "Quantize", 4), ("5", "Quantize Mix", "Quantize mixed", 5)] - ) + ) water_plane: BoolProperty( - name="Water Plane", - default=False, - description="Add water plane" - ) + name="Water Plane", + default=False, + description="Add water plane" + ) water_level: FloatProperty( - name="Level", - default=0.01, - min=-10000.0, - max=10000.0, - description="Water level" - ) + name="Level", + default=0.01, + min=-10000.0, + max=10000.0, + description="Water level" + ) remove_double: BoolProperty( - name="Remove Doubles", - default=False, - description="Remove doubles" - ) + name="Remove Doubles", + default=False, + description="Remove doubles" + ) refresh: BoolProperty( - name="Refresh", - default=False, - description="Refresh" - ) + name="Refresh", + default=False, + description="Refresh" + ) auto_refresh: BoolProperty( - name="Auto", - default=True, - description="Automatic refresh" - ) + name="Auto", + default=True, + description="Automatic refresh" + ) # ------------------------------------------------------------ # Register: + classes = ( AntLandscapeAddPanel, AntLandscapeToolsPanel, @@ -916,12 +918,16 @@ classes = ( ant_functions.Eroder, ) + def register(): for cls in classes: bpy.utils.register_class(cls) bpy.types.VIEW3D_MT_mesh_add.append(menu_func_landscape) - bpy.types.Object.ant_landscape = PointerProperty(type=AntLandscapePropertiesGroup, name="ANT_Landscape", description="Landscape properties") + bpy.types.Object.ant_landscape = PointerProperty( + type=AntLandscapePropertiesGroup, + name="ANT_Landscape", + description="Landscape properties") bpy.types.VIEW3D_MT_paint_weight.append(menu_func_eroder) diff --git a/ant_landscape/add_mesh_ant_landscape.py b/ant_landscape/add_mesh_ant_landscape.py index a148f448..e12561bc 100644 --- a/ant_landscape/add_mesh_ant_landscape.py +++ b/ant_landscape/add_mesh_ant_landscape.py @@ -6,30 +6,32 @@ # import modules import bpy from bpy.props import ( - BoolProperty, - EnumProperty, - FloatProperty, - IntProperty, - StringProperty, - FloatVectorProperty, - ) + BoolProperty, + EnumProperty, + FloatProperty, + IntProperty, + StringProperty, + FloatVectorProperty, +) from .ant_functions import ( - grid_gen, - sphere_gen, - create_mesh_object, - store_properties, - draw_ant_refresh, - draw_ant_main, - draw_ant_noise, - draw_ant_displace, - draw_ant_water, - ) + grid_gen, + sphere_gen, + create_mesh_object, + store_properties, + draw_ant_refresh, + draw_ant_main, + draw_ant_noise, + draw_ant_displace, + draw_ant_water, +) from ant_landscape import ant_noise # ------------------------------------------------------------ # Add landscape + + class AntAddLandscape(bpy.types.Operator): bl_idname = "mesh.landscape_add" bl_label = "Another Noise Tool - Landscape" @@ -37,131 +39,131 @@ class AntAddLandscape(bpy.types.Operator): bl_options = {'REGISTER', 'UNDO', 'PRESET'} ant_terrain_name: StringProperty( - name="Name", - default="Landscape" - ) + name="Name", + default="Landscape" + ) land_material: StringProperty( - name='Material', - default="", - description="Terrain material" - ) + name='Material', + default="", + description="Terrain material" + ) water_material: StringProperty( - name='Material', - default="", - description="Water plane material" - ) + name='Material', + default="", + description="Water plane material" + ) texture_block: StringProperty( - name="Texture", - default="" - ) + name="Texture", + default="" + ) at_cursor: BoolProperty( - name="Cursor", - default=True, - description="Place at cursor location", - ) + name="Cursor", + default=True, + description="Place at cursor location", + ) smooth_mesh: BoolProperty( - name="Smooth", - default=True, - description="Shade smooth" - ) + name="Smooth", + default=True, + description="Shade smooth" + ) tri_face: BoolProperty( - name="Triangulate", - default=False, - description="Triangulate faces" - ) + name="Triangulate", + default=False, + description="Triangulate faces" + ) sphere_mesh: BoolProperty( - name="Sphere", - default=False, - description="Generate uv sphere - remove doubles when ready" - ) + name="Sphere", + default=False, + description="Generate uv sphere - remove doubles when ready" + ) subdivision_x: IntProperty( - name="Subdivisions X", - default=128, - min=4, - max=6400, - description="Mesh X subdivisions" - ) + name="Subdivisions X", + default=128, + min=4, + max=6400, + description="Mesh X subdivisions" + ) subdivision_y: IntProperty( - default=128, - name="Subdivisions Y", - min=4, - max=6400, - description="Mesh Y subdivisions" - ) + default=128, + name="Subdivisions Y", + min=4, + max=6400, + description="Mesh Y subdivisions" + ) mesh_size: FloatProperty( - default=2.0, - name="Mesh Size", - min=0.01, - max=100000.0, - description="Mesh size" - ) + default=2.0, + name="Mesh Size", + min=0.01, + max=100000.0, + description="Mesh size" + ) mesh_size_x: FloatProperty( - default=2.0, - name="Mesh Size X", - min=0.01, - description="Mesh x size" - ) + default=2.0, + name="Mesh Size X", + min=0.01, + description="Mesh x size" + ) mesh_size_y: FloatProperty( - name="Mesh Size Y", - default=2.0, - min=0.01, - description="Mesh y size" - ) + name="Mesh Size Y", + default=2.0, + min=0.01, + description="Mesh y size" + ) random_seed: IntProperty( - name="Random Seed", - default=0, - min=0, - description="Randomize noise origin" - ) + name="Random Seed", + default=0, + min=0, + description="Randomize noise origin" + ) noise_offset_x: FloatProperty( - name="Offset X", - default=0.0, - description="Noise X Offset" - ) + name="Offset X", + default=0.0, + description="Noise X Offset" + ) noise_offset_y: FloatProperty( - name="Offset Y", - default=0.0, - description="Noise Y Offset" - ) + name="Offset Y", + default=0.0, + description="Noise Y Offset" + ) noise_offset_z: FloatProperty( - name="Offset Z", - default=0.0, - description="Noise Z Offset" - ) + name="Offset Z", + default=0.0, + description="Noise Z Offset" + ) noise_size_x: FloatProperty( - default=1.0, - name="Size X", - min=0.01, - max=1000.0, - description="Noise x size" - ) + default=1.0, + name="Size X", + min=0.01, + max=1000.0, + description="Noise x size" + ) noise_size_y: FloatProperty( - name="Size Y", - default=1.0, - min=0.01, - max=1000.0, - description="Noise y size" - ) + name="Size Y", + default=1.0, + min=0.01, + max=1000.0, + description="Noise y size" + ) noise_size_z: FloatProperty( - name="Size Z", - default=1.0, - min=0.01, - max=1000.0, - description="Noise Z size" - ) + name="Size Z", + default=1.0, + min=0.01, + max=1000.0, + description="Noise Z size" + ) noise_size: FloatProperty( - name="Noise Size", - default=1.0, - min=0.01, - max=1000.0, - description="Noise size" - ) + name="Noise Size", + default=1.0, + min=0.01, + max=1000.0, + description="Noise size" + ) noise_type: EnumProperty( - name="Noise Type", - default='hetero_terrain', - description="Noise type", - items = [ + name="Noise Type", + default='hetero_terrain', + description="Noise type", + items=[ ('multi_fractal', "Multi Fractal", "Blender: Multi Fractal algorithm", 0), ('ridged_multi_fractal', "Ridged MFractal", "Blender: Ridged Multi Fractal", 1), ('hybrid_multi_fractal', "Hybrid MFractal", "Blender: Hybrid Multi Fractal", 2), @@ -181,110 +183,110 @@ class AntAddLandscape(bpy.types.Operator): ('slick_rock', "Slick Rock", "A.N.T: slick rock", 16), ('planet_noise', "Planet Noise", "Planet Noise by: Farsthary", 17), ('blender_texture', "Blender Texture - Texture Nodes", "Blender texture data block", 18)] - ) + ) basis_type: EnumProperty( - name="Noise Basis", - default=ant_noise.noise_basis_default, - description="Noise basis algorithms", - items = ant_noise.noise_basis - ) + name="Noise Basis", + default=ant_noise.noise_basis_default, + description="Noise basis algorithms", + items=ant_noise.noise_basis + ) vl_basis_type: EnumProperty( - name="vlNoise Basis", - default=ant_noise.noise_basis_default, - description="VLNoise basis algorithms", - items = ant_noise.noise_basis - ) + name="vlNoise Basis", + default=ant_noise.noise_basis_default, + description="VLNoise basis algorithms", + items=ant_noise.noise_basis + ) distortion: FloatProperty( - name="Distortion", - default=1.0, - min=0.01, - max=100.0, - description="Distortion amount" - ) + name="Distortion", + default=1.0, + min=0.01, + max=100.0, + description="Distortion amount" + ) hard_noise: EnumProperty( - name="Soft Hard", - default="0", - description="Soft Noise, Hard noise", - items = [ + name="Soft Hard", + default="0", + description="Soft Noise, Hard noise", + items=[ ("0", "Soft", "Soft Noise", 0), ("1", "Hard", "Hard noise", 1)] - ) + ) noise_depth: IntProperty( - name="Depth", - default=8, - min=0, - max=16, - description="Noise Depth - number of frequencies in the fBm" - ) + name="Depth", + default=8, + min=0, + max=16, + description="Noise Depth - number of frequencies in the fBm" + ) amplitude: FloatProperty( - name="Amp", - default=0.5, - min=0.01, - max=1.0, - description="Amplitude" - ) + name="Amp", + default=0.5, + min=0.01, + max=1.0, + description="Amplitude" + ) frequency: FloatProperty( - name="Freq", - default=2.0, - min=0.01, - max=5.0, - description="Frequency" - ) + name="Freq", + default=2.0, + min=0.01, + max=5.0, + description="Frequency" + ) dimension: FloatProperty( - name="Dimension", - default=1.0, - min=0.01, - max=2.0, - description="H - fractal dimension of the roughest areas" - ) + name="Dimension", + default=1.0, + min=0.01, + max=2.0, + description="H - fractal dimension of the roughest areas" + ) lacunarity: FloatProperty( - name="Lacunarity", - min=0.01, - max=6.0, - default=2.0, - description="Lacunarity - gap between successive frequencies" - ) + name="Lacunarity", + min=0.01, + max=6.0, + default=2.0, + description="Lacunarity - gap between successive frequencies" + ) offset: FloatProperty( - name="Offset", - default=1.0, - min=0.01, - max=6.0, - description="Offset - raises the terrain from sea level" - ) + name="Offset", + default=1.0, + min=0.01, + max=6.0, + description="Offset - raises the terrain from sea level" + ) gain: FloatProperty( - name="Gain", - default=1.0, - min=0.01, - max=6.0, - description="Gain - scale factor" - ) + name="Gain", + default=1.0, + min=0.01, + max=6.0, + description="Gain - scale factor" + ) marble_bias: EnumProperty( - name="Bias", - default="0", - description="Marble bias", - items = [ + name="Bias", + default="0", + description="Marble bias", + items=[ ("0", "Sin", "Sin", 0), ("1", "Cos", "Cos", 1), ("2", "Tri", "Tri", 2), ("3", "Saw", "Saw", 3)] - ) + ) marble_sharp: EnumProperty( - name="Sharp", - default="0", - description="Marble sharpness", - items = [ + name="Sharp", + default="0", + description="Marble sharpness", + items=[ ("0", "Soft", "Soft", 0), ("1", "Sharp", "Sharp", 1), ("2", "Sharper", "Sharper", 2), ("3", "Soft inv.", "Soft", 3), ("4", "Sharp inv.", "Sharp", 4), ("5", "Sharper inv.", "Sharper", 5)] - ) + ) marble_shape: EnumProperty( - name="Shape", - default="0", - description="Marble shape", - items= [ + name="Shape", + default="0", + description="Marble shape", + items=[ ("0", "Default", "Default", 0), ("1", "Ring", "Ring", 1), ("2", "Swirl", "Swirl", 2), @@ -293,38 +295,38 @@ class AntAddLandscape(bpy.types.Operator): ("5", "Z", "Z", 5), ("6", "Y", "Y", 6), ("7", "X", "X", 7)] - ) + ) height: FloatProperty( - name="Height", - default=0.5, - min=-10000.0, - max=10000.0, - description="Noise intensity scale" - ) + name="Height", + default=0.5, + min=-10000.0, + max=10000.0, + description="Noise intensity scale" + ) height_invert: BoolProperty( - name="Invert", - default=False, - description="Height invert", - ) + name="Invert", + default=False, + description="Height invert", + ) height_offset: FloatProperty( - name="Offset", - default=0.0, - min=-10000.0, - max=10000.0, - description="Height offset" - ) + name="Offset", + default=0.0, + min=-10000.0, + max=10000.0, + description="Height offset" + ) fx_mixfactor: FloatProperty( - name="Mix Factor", - default=0.0, - min=-1.0, - max=1.0, - description="Effect mix factor: -1.0 = Noise, +1.0 = Effect" - ) + name="Mix Factor", + default=0.0, + min=-1.0, + max=1.0, + description="Effect mix factor: -1.0 = Noise, +1.0 = Effect" + ) fx_mix_mode: EnumProperty( - name="Effect Mix", - default="0", - description="Effect mix mode", - items = [ + name="Effect Mix", + default="0", + description="Effect mix mode", + items=[ ("0", "Mix", "Mix", 0), ("1", "Add", "Add", 1), ("2", "Sub", "Subtract", 2), @@ -334,13 +336,13 @@ class AntAddLandscape(bpy.types.Operator): ("6", "Mod", "Modulo", 6), ("7", "Min", "Minimum", 7), ("8", "Max", "Maximum", 8) - ] - ) + ] + ) fx_type: EnumProperty( - name="Effect Type", - default="0", - description="Effect type", - items = [ + name="Effect Type", + default="0", + description="Effect type", + items=[ ("0", "None", "No effect", 0), ("1", "Gradient", "Gradient", 1), ("2", "Waves", "Waves - Bumps", 2), @@ -363,194 +365,194 @@ class AntAddLandscape(bpy.types.Operator): ("19", "Stone", "Stone", 19), ("20", "Flat Turb", "Flat turbulence", 20), ("21", "Flat Voronoi", "Flat voronoi", 21) - ] - ) + ] + ) fx_bias: EnumProperty( - name="Effect Bias", - default="0", - description="Effect bias type", - items = [ + name="Effect Bias", + default="0", + description="Effect bias type", + items=[ ("0", "Sin", "Sin", 0), ("1", "Cos", "Cos", 1), ("2", "Tri", "Tri", 2), ("3", "Saw", "Saw", 3), ("4", "None", "None", 4)] - ) + ) fx_turb: FloatProperty( - name="Distortion", - default=0.0, - min=0.0, - max=1000.0, - description="Effect turbulence distortion" - ) + name="Distortion", + default=0.0, + min=0.0, + max=1000.0, + description="Effect turbulence distortion" + ) fx_depth: IntProperty( - name="Depth", - default=0, - min=0, - max=16, - description="Effect depth - number of frequencies" - ) + name="Depth", + default=0, + min=0, + max=16, + description="Effect depth - number of frequencies" + ) fx_amplitude: FloatProperty( - name="Amp", - default=0.5, - min=0.01, - max=1.0, - description="Amplitude" - ) + name="Amp", + default=0.5, + min=0.01, + max=1.0, + description="Amplitude" + ) fx_frequency: FloatProperty( - name="Freq", - default=2.0, - min=0.01, - max=5.0, - description="Frequency" - ) + name="Freq", + default=2.0, + min=0.01, + max=5.0, + description="Frequency" + ) fx_size: FloatProperty( - name="Effect Size", - default=1.0, - min=0.01, - max=1000.0, - description="Effect size" - ) + name="Effect Size", + default=1.0, + min=0.01, + max=1000.0, + description="Effect size" + ) fx_loc_x: FloatProperty( - name="Offset X", - default=0.0, - description="Effect x offset" - ) + name="Offset X", + default=0.0, + description="Effect x offset" + ) fx_loc_y: FloatProperty( - name="Offset Y", - default=0.0, - description="Effect y offset" - ) + name="Offset Y", + default=0.0, + description="Effect y offset" + ) fx_height: FloatProperty( - name="Intensity", - default=1.0, - min=-1000.0, - max=1000.0, - description="Effect intensity scale" - ) + name="Intensity", + default=1.0, + min=-1000.0, + max=1000.0, + description="Effect intensity scale" + ) fx_invert: BoolProperty( - name="Invert", - default=False, - description="Effect invert" - ) + name="Invert", + default=False, + description="Effect invert" + ) fx_offset: FloatProperty( - name="Offset", - default=0.0, - min=-1000.0, - max=1000.0, - description="Effect height offset" - ) + name="Offset", + default=0.0, + min=-1000.0, + max=1000.0, + description="Effect height offset" + ) edge_falloff: EnumProperty( - name="Falloff", - default="3", - description="Flatten edges", - items = [ + name="Falloff", + default="3", + description="Flatten edges", + items=[ ("0", "None", "None", 0), ("1", "Y", "Y Falloff", 1), ("2", "X", "X Falloff", 2), ("3", "X Y", "X Y Falloff", 3)] - ) + ) falloff_x: FloatProperty( - name="Falloff X", - default=4.0, - min=0.1, - max=100.0, - description="Falloff x scale" - ) + name="Falloff X", + default=4.0, + min=0.1, + max=100.0, + description="Falloff x scale" + ) falloff_y: FloatProperty( - name="Falloff Y", - default=4.0, - min=0.1, - max=100.0, - description="Falloff y scale" - ) + name="Falloff Y", + default=4.0, + min=0.1, + max=100.0, + description="Falloff y scale" + ) edge_level: FloatProperty( - name="Edge Level", - default=0.0, - min=-10000.0, - max=10000.0, - description="Edge level, sealevel offset" - ) + name="Edge Level", + default=0.0, + min=-10000.0, + max=10000.0, + description="Edge level, sealevel offset" + ) maximum: FloatProperty( - name="Maximum", - default=1.0, - min=-10000.0, - max=10000.0, - description="Maximum, flattens terrain at plateau level" - ) + name="Maximum", + default=1.0, + min=-10000.0, + max=10000.0, + description="Maximum, flattens terrain at plateau level" + ) minimum: FloatProperty( - name="Minimum", - default=-1.0, - min=-10000.0, - max=10000.0, - description="Minimum, flattens terrain at seabed level" - ) + name="Minimum", + default=-1.0, + min=-10000.0, + max=10000.0, + description="Minimum, flattens terrain at seabed level" + ) vert_group: StringProperty( - name="Vertex Group", - default="" - ) + name="Vertex Group", + default="" + ) strata: FloatProperty( - name="Amount", - default=5.0, - min=0.01, - max=1000.0, - description="Strata layers / terraces" - ) + name="Amount", + default=5.0, + min=0.01, + max=1000.0, + description="Strata layers / terraces" + ) strata_type: EnumProperty( - name="Strata", - default="0", - description="Strata types", - items = [ + name="Strata", + default="0", + description="Strata types", + items=[ ("0", "None", "No strata", 0), ("1", "Smooth", "Smooth transitions", 1), ("2", "Sharp Sub", "Sharp subtract transitions", 2), ("3", "Sharp Add", "Sharp add transitions", 3), ("4", "Quantize", "Quantize", 4), ("5", "Quantize Mix", "Quantize mixed", 5)] - ) + ) water_plane: BoolProperty( - name="Water Plane", - default=False, - description="Add water plane" - ) + name="Water Plane", + default=False, + description="Add water plane" + ) water_level: FloatProperty( - name="Level", - default=0.01, - min=-10000.0, - max=10000.0, - description="Water level" - ) + name="Level", + default=0.01, + min=-10000.0, + max=10000.0, + description="Water level" + ) remove_double: BoolProperty( - name="Remove Doubles", - default=False, - description="Remove doubles" - ) + name="Remove Doubles", + default=False, + description="Remove doubles" + ) show_main_settings: BoolProperty( - name="Main Settings", - default=True, - description="Show settings" - ) + name="Main Settings", + default=True, + description="Show settings" + ) show_noise_settings: BoolProperty( - name="Noise Settings", - default=True, - description="Show noise settings" - ) + name="Noise Settings", + default=True, + description="Show noise settings" + ) show_displace_settings: BoolProperty( - name="Displace Settings", - default=True, - description="Show displace settings" - ) + name="Displace Settings", + default=True, + description="Show displace settings" + ) refresh: BoolProperty( - name="Refresh", - default=False, - description="Refresh" - ) + name="Refresh", + default=False, + description="Refresh" + ) auto_refresh: BoolProperty( - name="Auto", - default=True, - description="Automatic refresh" - ) + name="Auto", + default=True, + description="Automatic refresh" + ) @classmethod def poll(self, context): @@ -567,12 +569,10 @@ class AntAddLandscape(bpy.types.Operator): draw_ant_displace(self, context, generate=True) draw_ant_water(self, context) - def invoke(self, context, event): self.refresh = True return self.execute(context) - def execute(self, context): if not self.refresh: return {'PASS_THROUGH'} @@ -652,7 +652,7 @@ class AntAddLandscape(bpy.types.Operator): self.fx_height, self.fx_offset, self.fx_invert - ] + ] scene = context.scene vl = context.view_layer @@ -661,37 +661,37 @@ class AntAddLandscape(bpy.types.Operator): if self.ant_terrain_name != "": new_name = self.ant_terrain_name else: - new_name = "Landscape" + new_name = "Landscape" if self.sphere_mesh: # sphere verts, faces = sphere_gen( - self.subdivision_y, - self.subdivision_x, - self.tri_face, - self.mesh_size, - ant_props, - False, - 0.0 - ) + self.subdivision_y, + self.subdivision_x, + self.tri_face, + self.mesh_size, + ant_props, + False, + 0.0, + ) new_ob = create_mesh_object(context, verts, [], faces, new_name) if self.remove_double: new_ob.select_set(True) - bpy.ops.object.mode_set(mode = 'EDIT') + bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.remove_doubles(threshold=0.0001, use_unselected=False) - bpy.ops.object.mode_set(mode = 'OBJECT') + bpy.ops.object.mode_set(mode='OBJECT') else: # grid verts, faces = grid_gen( - self.subdivision_x, - self.subdivision_y, - self.tri_face, - self.mesh_size_x, - self.mesh_size_y, - ant_props, - False, - 0.0 - ) + self.subdivision_x, + self.subdivision_y, + self.tri_face, + self.mesh_size_x, + self.mesh_size_y, + ant_props, + False, + 0.0, + ) new_ob = create_mesh_object(context, verts, [], faces, new_name) new_ob.select_set(True) @@ -712,33 +712,33 @@ class AntAddLandscape(bpy.types.Operator): if self.sphere_mesh: # sphere verts, faces = sphere_gen( - self.subdivision_y, - self.subdivision_x, - self.tri_face, - self.mesh_size, - ant_props, - self.water_plane, - self.water_level - ) - wobj = create_mesh_object(context, verts, [], faces, new_name+"_plane") + self.subdivision_y, + self.subdivision_x, + self.tri_face, + self.mesh_size, + ant_props, + self.water_plane, + self.water_level, + ) + wobj = create_mesh_object(context, verts, [], faces, new_name + "_plane") if self.remove_double: wobj.select_set(True) - bpy.ops.object.mode_set(mode = 'EDIT') + bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.remove_doubles(threshold=0.0001, use_unselected=False) - bpy.ops.object.mode_set(mode = 'OBJECT') + bpy.ops.object.mode_set(mode='OBJECT') else: # grid verts, faces = grid_gen( - 2, - 2, - self.tri_face, - self.mesh_size_x, - self.mesh_size_y, - ant_props, - self.water_plane, - self.water_level - ) - wobj = create_mesh_object(context, verts, [], faces, new_name+"_plane") + 2, + 2, + self.tri_face, + self.mesh_size_x, + self.mesh_size_y, + ant_props, + self.water_plane, + self.water_level, + ) + wobj = create_mesh_object(context, verts, [], faces, new_name + "_plane") wobj.select_set(True) diff --git a/ant_landscape/ant_functions.py b/ant_landscape/ant_functions.py index 77bb5c83..1337533c 100644 --- a/ant_landscape/ant_functions.py +++ b/ant_landscape/ant_functions.py @@ -9,16 +9,16 @@ # import modules import bpy from bpy.props import ( - BoolProperty, - FloatProperty, - StringProperty, - EnumProperty, - IntProperty, - PointerProperty, - ) + BoolProperty, + FloatProperty, + StringProperty, + EnumProperty, + IntProperty, + PointerProperty, +) from math import ( - sin, cos, pi, - ) + sin, cos, pi, +) from .ant_noise import noise_gen # ------------------------------------------------------------ @@ -29,6 +29,7 @@ from .ant_noise import noise_gen from bpy_extras import object_utils + def create_mesh_object(context, verts, edges, faces, name): # Create new mesh mesh = bpy.data.meshes.new(name) @@ -45,7 +46,7 @@ def grid_gen(sub_d_x, sub_d_y, tri, meshsize_x, meshsize_y, props, water_plane, faces = [] vappend = verts.append fappend = faces.append - for i in range (0, sub_d_x): + for i in range(0, sub_d_x): x = meshsize_x * (i / (sub_d_x - 1) - 1 / 2) for j in range(0, sub_d_y): y = meshsize_y * (j / (sub_d_y - 1) - 1 / 2) @@ -53,7 +54,7 @@ def grid_gen(sub_d_x, sub_d_y, tri, meshsize_x, meshsize_y, props, water_plane, z = noise_gen((x, y, 0), props) else: z = water_level - vappend((x,y,z)) + vappend((x, y, z)) if i > 0 and j > 0: A = i * sub_d_y + (j - 1) @@ -89,8 +90,8 @@ def sphere_gen(sub_d_x, sub_d_y, tri, meshsize, props, water_plane, water_level) vappend(((u + u * h), (v + v * h), (w + w * h))) count = 0 - for i in range (0, sub_d_y * (sub_d_x - 1)): - if count < sub_d_y - 1 : + for i in range(0, sub_d_y * (sub_d_x - 1)): + if count < sub_d_y - 1: A = i + 1 B = i C = (i + sub_d_y) @@ -115,7 +116,6 @@ class AntLandscapeRefresh(bpy.types.Operator): bl_description = "Refresh landscape with current settings" bl_options = {'REGISTER', 'UNDO'} - @classmethod def poll(cls, context): ob = bpy.context.active_object @@ -125,8 +125,8 @@ class AntLandscapeRefresh(bpy.types.Operator): # ant object items obj = bpy.context.active_object - bpy.ops.object.mode_set(mode = 'EDIT') - bpy.ops.object.mode_set(mode = 'OBJECT') + bpy.ops.object.mode_set(mode='EDIT') + bpy.ops.object.mode_set(mode='OBJECT') keys = obj.ant_landscape.keys() if keys: @@ -158,13 +158,14 @@ class AntLandscapeRefresh(bpy.types.Operator): # ------------------------------------------------------------ # Do regenerate + + class AntLandscapeRegenerate(bpy.types.Operator): bl_idname = "mesh.ant_landscape_regenerate" bl_label = "Regenerate" bl_description = "Regenerate landscape with current settings" bl_options = {'REGISTER', 'UNDO'} - @classmethod def poll(cls, context): ob = bpy.context.active_object @@ -172,7 +173,6 @@ class AntLandscapeRegenerate(bpy.types.Operator): return False return ob.ant_landscape - def execute(self, context): view_layer = bpy.context.view_layer @@ -192,32 +192,32 @@ class AntLandscapeRegenerate(bpy.types.Operator): if ob['sphere_mesh']: # sphere verts, faces = sphere_gen( - ob['subdivision_y'], - ob['subdivision_x'], - ob['tri_face'], - ob['mesh_size'], - ant_props, - False, - 0.0 - ) + ob['subdivision_y'], + ob['subdivision_x'], + ob['tri_face'], + ob['mesh_size'], + ant_props, + False, + 0.0, + ) new_ob = create_mesh_object(context, verts, [], faces, new_name) if ob['remove_double']: new_ob.select_set(True) - bpy.ops.object.mode_set(mode = 'EDIT') + bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.remove_doubles(threshold=0.0001, use_unselected=False) - bpy.ops.object.mode_set(mode = 'OBJECT') + bpy.ops.object.mode_set(mode='OBJECT') else: # grid verts, faces = grid_gen( - ob['subdivision_x'], - ob['subdivision_y'], - ob['tri_face'], - ob['mesh_size_x'], - ob['mesh_size_y'], - ant_props, - False, - 0.0 - ) + ob['subdivision_x'], + ob['subdivision_y'], + ob['tri_face'], + ob['mesh_size_x'], + ob['mesh_size_y'], + ant_props, + False, + 0.0, + ) new_ob = create_mesh_object(context, verts, [], faces, new_name) new_ob.select_set(True) @@ -235,33 +235,33 @@ class AntLandscapeRegenerate(bpy.types.Operator): if ob['sphere_mesh']: # sphere verts, faces = sphere_gen( - ob['subdivision_y'], - ob['subdivision_x'], - ob['tri_face'], - ob['mesh_size'], - ant_props, - ob['water_plane'], - ob['water_level'] - ) - wobj = create_mesh_object(context, verts, [], faces, new_name+"_plane") + ob['subdivision_y'], + ob['subdivision_x'], + ob['tri_face'], + ob['mesh_size'], + ant_props, + ob['water_plane'], + ob['water_level'], + ) + wobj = create_mesh_object(context, verts, [], faces, new_name + "_plane") if ob['remove_double']: wobj.select_set(True) - bpy.ops.object.mode_set(mode = 'EDIT') + bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.remove_doubles(threshold=0.0001, use_unselected=False) - bpy.ops.object.mode_set(mode = 'OBJECT') + bpy.ops.object.mode_set(mode='OBJECT') else: # grid verts, faces = grid_gen( - 2, - 2, - ob['tri_face'], - ob['mesh_size_x'], - ob['mesh_size_y'], - ant_props, - ob['water_plane'], - ob['water_level'] - ) - wobj = create_mesh_object(context, verts, [], faces, new_name+"_plane") + 2, + 2, + ob['tri_face'], + ob['mesh_size_x'], + ob['mesh_size_y'], + ant_props, + ob['water_plane'], + ob['water_level'], + ) + wobj = create_mesh_object(context, verts, [], faces, new_name + "_plane") wobj.select_set(True) @@ -310,41 +310,39 @@ class AntVgSlopeMap(bpy.types.Operator): bl_options = {'REGISTER', 'UNDO'} z_method: EnumProperty( - name="Method:", - default='SLOPE_Z', - items=[ + name="Method:", + default='SLOPE_Z', + items=[ ('SLOPE_Z', "Z Slope", "Slope for planar mesh"), ('SLOPE_XYZ', "Sphere Slope", "Slope for spherical mesh") - ]) + ]) group_name: StringProperty( - name="Vertex Group Name:", - default="Slope", - description="Name" - ) + name="Vertex Group Name:", + default="Slope", + description="Name" + ) select_flat: BoolProperty( - name="Vert Select:", - default=True, - description="Select vertices on flat surface" - ) + name="Vert Select:", + default=True, + description="Select vertices on flat surface" + ) select_range: FloatProperty( - name="Vert Select Range:", - default=0.0, - min=0.0, - max=1.0, - description="Increase to select more vertices on slopes" - ) + name="Vert Select Range:", + default=0.0, + min=0.0, + max=1.0, + description="Increase to select more vertices on slopes" + ) @classmethod def poll(cls, context): ob = context.object return (ob and ob.type == 'MESH') - def invoke(self, context, event): wm = context.window_manager return wm.invoke_props_dialog(self) - def execute(self, context): message = "Popup Values: %d, %f, %s, %s" % \ (self.select_flat, self.select_range, self.group_name, self.z_method) @@ -421,7 +419,7 @@ def draw_ant_main(self, context, generate=True): split.prop(self, "remove_double", toggle=True) box.prop(self, "ant_terrain_name") - box.prop_search(self, "land_material", bpy.data, "materials") + box.prop_search(self, "land_material", bpy.data, "materials") col = box.column(align=True) col.prop(self, "subdivision_x") @@ -450,11 +448,11 @@ def draw_ant_noise(self, context, generate=True): col = box.column(align=True) col.prop(self, "noise_offset_x") col.prop(self, "noise_offset_y") - if self.sphere_mesh == True or generate == False: + if self.sphere_mesh or generate == False: col.prop(self, "noise_offset_z") col.prop(self, "noise_size_x") col.prop(self, "noise_size_y") - if self.sphere_mesh == True or generate == False: + if self.sphere_mesh or generate == False: col.prop(self, "noise_size_z") col = box.column(align=True) @@ -647,7 +645,7 @@ def draw_ant_displace(self, context, generate=True): if not generate: col = box.column(align=False) - col.prop_search(self, "vert_group", bpy.context.object, "vertex_groups") + col.prop_search(self, "vert_group", bpy.context.object, "vertex_groups") def draw_ant_water(self, context): @@ -657,7 +655,7 @@ def draw_ant_water(self, context): col.prop(self, "water_plane", toggle=True) if self.water_plane: col = box.column(align=True) - col.prop_search(self, "water_material", bpy.data, "materials") + col.prop_search(self, "water_material", bpy.data, "materials") col = box.column() col.prop(self, "water_level") @@ -744,8 +742,8 @@ from .utils import numexpr_available def availableVertexGroupsOrNone(self, context): - groups = [ ('None', 'None', 'None', 1) ] - return groups + [(name, name, name, n+1) for n,name in enumerate(context.active_object.vertex_groups.keys())] + groups = [('None', 'None', 'None', 1)] + return groups + [(name, name, name, n + 1) for n, name in enumerate(context.active_object.vertex_groups.keys())] class Eroder(bpy.types.Operator): @@ -755,159 +753,160 @@ class Eroder(bpy.types.Operator): bl_options = {'REGISTER', 'UNDO', 'PRESET'} Iterations: IntProperty( - name="Iterations", - description="Number of overall iterations", - default=1, - min=1, - soft_max=100 - ) + name="Iterations", + description="Number of overall iterations", + default=1, + min=1, + soft_max=100 + ) IterRiver: IntProperty( - name="River Iterations", - description="Number of river iterations", - default=30, - min=1, - soft_max=1000 - ) + name="River Iterations", + description="Number of river iterations", + default=30, + min=1, + soft_max=1000 + ) IterAva: IntProperty( - name="Avalanche Iterations", - description="Number of avalanche iterations", - default=5, - min=1, - soft_max=10 - ) + name="Avalanche Iterations", + description="Number of avalanche iterations", + default=5, + min=1, + soft_max=10 + ) IterDiffuse: IntProperty( - name="Diffuse Iterations", - description="Number of diffuse iterations", - default=5, - min=1, - soft_max=10 - ) + name="Diffuse Iterations", + description="Number of diffuse iterations", + default=5, + min=1, + soft_max=10 + ) Ef: FloatProperty( - name="Rain on Plains", - description="1 gives equal rain across the terrain, 0 rains more at the mountain tops", - default=0.0, - min=0, - max=1 - ) + name="Rain on Plains", + description="1 gives equal rain across the terrain, 0 rains more at the mountain tops", + default=0.0, + min=0, + max=1 + ) Kd: FloatProperty( - name="Kd", - description="Thermal diffusion rate (1.0 is a fairly high rate)", - default=0.1, - min=0, - soft_max=100 - ) + name="Kd", + description="Thermal diffusion rate (1.0 is a fairly high rate)", + default=0.1, + min=0, + soft_max=100 + ) Kt: FloatProperty( - name="Kt", - description="Maximum stable talus angle", - default=radians(60), - min=0, - max=radians(90), - subtype='ANGLE' - ) + name="Kt", + description="Maximum stable talus angle", + default=radians(60), + min=0, + max=radians(90), + subtype='ANGLE' + ) Kr: FloatProperty( - name="Rain amount", - description="Total Rain amount", - default=.01, - min=0, - soft_max=1, - precision=3 - ) + name="Rain amount", + description="Total Rain amount", + default=.01, + min=0, + soft_max=1, + precision=3 + ) Kv: FloatProperty( - name="Rain variance", - description="Rain variance (0 is constant, 1 is uniform)", - default=0, - min=0, - max=1 - ) + name="Rain variance", + description="Rain variance (0 is constant, 1 is uniform)", + default=0, + min=0, + max=1 + ) userainmap: BoolProperty( - name="Use rain map", - description="Use active vertex group as a rain map", - default=True - ) + name="Use rain map", + description="Use active vertex group as a rain map", + default=True + ) Ks: FloatProperty( - name="Soil solubility", - description="Soil solubility - how quickly water quickly reaches saturation point", - default=0.5, - min=0, - soft_max=1 - ) + name="Soil solubility", + description="Soil solubility - how quickly water quickly reaches saturation point", + default=0.5, + min=0, + soft_max=1 + ) Kdep: FloatProperty( - name="Deposition rate", - description="Sediment deposition rate - how quickly silt is laid down once water stops flowing quickly", - default=0.1, - min=0, - soft_max=1 - ) - Kz: FloatProperty(name="Fluvial Erosion Rate", - description="Amount of sediment moved each main iteration - if 0, then rivers are formed but the mesh is not changed", - default=0.3, - min=0, - soft_max=20 - ) + name="Deposition rate", + description="Sediment deposition rate - how quickly silt is laid down once water stops flowing quickly", + default=0.1, + min=0, + soft_max=1 + ) + Kz: FloatProperty( + name="Fluvial Erosion Rate", + description="Amount of sediment moved each main iteration - if 0, then rivers are formed but the mesh is not changed", + default=0.3, + min=0, + soft_max=20) Kc: FloatProperty( - name="Carrying capacity", - description="Base sediment carrying capacity", - default=0.9, - min=0, - soft_max=1 - ) + name="Carrying capacity", + description="Base sediment carrying capacity", + default=0.9, + min=0, + soft_max=1 + ) Ka: FloatProperty( - name="Slope dependence", - description="Slope dependence of carrying capacity (not used)", - default=1.0, - min=0, - soft_max=2 - ) + name="Slope dependence", + description="Slope dependence of carrying capacity (not used)", + default=1.0, + min=0, + soft_max=2 + ) Kev: FloatProperty( - name="Evaporation", - description="Evaporation Rate per grid square in % - causes sediment to be dropped closer to the hills", - default=.5, - min=0, - soft_max=2 - ) + name="Evaporation", + description="Evaporation Rate per grid square in % - causes sediment to be dropped closer to the hills", + default=.5, + min=0, + soft_max=2 + ) numexpr: BoolProperty( - name="Numexpr", - description="Use numexpr module (if available)", - default=True - ) + name="Numexpr", + description="Use numexpr module (if available)", + default=True + ) Pd: FloatProperty( - name="Diffusion Amount", - description="Diffusion probability", - default=0.2, - min=0, - max=1 - ) + name="Diffusion Amount", + description="Diffusion probability", + default=0.2, + min=0, + max=1 + ) Pa: FloatProperty( - name="Avalanche Amount", - description="Avalanche amount", - default=0.5, - min=0, - max=1 - ) + name="Avalanche Amount", + description="Avalanche amount", + default=0.5, + min=0, + max=1 + ) Pw: FloatProperty( - name="River Amount", - description="Water erosion probability", - default=1, - min=0, - max=1 - ) + name="River Amount", + description="Water erosion probability", + default=1, + min=0, + max=1 + ) smooth: BoolProperty( - name="Smooth", - description="Set smooth shading", - default=True - ) + name="Smooth", + description="Set smooth shading", + default=True + ) showiterstats: BoolProperty( - name="Iteration Stats", - description="Show iteraration statistics", - default=False - ) - showmeshstats: BoolProperty(name="Mesh Stats", - description="Show mesh statistics", - default=False - ) + name="Iteration Stats", + description="Show iteraration statistics", + default=False + ) + showmeshstats: BoolProperty( + name="Mesh Stats", + description="Show mesh statistics", + default=False + ) stats = Stats() - counts= {} + counts = {} maps = { 'rainmap': lambda g, r, c: g.rainmap[r, c], 'scree': lambda g, r, c: g.avalanced[r, c], @@ -945,21 +944,36 @@ class Eroder(bpy.types.Operator): for i in range(self.Iterations): if self.IterRiver > 0: for i in range(self.IterRiver): - g.rivergeneration(self.Kr, self.Kv, self.userainmap, self.Kc, self.Ks, self.Kdep, self.Ka, self.Kev/100, 0,0,0,0, self.numexpr) + g.rivergeneration( + self.Kr, + self.Kv, + self.userainmap, + self.Kc, + self.Ks, + self.Kdep, + self.Ka, + self.Kev / 100, + 0, + 0, + 0, + 0, + self.numexpr, + ) if self.Kd > 0.0: for k in range(self.IterDiffuse): g.diffuse(self.Kd / 5, self.IterDiffuse, self.numexpr) - self.counts['diffuse']+=1 + self.counts['diffuse'] += 1 if self.Kt < radians(90) and self.Pa > 0: for k in range(self.IterAva): # since dx and dy are scaled to 1, tan(Kt) is the height for a given angle g.avalanche(tan(self.Kt), self.IterAva, self.Pa, self.numexpr) - self.counts['avalanche']+=1 + self.counts['avalanche'] += 1 if self.Kz > 0: - g.fluvial_erosion(self.Kr, self.Kv, self.userainmap, self.Kc, self.Ks, self.Kz*50, self.Ka, 0,0,0,0, self.numexpr) - self.counts['water']+=1 + g.fluvial_erosion(self.Kr, self.Kv, self.userainmap, self.Kc, self.Ks, + self.Kz * 50, self.Ka, 0, 0, 0, 0, self.numexpr) + self.counts['water'] += 1 newMesh = bpy.data.meshes.new(oldMesh.name) g.toBlenderMesh(newMesh) @@ -994,11 +1008,10 @@ class Eroder(bpy.types.Operator): return {'FINISHED'} - - def draw(self,context): + def draw(self, context): layout = self.layout - layout.operator('screen.repeat_last', text="Repeat", icon='FILE_REFRESH' ) + layout.operator('screen.repeat_last', text="Repeat", icon='FILE_REFRESH') layout.prop(self, 'Iterations') @@ -1029,4 +1042,4 @@ class Eroder(bpy.types.Operator): col.prop(self, 'Ef') - layout.prop(self,'smooth') + layout.prop(self, 'smooth') diff --git a/ant_landscape/ant_noise.py b/ant_landscape/ant_noise.py index 7eec29b6..9385d4b4 100644 --- a/ant_landscape/ant_noise.py +++ b/ant_landscape/ant_noise.py @@ -5,23 +5,23 @@ import bpy from mathutils.noise import ( - seed_set, - noise, - turbulence, - turbulence_vector, - fractal, - hybrid_multi_fractal, - multi_fractal, - ridged_multi_fractal, - hetero_terrain, - random_unit_vector, - variable_lacunarity, - voronoi, - ) + seed_set, + noise, + turbulence, + turbulence_vector, + fractal, + hybrid_multi_fractal, + multi_fractal, + ridged_multi_fractal, + hetero_terrain, + random_unit_vector, + variable_lacunarity, + voronoi, +) from math import ( - floor, sqrt, - sin, cos, pi, - ) + floor, sqrt, + sin, cos, pi, +) noise_basis_default = "BLENDER" noise_basis = [ @@ -39,6 +39,8 @@ noise_basis = [ # ------------------------------------------------------------ # Height scale: + + def Height_Scale(input, iscale, offset, invert): if invert != 0: return (1.0 - input) * iscale + offset @@ -176,14 +178,15 @@ def vlnTurbMode(coords, distort, basis, vlbasis, hardnoise): def vl_noise_turbulence(coords, distort, depth, basis, vlbasis, hardnoise, amp, freq): x, y, z = coords value = vlnTurbMode(coords, distort, basis, vlbasis, hardnoise) - i=0 + i = 0 for i in range(depth): - i+=1 - value += vlnTurbMode((x * (freq * i), y * (freq * i), z * (freq * i)), distort, basis, vlbasis, hardnoise) * (amp * 0.5 / i) + i += 1 + value += vlnTurbMode((x * (freq * i), y * (freq * i), z * (freq * i)), + distort, basis, vlbasis, hardnoise) * (amp * 0.5 / i) return value -## duo_multiFractal: +# duo_multiFractal: def double_multiFractal(coords, H, lacunarity, octaves, offset, gain, basis, vlbasis): x, y, z = coords n1 = multi_fractal((x * 1.5 + 1, y * 1.5 + 1, z * 1.5 + 1), 1.0, 1.0, 1.0, noise_basis=basis) * (offset * 0.5) @@ -191,27 +194,27 @@ def double_multiFractal(coords, H, lacunarity, octaves, offset, gain, basis, vlb return (n1 * n1 + n2 * n2) * 0.5 -## distorted_heteroTerrain: +# distorted_heteroTerrain: def distorted_heteroTerrain(coords, H, lacunarity, octaves, offset, distort, basis, vlbasis): x, y, z = coords h1 = (hetero_terrain((x, y, z), 1.0, 2.0, 1.0, 1.0, noise_basis=basis) * 0.5) - d = h1 * distort + d = h1 * distort h2 = (hetero_terrain((x + d, y + d, z + d), H, lacunarity, octaves, offset, noise_basis=vlbasis) * 0.25) return (h1 * h1 + h2 * h2) * 0.5 -## SlickRock: +# SlickRock: def slick_rock(coords, H, lacunarity, octaves, offset, gain, distort, basis, vlbasis): x, y, z = coords - n = multi_fractal((x,y,z), 1.0, 2.0, 2.0, noise_basis=basis) * distort * 0.25 + n = multi_fractal((x, y, z), 1.0, 2.0, 2.0, noise_basis=basis) * distort * 0.25 r = ridged_multi_fractal((x + n, y + n, z + n), H, lacunarity, octaves, offset + 0.1, gain * 2, noise_basis=vlbasis) return (n + (n * r)) * 0.5 -## vlhTerrain +# vlhTerrain def vl_hTerrain(coords, H, lacunarity, octaves, offset, basis, vlbasis, distort): x, y, z = coords - ht = hetero_terrain((x, y, z), H, lacunarity, octaves, offset, noise_basis=basis ) * 0.25 + ht = hetero_terrain((x, y, z), H, lacunarity, octaves, offset, noise_basis=basis) * 0.25 vl = ht * variable_lacunarity((x, y, z), distort, noise_type1=basis, noise_type2=vlbasis) * 0.5 + 0.5 return vl * ht @@ -219,13 +222,14 @@ def vl_hTerrain(coords, H, lacunarity, octaves, offset, basis, vlbasis, distort) # another turbulence def ant_turbulence(coords, depth, hardnoise, nbasis, amp, freq, distortion): x, y, z = coords - t = turbulence_vector((x/2, y/2, z/2), depth, 0, noise_basis=nbasis, amplitude_scale=amp, frequency_scale=freq) * 0.5 * distortion + t = turbulence_vector((x / 2, y / 2, z / 2), depth, 0, noise_basis=nbasis, + amplitude_scale=amp, frequency_scale=freq) * 0.5 * distortion return turbulence((t[0], t[1], t[2]), 2, hardnoise, noise_basis="VORONOI_F1") * 0.5 + 0.5 # rocks noise def rocks_noise(coords, depth, hardnoise, nbasis, distortion): - x,y,z = coords + x, y, z = coords p = turbulence((x, y, z), 4, 0, noise_basis='BLENDER') * 0.125 * distortion xx, yy, zz = x, y, z a = turbulence((xx + p, yy + p, zz), 2, 0, noise_basis='VORONOI_F2F1') @@ -269,16 +273,18 @@ def planet_noise(coords, oct=6, hard=0, noisebasis='PERLIN_ORIGINAL', nabla=0.00 return (zdy - ydz), (zdx - xdz), (ydx - xdy) -###---------------------------------------------------------------------- +# ---------------------------------------------------------------------- # v.1.04 Effect functions: def maximum(a, b): - if (a > b): b = a + if (a > b): + b = a return b def minimum(a, b): - if (a < b): b = a + if (a < b): + b = a return b @@ -286,38 +292,38 @@ def Mix_Modes(a, b, mixfactor, mode): mode = int(mode) a = a * (1.0 - mixfactor) b = b * (1.0 + mixfactor) - #1 mix + # 1 mix if mode == 0: return (a * (1.0 - 0.5) + b * 0.5) - #2 add + # 2 add elif mode == 1: return (a + b) - #3 sub. + # 3 sub. elif mode == 2: return (a - b) - #4 mult. + # 4 mult. elif mode == 3: return (a * b) - #5 abs diff. + # 5 abs diff. elif mode == 4: return (abs(a - b)) - #6 screen + # 6 screen elif mode == 5: return 1.0 - ((1.0 - a) * (1.0 - b) / 1.0) - #7 addmodulo + # 7 addmodulo elif mode == 6: return (a + b) % 1.0 - #8 min. + # 8 min. elif mode == 7: return minimum(a, b) - #9 max. + # 9 max. elif mode == 8: return maximum(a, b) else: return 0 -Bias_Types = [sin_bias, cos_bias, tri_bias, saw_bias, no_bias] +Bias_Types = [sin_bias, cos_bias, tri_bias, saw_bias, no_bias] Sharp_Types = [soft, sharp, sharper] @@ -337,46 +343,47 @@ def Effect_Basis_Function(coords, type, bias): iscale = 1.0 offset = 0.0 - ## gradient: + # gradient: if type == 1: effect = offset + iscale * (Bias_Types[bias](x + y)) - ## waves / bumps: + # waves / bumps: elif type == 2: effect = offset + iscale * 0.5 * (Bias_Types[bias](x * pi) + Bias_Types[bias](y * pi)) - ## zigzag: + # zigzag: elif type == 3: effect = offset + iscale * Bias_Types[bias](offset + iscale * sin(x * pi + sin(y * pi))) - ## wavy: + # wavy: elif type == 4: effect = offset + iscale * (Bias_Types[bias](cos(x) + sin(y) + cos(x * 2 + y * 2) - sin(-x * 4 + y * 4))) - ## sine bump: + # sine bump: elif type == 5: - effect = offset + iscale * 1 - Bias_Types[bias]((sin(x * pi) + sin(y * pi))) - ## dots: + effect = offset + iscale * 1 - Bias_Types[bias]((sin(x * pi) + sin(y * pi))) + # dots: elif type == 6: effect = offset + iscale * (Bias_Types[bias](x * pi * 2) * Bias_Types[bias](y * pi * 2)) - 0.5 - ## rings: + # rings: elif type == 7: - effect = offset + iscale * (Bias_Types[bias ](1.0 - (x * x + y * y))) - ## spiral: + effect = offset + iscale * (Bias_Types[bias](1.0 - (x * x + y * y))) + # spiral: elif type == 8: - effect = offset + iscale * Bias_Types[bias]( (x * sin(x * x + y * y) + y * cos(x * x + y * y)) / (x**2 + y**2 + 0.5)) * 2 - ## square / piramide: + effect = offset + iscale * \ + Bias_Types[bias]((x * sin(x * x + y * y) + y * cos(x * x + y * y)) / (x**2 + y**2 + 0.5)) * 2 + # square / piramide: elif type == 9: effect = offset + iscale * Bias_Types[bias](1.0 - sqrt((x * x)**10 + (y * y)**10)**0.1) - ## blocks: + # blocks: elif type == 10: - effect = (0.5 - max(Bias_Types[bias](x * pi) , Bias_Types[bias](y * pi))) + effect = (0.5 - max(Bias_Types[bias](x * pi), Bias_Types[bias](y * pi))) if effect > 0.0: effect = 1.0 effect = offset + iscale * effect - ## grid: + # grid: elif type == 11: effect = (0.025 - min(Bias_Types[bias](x * pi), Bias_Types[bias](y * pi))) if effect > 0.0: effect = 1.0 effect = offset + iscale * effect - ## tech: + # tech: elif type == 12: a = max(Bias_Types[bias](x * pi), Bias_Types[bias](y * pi)) b = max(Bias_Types[bias](x * pi * 2 + 2), Bias_Types[bias](y * pi * 2 + 2)) @@ -384,51 +391,51 @@ def Effect_Basis_Function(coords, type, bias): if effect > 0.5: effect = 1.0 effect = offset + iscale * effect - ## crackle: + # crackle: elif type == 13: t = turbulence((x, y, 0), 6, 0, noise_basis="BLENDER") * 0.25 effect = variable_lacunarity((x, y, t), 0.25, noise_type2='VORONOI_CRACKLE') if effect > 0.5: effect = 0.5 effect = offset + iscale * effect - ## sparse cracks noise: + # sparse cracks noise: elif type == 14: effect = 2.5 * abs(noise((x, y, 0), noise_basis="PERLIN_ORIGINAL")) - 0.1 if effect > 0.25: effect = 0.25 effect = offset + iscale * (effect * 2.5) - ## shattered rock noise: + # shattered rock noise: elif type == 15: effect = 0.5 + noise((x, y, 0), noise_basis="VORONOI_F2F1") if effect > 0.75: effect = 0.75 effect = offset + iscale * effect - ## lunar noise: + # lunar noise: elif type == 16: effect = 0.25 + 1.5 * voronoi((x, y, 0), distance_metric='DISTANCE_SQUARED')[0][0] if effect > 0.5: effect = 0.5 effect = offset + iscale * effect * 2 - ## cosine noise: + # cosine noise: elif type == 17: effect = cos(5 * noise((x, y, 0), noise_basis="BLENDER")) effect = offset + iscale * (effect * 0.5) - ## spikey noise: + # spikey noise: elif type == 18: n = 0.5 + 0.5 * turbulence((x * 5, y * 5, 0), 8, 0, noise_basis="BLENDER") effect = ((n * n)**5) effect = offset + iscale * effect - ## stone noise: + # stone noise: elif type == 19: effect = offset + iscale * (noise((x * 2, y * 2, 0), noise_basis="BLENDER") * 1.5 - 0.75) - ## Flat Turb: + # Flat Turb: elif type == 20: t = turbulence((x, y, 0), 6, 0, noise_basis="BLENDER") effect = t * 2.0 if effect > 0.25: effect = 0.25 effect = offset + iscale * effect - ## Flat Voronoi: + # Flat Voronoi: elif type == 21: t = 1 - voronoi((x, y, 0), distance_metric='DISTANCE_SQUARED')[0][0] effect = t * 2 - 1.5 @@ -448,19 +455,19 @@ def Effect_Basis_Function(coords, type, bias): def Effect_Function(coords, type, bias, turb, depth, frequency, amplitude): x, y, z = coords - ## turbulence: + # turbulence: if turb > 0.0: - t = turb * ( 0.5 + 0.5 * turbulence(coords, 6, 0, noise_basis="BLENDER")) + t = turb * (0.5 + 0.5 * turbulence(coords, 6, 0, noise_basis="BLENDER")) x = x + t y = y + t z = z + t result = Effect_Basis_Function((x, y, z), type, bias) * amplitude - ## fractalize: + # fractalize: if depth != 0: - i=0 + i = 0 for i in range(depth): - i+=1 + i += 1 x *= frequency y *= frequency result += Effect_Basis_Function((x, y, z), type, bias) * amplitude / i @@ -578,20 +585,21 @@ def noise_gen(coords, props): value = fractal(ncoords, dimension, lacunarity, depth, noise_basis=nbasis) elif ntype in [5, 'turbulence_vector']: - value = turbulence_vector(ncoords, depth, hardnoise, noise_basis=nbasis, amplitude_scale=amp, frequency_scale=freq)[0] + value = turbulence_vector(ncoords, depth, hardnoise, noise_basis=nbasis, + amplitude_scale=amp, frequency_scale=freq)[0] elif ntype in [6, 'variable_lacunarity']: value = variable_lacunarity(ncoords, distortion, noise_type1=nbasis, noise_type2=vlbasis) elif ntype in [7, 'marble_noise']: value = marble_noise( - (ncoords[0] - origin_x + x_offset), - (ncoords[1] - origin_y + y_offset), - (ncoords[2] - origin_z + z_offset), - (origin[0] + x_offset, origin[1] + y_offset, origin[2] + z_offset), nsize, - marbleshape, marblebias, marblesharpnes, - distortion, depth, hardnoise, nbasis, amp, freq - ) + (ncoords[0] - origin_x + x_offset), + (ncoords[1] - origin_y + y_offset), + (ncoords[2] - origin_z + z_offset), + (origin[0] + x_offset, origin[1] + y_offset, origin[2] + z_offset), nsize, + marbleshape, marblebias, marblesharpnes, + distortion, depth, hardnoise, nbasis, amp, freq + ) elif ntype in [8, 'shattered_hterrain']: value = shattered_hterrain(ncoords, dimension, lacunarity, depth, offset, distortion, nbasis) @@ -617,7 +625,7 @@ def noise_gen(coords, props): value = rocks_noise(ncoords, depth, hardnoise, nbasis, distortion) elif ntype in [16, 'slick_rock']: - value = slick_rock(ncoords,dimension, lacunarity, depth, offset, gain, distortion, nbasis, vlbasis) + value = slick_rock(ncoords, dimension, lacunarity, depth, offset, gain, distortion, nbasis, vlbasis) elif ntype in [17, 'planet_noise']: value = planet_noise(ncoords, depth, hardnoise, nbasis)[2] * 0.5 + 0.5 @@ -632,7 +640,7 @@ def noise_gen(coords, props): # Effect mix val = value - if fx_type in [0,"0"]: + if fx_type in [0, "0"]: fx_mixfactor = -1.0 fxval = val else: @@ -649,14 +657,15 @@ def noise_gen(coords, props): if not sphere: if falloff: ratio_x, ratio_y = abs(x) * 2 / meshsize_x, abs(y) * 2 / meshsize_y - fallofftypes = [0, - sqrt(ratio_y**falloffsize_y), - sqrt(ratio_x**falloffsize_x), - sqrt(ratio_x**falloffsize_x + ratio_y**falloffsize_y) - ] + fallofftypes = [ + 0, + sqrt(ratio_y**falloffsize_y), + sqrt(ratio_x**falloffsize_x), + sqrt(ratio_x**falloffsize_x + ratio_y**falloffsize_y) + ] dist = fallofftypes[falloff] value -= edge_level - if(dist < 1.0): + if dist < 1.0: dist = (dist * dist * (3 - 2 * dist)) value = (value - value * dist) + edge_level else: @@ -682,11 +691,11 @@ def noise_gen(coords, props): elif stratatype in [4, "4"]: strata = strata / height - value = int( value * strata ) * 1.0 / strata + value = int(value * strata) * 1.0 / strata elif stratatype in [5, "5"]: strata = strata / height - steps = (int( value * strata ) * 1.0 / strata) + steps = (int(value * strata) * 1.0 / strata) value = (value * (1.0 - 0.5) + steps * 0.5) # Clamp height min max diff --git a/ant_landscape/eroder.py b/ant_landscape/eroder.py index 558d2edb..ae1326df 100644 --- a/ant_landscape/eroder.py +++ b/ant_landscape/eroder.py @@ -43,7 +43,6 @@ class Grid: self.sedmax = 1.0 self.scourmin = 1.0 - def init_water_and_sediment(self): if self.water is None: self.water = np.zeros(self.center.shape, dtype=np.single) @@ -60,39 +59,34 @@ class Grid: if self.avalanced is None: self.avalanced = np.zeros(self.center.shape, dtype=np.single) - def __str__(self): return ''.join(self.__str_iter__(fmt="%.3f")) - def __str_iter__(self, fmt): for row in self.center[::]: - values=[] + values = [] for v in row: - values.append(fmt%v) - yield ' '.join(values) + '\n' - + values.append(fmt % v) + yield ' '.join(values) + '\n' @staticmethod def fromFile(filename): if filename == '-': filename = sys.stdin - g=Grid() - g.center=np.loadtxt(filename,np.single) + g = Grid() + g.center = np.loadtxt(filename, np.single) return g - def toFile(self, filename, fmt="%.3f"): - if filename == '-' : + if filename == '-': filename = sys.stdout.fileno() - with open(filename,"w") as f: + with open(filename, "w") as f: for line in self.__str_iter__(fmt): f.write(line) - - def raw(self,format="%.3f"): - fstr=format+" "+ format+" "+ format+" " - a=self.center / self.zscale + def raw(self, format="%.3f"): + fstr = format + " " + format + " " + format + " " + a = self.center / self.zscale minx = 0.0 if self.minx is None else self.minx miny = 0.0 if self.miny is None else self.miny maxx = 1.0 if self.maxx is None else self.maxx @@ -105,21 +99,19 @@ class Grid: for col in range(a.shape[1] - 1): col0 = minx + col * dx col1 = col0 + dx - yield (fstr%(row0 ,col0 ,a[row ][col ])+ - fstr%(row0 ,col1 ,a[row ][col+1])+ - fstr%(row1 ,col0 ,a[row+1][col ])+"\n") - yield (fstr%(row0 ,col1 ,a[row ][col+1])+ - fstr%(row1 ,col0 ,a[row+1][col ])+ - fstr%(row1 ,col1 ,a[row+1][col+1])+"\n") - + yield (fstr % (row0, col0, a[row][col]) + + fstr % (row0, col1, a[row][col + 1]) + + fstr % (row1, col0, a[row + 1][col]) + "\n") + yield (fstr % (row0, col1, a[row][col + 1]) + + fstr % (row1, col0, a[row + 1][col]) + + fstr % (row1, col1, a[row + 1][col + 1]) + "\n") def toRaw(self, filename, infomap=None): - with open(filename if type(filename) == str else sys.stdout.fileno() , "w") as f: + with open(filename if type(filename) == str else sys.stdout.fileno(), "w") as f: f.writelines(self.raw()) if infomap: - with open(os.path.splitext(filename)[0]+".inf" if type(filename) == str else sys.stdout.fileno() , "w") as f: - f.writelines("\n".join("%-15s: %s"%t for t in sorted(infomap.items()))) - + with open(os.path.splitext(filename)[0] + ".inf" if type(filename) == str else sys.stdout.fileno(), "w") as f: + f.writelines("\n".join("%-15s: %s" % t for t in sorted(infomap.items()))) @staticmethod def fromRaw(filename): @@ -128,15 +120,14 @@ class Grid: """ g = Grid.fromFile(filename) # we assume tris and an axis aligned grid - g.center = np.reshape(g.center,(-1,3)) + g.center = np.reshape(g.center, (-1, 3)) g._sort() return g - def _sort(self, expfact): # keep unique vertices only by creating a set and sort first on x then on y coordinate # using rather slow python sort but couldn't wrap my head around np.lexsort - verts = sorted(list({ tuple(t) for t in self.center[::] })) + verts = sorted(list({tuple(t) for t in self.center[::]})) x = set(c[0] for c in verts) y = set(c[1] for c in verts) nx = len(x) @@ -145,41 +136,40 @@ class Grid: self.maxx = max(x) self.miny = min(y) self.maxy = max(y) - xscale = (self.maxx-self.minx)/(nx-1) - yscale = (self.maxy-self.miny)/(ny-1) + xscale = (self.maxx - self.minx) / (nx - 1) + yscale = (self.maxy - self.miny) / (ny - 1) # note: a purely flat plane cannot be scaled - if (yscale != 0.0) and (abs(xscale/yscale) - 1.0 > 1e-3): - raise ValueError("Mesh spacing not square %d x %d %.4f x %4.f"%(nx,ny,xscale,yscale)) + if (yscale != 0.0) and (abs(xscale / yscale) - 1.0 > 1e-3): + raise ValueError("Mesh spacing not square %d x %d %.4f x %4.f" % (nx, ny, xscale, yscale)) self.zscale = 1.0 - if abs(yscale) > 1e-6 : - self.zscale = 1.0/yscale + if abs(yscale) > 1e-6: + self.zscale = 1.0 / yscale # keep just the z-values and null any offset - # we might catch a reshape error that will occur if nx*ny != # of vertices (if we are not dealing with a heightfield but with a mesh with duplicate x,y coords, like an axis aligned cube - self.center = np.array([c[2] for c in verts],dtype=np.single).reshape(nx,ny) - self.center = (self.center-np.amin(self.center))*self.zscale + # we might catch a reshape error that will occur if nx*ny != # of vertices + # (if we are not dealing with a heightfield but with a mesh with duplicate + # x,y coords, like an axis aligned cube + self.center = np.array([c[2] for c in verts], dtype=np.single).reshape(nx, ny) + self.center = (self.center - np.amin(self.center)) * self.zscale if self.rainmap is not None: rmscale = np.max(self.center) - self.rainmap = expfact + (1-expfact)*(self.center/rmscale) - + self.rainmap = expfact + (1 - expfact) * (self.center / rmscale) @staticmethod def fromBlenderMesh(me, vg, expfact): g = Grid() - g.center = np.asarray(list(tuple(v.co) for v in me.vertices), dtype=np.single ) + g.center = np.asarray(list(tuple(v.co) for v in me.vertices), dtype=np.single) g.rainmap = None if vg is not None: for v in me.vertices: - vg.add([v.index],0.0,'ADD') - g.rainmap=np.asarray(list( (v.co[0], v.co[1], vg.weight(v.index)) for v in me.vertices), dtype=np.single ) + vg.add([v.index], 0.0, 'ADD') + g.rainmap = np.asarray(list((v.co[0], v.co[1], vg.weight(v.index)) for v in me.vertices), dtype=np.single) g._sort(expfact) return g - def setrainmap(self, rainmap): self.rainmap = rainmap - def _verts(self, surface): a = surface / self.zscale minx = 0.0 if self.minx is None else self.minx @@ -192,90 +182,81 @@ class Grid: row0 = miny + row * dy for col in range(a.shape[1]): col0 = minx + col * dx - yield (row0 ,col0 ,a[row ][col ]) - + yield (row0, col0, a[row][col]) def _faces(self): nrow, ncol = self.center.shape - for row in range(nrow-1): - for col in range(ncol-1): - vi = row * ncol + col - yield (vi, vi+ncol, vi+1) - yield (vi+1, vi+ncol, vi+ncol+1) - + for row in range(nrow - 1): + for col in range(ncol - 1): + vi = row * ncol + col + yield (vi, vi + ncol, vi + 1) + yield (vi + 1, vi + ncol, vi + ncol + 1) def toBlenderMesh(self, me): # pass me as argument so that we don't need to import bpy and create a dependency - # the docs state that from_pydata takes iterators as arguments but it will fail with generators because it does len(arg) - me.from_pydata(list(self._verts(self.center)),[],list(self._faces())) - + # the docs state that from_pydata takes iterators as arguments but it will + # fail with generators because it does len(arg) + me.from_pydata(list(self._verts(self.center)), [], list(self._faces())) def toWaterMesh(self, me): # pass me as argument so that we don't need to import bpy and create a dependency - # the docs state that from_pydata takes iterators as arguments but it will fail with generators because it does len(arg) - me.from_pydata(list(self._verts(self.water)),[],list(self._faces())) - + # the docs state that from_pydata takes iterators as arguments but it will + # fail with generators because it does len(arg) + me.from_pydata(list(self._verts(self.water)), [], list(self._faces())) def peak(self, value=1): - nx,ny = self.center.shape - self.center[int(nx/2),int(ny/2)] += value - + nx, ny = self.center.shape + self.center[int(nx / 2), int(ny / 2)] += value def shelf(self, value=1): - nx,ny = self.center.shape - self.center[:nx/2] += value - + nx, ny = self.center.shape + self.center[:nx / 2] += value def mesa(self, value=1): - nx,ny = self.center.shape - self.center[nx/4:3*nx/4,ny/4:3*ny/4] += value - + nx, ny = self.center.shape + self.center[nx / 4:3 * nx / 4, ny / 4:3 * ny / 4] += value def random(self, value=1): - self.center += np.random.random_sample(self.center.shape)*value - + self.center += np.random.random_sample(self.center.shape) * value def neighborgrid(self): - self.up = np.roll(self.center,-1,0) - self.down = np.roll(self.center,1,0) - self.left = np.roll(self.center,-1,1) - self.right = np.roll(self.center,1,1) - + self.up = np.roll(self.center, -1, 0) + self.down = np.roll(self.center, 1, 0) + self.left = np.roll(self.center, -1, 1) + self.right = np.roll(self.center, 1, 1) def zeroedge(self, quantity=None): c = self.center if quantity is None else quantity - c[0,:] = 0 - c[-1,:] = 0 - c[:,0] = 0 - c[:,-1] = 0 - + c[0, :] = 0 + c[-1, :] = 0 + c[:, 0] = 0 + c[:, -1] = 0 def diffuse(self, Kd, IterDiffuse, numexpr): self.zeroedge() - c = self.center[1:-1,1:-1] - up = self.center[ :-2,1:-1] - down = self.center[2: ,1:-1] + c = self.center[1:-1, 1:-1] + up = self.center[:-2, 1:-1] + down = self.center[2:, 1:-1] left = self.center[1:-1, :-2] - right = self.center[1:-1,2: ] + right = self.center[1:-1, 2:] if(numexpr and numexpr_available): - self.center[1:-1,1:-1] = ne.evaluate('c + Kd * (up + down + left + right - 4.0 * c)') + self.center[1:-1, 1:-1] = ne.evaluate('c + Kd * (up + down + left + right - 4.0 * c)') else: - self.center[1:-1,1:-1] = c + (Kd/IterDiffuse) * (up + down + left + right - 4.0 * c) + self.center[1:-1, 1:-1] = c + (Kd / IterDiffuse) * (up + down + left + right - 4.0 * c) self.maxrss = max(getmemsize(), self.maxrss) return self.center - def avalanche(self, delta, iterava, prob, numexpr): self.zeroedge() - c = self.center[1:-1,1:-1] - up = self.center[ :-2,1:-1] - down = self.center[2: ,1:-1] - left = self.center[1:-1, :-2] - right = self.center[1:-1,2: ] + c = self.center[1:-1, 1:-1] + up = self.center[:-2, 1:-1] + down = self.center[2:, 1:-1] + left = self.center[1:-1, :-2] + right = self.center[1:-1, 2:] where = np.where if(numexpr and numexpr_available): - self.center[1:-1,1:-1] = ne.evaluate('c + where((up -c) > delta ,(up -c -delta)/2, 0) \ + self.center[1:-1, 1:-1] = ne.evaluate('c + where((up -c) > delta ,(up -c -delta)/2, 0) \ + where((down -c) > delta ,(down -c -delta)/2, 0) \ + where((left -c) > delta ,(left -c -delta)/2, 0) \ + where((right-c) > delta ,(right-c -delta)/2, 0) \ @@ -286,38 +267,36 @@ class Grid: else: sa = ( # incoming - where((up -c) > delta ,(up -c -delta)/2, 0) - + where((down -c) > delta ,(down -c -delta)/2, 0) - + where((left -c) > delta ,(left -c -delta)/2, 0) - + where((right-c) > delta ,(right-c -delta)/2, 0) + where((up - c) > delta, (up - c - delta) / 2, 0) + + where((down - c) > delta, (down - c - delta) / 2, 0) + + where((left - c) > delta, (left - c - delta) / 2, 0) + + where((right - c) > delta, (right - c - delta) / 2, 0) # outgoing - + where((up -c) < -delta,(up -c +delta)/2, 0) - + where((down -c) < -delta,(down -c +delta)/2, 0) - + where((left -c) < -delta,(left -c +delta)/2, 0) - + where((right-c) < -delta,(right-c +delta)/2, 0) - ) - randarray = np.random.randint(0,100,sa.shape) *0.01 + + where((up - c) < -delta, (up - c + delta) / 2, 0) + + where((down - c) < -delta, (down - c + delta) / 2, 0) + + where((left - c) < -delta, (left - c + delta) / 2, 0) + + where((right - c) < -delta, (right - c + delta) / 2, 0) + ) + randarray = np.random.randint(0, 100, sa.shape) * 0.01 sa = where(randarray < prob, sa, 0) - self.avalanced[1:-1,1:-1] = self.avalanced[1:-1,1:-1] + sa/iterava - self.center[1:-1,1:-1] = c + sa/iterava + self.avalanced[1:-1, 1:-1] = self.avalanced[1:-1, 1:-1] + sa / iterava + self.center[1:-1, 1:-1] = c + sa / iterava self.maxrss = max(getmemsize(), self.maxrss) return self.center - def rain(self, amount=1, variance=0, userainmap=False): - self.water += (1.0 - np.random.random(self.water.shape) * variance) * (amount if ((self.rainmap is None) or (not userainmap)) else self.rainmap * amount) - + self.water += (1.0 - np.random.random(self.water.shape) * variance) * \ + (amount if ((self.rainmap is None) or (not userainmap)) else self.rainmap * amount) def spring(self, amount, px, py, radius): # px, py and radius are all fractions nx, ny = self.center.shape - rx = max(int(nx*radius),1) - ry = max(int(ny*radius),1) - px = int(nx*px) - py = int(ny*py) - self.water[px-rx:px+rx+1,py-ry:py+ry+1] += amount - + rx = max(int(nx * radius), 1) + ry = max(int(ny * radius), 1) + px = int(nx * px) + py = int(ny * py) + self.water[px - rx:px + rx + 1, py - ry:py + ry + 1] += amount def river(self, Kc, Ks, Kdep, Ka, Kev, numexpr): zeros = np.zeros @@ -328,11 +307,11 @@ class Grid: arctan = np.arctan sin = np.sin - center = (slice( 1, -1,None),slice( 1, -1,None)) - up = (slice(None, -2,None),slice( 1, -1,None)) - down = (slice( 2, None,None),slice( 1, -1,None)) - left = (slice( 1, -1,None),slice(None, -2,None)) - right = (slice( 1, -1,None),slice( 2,None,None)) + center = (slice(1, -1, None), slice(1, -1, None)) + up = (slice(None, -2, None), slice(1, -1, None)) + down = (slice(2, None, None), slice(1, -1, None)) + left = (slice(1, -1, None), slice(None, -2, None)) + right = (slice(1, -1, None), slice(2, None, None)) water = self.water rock = self.center @@ -348,7 +327,7 @@ class Grid: svdw = zeros(water[center].shape) sds = zeros(water[center].shape) angle = zeros(water[center].shape) - for d in (up,down,left,right): + for d in (up, down, left, right): if(numexpr and numexpr_available): hdd = height[d] hcc = height[center] @@ -356,25 +335,26 @@ class Grid: inflow = ne.evaluate('dw > 0') wdd = water[d] wcc = water[center] - dw = ne.evaluate('where(inflow, where(wdddw, -wcc, dw))/4.0') # nested where() represent min() and max() - sdw = ne.evaluate('sdw + dw') - scd = sc[d] - scc = sc[center] - rockd= rock[d] - rockc= rock[center] - sds = ne.evaluate('sds + dw * where(inflow, scd, scc)') + # nested where() represent min() and max() + dw = ne.evaluate('where(inflow, where(wdddw, -wcc, dw))/4.0') + sdw = ne.evaluate('sdw + dw') + scd = sc[d] + scc = sc[center] + rockd = rock[d] + rockc = rock[center] + sds = ne.evaluate('sds + dw * where(inflow, scd, scc)') svdw = ne.evaluate('svdw + abs(dw)') - angle= ne.evaluate('angle + arctan(abs(rockd-rockc))') + angle = ne.evaluate('angle + arctan(abs(rockd-rockc))') else: - dw = (height[d]-height[center]) + dw = (height[d] - height[center]) inflow = dw > 0 - dw = where(inflow, min(water[d], dw), max(-water[center], dw))/4.0 - sdw = sdw + dw - sds = sds + dw * where(inflow, sc[d], sc[center]) + dw = where(inflow, min(water[d], dw), max(-water[center], dw)) / 4.0 + sdw = sdw + dw + sds = sds + dw * where(inflow, sc[d], sc[center]) svdw = svdw + abs(dw) - angle= angle + np.arctan(abs(rock[d]-rock[center])) + angle = angle + np.arctan(abs(rock[d] - rock[center])) - if(numexpr and numexpr_available): + if numexpr and numexpr_available: wcc = water[center] scc = sediment[center] rcc = rock[center] @@ -391,10 +371,10 @@ class Grid: wcc = water[center] scc = sediment[center] rcc = rock[center] - water[center] = wcc * (1-Kev) + sdw + water[center] = wcc * (1 - Kev) + sdw sediment[center] = scc + sds sc = where(wcc > 0, scc / wcc, 2 * Kc) - fKc = Kc*svdw + fKc = Kc * svdw ds = where(fKc > sc, (fKc - sc) * Ks, (fKc - sc) * Kdep) * wcc self.flowrate[center] = svdw self.scour[center] = ds @@ -402,7 +382,6 @@ class Grid: self.capacity[center] = fKc sediment[center] = scc + ds + sds - def flow(self, Kc, Ks, Kz, Ka, numexpr): zeros = np.zeros where = np.where @@ -412,16 +391,30 @@ class Grid: arctan = np.arctan sin = np.sin - center = (slice( 1, -1,None),slice( 1, -1,None)) + center = (slice(1, -1, None), slice(1, -1, None)) rock = self.center ds = self.scour[center] rcc = rock[center] rock[center] = rcc - ds * Kz # there isn't really a bottom to the rock but negative values look ugly - rock[center] = where(rcc<0,0,rcc) - - - def rivergeneration(self, rainamount, rainvariance, userainmap, Kc, Ks, Kdep, Ka, Kev, Kspring, Kspringx, Kspringy, Kspringr, numexpr): + rock[center] = where(rcc < 0, 0, rcc) + + def rivergeneration( + self, + rainamount, + rainvariance, + userainmap, + Kc, + Ks, + Kdep, + Ka, + Kev, + Kspring, + Kspringx, + Kspringy, + Kspringr, + numexpr, + ): self.init_water_and_sediment() self.rain(rainamount, rainvariance, userainmap) self.zeroedge(self.water) @@ -429,32 +422,43 @@ class Grid: self.river(Kc, Ks, Kdep, Ka, Kev, numexpr) self.watermax = np.max(self.water) - - def fluvial_erosion(self, rainamount, rainvariance, userainmap, Kc, Ks, Kdep, Ka, Kspring, Kspringx, Kspringy, Kspringr, numexpr): + def fluvial_erosion( + self, + rainamount, + rainvariance, + userainmap, + Kc, + Ks, + Kdep, + Ka, + Kspring, + Kspringx, + Kspringy, + Kspringr, + numexpr, + ): self.flow(Kc, Ks, Kdep, Ka, numexpr) self.flowratemax = np.max(self.flowrate) self.scourmax = np.max(self.scour) self.scourmin = np.min(self.scour) self.sedmax = np.max(self.sediment) - def analyze(self): self.neighborgrid() # just looking at up and left to avoid needless double calculations - slopes=np.concatenate((np.abs(self.left - self.center),np.abs(self.up - self.center))) - return '\n'.join(["%-15s: %.3f"%t for t in [ - ('height average', np.average(self.center)), - ('height median', np.median(self.center)), - ('height max', np.max(self.center)), - ('height min', np.min(self.center)), - ('height std', np.std(self.center)), - ('slope average', np.average(slopes)), - ('slope median', np.median(slopes)), - ('slope max', np.max(slopes)), - ('slope min', np.min(slopes)), - ('slope std', np.std(slopes)) - ]] - ) + slopes = np.concatenate((np.abs(self.left - self.center), np.abs(self.up - self.center))) + return '\n'.join(["%-15s: %.3f" % t for t in [ + ('height average', np.average(self.center)), + ('height median', np.median(self.center)), + ('height max', np.max(self.center)), + ('height min', np.min(self.center)), + ('height std', np.std(self.center)), + ('slope average', np.average(slopes)), + ('slope median', np.median(slopes)), + ('slope max', np.max(slopes)), + ('slope min', np.min(slopes)), + ('slope std', np.std(slopes)) + ]]) class TestGrid(unittest.TestCase): @@ -462,12 +466,11 @@ class TestGrid(unittest.TestCase): def test_diffuse(self): g = Grid(5) g.peak(1) - self.assertEqual(g.center[2,2],1.0) + self.assertEqual(g.center[2, 2], 1.0) g.diffuse(0.1, numexpr=False) - for n in [(2,1),(2,3),(1,2),(3,2)]: - self.assertAlmostEqual(g.center[n],0.1) - self.assertAlmostEqual(g.center[2,2],0.6) - + for n in [(2, 1), (2, 3), (1, 2), (3, 2)]: + self.assertAlmostEqual(g.center[n], 0.1) + self.assertAlmostEqual(g.center[2, 2], 0.6) def test_diffuse_numexpr(self): g = Grid(5) @@ -476,8 +479,7 @@ class TestGrid(unittest.TestCase): h = Grid(5) h.peak(1) h.diffuse(0.1, numexpr=True) - self.assertEqual(list(g.center.flat),list(h.center.flat)) - + self.assertEqual(list(g.center.flat), list(h.center.flat)) def test_avalanche_numexpr(self): g = Grid(5) @@ -488,7 +490,7 @@ class TestGrid(unittest.TestCase): h.avalanche(0.1, numexpr=True) print(g) print(h) - np.testing.assert_almost_equal(g.center,h.center) + np.testing.assert_almost_equal(g.center, h.center) if __name__ == "__main__": @@ -501,7 +503,8 @@ if __name__ == "__main__": parser.add_argument('-Kh', dest='Kh', type=float, default=6, help='Maximum stable cliff height') parser.add_argument('-Kp', dest='Kp', type=float, default=0.1, help='Avalanche probability for unstable cliffs') parser.add_argument('-Kr', dest='Kr', type=float, default=0.1, help='Average amount of rain per iteration') - parser.add_argument('-Kspring', dest='Kspring', type=float, default=0.0, help='Average amount of wellwater per iteration') + parser.add_argument('-Kspring', dest='Kspring', type=float, default=0.0, + help='Average amount of wellwater per iteration') parser.add_argument('-Kspringx', dest='Kspringx', type=float, default=0.5, help='relative x position of spring') parser.add_argument('-Kspringy', dest='Kspringy', type=float, default=0.5, help='relative y position of spring') parser.add_argument('-Kspringr', dest='Kspringr', type=float, default=0.02, help='radius of spring') @@ -509,26 +512,45 @@ if __name__ == "__main__": parser.add_argument('-Ks', dest='Ks', type=float, default=0.1, help='Soil softness constant') parser.add_argument('-Kc', dest='Kc', type=float, default=1.0, help='Sediment capacity') parser.add_argument('-Ka', dest='Ka', type=float, default=2.0, help='Slope dependency of erosion') - parser.add_argument('-ri', action='store_true', dest='rawin', default=False, help='use Blender raw format for input') - parser.add_argument('-ro', action='store_true', dest='rawout', default=False, help='use Blender raw format for output') - parser.add_argument('-i', action='store_true', dest='useinputfile', default=False, help='use an inputfile (instead of just a synthesized grid)') - parser.add_argument('-t', action='store_true', dest='timingonly', default=False, help='do not write anything to an output file') + parser.add_argument( + '-ri', + action='store_true', + dest='rawin', + default=False, + help='use Blender raw format for input') + parser.add_argument( + '-ro', + action='store_true', + dest='rawout', + default=False, + help='use Blender raw format for output') + parser.add_argument('-i', action='store_true', dest='useinputfile', default=False, + help='use an inputfile (instead of just a synthesized grid)') + parser.add_argument( + '-t', + action='store_true', + dest='timingonly', + default=False, + help='do not write anything to an output file') parser.add_argument('-infile', type=str, default="-", help='input filename') parser.add_argument('-outfile', type=str, default="-", help='output filename') parser.add_argument('-Gn', dest='gridsize', type=int, default=20, help='Gridsize (always square)') parser.add_argument('-Gp', dest='gridpeak', type=float, default=0, help='Add peak with given height') parser.add_argument('-Gs', dest='gridshelf', type=float, default=0, help='Add shelve with given height') parser.add_argument('-Gm', dest='gridmesa', type=float, default=0, help='Add mesa with given height') - parser.add_argument('-Gr', dest='gridrandom', type=float, default=0, help='Add random values between 0 and given value') + parser.add_argument('-Gr', dest='gridrandom', type=float, default=0, + help='Add random values between 0 and given value') parser.add_argument('-m', dest='threads', type=int, default=1, help='number of threads to use') parser.add_argument('-u', action='store_true', dest='unittest', default=False, help='perform unittests') - parser.add_argument('-a', action='store_true', dest='analyze', default=False, help='show some statistics of input and output meshes') - parser.add_argument('-d', action='store_true', dest='dump', default=False, help='show sediment and water meshes at end of run') + parser.add_argument('-a', action='store_true', dest='analyze', default=False, + help='show some statistics of input and output meshes') + parser.add_argument('-d', action='store_true', dest='dump', default=False, + help='show sediment and water meshes at end of run') parser.add_argument('-n', action='store_true', dest='usenumexpr', default=False, help='use numexpr optimizations') args = parser.parse_args() print("\nInput arguments:") - print("\n".join("%-15s: %s"%t for t in sorted(vars(args).items())), file=sys.stderr) + print("\n".join("%-15s: %s" % t for t in sorted(vars(args).items())), file=sys.stderr) if args.unittest: unittest.main(argv=[sys.argv[0]]) @@ -542,13 +564,17 @@ if __name__ == "__main__": else: grid = Grid(args.gridsize) - if args.gridpeak > 0 : grid.peak(args.gridpeak) - if args.gridmesa > 0 : grid.mesa(args.gridmesa) - if args.gridshelf > 0 : grid.shelf(args.gridshelf) - if args.gridrandom > 0 : grid.random(args.gridrandom) + if args.gridpeak > 0: + grid.peak(args.gridpeak) + if args.gridmesa > 0: + grid.mesa(args.gridmesa) + if args.gridshelf > 0: + grid.shelf(args.gridshelf) + if args.gridrandom > 0: + grid.random(args.gridrandom) if args.analyze: - print('\nstatistics of the input grid:\n\n', grid.analyze(), file=sys.stderr, sep='' ) + print('\nstatistics of the input grid:\n\n', grid.analyze(), file=sys.stderr, sep='') t = getptime() for g in range(args.iterations): if args.Kd > 0: @@ -556,9 +582,20 @@ if __name__ == "__main__": if args.Kh > 0 and args.Kp > rand(): grid.avalanche(args.Kh, args.usenumexpr) if args.Kr > 0 or args.Kspring > 0: - grid.fluvial_erosion(args.Kr, args.Kc, args.Ks, args.Kdep, args.Ka, args.Kspring, args.Kspringx, args.Kspringy, args.Kspringr, args.usenumexpr) + grid.fluvial_erosion( + args.Kr, + args.Kc, + args.Ks, + args.Kdep, + args.Ka, + args.Kspring, + args.Kspringx, + args.Kspringy, + args.Kspringr, + args.usenumexpr, + ) t = getptime() - t - print("\nElapsed time: %.1f seconds, max memory %.1f Mb.\n"%(t,grid.maxrss), file=sys.stderr) + print("\nElapsed time: %.1f seconds, max memory %.1f Mb.\n" % (t, grid.maxrss), file=sys.stderr) if args.analyze: print('\nstatistics of the output grid:\n\n', grid.analyze(), file=sys.stderr, sep='') @@ -569,6 +606,6 @@ if __name__ == "__main__": grid.toFile(args.outfile) if args.dump: - print("sediment\n", np.array_str(grid.sediment,precision=3), file=sys.stderr) - print("water\n", np.array_str(grid.water,precision=3), file=sys.stderr) - print("sediment concentration\n", np.array_str(grid.sediment/grid.water,precision=3), file=sys.stderr) + print("sediment\n", np.array_str(grid.sediment, precision=3), file=sys.stderr) + print("water\n", np.array_str(grid.water, precision=3), file=sys.stderr) + print("sediment concentration\n", np.array_str(grid.sediment / grid.water, precision=3), file=sys.stderr) diff --git a/ant_landscape/mesh_ant_displace.py b/ant_landscape/mesh_ant_displace.py index 5638d96a..132ca6fe 100644 --- a/ant_landscape/mesh_ant_displace.py +++ b/ant_landscape/mesh_ant_displace.py @@ -7,24 +7,26 @@ # import modules import bpy from bpy.props import ( - BoolProperty, - EnumProperty, - FloatProperty, - IntProperty, - StringProperty, - FloatVectorProperty, - ) + BoolProperty, + EnumProperty, + FloatProperty, + IntProperty, + StringProperty, + FloatVectorProperty, +) from .ant_functions import ( - draw_ant_refresh, - draw_ant_main, - draw_ant_noise, - draw_ant_displace, - ) + draw_ant_refresh, + draw_ant_main, + draw_ant_noise, + draw_ant_displace, +) from .ant_noise import noise_gen from ant_landscape import ant_noise # ------------------------------------------------------------ # Do vert displacement + + class AntMeshDisplace(bpy.types.Operator): bl_idname = "mesh.ant_displace" bl_label = "Another Noise Tool - Displace" @@ -32,131 +34,131 @@ class AntMeshDisplace(bpy.types.Operator): bl_options = {'REGISTER', 'UNDO', 'PRESET'} ant_terrain_name: StringProperty( - name="Name", - default="Landscape" - ) + name="Name", + default="Landscape" + ) land_material: StringProperty( - name='Material', - default="", - description="Terrain material" - ) + name='Material', + default="", + description="Terrain material" + ) water_material: StringProperty( - name='Material', - default="", - description="Water plane material" - ) + name='Material', + default="", + description="Water plane material" + ) texture_block: StringProperty( - name="Texture", - default="" - ) + name="Texture", + default="" + ) at_cursor: BoolProperty( - name="Cursor", - default=True, - description="Place at cursor location", - ) + name="Cursor", + default=True, + description="Place at cursor location", + ) smooth_mesh: BoolProperty( - name="Smooth", - default=True, - description="Shade smooth" - ) + name="Smooth", + default=True, + description="Shade smooth" + ) tri_face: BoolProperty( - name="Triangulate", - default=False, - description="Triangulate faces" - ) + name="Triangulate", + default=False, + description="Triangulate faces" + ) sphere_mesh: BoolProperty( - name="Sphere", - default=False, - description="Generate uv sphere - remove doubles when ready" - ) + name="Sphere", + default=False, + description="Generate uv sphere - remove doubles when ready" + ) subdivision_x: IntProperty( - name="Subdivisions X", - default=128, - min=4, - max=6400, - description="Mesh X subdivisions" - ) + name="Subdivisions X", + default=128, + min=4, + max=6400, + description="Mesh X subdivisions" + ) subdivision_y: IntProperty( - default=128, - name="Subdivisions Y", - min=4, - max=6400, - description="Mesh Y subdivisions" - ) + default=128, + name="Subdivisions Y", + min=4, + max=6400, + description="Mesh Y subdivisions" + ) mesh_size: FloatProperty( - default=2.0, - name="Mesh Size", - min=0.01, - max=100000.0, - description="Mesh size" - ) + default=2.0, + name="Mesh Size", + min=0.01, + max=100000.0, + description="Mesh size" + ) mesh_size_x: FloatProperty( - default=2.0, - name="Mesh Size X", - min=0.01, - description="Mesh x size" - ) + default=2.0, + name="Mesh Size X", + min=0.01, + description="Mesh x size" + ) mesh_size_y: FloatProperty( - name="Mesh Size Y", - default=2.0, - min=0.01, - description="Mesh y size" - ) + name="Mesh Size Y", + default=2.0, + min=0.01, + description="Mesh y size" + ) random_seed: IntProperty( - name="Random Seed", - default=0, - min=0, - description="Randomize noise origin" - ) + name="Random Seed", + default=0, + min=0, + description="Randomize noise origin" + ) noise_offset_x: FloatProperty( - name="Offset X", - default=0.0, - description="Noise X Offset" - ) + name="Offset X", + default=0.0, + description="Noise X Offset" + ) noise_offset_y: FloatProperty( - name="Offset Y", - default=0.0, - description="Noise Y Offset" - ) + name="Offset Y", + default=0.0, + description="Noise Y Offset" + ) noise_offset_z: FloatProperty( - name="Offset Z", - default=0.0, - description="Noise Z Offset" - ) + name="Offset Z", + default=0.0, + description="Noise Z Offset" + ) noise_size_x: FloatProperty( - default=1.0, - name="Size X", - min=0.01, - max=1000.0, - description="Noise x size" - ) + default=1.0, + name="Size X", + min=0.01, + max=1000.0, + description="Noise x size" + ) noise_size_y: FloatProperty( - name="Size Y", - default=1.0, - min=0.01, - max=1000.0, - description="Noise y size" - ) + name="Size Y", + default=1.0, + min=0.01, + max=1000.0, + description="Noise y size" + ) noise_size_z: FloatProperty( - name="Size Z", - default=1.0, - min=0.01, - max=1000.0, - description="Noise Z size" - ) + name="Size Z", + default=1.0, + min=0.01, + max=1000.0, + description="Noise Z size" + ) noise_size: FloatProperty( - name="Noise Size", - default=0.25, - min=0.01, - max=1000.0, - description="Noise size" - ) + name="Noise Size", + default=0.25, + min=0.01, + max=1000.0, + description="Noise size" + ) noise_type: EnumProperty( - name="Noise Type", - default='hetero_terrain', - description="Noise type", - items = [ + name="Noise Type", + default='hetero_terrain', + description="Noise type", + items=[ ('multi_fractal', "Multi Fractal", "Blender: Multi Fractal algorithm", 0), ('ridged_multi_fractal', "Ridged MFractal", "Blender: Ridged Multi Fractal", 1), ('hybrid_multi_fractal', "Hybrid MFractal", "Blender: Hybrid Multi Fractal", 2), @@ -176,110 +178,110 @@ class AntMeshDisplace(bpy.types.Operator): ('slick_rock', "Slick Rock", "A.N.T: slick rock", 16), ('planet_noise', "Planet Noise", "Planet Noise by: Farsthary", 17), ('blender_texture', "Blender Texture - Texture Nodes", "Blender texture data block", 18)] - ) + ) basis_type: EnumProperty( - name="Noise Basis", - default=ant_noise.noise_basis_default, - description="Noise basis algorithms", - items = ant_noise.noise_basis - ) + name="Noise Basis", + default=ant_noise.noise_basis_default, + description="Noise basis algorithms", + items=ant_noise.noise_basis + ) vl_basis_type: EnumProperty( - name="vlNoise Basis", - default=ant_noise.noise_basis_default, - description="VLNoise basis algorithms", - items = ant_noise.noise_basis - ) + name="vlNoise Basis", + default=ant_noise.noise_basis_default, + description="VLNoise basis algorithms", + items=ant_noise.noise_basis + ) distortion: FloatProperty( - name="Distortion", - default=1.0, - min=0.01, - max=100.0, - description="Distortion amount" - ) + name="Distortion", + default=1.0, + min=0.01, + max=100.0, + description="Distortion amount" + ) hard_noise: EnumProperty( - name="Soft Hard", - default="0", - description="Soft Noise, Hard noise", - items = [ + name="Soft Hard", + default="0", + description="Soft Noise, Hard noise", + items=[ ("0", "Soft", "Soft Noise", 0), ("1", "Hard", "Hard noise", 1)] - ) + ) noise_depth: IntProperty( - name="Depth", - default=8, - min=0, - max=16, - description="Noise Depth - number of frequencies in the fBm" - ) + name="Depth", + default=8, + min=0, + max=16, + description="Noise Depth - number of frequencies in the fBm" + ) amplitude: FloatProperty( - name="Amp", - default=0.5, - min=0.01, - max=1.0, - description="Amplitude" - ) + name="Amp", + default=0.5, + min=0.01, + max=1.0, + description="Amplitude" + ) frequency: FloatProperty( - name="Freq", - default=2.0, - min=0.01, - max=5.0, - description="Frequency" - ) + name="Freq", + default=2.0, + min=0.01, + max=5.0, + description="Frequency" + ) dimension: FloatProperty( - name="Dimension", - default=1.0, - min=0.01, - max=2.0, - description="H - fractal dimension of the roughest areas" - ) + name="Dimension", + default=1.0, + min=0.01, + max=2.0, + description="H - fractal dimension of the roughest areas" + ) lacunarity: FloatProperty( - name="Lacunarity", - min=0.01, - max=6.0, - default=2.0, - description="Lacunarity - gap between successive frequencies" - ) + name="Lacunarity", + min=0.01, + max=6.0, + default=2.0, + description="Lacunarity - gap between successive frequencies" + ) offset: FloatProperty( - name="Offset", - default=1.0, - min=0.01, - max=6.0, - description="Offset - raises the terrain from sea level" - ) + name="Offset", + default=1.0, + min=0.01, + max=6.0, + description="Offset - raises the terrain from sea level" + ) gain: FloatProperty( - name="Gain", - default=1.0, - min=0.01, - max=6.0, - description="Gain - scale factor" - ) + name="Gain", + default=1.0, + min=0.01, + max=6.0, + description="Gain - scale factor" + ) marble_bias: EnumProperty( - name="Bias", - default="0", - description="Marble bias", - items = [ + name="Bias", + default="0", + description="Marble bias", + items=[ ("0", "Sin", "Sin", 0), ("1", "Cos", "Cos", 1), ("2", "Tri", "Tri", 2), ("3", "Saw", "Saw", 3)] - ) + ) marble_sharp: EnumProperty( - name="Sharp", - default="0", - description="Marble sharpness", - items = [ + name="Sharp", + default="0", + description="Marble sharpness", + items=[ ("0", "Soft", "Soft", 0), ("1", "Sharp", "Sharp", 1), ("2", "Sharper", "Sharper", 2), ("3", "Soft inv.", "Soft", 3), ("4", "Sharp inv.", "Sharp", 4), ("5", "Sharper inv.", "Sharper", 5)] - ) + ) marble_shape: EnumProperty( - name="Shape", - default="0", - description="Marble shape", - items= [ + name="Shape", + default="0", + description="Marble shape", + items=[ ("0", "Default", "Default", 0), ("1", "Ring", "Ring", 1), ("2", "Swirl", "Swirl", 2), @@ -288,39 +290,39 @@ class AntMeshDisplace(bpy.types.Operator): ("5", "Z", "Z", 5), ("6", "Y", "Y", 6), ("7", "X", "X", 7)] - ) + ) height: FloatProperty( - name="Height", - default=0.25, - min=-10000.0, - max=10000.0, - description="Noise intensity scale" - ) + name="Height", + default=0.25, + min=-10000.0, + max=10000.0, + description="Noise intensity scale" + ) height_invert: BoolProperty( - name="Invert", - default=False, - description="Height invert", - ) + name="Invert", + default=False, + description="Height invert", + ) height_offset: FloatProperty( - name="Offset", - default=0.0, - min=-10000.0, - max=10000.0, - description="Height offset" - ) + name="Offset", + default=0.0, + min=-10000.0, + max=10000.0, + description="Height offset" + ) fx_mixfactor: FloatProperty( - name="Mix Factor", - default=0.0, - min=-1.0, - max=1.0, - description="Effect mix factor: -1.0 = Noise, +1.0 = Effect" - ) + name="Mix Factor", + default=0.0, + min=-1.0, + max=1.0, + description="Effect mix factor: -1.0 = Noise, +1.0 = Effect" + ) fx_mix_mode: EnumProperty( - name="Effect Mix", - default="0", - description="Effect mix mode", - items = [ + name="Effect Mix", + default="0", + description="Effect mix mode", + items=[ ("0", "Mix", "Mix", 0), ("1", "Add", "Add", 1), ("2", "Sub", "Subtract", 2), @@ -330,13 +332,13 @@ class AntMeshDisplace(bpy.types.Operator): ("6", "Mod", "Modulo", 6), ("7", "Min", "Minimum", 7), ("8", "Max", "Maximum", 8) - ] - ) + ] + ) fx_type: EnumProperty( - name="Effect Type", - default="0", - description="Effect type", - items = [ + name="Effect Type", + default="0", + description="Effect type", + items=[ ("0", "None", "No effect", 0), ("1", "Gradient", "Gradient", 1), ("2", "Waves", "Waves - Bumps", 2), @@ -359,223 +361,220 @@ class AntMeshDisplace(bpy.types.Operator): ("19", "Stone", "Stone", 19), ("20", "Flat Turb", "Flat turbulence", 20), ("21", "Flat Voronoi", "Flat voronoi", 21) - ] - ) + ] + ) fx_bias: EnumProperty( - name="Effect Bias", - default="0", - description="Effect bias type", - items = [ + name="Effect Bias", + default="0", + description="Effect bias type", + items=[ ("0", "Sin", "Sin", 0), ("1", "Cos", "Cos", 1), ("2", "Tri", "Tri", 2), ("3", "Saw", "Saw", 3), ("4", "None", "None", 4) - ] - ) + ] + ) fx_turb: FloatProperty( - name="Distortion", - default=0.0, - min=0.0, - max=1000.0, - description="Effect turbulence distortion" - ) + name="Distortion", + default=0.0, + min=0.0, + max=1000.0, + description="Effect turbulence distortion" + ) fx_depth: IntProperty( - name="Depth", - default=0, - min=0, - max=16, - description="Effect depth - number of frequencies" - ) + name="Depth", + default=0, + min=0, + max=16, + description="Effect depth - number of frequencies" + ) fx_amplitude: FloatProperty( - name="Amp", - default=0.5, - min=0.01, - max=1.0, - description="Amplitude" - ) + name="Amp", + default=0.5, + min=0.01, + max=1.0, + description="Amplitude" + ) fx_frequency: FloatProperty( - name="Freq", - default=2.0, - min=0.01, - max=5.0, - description="Frequency" - ) + name="Freq", + default=2.0, + min=0.01, + max=5.0, + description="Frequency" + ) fx_size: FloatProperty( - name="Effect Size", - default=1.0, - min=0.01, - max=1000.0, - description="Effect size" - ) + name="Effect Size", + default=1.0, + min=0.01, + max=1000.0, + description="Effect size" + ) fx_loc_x: FloatProperty( - name="Offset X", - default=0.0, - description="Effect x offset" - ) + name="Offset X", + default=0.0, + description="Effect x offset" + ) fx_loc_y: FloatProperty( - name="Offset Y", - default=0.0, - description="Effect y offset" - ) + name="Offset Y", + default=0.0, + description="Effect y offset" + ) fx_height: FloatProperty( - name="Intensity", - default=1.0, - min=-1000.0, - max=1000.0, - description="Effect intensity scale" - ) + name="Intensity", + default=1.0, + min=-1000.0, + max=1000.0, + description="Effect intensity scale" + ) fx_invert: BoolProperty( - name="Invert", - default=False, - description="Effect invert" - ) + name="Invert", + default=False, + description="Effect invert" + ) fx_offset: FloatProperty( - name="Offset", - default=0.0, - min=-1000.0, - max=1000.0, - description="Effect height offset" - ) + name="Offset", + default=0.0, + min=-1000.0, + max=1000.0, + description="Effect height offset" + ) edge_falloff: EnumProperty( - name="Falloff", - default="0", - description="Flatten edges", - items = [ + name="Falloff", + default="0", + description="Flatten edges", + items=[ ("0", "None", "None", 0), ("1", "Y", "Y Falloff", 1), ("2", "X", "X Falloff", 2), ("3", "X Y", "X Y Falloff", 3)] - ) + ) falloff_x: FloatProperty( - name="Falloff X", - default=4.0, - min=0.1, - max=100.0, - description="Falloff x scale" - ) + name="Falloff X", + default=4.0, + min=0.1, + max=100.0, + description="Falloff x scale" + ) falloff_y: FloatProperty( - name="Falloff Y", - default=4.0, - min=0.1, - max=100.0, - description="Falloff y scale" - ) + name="Falloff Y", + default=4.0, + min=0.1, + max=100.0, + description="Falloff y scale" + ) edge_level: FloatProperty( - name="Edge Level", - default=0.0, - min=-10000.0, - max=10000.0, - description="Edge level, sealevel offset" - ) + name="Edge Level", + default=0.0, + min=-10000.0, + max=10000.0, + description="Edge level, sealevel offset" + ) maximum: FloatProperty( - name="Maximum", - default=1.0, - min=-10000.0, - max=10000.0, - description="Maximum, flattens terrain at plateau level" - ) + name="Maximum", + default=1.0, + min=-10000.0, + max=10000.0, + description="Maximum, flattens terrain at plateau level" + ) minimum: FloatProperty( - name="Minimum", - default=-1.0, - min=-10000.0, - max=10000.0, - description="Minimum, flattens terrain at seabed level" - ) + name="Minimum", + default=-1.0, + min=-10000.0, + max=10000.0, + description="Minimum, flattens terrain at seabed level" + ) vert_group: StringProperty( - name="Vertex Group", - default="" - ) + name="Vertex Group", + default="" + ) strata: FloatProperty( - name="Amount", - default=5.0, - min=0.01, - max=1000.0, - description="Strata layers / terraces" - ) + name="Amount", + default=5.0, + min=0.01, + max=1000.0, + description="Strata layers / terraces" + ) strata_type: EnumProperty( - name="Strata", - default="0", - description="Strata types", - items = [ + name="Strata", + default="0", + description="Strata types", + items=[ ("0", "None", "No strata", 0), ("1", "Smooth", "Smooth transitions", 1), ("2", "Sharp Sub", "Sharp subtract transitions", 2), ("3", "Sharp Add", "Sharp add transitions", 3), ("4", "Quantize", "Quantize", 4), ("5", "Quantize Mix", "Quantize mixed", 5)] - ) + ) water_plane: BoolProperty( - name="Water Plane", - default=False, - description="Add water plane" - ) + name="Water Plane", + default=False, + description="Add water plane" + ) water_level: FloatProperty( - name="Level", - default=0.01, - min=-10000.0, - max=10000.0, - description="Water level" - ) + name="Level", + default=0.01, + min=-10000.0, + max=10000.0, + description="Water level" + ) remove_double: BoolProperty( - name="Remove Doubles", - default=False, - description="Remove doubles" - ) + name="Remove Doubles", + default=False, + description="Remove doubles" + ) direction: EnumProperty( - name="Direction", - default="NORMAL", - description="Displacement direction", - items = [ + name="Direction", + default="NORMAL", + description="Displacement direction", + items=[ ("NORMAL", "Normal", "Displace along vertex normal direction", 0), ("Z", "Z", "Displace in the Z direction", 1), ("Y", "Y", "Displace in the Y direction", 2), ("X", "X", "Displace in the X direction", 3)] - ) + ) show_main_settings: BoolProperty( - name="Main Settings", - default=True, - description="Show settings" - ) + name="Main Settings", + default=True, + description="Show settings" + ) show_noise_settings: BoolProperty( - name="Noise Settings", - default=True, - description="Show noise settings" - ) + name="Noise Settings", + default=True, + description="Show noise settings" + ) show_displace_settings: BoolProperty( - name="Displace Settings", - default=True, - description="Show terrain settings" - ) + name="Displace Settings", + default=True, + description="Show terrain settings" + ) refresh: BoolProperty( - name="Refresh", - default=False, - description="Refresh" - ) + name="Refresh", + default=False, + description="Refresh" + ) auto_refresh: BoolProperty( - name="Auto", - default=False, - description="Automatic refresh" - ) + name="Auto", + default=False, + description="Automatic refresh" + ) def draw(self, context): draw_ant_refresh(self, context) draw_ant_noise(self, context, generate=False) draw_ant_displace(self, context, generate=False) - @classmethod def poll(cls, context): ob = context.object return (ob and ob.type == 'MESH') - def invoke(self, context, event): self.refresh = True return self.execute(context) - def execute(self, context): if not self.refresh: return {'PASS_THROUGH'} @@ -649,7 +648,7 @@ class AntMeshDisplace(bpy.types.Operator): self.fx_height, self.fx_offset, self.fx_invert - ] + ] # do displace mesh = ob.data diff --git a/ant_landscape/stats.py b/ant_landscape/stats.py index c8793dd9..b217a7b0 100644 --- a/ant_landscape/stats.py +++ b/ant_landscape/stats.py @@ -5,15 +5,16 @@ from time import time try: import psutil # print('psutil available') - psutil_available=True + psutil_available = True except ImportError: - psutil_available=False + psutil_available = False + class Stats: def __init__(self): self.memstats_available = False if psutil_available: - self.process=psutil.Process() + self.process = psutil.Process() self.memstats_available = True self.reset() diff --git a/ant_landscape/test.py b/ant_landscape/test.py index 39d060c4..65e641bd 100644 --- a/ant_landscape/test.py +++ b/ant_landscape/test.py @@ -18,6 +18,6 @@ if __name__ == '__main__': a = cos(a) print(stats.time()) print(stats.memory()) - a = cos(a)**2+sin(a)**2 + a = cos(a) ** 2 + sin(a) ** 2 print(stats.time()) print(stats.memory()) diff --git a/ant_landscape/utils.py b/ant_landscape/utils.py index 7687bd54..9451f83a 100644 --- a/ant_landscape/utils.py +++ b/ant_landscape/utils.py @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-or-later -numexpr_available=False +numexpr_available = False try: import numexpr - numexpr_available=True + numexpr_available = True except ImportError: pass -- cgit v1.2.3 From d0fa5bc86185a32acbf9ea79ea209d9d8a1f2935 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 2 Aug 2022 16:25:50 -0500 Subject: Tissue: Use vertex color functions instead of operators Needed after rB44aa9e40ffe40c0b2. --- mesh_tissue/weight_tools.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mesh_tissue/weight_tools.py b/mesh_tissue/weight_tools.py index 2736945e..8576b88c 100644 --- a/mesh_tissue/weight_tools.py +++ b/mesh_tissue/weight_tools.py @@ -2513,7 +2513,7 @@ class vertex_group_to_vertex_colors(Operator): bpy.ops.object.mode_set(mode='OBJECT') group_name = obj.vertex_groups[group_id].name - bpy.ops.mesh.vertex_color_add() + me.vertex_colors.new() colors_id = obj.data.vertex_colors.active_index colors_name = group_name @@ -2694,8 +2694,8 @@ class curvature_to_vertex_groups(Operator): def execute(self, context): bpy.ops.object.mode_set(mode='OBJECT') - bpy.ops.mesh.vertex_color_add() vertex_colors = context.active_object.data.vertex_colors + vertex_colors.new() vertex_colors[-1].active = True vertex_colors[-1].active_render = True vertex_colors[-1].name = "Curvature" @@ -2706,7 +2706,7 @@ class curvature_to_vertex_groups(Operator): blur_iterations=self.blur_iterations, clean_angle=self.max_angle, dirt_angle=self.min_angle) bpy.ops.object.vertex_colors_to_vertex_groups(invert=self.invert) - bpy.ops.mesh.vertex_color_remove() + vertex_colors.remove(vertex_colors.active) return {'FINISHED'} class face_area_to_vertex_groups(Operator): -- cgit v1.2.3 From 4b25801f640ee49f49c63d4de6871504236ce6b7 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 5 Aug 2022 15:07:16 +1000 Subject: Fix T99496: Magic UV error in menu search --- magic_uv/common.py | 7 +++++-- magic_uv/op/copy_paste_uv_object.py | 9 +++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/magic_uv/common.py b/magic_uv/common.py index fccce1c4..f76fcc67 100644 --- a/magic_uv/common.py +++ b/magic_uv/common.py @@ -1242,11 +1242,14 @@ def __is_points_in_polygon(points, subject_points): def get_uv_editable_objects(context): if compat.check_version(2, 80, 0) < 0: - objs = [context.active_object] + objs = [] else: objs = [o for o in bpy.data.objects if compat.get_object_select(o) and o.type == 'MESH'] - objs.append(context.active_object) + + ob = context.active_object + if ob is not None: + objs.append(ob) objs = list(set(objs)) return objs diff --git a/magic_uv/op/copy_paste_uv_object.py b/magic_uv/op/copy_paste_uv_object.py index 39795b52..897891e4 100644 --- a/magic_uv/op/copy_paste_uv_object.py +++ b/magic_uv/op/copy_paste_uv_object.py @@ -30,15 +30,16 @@ def _is_valid_context(context): if not common.is_valid_space(context, ['VIEW_3D']): return False + # Only object mode is allowed to execute. + ob = context.object + if ob is not None and ob.mode != 'OBJECT': + return False + # Multiple objects editing mode is not supported in this feature. objs = common.get_uv_editable_objects(context) if len(objs) != 1: return False - # only object mode is allowed to execute - if context.object.mode != 'OBJECT': - return False - return True -- cgit v1.2.3 From 32e2c3d61e48271ca63f77bf3b5dd8b4b607aa1f Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sat, 6 Aug 2022 11:45:14 +0200 Subject: glTF: Bump version after blender-v3.3-release branch creation --- io_scene_gltf2/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 98210e82..b4d05c47 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -4,7 +4,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (3, 3, 17), + "version": (3, 4, 0), 'blender': (3, 3, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', -- cgit v1.2.3 From c648829f83d66969cd6afb954630b8c4c6971f3f Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sat, 6 Aug 2022 11:50:04 +0200 Subject: glTF importer: update comment --- io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py b/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py index 6f6279dc..cc001b72 100755 --- a/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py +++ b/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py @@ -616,7 +616,9 @@ def occlusion(mh: MaterialHelper, location, occlusion_socket): ) -# => [Add Emission] => [Mix Alpha] => [Material Output] +# => [Add Emission] => [Mix Alpha] => [Material Output] if needed, only for SpecGlossiness +# => [Volume] => [Add Shader] => [Material Output] if needed +# => [Velvet] => [Add Shader] => [Material Output] if nedded def make_output_nodes( mh: MaterialHelper, location, -- cgit v1.2.3 From 5b2953ad0883223bf8f58cf2bd650891f759bb65 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sat, 6 Aug 2022 11:52:04 +0200 Subject: glTF exporter: performance: cache parent/children data to avoid bad .children performance when used a lot Thanks rotoglup! --- io_scene_gltf2/__init__.py | 2 +- .../blender/exp/gltf2_blender_gather_tree.py | 28 ++++++++++++++-------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 98210e82..65cfb759 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -4,7 +4,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (3, 3, 17), + "version": (3, 3, 18), 'blender': (3, 3, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py index 36772667..c654b445 100644 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py @@ -95,10 +95,18 @@ class VExportTree: bpy.context.window.scene = blender_scene depsgraph = bpy.context.evaluated_depsgraph_get() + # Gather parent/children information once, as calling bobj.children is + # very expensive operation : takes O(len(bpy.data.objects)) time. + blender_children = dict() + for bobj in bpy.data.objects: + bparent = bobj.parent + blender_children.setdefault(bobj, []) + blender_children.setdefault(bparent, []).append(bobj) + for blender_object in [obj.original for obj in depsgraph.scene_eval.objects if obj.parent is None]: - self.recursive_node_traverse(blender_object, None, None, Matrix.Identity(4)) + self.recursive_node_traverse(blender_object, None, None, Matrix.Identity(4), blender_children) - def recursive_node_traverse(self, blender_object, blender_bone, parent_uuid, parent_coll_matrix_world, armature_uuid=None, dupli_world_matrix=None): + def recursive_node_traverse(self, blender_object, blender_bone, parent_uuid, parent_coll_matrix_world, blender_children, armature_uuid=None, dupli_world_matrix=None): node = VExportNode() node.uuid = str(uuid.uuid4()) node.parent_uuid = parent_uuid @@ -199,42 +207,42 @@ class VExportTree: # standard children if blender_bone is None and blender_object.is_instancer is False: - for child_object in blender_object.children: + for child_object in blender_children[blender_object]: if child_object.parent_bone: # Object parented to bones # Will be manage later continue else: # Classic parenting - self.recursive_node_traverse(child_object, None, node.uuid, parent_coll_matrix_world) + self.recursive_node_traverse(child_object, None, node.uuid, parent_coll_matrix_world, blender_children) # Collections if blender_object.instance_type == 'COLLECTION' and blender_object.instance_collection: for dupli_object in blender_object.instance_collection.all_objects: if dupli_object.parent is not None: continue - self.recursive_node_traverse(dupli_object, None, node.uuid, node.matrix_world) + self.recursive_node_traverse(dupli_object, None, node.uuid, node.matrix_world, blender_children) # Armature : children are bones with no parent if blender_object.type == "ARMATURE" and blender_bone is None: for b in [b for b in blender_object.pose.bones if b.parent is None]: - self.recursive_node_traverse(blender_object, b, node.uuid, parent_coll_matrix_world, node.uuid) + self.recursive_node_traverse(blender_object, b, node.uuid, parent_coll_matrix_world, blender_children, node.uuid) # Bones if blender_object.type == "ARMATURE" and blender_bone is not None: for b in blender_bone.children: - self.recursive_node_traverse(blender_object, b, node.uuid, parent_coll_matrix_world, armature_uuid) + self.recursive_node_traverse(blender_object, b, node.uuid, parent_coll_matrix_world, blender_children, armature_uuid) # Object parented to bone if blender_bone is not None: - for child_object in [c for c in blender_object.children if c.parent_bone is not None and c.parent_bone == blender_bone.name]: - self.recursive_node_traverse(child_object, None, node.uuid, parent_coll_matrix_world) + for child_object in [c for c in blender_children[blender_object] if c.parent_bone is not None and c.parent_bone == blender_bone.name]: + self.recursive_node_traverse(child_object, None, node.uuid, parent_coll_matrix_world, blender_children) # Duplis if blender_object.is_instancer is True and blender_object.instance_type != 'COLLECTION': depsgraph = bpy.context.evaluated_depsgraph_get() for (dupl, mat) in [(dup.object.original, dup.matrix_world.copy()) for dup in depsgraph.object_instances if dup.parent and id(dup.parent.original) == id(blender_object)]: - self.recursive_node_traverse(dupl, None, node.uuid, parent_coll_matrix_world, dupli_world_matrix=mat) + self.recursive_node_traverse(dupl, None, node.uuid, parent_coll_matrix_world, blender_children, dupli_world_matrix=mat) def get_all_objects(self): return [n.uuid for n in self.nodes.values() if n.blender_type != VExportNode.BONE] -- cgit v1.2.3 From 69b9725ba0f8d4b28da3e60e35b4bf009ff6583f Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sat, 6 Aug 2022 11:57:45 +0200 Subject: glTF exporter: avoid crash when all drivers are invalids --- io_scene_gltf2/__init__.py | 2 +- io_scene_gltf2/blender/exp/gltf2_blender_gather_drivers.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 65cfb759..865e3cd4 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -4,7 +4,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (3, 3, 18), + "version": (3, 3, 19), 'blender': (3, 3, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_drivers.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_drivers.py index 4b24599a..9da5cc65 100644 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_drivers.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_drivers.py @@ -74,7 +74,8 @@ def get_sk_drivers(blender_armature_uuid, export_settings): else: all_sorted_channels.append(existing_idx[i]) - if len(all_sorted_channels) > 0: + # Checks there are some driver on SK, and that there is not only invalid drivers + if len(all_sorted_channels) > 0 and not all([i is None for i in all_sorted_channels]): drivers.append((child_uuid, tuple(all_sorted_channels))) return tuple(drivers) -- cgit v1.2.3 From a24be9789174435fa723c4864969ad05a9fb5c01 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sat, 6 Aug 2022 12:02:33 +0200 Subject: glTF: add operator descriptions in menu --- io_scene_gltf2/__init__.py | 2 +- io_scene_gltf2/blender/com/gltf2_blender_ui.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 865e3cd4..5b2b12ed 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -4,7 +4,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (3, 3, 19), + "version": (3, 3, 20), 'blender': (3, 3, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/blender/com/gltf2_blender_ui.py b/io_scene_gltf2/blender/com/gltf2_blender_ui.py index 6c77ccda..6a667630 100644 --- a/io_scene_gltf2/blender/com/gltf2_blender_ui.py +++ b/io_scene_gltf2/blender/com/gltf2_blender_ui.py @@ -17,6 +17,7 @@ def create_gltf_ao_group(operator, group_name): class NODE_OT_GLTF_SETTINGS(bpy.types.Operator): bl_idname = "node.gltf_settings_node_operator" bl_label = "glTF Settings" + bl_description = "Add a node to the active tree for glTF export" @classmethod @@ -46,6 +47,7 @@ def add_gltf_settings_to_menu(self, context) : class NODE_OT_GLTF_PBR_NON_CONVERTED_EXTENSIONS(bpy.types.Operator): bl_idname = "node.gltf_pbr_non_converted_extensions_operator" bl_label = "glTF Original PBR data" + bl_description = "Add a node to the active tree for glTF export" @classmethod def poll(cls, context): -- cgit v1.2.3 From c58c8f3c3525247c6f584684260eae4ea87a57a3 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sat, 6 Aug 2022 12:07:14 +0200 Subject: glTF: rename glTF Settings node to glTF Material Output --- io_scene_gltf2/__init__.py | 4 ++-- io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py | 6 +++++- io_scene_gltf2/blender/com/gltf2_blender_ui.py | 5 ++--- io_scene_gltf2/blender/exp/gltf2_blender_get.py | 6 +++--- io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py | 2 +- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 5b2b12ed..e3e2c483 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -4,7 +4,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (3, 3, 20), + "version": (3, 3, 21), 'blender': (3, 3, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', @@ -1257,7 +1257,7 @@ class GLTF_AddonPreferences(bpy.types.AddonPreferences): settings_node_ui : bpy.props.BoolProperty( default= False, - description="Displays glTF Settings node in Shader Editor (Menu Add > Output)" + description="Displays glTF Material Output node in Shader Editor (Menu Add > Output)" ) KHR_materials_variants_ui : bpy.props.BoolProperty( diff --git a/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py b/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py index a44562a1..0bca9933 100755 --- a/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py +++ b/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py @@ -3,9 +3,13 @@ import bpy -def get_gltf_node_name(): +# Get compatibility at export with old files +def get_gltf_node_old_name(): return "glTF Settings" +def get_gltf_node_name(): + return "glTF Material Output" + def create_settings_group(name): gltf_node_group = bpy.data.node_groups.new(name, 'ShaderNodeTree') gltf_node_group.inputs.new("NodeSocketFloat", "Occlusion") diff --git a/io_scene_gltf2/blender/com/gltf2_blender_ui.py b/io_scene_gltf2/blender/com/gltf2_blender_ui.py index 6a667630..2f24c081 100644 --- a/io_scene_gltf2/blender/com/gltf2_blender_ui.py +++ b/io_scene_gltf2/blender/com/gltf2_blender_ui.py @@ -5,7 +5,7 @@ import bpy from ..com.gltf2_blender_material_helpers import get_gltf_node_name, create_settings_group from ..com.gltf2_blender_material_helpers import get_gltf_pbr_non_converted_name, create_gltf_pbr_non_converted_group -################ glTF Settings node ########################################### +################ glTF Material Output node ########################################### def create_gltf_ao_group(operator, group_name): @@ -16,10 +16,9 @@ def create_gltf_ao_group(operator, group_name): class NODE_OT_GLTF_SETTINGS(bpy.types.Operator): bl_idname = "node.gltf_settings_node_operator" - bl_label = "glTF Settings" + bl_label = "glTF Material Ouptut" bl_description = "Add a node to the active tree for glTF export" - @classmethod def poll(cls, context): space = context.space_data diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_get.py b/io_scene_gltf2/blender/exp/gltf2_blender_get.py index 17eacf86..da6e9b57 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_get.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_get.py @@ -4,7 +4,7 @@ import bpy from mathutils import Vector, Matrix -from ..com.gltf2_blender_material_helpers import get_gltf_node_name, get_gltf_pbr_non_converted_name +from ..com.gltf2_blender_material_helpers import get_gltf_node_name, get_gltf_node_old_name, get_gltf_pbr_non_converted_name from ...blender.com.gltf2_blender_conversion import texture_transform_blender_to_gltf from io_scene_gltf2.io.com import gltf2_io_debug from io_scene_gltf2.blender.exp import gltf2_blender_search_node_tree @@ -94,11 +94,11 @@ def get_socket_old(blender_material: bpy.types.Material, name: str): :param name: the name of the socket :return: a blender NodeSocket """ - gltf_node_group_name = get_gltf_node_name().lower() + gltf_node_group_names = [get_gltf_node_name().lower(), get_gltf_node_old_name().lower()] if blender_material.node_tree and blender_material.use_nodes: nodes = [n for n in blender_material.node_tree.nodes if \ isinstance(n, bpy.types.ShaderNodeGroup) and \ - (n.node_tree.name.startswith('glTF Metallic Roughness') or n.node_tree.name.lower() == gltf_node_group_name)] + (n.node_tree.name.startswith('glTF Metallic Roughness') or n.node_tree.name.lower() in gltf_node_group_names)] inputs = sum([[input for input in node.inputs if input.name == name] for node in nodes], []) if inputs: return inputs[0] diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py b/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py index cc001b72..696aa1fc 100755 --- a/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py +++ b/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py @@ -65,7 +65,7 @@ def pbr_metallic_roughness(mh: MaterialHelper): need_volume_node = True - # We also need glTF Settings Node, to set thicknessFactor and thicknessTexture + # We also need glTF Material Output Node, to set thicknessFactor and thicknessTexture mh.settings_node = make_settings_node(mh) mh.settings_node.location = additional_location mh.settings_node.width = 180 -- cgit v1.2.3 From ef0027e72d295f5bbae1905c6260878851131eef Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sat, 6 Aug 2022 12:11:15 +0200 Subject: glTF exporter: add bufferView Target at export --- io_scene_gltf2/__init__.py | 2 +- .../blender/exp/gltf2_blender_gather_primitive_attributes.py | 4 ++-- io_scene_gltf2/blender/exp/gltf2_blender_gather_primitives.py | 2 +- io_scene_gltf2/io/com/gltf2_io_constants.py | 3 +++ io_scene_gltf2/io/exp/gltf2_io_binary_data.py | 7 ++++--- io_scene_gltf2/io/exp/gltf2_io_buffer.py | 2 +- 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index e3e2c483..6146be8a 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -4,7 +4,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (3, 3, 21), + "version": (3, 3, 22), 'blender': (3, 3, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitive_attributes.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitive_attributes.py index f28a1f10..8572d185 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitive_attributes.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitive_attributes.py @@ -44,7 +44,7 @@ def array_to_accessor(array, component_type, data_type, include_max_and_min=Fals amin = np.amin(array, axis=0).tolist() return gltf2_io.Accessor( - buffer_view=gltf2_io_binary_data.BinaryData(array.tobytes()), + buffer_view=gltf2_io_binary_data.BinaryData(array.tobytes(), gltf2_io_constants.BufferViewTarget.ARRAY_BUFFER), byte_offset=None, component_type=component_type, count=len(array), @@ -142,7 +142,7 @@ def __gather_colors(blender_primitive, export_settings): comp_type = gltf2_io_constants.ComponentType.Float attributes[color_id] = gltf2_io.Accessor( - buffer_view=gltf2_io_binary_data.BinaryData(colors.tobytes()), + buffer_view=gltf2_io_binary_data.BinaryData(colors.tobytes(), gltf2_io_constants.BufferViewTarget.ARRAY_BUFFER), byte_offset=None, component_type=comp_type, count=len(colors), diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitives.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitives.py index b2ffb6b3..576a1418 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitives.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitives.py @@ -150,7 +150,7 @@ def __gather_indices(blender_primitive, blender_mesh, modifiers, export_settings return None element_type = gltf2_io_constants.DataType.Scalar - binary_data = gltf2_io_binary_data.BinaryData(indices.tobytes()) + binary_data = gltf2_io_binary_data.BinaryData(indices.tobytes(), bufferViewTarget=gltf2_io_constants.BufferViewTarget.ELEMENT_ARRAY_BUFFER) return gltf2_blender_gather_accessors.gather_accessor( binary_data, component_type, diff --git a/io_scene_gltf2/io/com/gltf2_io_constants.py b/io_scene_gltf2/io/com/gltf2_io_constants.py index 175804a3..816220d9 100755 --- a/io_scene_gltf2/io/com/gltf2_io_constants.py +++ b/io_scene_gltf2/io/com/gltf2_io_constants.py @@ -118,6 +118,9 @@ class TextureWrap(IntEnum): MirroredRepeat = 33648 Repeat = 10497 +class BufferViewTarget(IntEnum): + ARRAY_BUFFER = 34962 + ELEMENT_ARRAY_BUFFER = 34963 ################# # LEGACY DEFINES diff --git a/io_scene_gltf2/io/exp/gltf2_io_binary_data.py b/io_scene_gltf2/io/exp/gltf2_io_binary_data.py index 10405551..6a617628 100755 --- a/io_scene_gltf2/io/exp/gltf2_io_binary_data.py +++ b/io_scene_gltf2/io/exp/gltf2_io_binary_data.py @@ -9,10 +9,11 @@ from io_scene_gltf2.io.com import gltf2_io_constants class BinaryData: """Store for gltf binary data that can later be stored in a buffer.""" - def __init__(self, data: bytes): + def __init__(self, data: bytes, bufferViewTarget=None): if not isinstance(data, bytes): raise TypeError("Data is not a bytes array") self.data = data + self.bufferViewTarget = bufferViewTarget def __eq__(self, other): return self.data == other.data @@ -21,9 +22,9 @@ class BinaryData: return hash(self.data) @classmethod - def from_list(cls, lst: typing.List[typing.Any], gltf_component_type: gltf2_io_constants.ComponentType): + def from_list(cls, lst: typing.List[typing.Any], gltf_component_type: gltf2_io_constants.ComponentType, bufferViewTarget=None): format_char = gltf2_io_constants.ComponentType.to_type_code(gltf_component_type) - return BinaryData(array.array(format_char, lst).tobytes()) + return BinaryData(array.array(format_char, lst).tobytes(), bufferViewTarget) @property def byte_length(self): diff --git a/io_scene_gltf2/io/exp/gltf2_io_buffer.py b/io_scene_gltf2/io/exp/gltf2_io_buffer.py index 5fae3834..4b70e789 100755 --- a/io_scene_gltf2/io/exp/gltf2_io_buffer.py +++ b/io_scene_gltf2/io/exp/gltf2_io_buffer.py @@ -35,7 +35,7 @@ class Buffer: extensions=None, extras=None, name=None, - target=None + target=binary_data.bufferViewTarget ) return buffer_view -- cgit v1.2.3 From eca1bdeb88a35deeaec8f2969953dd6d503315a3 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sat, 6 Aug 2022 12:15:54 +0200 Subject: glTF exporter: KHR_material_volume: thickness is now default to 0.0 (as in specification) --- io_scene_gltf2/__init__.py | 2 +- io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py | 2 +- io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_volume.py | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 6146be8a..3fb5ad4d 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -4,7 +4,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (3, 3, 22), + "version": (3, 3, 23), 'blender': (3, 3, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py b/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py index 0bca9933..7dfef7dc 100755 --- a/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py +++ b/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py @@ -14,7 +14,7 @@ def create_settings_group(name): gltf_node_group = bpy.data.node_groups.new(name, 'ShaderNodeTree') gltf_node_group.inputs.new("NodeSocketFloat", "Occlusion") thicknessFactor = gltf_node_group.inputs.new("NodeSocketFloat", "Thickness") - thicknessFactor.default_value = 1.0 + thicknessFactor.default_value = 0.0 gltf_node_group.nodes.new('NodeGroupOutput') gltf_node_group_input = gltf_node_group.nodes.new('NodeGroupInput') gltf_node_group_input.location = -200, 0 diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_volume.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_volume.py index 57b00700..8a69e3f6 100644 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_volume.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_volume.py @@ -27,12 +27,12 @@ def export_volume(blender_material, export_settings): thicknesss_socket = gltf2_blender_get.get_socket_old(blender_material, 'Thickness') if thicknesss_socket is None: - volume_extension['thicknessFactor'] = 1.0 + # If no thickness (here because there is no glTF Material Output node), no volume extension export + return None, None density_socket = gltf2_blender_get.get_socket(blender_material, 'Density', volume=True) attenuation_color_socket = gltf2_blender_get.get_socket(blender_material, 'Color', volume=True) - if density_socket is None or attenuation_color_socket is None: - return None, None + # Even if density or attenuation are not set, we export volume extension if isinstance(attenuation_color_socket, bpy.types.NodeSocket): rgb = gltf2_blender_get.get_const_from_default_value_socket(attenuation_color_socket, kind='RGB') -- cgit v1.2.3 From ae29cfd1860e53e61d1b58d9c406638927a8ab40 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Mon, 8 Aug 2022 17:03:41 +0200 Subject: glTF: Merge glTF Material Output nodes into a single one --- io_scene_gltf2/__init__.py | 2 +- .../blender/com/gltf2_blender_material_helpers.py | 20 +++---------- io_scene_gltf2/blender/com/gltf2_blender_ui.py | 34 ---------------------- .../blender/exp/gltf2_blender_gather_image.py | 4 +-- .../exp/gltf2_blender_gather_materials_specular.py | 4 +-- io_scene_gltf2/blender/exp/gltf2_blender_get.py | 20 +------------ .../imp/gltf2_blender_KHR_materials_sheen.py | 2 +- .../imp/gltf2_blender_pbrMetallicRoughness.py | 32 ++------------------ 8 files changed, 14 insertions(+), 104 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 3fb5ad4d..503a9edd 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -4,7 +4,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (3, 3, 23), + "version": (3, 3, 24), 'blender': (3, 3, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py b/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py index 7dfef7dc..4f8417e9 100755 --- a/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py +++ b/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py @@ -17,21 +17,9 @@ def create_settings_group(name): thicknessFactor.default_value = 0.0 gltf_node_group.nodes.new('NodeGroupOutput') gltf_node_group_input = gltf_node_group.nodes.new('NodeGroupInput') - gltf_node_group_input.location = -200, 0 - return gltf_node_group - -def get_gltf_pbr_non_converted_name(): - return "original glTF PBR data" - -def create_gltf_pbr_non_converted_group(name): - gltf_node_group = bpy.data.node_groups.new(name, 'ShaderNodeTree') - - specular = gltf_node_group.inputs.new("NodeSocketFloat", "specular glTF") + specular = gltf_node_group.inputs.new("NodeSocketFloat", "Specular") specular.default_value = 1.0 - specularColor = gltf_node_group.inputs.new("NodeSocketColor", "specularColor glTF") + specularColor = gltf_node_group.inputs.new("NodeSocketColor", "Specular Color") specularColor.default_value = [1.0,1.0,1.0,1.0] - - gltf_node_group.nodes.new('NodeGroupOutput') - gltf_node_group_input = gltf_node_group.nodes.new('NodeGroupInput') - gltf_node_group_input.location = -400, 0 - return gltf_node_group \ No newline at end of file + gltf_node_group_input.location = -200, 0 + return gltf_node_group diff --git a/io_scene_gltf2/blender/com/gltf2_blender_ui.py b/io_scene_gltf2/blender/com/gltf2_blender_ui.py index 2f24c081..406a15f1 100644 --- a/io_scene_gltf2/blender/com/gltf2_blender_ui.py +++ b/io_scene_gltf2/blender/com/gltf2_blender_ui.py @@ -3,7 +3,6 @@ import bpy from ..com.gltf2_blender_material_helpers import get_gltf_node_name, create_settings_group -from ..com.gltf2_blender_material_helpers import get_gltf_pbr_non_converted_name, create_gltf_pbr_non_converted_group ################ glTF Material Output node ########################################### @@ -43,36 +42,6 @@ def add_gltf_settings_to_menu(self, context) : if bpy.context.preferences.addons['io_scene_gltf2'].preferences.settings_node_ui is True: self.layout.operator("node.gltf_settings_node_operator") -class NODE_OT_GLTF_PBR_NON_CONVERTED_EXTENSIONS(bpy.types.Operator): - bl_idname = "node.gltf_pbr_non_converted_extensions_operator" - bl_label = "glTF Original PBR data" - bl_description = "Add a node to the active tree for glTF export" - - @classmethod - def poll(cls, context): - space = context.space_data - return space.type == "NODE_EDITOR" \ - and context.object and context.object.active_material \ - and context.object.active_material.use_nodes is True \ - and bpy.context.preferences.addons['io_scene_gltf2'].preferences.settings_node_ui is True - - def execute(self, context): - gltf_node_name = get_gltf_pbr_non_converted_name() - if gltf_node_name in bpy.data.node_groups: - my_group = bpy.data.node_groups[get_gltf_pbr_non_converted_name()] - else: - my_group = create_gltf_pbr_non_converted_group(gltf_node_name) - node_tree = context.object.active_material.node_tree - new_node = node_tree.nodes.new("ShaderNodeGroup") - new_node.node_tree = bpy.data.node_groups[my_group.name] - return {"FINISHED"} - - -def add_gltf_pbr_non_converted_extensions_to_menu(self, context) : - if bpy.context.preferences.addons['io_scene_gltf2'].preferences.settings_node_ui is True: - self.layout.operator("node.gltf_pbr_non_converted_extensions_operator") - - ################################### KHR_materials_variants #################### # Global UI panel @@ -469,9 +438,7 @@ class SCENE_OT_gltf2_remove_material_variant(bpy.types.Operator): def register(): bpy.utils.register_class(NODE_OT_GLTF_SETTINGS) - bpy.utils.register_class(NODE_OT_GLTF_PBR_NON_CONVERTED_EXTENSIONS) bpy.types.NODE_MT_category_SH_NEW_OUTPUT.append(add_gltf_settings_to_menu) - bpy.types.NODE_MT_category_SH_NEW_OUTPUT.append(add_gltf_pbr_non_converted_extensions_to_menu) def variant_register(): bpy.utils.register_class(SCENE_OT_gltf2_display_variant) @@ -499,7 +466,6 @@ def variant_register(): def unregister(): bpy.utils.unregister_class(NODE_OT_GLTF_SETTINGS) - bpy.utils.unregister_class(NODE_OT_GLTF_PBR_NON_CONVERTED_EXTENSIONS) def variant_unregister(): bpy.utils.unregister_class(SCENE_OT_gltf2_variant_add) diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py index 69900c1b..81e79a50 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_image.py @@ -188,7 +188,7 @@ def __get_image_data(sockets, export_settings) -> ExportImage: results = [__get_tex_from_socket(socket, export_settings) for socket in sockets] # Check if we need a simple mapping or more complex calculation - if any([socket.name == "Specular" for socket in sockets]): + if any([socket.name == "Specular" and socket.node.type == "BSDF_PRINCIPLED" for socket in sockets]): return __get_image_data_specular(sockets, results, export_settings) else: return __get_image_data_mapping(sockets, results, export_settings) @@ -238,7 +238,7 @@ def __get_image_data_mapping(sockets, results, export_settings) -> ExportImage: dst_chan = Channel.G elif socket.name == 'Thickness': # For KHR_materials_volume dst_chan = Channel.G - elif socket.name == "specular glTF": # For original KHR_material_specular + elif socket.name == "Specular": # For original KHR_material_specular dst_chan = Channel.A elif socket.name == "Sigma": # For KHR_materials_sheen dst_chan = Channel.A diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_specular.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_specular.py index 30b32198..22414b13 100644 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_specular.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_specular.py @@ -13,8 +13,8 @@ from io_scene_gltf2.blender.exp import gltf2_blender_gather_texture_info def export_original_specular(blender_material, export_settings): specular_extension = {} - original_specular_socket = gltf2_blender_get.get_socket_original(blender_material, 'specular glTF') - original_specularcolor_socket = gltf2_blender_get.get_socket_original(blender_material, 'specularColor glTF') + original_specular_socket = gltf2_blender_get.get_socket_old(blender_material, 'Specular') + original_specularcolor_socket = gltf2_blender_get.get_socket_old(blender_material, 'Specular Color') if original_specular_socket is None or original_specularcolor_socket is None: return None, None diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_get.py b/io_scene_gltf2/blender/exp/gltf2_blender_get.py index da6e9b57..9e468186 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_get.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_get.py @@ -4,7 +4,7 @@ import bpy from mathutils import Vector, Matrix -from ..com.gltf2_blender_material_helpers import get_gltf_node_name, get_gltf_node_old_name, get_gltf_pbr_non_converted_name +from ..com.gltf2_blender_material_helpers import get_gltf_node_name, get_gltf_node_old_name from ...blender.com.gltf2_blender_conversion import texture_transform_blender_to_gltf from io_scene_gltf2.io.com import gltf2_io_debug from io_scene_gltf2.blender.exp import gltf2_blender_search_node_tree @@ -105,24 +105,6 @@ def get_socket_old(blender_material: bpy.types.Material, name: str): return None -def get_socket_original(blender_material: bpy.types.Material, name: str): - """ - For a given material input name, retrieve the corresponding node tree socket in the special glTF node group. - - :param blender_material: a blender material for which to get the socket - :param name: the name of the socket - :return: a blender NodeSocket - """ - gltf_node_group_name = get_gltf_pbr_non_converted_name().lower() - if blender_material.node_tree and blender_material.use_nodes: - nodes = [n for n in blender_material.node_tree.nodes if \ - isinstance(n, bpy.types.ShaderNodeGroup) and n.node_tree.name.lower() == gltf_node_group_name] - inputs = sum([[input for input in node.inputs if input.name == name] for node in nodes], []) - if inputs: - return inputs[0] - - return None - def check_if_is_linked_to_active_output(shader_socket): for link in shader_socket.links: if isinstance(link.to_node, bpy.types.ShaderNodeOutputMaterial) and link.to_node.is_active_output is True: diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_sheen.py b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_sheen.py index 3560d094..aa5cef75 100644 --- a/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_sheen.py +++ b/io_scene_gltf2/blender/imp/gltf2_blender_KHR_materials_sheen.py @@ -4,7 +4,7 @@ from ...io.com.gltf2_io import TextureInfo from .gltf2_blender_texture import texture from .gltf2_blender_image import BlenderImage -from ..exp.gltf2_blender_image import TmpImageGuard, make_temp_image_copy +from ..exp.gltf2_blender_image import TmpImageGuard import numpy as np import bpy diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py b/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py index 696aa1fc..d130f4e6 100755 --- a/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py +++ b/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py @@ -5,7 +5,6 @@ from re import M import bpy from ...io.com.gltf2_io import TextureInfo, MaterialPBRMetallicRoughness from ..com.gltf2_blender_material_helpers import get_gltf_node_name, create_settings_group -from ..com.gltf2_blender_material_helpers import get_gltf_pbr_non_converted_name, create_gltf_pbr_non_converted_group from .gltf2_blender_texture import texture from .gltf2_blender_KHR_materials_clearcoat import \ clearcoat, clearcoat_roughness, clearcoat_normal @@ -28,7 +27,6 @@ class MaterialHelper: pymat.pbr_metallic_roughness = \ MaterialPBRMetallicRoughness.from_dict({}) self.settings_node = None - self.original_pbr_node = None def is_opaque(self): alpha_mode = self.pymat.alpha_mode @@ -51,7 +49,7 @@ def pbr_metallic_roughness(mh: MaterialHelper): # This value may be overidden later if IOR extension is set on file pbr_node.inputs['IOR'].default_value = GLTF_IOR - if mh.pymat.occlusion_texture is not None: + if mh.pymat.occlusion_texture is not None or (mh.pymat.extensions and 'KHR_materials_specular' in mh.pymat.extensions): if mh.settings_node is None: mh.settings_node = make_settings_node(mh) mh.settings_node.location = additional_location @@ -87,12 +85,6 @@ def pbr_metallic_roughness(mh: MaterialHelper): make_velvet_socket=need_velvet_node ) - if mh.pymat.extensions and 'KHR_materials_specular' in mh.pymat.extensions: - # We need glTF PBR Non Converted Extensions Node - mh.original_pbr_node = make_pbr_non_converted_extensions_node(mh) - mh.original_pbr_node.location = additional_location - mh.original_pbr_node.width = 180 - additional_location = additional_location[0], additional_location[1] - 150 if mh.pymat.extensions and 'KHR_materials_sheen': pass #TOTOEXT @@ -171,8 +163,8 @@ def pbr_metallic_roughness(mh: MaterialHelper): location_specular_tint=locs['specularColorTexture'], specular_socket=pbr_node.inputs['Specular'], specular_tint_socket=pbr_node.inputs['Specular Tint'], - original_specular_socket=mh.original_pbr_node.inputs[0] if mh.original_pbr_node else None, - original_specularcolor_socket=mh.original_pbr_node.inputs[1] if mh.original_pbr_node else None, + original_specular_socket=mh.settings_node.inputs[2] if mh.settings_node else None, + original_specularcolor_socket=mh.settings_node.inputs[3] if mh.settings_node else None, location_original_specular=locs['original_specularTexture'], location_original_specularcolor=locs['original_specularColorTexture'] ) @@ -752,21 +744,3 @@ def get_settings_group(): # Create a new node group gltf_node_group = create_settings_group(gltf_node_group_name) return gltf_node_group - -def make_pbr_non_converted_extensions_node(mh): - """ - Make a Group node with a hookup for PBR Non Converted Extensions. No effect in Blender, but - used to tell the exporter what the original map(s) should be. - """ - node = mh.node_tree.nodes.new('ShaderNodeGroup') - node.node_tree = get_pbr_non_converted_extensions_group() - return node - -def get_pbr_non_converted_extensions_group(): - gltf_node_group_name = get_gltf_pbr_non_converted_name() - if gltf_node_group_name in bpy.data.node_groups: - gltf_node_group = bpy.data.node_groups[gltf_node_group_name] - else: - # Create a new node group - gltf_node_group = create_gltf_pbr_non_converted_group(gltf_node_group_name) - return gltf_node_group \ No newline at end of file -- cgit v1.2.3 From fd5afdfe2f67769886eaf50672baca14f3aebf5c Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 9 Aug 2022 17:08:43 +1000 Subject: object_print3d_utils: replace f-strings by str.format() for I18n Unfortunately, messages cannot be properly extracted from f-strings. Use `str.format()` method instead. Ref D15615 --- object_print3d_utils/export.py | 4 +++- object_print3d_utils/operators.py | 47 +++++++++++++++++++++++---------------- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/object_print3d_utils/export.py b/object_print3d_utils/export.py index d17cd45f..e4e3550b 100644 --- a/object_print3d_utils/export.py +++ b/object_print3d_utils/export.py @@ -5,6 +5,8 @@ import bpy +from bpy.app.translations import pgettext_tip as tip_ + def image_get(mat): from bpy_extras import node_shader_utils @@ -153,7 +155,7 @@ def write_mesh(context, report_cb): if 'FINISHED' in ret: if report_cb is not None: - report_cb({'INFO'}, f"Exported: {filepath!r}") + report_cb({'INFO'}, tip_("Exported: {!r}").format(filepath)) return True diff --git a/object_print3d_utils/operators.py b/object_print3d_utils/operators.py index 85f268e7..d47aa84a 100644 --- a/object_print3d_utils/operators.py +++ b/object_print3d_utils/operators.py @@ -13,6 +13,8 @@ from bpy.props import ( ) import bmesh +from bpy.app.translations import pgettext_tip as tip_ + from . import report @@ -87,7 +89,7 @@ class MESH_OT_print3d_info_volume(Operator): volume_str = clean_float(volume_unit, 4) volume_fmt = f"{volume_str} {symbol}" - report.update((f"Volume: {volume_fmt}³", None)) + report.update((tip_("Volume: {}³").format(volume_fmt), None)) return {'FINISHED'} @@ -118,7 +120,7 @@ class MESH_OT_print3d_info_area(Operator): area_str = clean_float(area_unit, 4) area_fmt = f"{area_str} {symbol}" - report.update((f"Area: {area_fmt}²", None)) + report.update((tip_("Area: {}²").format(area_fmt), None)) return {'FINISHED'} @@ -161,8 +163,12 @@ class MESH_OT_print3d_check_solid(Operator): (i for i, ele in enumerate(bm.edges) if ele.is_manifold and (not ele.is_contiguous)), ) - info.append((f"Non Manifold Edge: {len(edges_non_manifold)}", (bmesh.types.BMEdge, edges_non_manifold))) - info.append((f"Bad Contig. Edges: {len(edges_non_contig)}", (bmesh.types.BMEdge, edges_non_contig))) + info.append( + (tip_("Non Manifold Edge: {}").format( + len(edges_non_manifold)), + (bmesh.types.BMEdge, + edges_non_manifold))) + info.append((tip_("Bad Contig. Edges: {}").format(len(edges_non_contig)), (bmesh.types.BMEdge, edges_non_contig))) bm.free() @@ -180,7 +186,7 @@ class MESH_OT_print3d_check_intersections(Operator): from . import mesh_helpers faces_intersect = mesh_helpers.bmesh_check_self_intersect_object(obj) - info.append((f"Intersect Face: {len(faces_intersect)}", (bmesh.types.BMFace, faces_intersect))) + info.append((tip_("Intersect Face: {}").format(len(faces_intersect)), (bmesh.types.BMFace, faces_intersect))) def execute(self, context): return execute_check(self, context) @@ -208,8 +214,8 @@ class MESH_OT_print3d_check_degenerate(Operator): faces_zero = array.array('i', (i for i, ele in enumerate(bm.faces) if ele.calc_area() <= threshold)) edges_zero = array.array('i', (i for i, ele in enumerate(bm.edges) if ele.calc_length() <= threshold)) - info.append((f"Zero Faces: {len(faces_zero)}", (bmesh.types.BMFace, faces_zero))) - info.append((f"Zero Edges: {len(edges_zero)}", (bmesh.types.BMEdge, edges_zero))) + info.append((tip_("Zero Faces: {}").format(len(faces_zero)), (bmesh.types.BMFace, faces_zero))) + info.append((tip_("Zero Edges: {}").format(len(edges_zero)), (bmesh.types.BMEdge, edges_zero))) bm.free() @@ -239,7 +245,7 @@ class MESH_OT_print3d_check_distorted(Operator): (i for i, ele in enumerate(bm.faces) if mesh_helpers.face_is_distorted(ele, angle_distort)) ) - info.append((f"Non-Flat Faces: {len(faces_distort)}", (bmesh.types.BMFace, faces_distort))) + info.append((tip_("Non-Flat Faces: {}").format(len(faces_distort)), (bmesh.types.BMFace, faces_distort))) bm.free() @@ -263,7 +269,7 @@ class MESH_OT_print3d_check_thick(Operator): print_3d = scene.print_3d faces_error = mesh_helpers.bmesh_check_thick_object(obj, print_3d.thickness_min) - info.append((f"Thin Faces: {len(faces_error)}", (bmesh.types.BMFace, faces_error))) + info.append((tip_("Thin Faces: {}").format(len(faces_error)), (bmesh.types.BMFace, faces_error))) def execute(self, context): return execute_check(self, context) @@ -290,7 +296,7 @@ class MESH_OT_print3d_check_sharp(Operator): if ele.is_manifold and ele.calc_face_angle_signed() > angle_sharp ] - info.append((f"Sharp Edge: {len(edges_sharp)}", (bmesh.types.BMEdge, edges_sharp))) + info.append((tip_("Sharp Edge: {}").format(len(edges_sharp)), (bmesh.types.BMEdge, edges_sharp))) bm.free() def execute(self, context): @@ -327,7 +333,7 @@ class MESH_OT_print3d_check_overhang(Operator): if z_down_angle(ele.normal, 4.0) < angle_overhang ] - info.append((f"Overhang Face: {len(faces_overhang)}", (bmesh.types.BMFace, faces_overhang))) + info.append((tip_("Overhang Face: {}").format(len(faces_overhang)), (bmesh.types.BMFace, faces_overhang))) bm.free() def execute(self, context): @@ -390,7 +396,7 @@ class MESH_OT_print3d_clean_distorted(Operator): bmesh.ops.triangulate(bm, faces=elems_triangulate) mesh_helpers.bmesh_to_object(obj, bm) - self.report({'INFO'}, f"Triangulated {len(elems_triangulate)} faces") + self.report({'INFO'}, tip_("Triangulated {} faces").format(len(elems_triangulate))) return {'FINISHED'} @@ -441,7 +447,7 @@ class MESH_OT_print3d_clean_non_manifold(Operator): edges = bm_key[1] - bm_key_orig[1] faces = bm_key[2] - bm_key_orig[2] - self.report({'INFO'}, f"Modified: {verts:+} vertices, {edges:+} edges, {faces:+} faces") + self.report({'INFO'}, tip_("Modified: {:+} vertices, {:+} edges, {:+} faces").format(verts, edges, faces)) return {'FINISHED'} @@ -616,7 +622,7 @@ def _scale(scale, report=None, report_suffix=""): bpy.ops.transform.resize(value=(scale,) * 3) if report is not None: scale_fmt = clean_float(scale, 6) - report({'INFO'}, f"Scaled by {scale_fmt}{report_suffix}") + report({'INFO'}, tip_("Scaled by {}{}").format(scale_fmt, report_suffix)) class MESH_OT_print3d_scale_to_volume(Operator): @@ -638,7 +644,7 @@ class MESH_OT_print3d_scale_to_volume(Operator): def execute(self, context): scale = math.pow(self.volume, 1 / 3) / math.pow(self.volume_init, 1 / 3) scale_fmt = clean_float(scale, 6) - self.report({'INFO'}, f"Scaled by {scale_fmt}") + self.report({'INFO'}, tip_("Scaled by {}").format(scale_fmt)) _scale(scale, self.report) return {'FINISHED'} @@ -689,7 +695,7 @@ class MESH_OT_print3d_scale_to_bounds(Operator): def execute(self, context): scale = self.length / self.length_init axis = "XYZ"[self.axis_init] - _scale(scale, report=self.report, report_suffix=f", Clamping {axis}-Axis") + _scale(scale, report=self.report, report_suffix=tip_(", Clamping {}-Axis").format(axis)) return {'FINISHED'} def invoke(self, context, event): @@ -763,7 +769,10 @@ class MESH_OT_print3d_align_to_xy(Operator): normal = Vector((0.0, 0.0, 0.0)) if face_areas: for face in faces: - normal += (face.normal * face.calc_area()) + if mode_orig == 'EDIT_MESH': + normal += (face.normal * face.calc_area()) + else: + normal += (face.normal * face.area) else: for face in faces: normal += face.normal @@ -777,9 +786,9 @@ class MESH_OT_print3d_align_to_xy(Operator): if len(skip_invalid) > 0: for name in skip_invalid: - print(f"Align to XY: Skipping object {name}. No faces selected.") + print(tip_("Align to XY: Skipping object {}. No faces selected.").format(name)) if len(skip_invalid) == 1: - self.report({'WARNING'}, "Skipping object. No faces selected" % skip_invalid[0]) + self.report({'WARNING'}, tip_("Skipping object {}. No faces selected").format(skip_invalid[0])) else: self.report({'WARNING'}, "Skipping some objects. No faces selected. See terminal") return {'FINISHED'} -- cgit v1.2.3 From 99576a8478e1ea90bb112e1fd2ff6aa851c05bdc Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 9 Aug 2022 19:23:13 +0200 Subject: Cleanup: fix typos in blender addons Contributed by luzpaz. Differential Revision: https://developer.blender.org/D15646 --- add_mesh_extra_objects/Blocks.py | 6 +++--- io_mesh_stl/blender_utils.py | 2 +- io_mesh_stl/stl_utils.py | 2 +- io_scene_fbx/export_fbx_bin.py | 2 +- io_scene_gltf2/blender/com/gltf2_blender_ui.py | 6 +++--- .../blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py | 2 +- io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py | 4 ++-- materials_utils/functions.py | 2 +- power_sequencer/operators/fade_add.py | 2 +- power_sequencer/operators/mouse_trim_modal.py | 2 +- precision_drawing_tools/pdt_tangent.py | 4 ++-- render_povray/particles.py | 2 +- rigify/rigs/limbs/limb_rigs.py | 2 +- 13 files changed, 19 insertions(+), 19 deletions(-) diff --git a/add_mesh_extra_objects/Blocks.py b/add_mesh_extra_objects/Blocks.py index 22676670..c3dcd0d5 100644 --- a/add_mesh_extra_objects/Blocks.py +++ b/add_mesh_extra_objects/Blocks.py @@ -269,7 +269,7 @@ def MakeABlock(bounds, segsize, vll=0, Offsets=None, FaceExclude=[], bounds: a list of boundary positions: 0:left, 1:right, 2:bottom, 3:top, 4:back, 5:front segsize: the maximum size before lengthwise subdivision occurs - vll: the number of vertexes already in the mesh. len(mesh.verts) should + vll: the number of vertices already in the mesh. len(mesh.verts) should give this number. Offsets: list of coordinate delta values. Offsets are lists, [x,y,z] in @@ -365,7 +365,7 @@ def MakeAKeystone(xpos, width, zpos, ztop, zbtm, thick, bevel, vll=0, FaceExclud zbtm: distance from zpos to the bottom thick: thickness bevel: the amount to raise the back vertex to account for arch beveling - vll: the number of vertexes already in the mesh. len(mesh.verts) should give this number + vll: the number of vertices already in the mesh. len(mesh.verts) should give this number faceExclude: list of faces to exclude from the faces list. 0:left, 1:right, 2:bottom, 3:top, 4:back, 5:front xBevScl: how much to divide the end (+- x axis) bevel dimensions. @@ -779,7 +779,7 @@ class rowOb: def arch(ra, rt, x, z, archStart, archEnd, bevel, bevAngle, vll): __doc__ = """\ - Makes a list of faces and vertexes for arches. + Makes a list of faces and vertices for arches. ra: the radius of the arch, to the center of the bricks rt: the thickness of the arch x: x center location of the circular arc, as if the arch opening were centered on x = 0 diff --git a/io_mesh_stl/blender_utils.py b/io_mesh_stl/blender_utils.py index fdf353df..5c84152e 100644 --- a/io_mesh_stl/blender_utils.py +++ b/io_mesh_stl/blender_utils.py @@ -48,7 +48,7 @@ def faces_from_mesh(ob, global_matrix, use_mesh_modifiers=False): """ From an object, return a generator over a list of faces. - Each faces is a list of his vertexes. Each vertex is a tuple of + Each faces is a list of his vertices. Each vertex is a tuple of his coordinate. use_mesh_modifiers diff --git a/io_mesh_stl/stl_utils.py b/io_mesh_stl/stl_utils.py index 465806ab..e041c527 100644 --- a/io_mesh_stl/stl_utils.py +++ b/io_mesh_stl/stl_utils.py @@ -168,7 +168,7 @@ def _binary_write(filepath, faces): for face in faces: # calculate face normal - # write normal + vertexes + pad as attributes + # write normal + vertices + pad as attributes fw(struct.pack('<3f', *normal(*face)) + pack(*itertools.chain.from_iterable(face))) # attribute byte count (unused) fw(b'\0\0') diff --git a/io_scene_fbx/export_fbx_bin.py b/io_scene_fbx/export_fbx_bin.py index c34a5182..f560b19c 100644 --- a/io_scene_fbx/export_fbx_bin.py +++ b/io_scene_fbx/export_fbx_bin.py @@ -1528,7 +1528,7 @@ def fbx_data_armature_elements(root, arm_obj, scene_data): elem_data_single_int32(fbx_skin, b"Version", FBX_DEFORMER_SKIN_VERSION) elem_data_single_float64(fbx_skin, b"Link_DeformAcuracy", 50.0) # Only vague idea what it is... - # Pre-process vertex weights (also to check vertices assigned ot more than four bones). + # Pre-process vertex weights (also to check vertices assigned to more than four bones). ob = ob_obj.bdata bo_vg_idx = {bo_obj.bdata.name: ob.vertex_groups[bo_obj.bdata.name].index for bo_obj in clusters.keys() if bo_obj.bdata.name in ob.vertex_groups} diff --git a/io_scene_gltf2/blender/com/gltf2_blender_ui.py b/io_scene_gltf2/blender/com/gltf2_blender_ui.py index 406a15f1..0788bbbc 100644 --- a/io_scene_gltf2/blender/com/gltf2_blender_ui.py +++ b/io_scene_gltf2/blender/com/gltf2_blender_ui.py @@ -15,7 +15,7 @@ def create_gltf_ao_group(operator, group_name): class NODE_OT_GLTF_SETTINGS(bpy.types.Operator): bl_idname = "node.gltf_settings_node_operator" - bl_label = "glTF Material Ouptut" + bl_label = "glTF Material Output" bl_description = "Add a node to the active tree for glTF export" @classmethod @@ -208,10 +208,10 @@ class SCENE_OT_gltf2_assign_to_variant(bpy.types.Operator): return {'FINISHED'} -# Operator to reset mesh to orignal (using default material when exists) +# Operator to reset mesh to original (using default material when exists) class SCENE_OT_gltf2_reset_to_original(bpy.types.Operator): bl_idname = "scene.gltf2_reset_to_original" - bl_label = "Reset to Orignal" + bl_label = "Reset to Original" bl_options = {'REGISTER'} @classmethod diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py index ba331b74..53d78945 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animation_sampler_keyframes.py @@ -405,7 +405,7 @@ def gather_keyframes(blender_obj_uuid: str, # for both in and out tangents, we guarantee that (even if there are errors or numerical imprecisions) # symmetrical control points translate to symmetrical tangents. # Note: I am not sure that linearity is never broken with quaternions and their normalization. - # Especially at sign swap it might occure that the value gets negated but the control point not. + # Especially at sign swap it might occur that the value gets negated but the control point not. # I have however not once encountered an issue with this. key.in_tangent = [ c.keyframe_points[i].co[1] + (c.keyframe_points[i].handle_left[1] - c.keyframe_points[i].co[1]) / (c.keyframe_points[i].handle_left[0] - c.keyframe_points[i].co[0]) diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py b/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py index d130f4e6..b6b8e19f 100755 --- a/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py +++ b/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py @@ -46,7 +46,7 @@ def pbr_metallic_roughness(mh: MaterialHelper): additional_location = 40, -370 # For occlusion and/or volume / original PBR extensions # Set IOR to 1.5, this is the default in glTF - # This value may be overidden later if IOR extension is set on file + # This value may be overridden later if IOR extension is set on file pbr_node.inputs['IOR'].default_value = GLTF_IOR if mh.pymat.occlusion_texture is not None or (mh.pymat.extensions and 'KHR_materials_specular' in mh.pymat.extensions): @@ -610,7 +610,7 @@ def occlusion(mh: MaterialHelper, location, occlusion_socket): # => [Add Emission] => [Mix Alpha] => [Material Output] if needed, only for SpecGlossiness # => [Volume] => [Add Shader] => [Material Output] if needed -# => [Velvet] => [Add Shader] => [Material Output] if nedded +# => [Velvet] => [Add Shader] => [Material Output] if needed def make_output_nodes( mh: MaterialHelper, location, diff --git a/materials_utils/functions.py b/materials_utils/functions.py index 9397257a..93d700d8 100644 --- a/materials_utils/functions.py +++ b/materials_utils/functions.py @@ -333,7 +333,7 @@ def mu_select_by_material_name(self, find_material_name, extend_selection = Fals elif not internal: # Some object types are not supported # mostly because don't really support selecting by material (like Font/Text objects) - # ore that they don't support multiple materials/are just "weird" (i.e. Meta balls) + # or that they don't support multiple materials/are just "weird" (i.e. Meta balls) self.report({'WARNING'}, "The type '" + obj.type + "' isn't supported in Edit mode by Material Utilities!") diff --git a/power_sequencer/operators/fade_add.py b/power_sequencer/operators/fade_add.py index ab692dba..9a3d1973 100644 --- a/power_sequencer/operators/fade_add.py +++ b/power_sequencer/operators/fade_add.py @@ -13,7 +13,7 @@ class POWER_SEQUENCER_OT_fade_add(bpy.types.Operator): Fade options: - In, Out, In and Out create a fade animation of the given duration from - the start of the sequence, to the end of the sequence, or on boths sides + the start of the sequence, to the end of the sequence, or on both sides - From playhead: the fade animation goes from the start of sequences under the playhead to the playhead - To playhead: the fade animation goes from the playhead to the end of sequences under the playhead diff --git a/power_sequencer/operators/mouse_trim_modal.py b/power_sequencer/operators/mouse_trim_modal.py index e8f751c9..8136adae 100644 --- a/power_sequencer/operators/mouse_trim_modal.py +++ b/power_sequencer/operators/mouse_trim_modal.py @@ -31,7 +31,7 @@ class POWER_SEQUENCER_OT_mouse_trim(bpy.types.Operator): *brief* Cut or Trim strips quickly with the mouse cursor - Click somehwere in the Sequencer to insert a cut, click and drag to trim + Click somewhere in the Sequencer to insert a cut, click and drag to trim With this function you can quickly cut and remove a section of strips while keeping or collapsing the remaining gap. Press Ctrl to snap to cuts. diff --git a/precision_drawing_tools/pdt_tangent.py b/precision_drawing_tools/pdt_tangent.py index 88abd2d0..f9ccda51 100644 --- a/precision_drawing_tools/pdt_tangent.py +++ b/precision_drawing_tools/pdt_tangent.py @@ -199,8 +199,8 @@ def tangent_setup(context, pg, plane, obj_data, centre_0, centre_1, centre_2, ra obj_data: All the data of the chosen object centre_0: Centre coordinates of the first arc centre_1: Centre coordinates of the second arc - centre_2: Coordinates fo the point - radius_0: Radius if the first Arc + centre_2: Coordinates of the point + radius_0: Radius of the first Arc radius_1: Radius of the second Arc Returns: diff --git a/render_povray/particles.py b/render_povray/particles.py index 18cc0ac7..fdbd4647 100644 --- a/render_povray/particles.py +++ b/render_povray/particles.py @@ -75,7 +75,7 @@ def export_hair(file, ob, mod, p_sys, global_matrix): # When you render, the entire dependency graph will be # evaluated at render resolution, including the particles. # In the viewport it will be at viewport resolution. - # So there is no need fo render engines to use this function anymore, + # So there is no need for render engines to use this function anymore, # it's automatic now. steps = p_sys_settings.display_step steps = 2**steps # or + 1 # Formerly : len(particle.hair_keys) diff --git a/rigify/rigs/limbs/limb_rigs.py b/rigify/rigs/limbs/limb_rigs.py index f60666a0..a094e176 100644 --- a/rigify/rigs/limbs/limb_rigs.py +++ b/rigify/rigs/limbs/limb_rigs.py @@ -959,7 +959,7 @@ class BaseLimbRig(BaseRig): params.limb_uniform_scale = bpy.props.BoolProperty( name = "Support Uniform Scaling", default = False, - description = "Suport uniformly scaling the limb via the gear control at the base" + description = "Support uniformly scaling the limb via the gear control at the base" ) # Setting up extra layers for the FK and tweak -- cgit v1.2.3 From 715015fd658e203d8344ac252a2730150f4606aa Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Wed, 10 Aug 2022 11:40:53 +0200 Subject: glTF: fix typo --- io_scene_gltf2/__init__.py | 2 +- io_scene_gltf2/blender/com/gltf2_blender_ui.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 503a9edd..4c87c07b 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -4,7 +4,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (3, 3, 24), + "version": (3, 3, 25), 'blender': (3, 3, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/blender/com/gltf2_blender_ui.py b/io_scene_gltf2/blender/com/gltf2_blender_ui.py index 406a15f1..128db592 100644 --- a/io_scene_gltf2/blender/com/gltf2_blender_ui.py +++ b/io_scene_gltf2/blender/com/gltf2_blender_ui.py @@ -15,7 +15,7 @@ def create_gltf_ao_group(operator, group_name): class NODE_OT_GLTF_SETTINGS(bpy.types.Operator): bl_idname = "node.gltf_settings_node_operator" - bl_label = "glTF Material Ouptut" + bl_label = "glTF Material Output" bl_description = "Add a node to the active tree for glTF export" @classmethod -- cgit v1.2.3 From af8d747d00ac10b2196bf81295ce5f730be9af6a Mon Sep 17 00:00:00 2001 From: Ryan Inch Date: Fri, 12 Aug 2022 04:49:35 -0400 Subject: Collection Manager: Fix T100326. Task: T69577 Add guards to the functions triggered by checkbox updates in the preferences. This prevents them from being called multiple times, such as when the mouse is hovered over the checkbox and the backspace key is repeatedly pressed. --- object_collection_manager/__init__.py | 2 +- object_collection_manager/cm_init.py | 4 ++++ object_collection_manager/qcd_init.py | 28 ++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/object_collection_manager/__init__.py b/object_collection_manager/__init__.py index 1c0c7267..fd2bd44b 100644 --- a/object_collection_manager/__init__.py +++ b/object_collection_manager/__init__.py @@ -6,7 +6,7 @@ bl_info = { "name": "Collection Manager", "description": "Manage collections and their objects", "author": "Ryan Inch", - "version": (2, 24, 2), + "version": (2, 24, 3), "blender": (2, 80, 0), "location": "View3D - Object Mode (Shortcut - M)", "warning": '', # used for warning icon and text in addons panel diff --git a/object_collection_manager/cm_init.py b/object_collection_manager/cm_init.py index 38080cb8..679d8d3e 100644 --- a/object_collection_manager/cm_init.py +++ b/object_collection_manager/cm_init.py @@ -144,6 +144,10 @@ def disable_objects_menu_addition(self, context): def register_disable_objects_hotkeys(): + if addon_disable_objects_hotkey_keymaps: + # guard to handle default value updates (mouse hover + backspace) + return + wm = bpy.context.window_manager if wm.keyconfigs.addon: # not present when started with --background km = wm.keyconfigs.addon.keymaps.new(name='Object Mode') diff --git a/object_collection_manager/qcd_init.py b/object_collection_manager/qcd_init.py index c2d555f5..6d4ac7e7 100644 --- a/object_collection_manager/qcd_init.py +++ b/object_collection_manager/qcd_init.py @@ -27,6 +27,7 @@ from bpy.app.handlers import persistent addon_qcd_keymaps = [] addon_qcd_view_hotkey_keymaps = [] addon_qcd_view_edit_mode_hotkey_keymaps = [] +qcd_registered = False qcd_classes = ( @@ -76,6 +77,11 @@ def load_pre_handler(dummy): def register_qcd(): + global qcd_registered + if qcd_registered: + # guard to handle default value updates (mouse hover + backspace) + return + for cls in qcd_classes: bpy.utils.register_class(cls) @@ -107,8 +113,14 @@ def register_qcd(): if prefs.enable_qcd_3dview_header_widget: register_qcd_3dview_header_widget() + qcd_registered = True + def register_qcd_view_hotkeys(): + if addon_qcd_view_hotkey_keymaps: + # guard to handle default value updates (mouse hover + backspace) + return + wm = bpy.context.window_manager if wm.keyconfigs.addon: # not present when started with --background # create qcd hotkeys @@ -180,6 +192,10 @@ def register_qcd_view_hotkeys(): def register_qcd_view_edit_mode_hotkeys(): + if addon_qcd_view_edit_mode_hotkey_keymaps: + # guard to handle default value updates (mouse hover + backspace) + return + wm = bpy.context.window_manager if wm.keyconfigs.addon: # not present when started with --background # create qcd hotkeys @@ -248,12 +264,22 @@ def register_qcd_view_edit_mode_hotkeys(): def register_qcd_3dview_header_widget(): + # unregister first to guard against default value updates (mouse hover + backspace) + # if the widget isn't registered it will just do nothing + unregister_qcd_3dview_header_widget() + + # add the widget to the header, and an update function to the top bar to get view layer changes bpy.types.VIEW3D_HT_header.append(ui.view3d_header_qcd_slots) bpy.types.TOPBAR_HT_upper_bar.append(ui.view_layer_update) def unregister_qcd(): + global qcd_registered + if not qcd_registered: + # guard to handle default value updates (mouse hover + backspace) + return + unregister_qcd_3dview_header_widget() for cls in qcd_classes: @@ -279,6 +305,8 @@ def unregister_qcd(): unregister_qcd_view_edit_mode_hotkeys() + qcd_registered = False + def unregister_qcd_view_hotkeys(): # remove keymaps when qcd view hotkeys are deactivated -- cgit v1.2.3 From 94ebaf04922906924e26761b9cf408daf34c6aeb Mon Sep 17 00:00:00 2001 From: Jesse Yurkovich Date: Sat, 13 Aug 2022 17:36:46 -0700 Subject: Fix T93189: Missing UNDO operator options for Curve Tools addon A variety of operators were missing appropriate bl_options for correct undo support. This results in either crashes (auto loft, bi-rail) or other odd behaviors around returning the scene to the correct previous state (sometimes having to hit ctrl-z twice to undo once, sometimes undo skips steps, etc.) Differential Revision: https://developer.blender.org/D13283 --- curve_tools/auto_loft.py | 1 + curve_tools/operators.py | 9 +++++++++ curve_tools/splines_sequence.py | 1 + 3 files changed, 11 insertions(+) diff --git a/curve_tools/auto_loft.py b/curve_tools/auto_loft.py index 3092e6b8..b14aaf3b 100644 --- a/curve_tools/auto_loft.py +++ b/curve_tools/auto_loft.py @@ -12,6 +12,7 @@ class OperatorAutoLoftCurves(Operator): bl_idname = "curvetools.create_auto_loft" bl_label = "Loft" bl_description = "Lofts selected curves" + bl_options = {'UNDO'} @classmethod def poll(cls, context): diff --git a/curve_tools/operators.py b/curve_tools/operators.py index e4480f88..d4ada318 100644 --- a/curve_tools/operators.py +++ b/curve_tools/operators.py @@ -145,6 +145,7 @@ class OperatorOriginToSpline0Start(bpy.types.Operator): bl_idname = "curvetools.operatororigintospline0start" bl_label = "OriginToSpline0Start" bl_description = "Sets the origin of the active/selected curve to the starting point of the (first) spline. Nice for curve modifiers" + bl_options = {'UNDO'} @classmethod @@ -182,6 +183,7 @@ class OperatorIntersectCurves(bpy.types.Operator): bl_idname = "curvetools.operatorintersectcurves" bl_label = "Intersect" bl_description = "Intersects selected curves" + bl_options = {'UNDO'} @classmethod @@ -233,6 +235,7 @@ class OperatorLoftCurves(bpy.types.Operator): bl_idname = "curvetools.operatorloftcurves" bl_label = "Loft" bl_description = "Lofts selected curves" + bl_options = {'UNDO'} @classmethod @@ -258,6 +261,7 @@ class OperatorSweepCurves(bpy.types.Operator): bl_idname = "curvetools.operatorsweepcurves" bl_label = "Sweep" bl_description = "Sweeps the active curve along to other curve (rail)" + bl_options = {'UNDO'} @classmethod @@ -283,6 +287,7 @@ class OperatorBirail(bpy.types.Operator): bl_idname = "curvetools.operatorbirail" bl_label = "Birail" bl_description = "Generates a birailed surface from 3 selected curves -- in order: rail1, rail2 and profile" + bl_options = {'UNDO'} @classmethod @@ -306,6 +311,7 @@ class OperatorSplinesSetResolution(bpy.types.Operator): bl_idname = "curvetools.operatorsplinessetresolution" bl_label = "SplinesSetResolution" bl_description = "Sets the resolution of all splines" + bl_options = {'UNDO'} @classmethod @@ -330,6 +336,7 @@ class OperatorSplinesRemoveZeroSegment(bpy.types.Operator): bl_idname = "curvetools.operatorsplinesremovezerosegment" bl_label = "SplinesRemoveZeroSegment" bl_description = "Removes splines with no segments -- they seem to creep up, sometimes" + bl_options = {'UNDO'} @classmethod @@ -364,6 +371,7 @@ class OperatorSplinesRemoveShort(bpy.types.Operator): bl_idname = "curvetools.operatorsplinesremoveshort" bl_label = "SplinesRemoveShort" bl_description = "Removes splines with a length smaller than the threshold" + bl_options = {'UNDO'} @classmethod @@ -393,6 +401,7 @@ class OperatorSplinesJoinNeighbouring(bpy.types.Operator): bl_idname = "curvetools.operatorsplinesjoinneighbouring" bl_label = "SplinesJoinNeighbouring" bl_description = "Joins neighbouring splines within a distance smaller than the threshold" + bl_options = {'UNDO'} @classmethod diff --git a/curve_tools/splines_sequence.py b/curve_tools/splines_sequence.py index 2588b5d3..89b9624c 100644 --- a/curve_tools/splines_sequence.py +++ b/curve_tools/splines_sequence.py @@ -214,6 +214,7 @@ class RearrangeSpline(bpy.types.Operator): bl_idname = "curvetools.rearrange_spline" bl_label = "Rearrange Spline" bl_description = "Rearrange Spline" + bl_options = {'UNDO'} Types = [('NEXT', "Next", "next"), ('PREV', "Prev", "prev")] -- cgit v1.2.3 From d9d24cef3eac7e4ab8529fcbc3a41adad4e75524 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Tue, 16 Aug 2022 15:20:48 +0200 Subject: Archimesh: Register Undo for some missing operators Two operators were missing the option to enable the Undo registering. --- archimesh/achm_main_panel.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/archimesh/achm_main_panel.py b/archimesh/achm_main_panel.py index fda383f4..482e26fd 100644 --- a/archimesh/achm_main_panel.py +++ b/archimesh/achm_main_panel.py @@ -35,6 +35,7 @@ class ARCHIMESH_OT_Hole(Operator): bl_label = "Auto Holes" bl_description = "Enable windows and doors holes for any selected object (needs wall thickness)" bl_category = 'View' + bl_options = {'UNDO', 'REGISTER'} # ------------------------------ # Execute @@ -169,6 +170,7 @@ class ARCHIMESH_OT_Pencil(Operator): bl_label = "Room from Draw" bl_description = "Create a room base on grease pencil strokes (draw from top view (7 key))" bl_category = 'View' + bl_options = {'UNDO', 'REGISTER'} # ------------------------------ # Execute -- cgit v1.2.3 From 9638ec565580fd611577249fda938a5cd1146694 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Mon, 15 Aug 2022 10:22:13 +0200 Subject: glTF exporter: add comment --- .../blender/exp/gltf2_blender_gather_primitive_attributes.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitive_attributes.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitive_attributes.py index 8572d185..1af588b9 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitive_attributes.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitive_attributes.py @@ -173,6 +173,13 @@ def __gather_skins(blender_primitive, export_settings): max_bone_set_index += 1 max_bone_set_index -= 1 + # Here, a set represents a group of 4 weights. + # So max_bone_set_index value: + # if -1 => No weights + # if 1 => Max 4 weights + # if 2 => Max 8 weights + # etc... + # If no skinning if max_bone_set_index < 0: return attributes -- cgit v1.2.3 From 57b65bd3f485d2477998f6991422f67be6ce5158 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Wed, 17 Aug 2022 08:03:02 +0200 Subject: glTF exporter: cleanup option description --- io_scene_gltf2/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 4c87c07b..ee60f4aa 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -4,7 +4,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (3, 3, 25), + "version": (3, 3, 26), 'blender': (3, 3, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', @@ -406,8 +406,8 @@ class ExportGLTF2_Base: export_anim_single_armature: BoolProperty( name='Export all Armature Actions', description=( - "Export all actions of a single armature. " - "WARNING: works only if you exports a single armature" + "Export all actions, bound to a single armature. " + "WARNING: Option does not support exports including multiple armatures" ), default=True ) -- cgit v1.2.3 From 296383210971045cb0b4704847d1e427bfa6817d Mon Sep 17 00:00:00 2001 From: Aras Pranckevicius Date: Wed, 24 Aug 2022 15:17:18 +0300 Subject: object_print3d_utils: switch 3D Print Toolbox obj export to use the new exporter The 3D Print Toolbox addon was using the Python based exporter API, switch it to use the C++ based obj exporter. This is faster, supports vertex color attributes, and allows to remove the Python based exporter some day. Reviewed By: Campbell Barton Differential Revision: https://developer.blender.org/D15769 --- object_print3d_utils/export.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/object_print3d_utils/export.py b/object_print3d_utils/export.py index e4e3550b..11ce5e00 100644 --- a/object_print3d_utils/export.py +++ b/object_print3d_utils/export.py @@ -134,17 +134,17 @@ def write_mesh(context, report_cb): use_normals=export_data_layers, ) elif export_format == 'OBJ': - addon_ensure("io_scene_obj") filepath = bpy.path.ensure_ext(filepath, ".obj") - ret = bpy.ops.export_scene.obj( + ret = bpy.ops.wm.obj_export( filepath=filepath, - use_mesh_modifiers=True, - use_selection=True, - global_scale=global_scale, + apply_modifiers=True, + export_selected_objects=True, + scaling_factor=global_scale, path_mode=path_mode, - use_normals=export_data_layers, - use_uvs=export_data_layers, - use_materials=export_data_layers, + export_normals=export_data_layers, + export_uv=export_data_layers, + export_materials=export_data_layers, + export_colors=export_data_layers, ) else: assert 0 -- cgit v1.2.3 From e0d33461ceafa3734eabbc216fdc1914536ea725 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Wed, 24 Aug 2022 17:09:52 +0200 Subject: glTF exporter: do not bake selected skinned meshes, as glTF transforms must be ignored on skinned meshes --- io_scene_gltf2/__init__.py | 2 +- .../blender/exp/gltf2_blender_gather_animations.py | 29 ++++++++++++---------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index ee60f4aa..52ae1242 100755 --- a/io_scene_gltf2/__init__.py +++ b/io_scene_gltf2/__init__.py @@ -4,7 +4,7 @@ bl_info = { 'name': 'glTF 2.0 format', 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors', - "version": (3, 3, 26), + "version": (3, 3, 27), 'blender': (3, 3, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py index 2d579d5b..bdb2ee00 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_animations.py @@ -66,19 +66,22 @@ def gather_animations( obj_uuid: int, # (Only when force sampling is ON) # If force sampling is OFF, can lead to inconsistent export anyway if export_settings['gltf_selected'] is True and blender_object.type != "ARMATURE" and export_settings['gltf_force_sampling'] is True: - channels = __gather_channels_baked(obj_uuid, export_settings) - if channels is not None: - animation = gltf2_io.Animation( - channels=channels, - extensions=None, # as other animations - extras=None, # Because there is no animation to get extras from - name=blender_object.name, # Use object name as animation name - samplers=[] - ) - - __link_samplers(animation, export_settings) - if animation is not None: - animations.append(animation) + # We also have to check if this is a skinned mesh, because we don't have to force animation baking on this case + # (skinned meshes TRS must be ignored, says glTF specification) + if export_settings['vtree'].nodes[obj_uuid].skin is None: + channels = __gather_channels_baked(obj_uuid, export_settings) + if channels is not None: + animation = gltf2_io.Animation( + channels=channels, + extensions=None, # as other animations + extras=None, # Because there is no animation to get extras from + name=blender_object.name, # Use object name as animation name + samplers=[] + ) + + __link_samplers(animation, export_settings) + if animation is not None: + animations.append(animation) elif export_settings['gltf_selected'] is True and blender_object.type == "ARMATURE": # We need to bake all bones. Because some bone can have some constraints linking to # some other armature bones, for example -- cgit v1.2.3 From 0e3978e17a45b95ad746b8090ac11fafc9ba6886 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Wed, 24 Aug 2022 11:14:17 +0200 Subject: Fix: nodewrangler error accessing space.node_tree when not available Revert part of rBAad1966474ed2 in nw_check() which was giving no benefit afaict (and instead tried to access space.node_tree for Spaces other than the Node Editor, spamming the console with errors). Differential Revision: https://developer.blender.org/D15770 --- node_wrangler.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/node_wrangler.py b/node_wrangler.py index 290ac082..cc1b85fe 100644 --- a/node_wrangler.py +++ b/node_wrangler.py @@ -830,12 +830,13 @@ class NWNodeWrangler(bpy.types.AddonPreferences): def nw_check(context): space = context.space_data - editor_is_valid = space.type == 'NODE_EDITOR' - valid_trees = ["ShaderNodeTree", "CompositorNodeTree", "TextureNodeTree", "GeometryNodeTree"] - tree_is_valid = space.node_tree is not None and space.tree_type in valid_trees - return editor_is_valid and tree_is_valid + valid = False + if space.type == 'NODE_EDITOR' and space.node_tree is not None and space.tree_type in valid_trees: + valid = True + + return valid class NWBase: @classmethod -- cgit v1.2.3 From 0b6aaad9be3862f98fe4acc376a084c95f531669 Mon Sep 17 00:00:00 2001 From: Damien Picard Date: Sat, 16 Jul 2022 20:39:30 +0200 Subject: AnimAll and Sun Position: update translations for regeneration ease Bring back the auto-generated block delimiters Also: sort unregister() in reverse order to register() --- animation_animall/__init__.py | 4 +-- animation_animall/translations.py | 9 ++++++- sun_position/__init__.py | 13 ++++------ sun_position/translations.py | 53 ++++++++++++++++++--------------------- 4 files changed, 38 insertions(+), 41 deletions(-) diff --git a/animation_animall/__init__.py b/animation_animall/__init__.py index 1ff7bc69..3088a1a6 100644 --- a/animation_animall/__init__.py +++ b/animation_animall/__init__.py @@ -662,14 +662,12 @@ def register(): register_classes() bpy.types.Scene.animall_properties = bpy.props.PointerProperty(type=AnimallProperties) update_panel(None, bpy.context) - bpy.app.translations.register(__name__, translations.translations_dict) def unregister(): + bpy.app.translations.unregister(__name__) del bpy.types.Scene.animall_properties unregister_classes() - bpy.app.translations.unregister(__name__) - if __name__ == "__main__": register() diff --git a/animation_animall/translations.py b/animation_animall/translations.py index 9ced1807..f81dd7c2 100644 --- a/animation_animall/translations.py +++ b/animation_animall/translations.py @@ -1,11 +1,16 @@ # SPDX-License-Identifier: GPL-2.0-or-later +# ##### BEGIN AUTOGENERATED I18N SECTION ##### +# NOTE: You can safely move around this auto-generated block (with the begin/end markers!), +# and edit the translations by hand. +# Just carefully respect the format of the tuple! + # Tuple of tuples: # ((msgctxt, msgid), (sources, gen_comments), (lang, translation, (is_fuzzy, comments)), ...) translations_tuple = ( (("*", ""), ((), ()), - ("fr_FR", "Project-Id-Version: AnimAll 0.9.6 (0)\nReport-Msgid-Bugs-To: \nPOT-Creation-Date: 2022-06-24 00:41:10.347798\nPO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\nLast-Translator: FULL NAME \nLanguage-Team: LANGUAGE \nLanguage: __POT__\nMIME-Version: 1.0\nContent-Type: text/plain; charset=UTF-8\nContent-Transfer-Encoding: 8bit", + ("fr_FR", "Project-Id-Version: AnimAll 0.9.6 (0)\n", (False, ("Blender's translation file (po format).", "Copyright (C) 2022 The Blender Foundation.", @@ -248,3 +253,5 @@ for msg in translations_tuple: for lang, trans, (is_fuzzy, comments) in msg[2:]: if trans and not is_fuzzy: translations_dict.setdefault(lang, {})[key] = trans + +# ##### END AUTOGENERATED I18N SECTION ##### diff --git a/sun_position/__init__.py b/sun_position/__init__.py index 9a3e5b72..5b2a89ce 100644 --- a/sun_position/__init__.py +++ b/sun_position/__init__.py @@ -48,16 +48,13 @@ def register(): register_classes() bpy.types.Scene.sun_pos_properties = ( bpy.props.PointerProperty(type=properties.SunPosProperties, - name="Sun Position", - description="Sun Position Settings")) - - bpy.app.translations.register(__name__, translations.translations_dict) + name="Sun Position", + description="Sun Position Settings")) bpy.app.handlers.frame_change_post.append(sun_calc.sun_handler) - + bpy.app.translations.register(__name__, translations.translations_dict) def unregister(): - del bpy.types.Scene.sun_pos_properties - unregister_classes() - bpy.app.translations.unregister(__name__) bpy.app.handlers.frame_change_post.remove(sun_calc.sun_handler) + del bpy.types.Scene.sun_pos_properties + unregister_classes() diff --git a/sun_position/translations.py b/sun_position/translations.py index cd247ffd..173c8366 100644 --- a/sun_position/translations.py +++ b/sun_position/translations.py @@ -1,11 +1,16 @@ # SPDX-License-Identifier: GPL-2.0-or-later +# ##### BEGIN AUTOGENERATED I18N SECTION ##### +# NOTE: You can safely move around this auto-generated block (with the begin/end markers!), +# and edit the translations by hand. +# Just carefully respect the format of the tuple! + # Tuple of tuples: # ((msgctxt, msgid), (sources, gen_comments), (lang, translation, (is_fuzzy, comments)), ...) translations_tuple = ( (("*", ""), ((), ()), - ("fr_FR", "Project-Id-Version: Sun Position 3.1.2 (0)\nReport-Msgid-Bugs-To: \nPOT-Creation-Date: 2022-06-30 15:02:06.261278\nPO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\nLast-Translator: FULL NAME \nLanguage-Team: LANGUAGE \nLanguage: __POT__\nMIME-Version: 1.0\nContent-Type: text/plain; charset=UTF-8\nContent-Transfer-Encoding: 8bit", + ("fr_FR", "Project-Id-Version: Sun Position 3.1.2 (0)\n", (False, ("Blender's translation file (po format).", "Copyright (C) 2022 The Blender Foundation.", @@ -117,12 +122,6 @@ translations_tuple = ( ("fr_FR", "Préréglages de position du Soleil", (False, ())), ), - (("Operator", "Synchroniser Soleil et texture"), - (("bpy.types.WORLD_OT_sunpos_show_hdr",), - ()), - ("fr_FR", "", - (False, ())), - ), (("*", "UTC zone"), (("bpy.types.SunPosProperties.UTC_zone",), ()), @@ -287,12 +286,6 @@ translations_tuple = ( ("fr_FR", "Objet soleil à utiliser dans la scène", (False, ())), ), - (("*", "Day Time"), - (("bpy.types.SunPosProperties.UTC_zone",), - ()), - ("fr_FR", "Heure", - (False, ())), - ), (("*", "Time of the day"), (("bpy.types.SunPosProperties.time",), ()), @@ -414,42 +407,42 @@ translations_tuple = ( (False, ())), ), (("*", "UTC:"), - (("scripts/addons/sun_position/ui_sun.py:270",), + (("scripts/addons/sun_position/ui_sun.py:272",), ()), ("fr_FR", "UTC : ", (False, ())), ), - (("*", "Sunrise:"), - (("scripts/addons/sun_position/ui_sun.py:285",), - ()), - ("fr_FR", "Lever : ", - (False, ())), - ), - (("*", "Sunset:"), - (("scripts/addons/sun_position/ui_sun.py:288",), - ()), - ("fr_FR", "Coucher : ", - (False, ())), - ), (("*", "Please select World in the World panel."), (("scripts/addons/sun_position/ui_sun.py:95", "scripts/addons/sun_position/ui_sun.py:153"), ()), - ("fr_FR", "Veuillez sélecttionner le monde dans le panneau Monde", + ("fr_FR", "Veuillez sélectionner le monde dans le panneau Monde", (False, ())), ), (("*", "Azimuth:"), - (("scripts/addons/sun_position/ui_sun.py:206",), + (("scripts/addons/sun_position/ui_sun.py:205",), ()), ("fr_FR", "Azimut :", (False, ())), ), (("*", "Elevation:"), - (("scripts/addons/sun_position/ui_sun.py:209",), + (("scripts/addons/sun_position/ui_sun.py:208",), ()), ("fr_FR", "Hauteur :", (False, ())), ), + (("*", "Sunrise:"), + (("scripts/addons/sun_position/ui_sun.py:284",), + ()), + ("fr_FR", "Lever : ", + (False, ())), + ), + (("*", "Sunset:"), + (("scripts/addons/sun_position/ui_sun.py:287",), + ()), + ("fr_FR", "Coucher : ", + (False, ())), + ), (("*", "Please activate Use Nodes in the World panel."), (("scripts/addons/sun_position/ui_sun.py:92", "scripts/addons/sun_position/ui_sun.py:150"), @@ -465,3 +458,5 @@ for msg in translations_tuple: for lang, trans, (is_fuzzy, comments) in msg[2:]: if trans and not is_fuzzy: translations_dict.setdefault(lang, {})[key] = trans + +# ##### END AUTOGENERATED I18N SECTION ##### -- cgit v1.2.3 From 20652ce40ef1d33a21a80a70d08c7deff348d665 Mon Sep 17 00:00:00 2001 From: Damien Picard Date: Sun, 28 Aug 2022 14:55:23 +0200 Subject: Sun Position: fix translations --- sun_position/properties.py | 1 + sun_position/translations.py | 37 ++++++++++++++++++++++++++++++++++--- sun_position/ui_sun.py | 8 ++++---- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/sun_position/properties.py b/sun_position/properties.py index ac791d43..af2734a4 100644 --- a/sun_position/properties.py +++ b/sun_position/properties.py @@ -174,6 +174,7 @@ class SunPosProperties(PropertyGroup): update=sun_update) bind_to_sun: BoolProperty( + name="Bind Texture to Sun", description="If true, Environment texture moves with sun", default=False, update=sun_update) diff --git a/sun_position/translations.py b/sun_position/translations.py index 173c8366..c50b797c 100644 --- a/sun_position/translations.py +++ b/sun_position/translations.py @@ -105,7 +105,8 @@ translations_tuple = ( ), (("*", "Sun Position"), (("bpy.types.Scene.sun_pos_properties", - "bpy.types.SUNPOS_PT_Panel"), + "bpy.types.SUNPOS_PT_Panel", + "Add-on Sun Position info: name"), ()), ("fr_FR", "Position du Soleil", (False, ())), @@ -122,6 +123,12 @@ translations_tuple = ( ("fr_FR", "Préréglages de position du Soleil", (False, ())), ), + (("Operator", "Sync Sun to Texture"), + (("bpy.types.WORLD_OT_sunpos_show_hdr",), + ()), + ("fr_FR", "Synchroniser Soleil et texture", + (False, ())), + ), (("*", "UTC zone"), (("bpy.types.SunPosProperties.UTC_zone",), ()), @@ -134,6 +141,13 @@ translations_tuple = ( ("fr_FR", "Fuseau horaire : différence avec Greenwich, Angleterre, en heures", (False, ())), ), + (("*", "Bind Texture to Sun"), + (("bpy.types.SunPosProperties.bind_to_sun", + "scripts/addons/sun_position/ui_sun.py:119"), + ()), + ("fr_FR", "Lier la texture au Soleil", + (False, ())), + ), (("*", "If true, Environment texture moves with sun"), (("bpy.types.SunPosProperties.bind_to_sun",), ()), @@ -185,7 +199,6 @@ translations_tuple = ( (("*", "Name of texture to use. World nodes must be enabled and color set to Environment Texture"), (("bpy.types.SunPosProperties.hdr_texture",), ()), - # TODO ("fr_FR", "Nom de la texture à utiliser. Les nœuds de shader du monde doivent être activés, et la couleur utiliser une texture d’environnement", (False, ())), ), @@ -377,7 +390,7 @@ translations_tuple = ( (False, ())), ), (("*", "Show options or labels:"), - (("scripts/addons/sun_position/properties.py:241",), + (("scripts/addons/sun_position/properties.py:242",), ()), ("fr_FR", "Afficher les options et étiquettes :", (False, ())), @@ -419,6 +432,12 @@ translations_tuple = ( ("fr_FR", "Veuillez sélectionner le monde dans le panneau Monde", (False, ())), ), + (("*", "Release binding"), + (("scripts/addons/sun_position/ui_sun.py:116",), + ()), + ("fr_FR", "Annuler le lien", + (False, ())), + ), (("*", "Azimuth:"), (("scripts/addons/sun_position/ui_sun.py:205",), ()), @@ -450,6 +469,18 @@ translations_tuple = ( ("fr_FR", "Veuillez activer Utiliser nœuds dans le panneau Monde", (False, ())), ), + (("*", "World > Sun Position"), + (("Add-on Sun Position info: location",), + ()), + ("fr_FR", "Monde > Position du Soleil", + (False, ())), + ), + (("*", "Show sun position with objects and/or sky texture"), + (("Add-on Sun Position info: description",), + ()), + ("fr_FR", "Afficher la position du Soleil avec des objets ou une texture de ciel", + (False, ())), + ), ) translations_dict = {} diff --git a/sun_position/ui_sun.py b/sun_position/ui_sun.py index 05f0dcab..c6eebc33 100644 --- a/sun_position/ui_sun.py +++ b/sun_position/ui_sun.py @@ -112,11 +112,11 @@ class SUNPOS_PT_Panel(bpy.types.Panel): col = flow.column(align=True) if sp.bind_to_sun: - prop_text="Release binding" + col.prop(sp, "bind_to_sun", toggle=True, icon="CONSTRAINT", + text="Release binding") else: - prop_text="Bind Texture to Sun " - col.prop(sp, "bind_to_sun", toggle=True, icon="CONSTRAINT", - text=prop_text) + col.prop(sp, "bind_to_sun", toggle=True, icon="CONSTRAINT", + text="Bind Texture to Sun") row = col.row(align=True) row.enabled = not sp.bind_to_sun -- cgit v1.2.3 From ddc9d5d0a0c64c56e5da6a0f5e99e2ca20b46649 Mon Sep 17 00:00:00 2001 From: Damien Picard Date: Mon, 29 Aug 2022 00:20:47 +0200 Subject: AnimAll: fix translations --- animation_animall/__init__.py | 74 +++++++------- animation_animall/translations.py | 205 +++++++++++++++++++++++++++++--------- 2 files changed, 193 insertions(+), 86 deletions(-) diff --git a/animation_animall/__init__.py b/animation_animall/__init__.py index 3088a1a6..d679c970 100644 --- a/animation_animall/__init__.py +++ b/animation_animall/__init__.py @@ -16,7 +16,8 @@ import bpy from bpy.types import (Operator, Panel, AddonPreferences) from bpy.props import (BoolProperty, StringProperty) from bpy.app.handlers import persistent -from bpy.app.translations import pgettext_iface +from bpy.app.translations import (pgettext_iface as iface_, + pgettext_data as data_) from . import translations @@ -205,7 +206,7 @@ class VIEW3D_PT_animall(Panel): row.prop(shape_key, "value", text=shape_key.name, icon="SHAPEKEY_DATA") row.prop(obj, "show_only_shape_key", text="") if shape_key.value < 1: - col.label(text=pgettext_iface('Maybe set "%s" to 1.0?') % shape_key.name, icon="INFO") + col.label(text=iface_('Maybe set "%s" to 1.0?') % shape_key.name, icon="INFO") elif shape_key is not None: col = layout.column(align=True) col.label(text="Cannot key on Basis Shape", icon="ERROR") @@ -234,7 +235,7 @@ class ANIM_OT_insert_keyframe_animall(Operator): bl_description = "Insert a Keyframe" bl_options = {'REGISTER', 'UNDO'} - def execute(op, context): + def execute(self, context): animall_properties = context.scene.animall_properties if context.mode == 'OBJECT': @@ -256,12 +257,12 @@ class ANIM_OT_insert_keyframe_animall(Operator): sk_name = obj.active_shape_key.name for p_i, point in enumerate(obj.active_shape_key.data): if not animall_properties.key_selected or data.points[p_i].select: - insert_key(point, 'co', group="%s Point %s" % (sk_name, p_i)) + insert_key(point, 'co', group=data_("%s Point %s") % (sk_name, p_i)) if animall_properties.key_point_location: for p_i, point in enumerate(data.points): if not animall_properties.key_selected or point.select: - insert_key(point, 'co_deform', group="Point %s" % p_i) + insert_key(point, 'co_deform', group=data_("Point %s") % p_i) else: if animall_properties.key_material_index: @@ -269,7 +270,7 @@ class ANIM_OT_insert_keyframe_animall(Operator): if (not animall_properties.key_selected or any(point.select for point in spline.points) or any(point.select_control_point for point in spline.bezier_points)): - insert_key(spline, 'material_index', group="Spline %s" % s_i) + insert_key(spline, 'material_index', group=data_("Spline %s") % s_i) for s_i, spline in enumerate(data.splines): if spline.type == 'BEZIER': @@ -279,27 +280,27 @@ class ANIM_OT_insert_keyframe_animall(Operator): or CV.select_left_handle or CV.select_right_handle): if animall_properties.key_point_location: - insert_key(CV, 'co', group="Spline %s CV %s" % (s_i, v_i)) - insert_key(CV, 'handle_left', group="Spline %s CV %s" % (s_i, v_i)) - insert_key(CV, 'handle_right', group="Spline %s CV %s" % (s_i, v_i)) + insert_key(CV, 'co', group=data_("Spline %s CV %s") % (s_i, v_i)) + insert_key(CV, 'handle_left', group=data_("Spline %s CV %s") % (s_i, v_i)) + insert_key(CV, 'handle_right', group=data_("Spline %s CV %s") % (s_i, v_i)) if animall_properties.key_radius: - insert_key(CV, 'radius', group="Spline %s CV %s" % (s_i, v_i)) + insert_key(CV, 'radius', group=data_("Spline %s CV %s") % (s_i, v_i)) if animall_properties.key_tilt: - insert_key(CV, 'tilt', group="Spline %s CV %s" % (s_i, v_i)) + insert_key(CV, 'tilt', group=data_("Spline %s CV %s") % (s_i, v_i)) elif spline.type in ('POLY', 'NURBS'): for v_i, CV in enumerate(spline.points): if not animall_properties.key_selected or CV.select: if animall_properties.key_point_location: - insert_key(CV, 'co', group="Spline %s CV %s" % (s_i, v_i)) + insert_key(CV, 'co', group=data_("Spline %s CV %s") % (s_i, v_i)) if animall_properties.key_radius: - insert_key(CV, 'radius', group="Spline %s CV %s" % (s_i, v_i)) + insert_key(CV, 'radius', group=data_("Spline %s CV %s") % (s_i, v_i)) if animall_properties.key_tilt: - insert_key(CV, 'tilt', group="Spline %s CV %s" % (s_i, v_i)) + insert_key(CV, 'tilt', group=data_("Spline %s CV %s") % (s_i, v_i)) bpy.ops.object.mode_set(mode='OBJECT') @@ -309,37 +310,37 @@ class ANIM_OT_insert_keyframe_animall(Operator): if animall_properties.key_point_location: for v_i, vert in enumerate(data.vertices): if not animall_properties.key_selected or vert.select: - insert_key(vert, 'co', group="Vertex %s" % v_i) + insert_key(vert, 'co', group=data_("Vertex %s") % v_i) if animall_properties.key_vertex_bevel: for v_i, vert in enumerate(data.vertices): if not animall_properties.key_selected or vert.select: - insert_key(vert, 'bevel_weight', group="Vertex %s" % v_i) + insert_key(vert, 'bevel_weight', group=data_("Vertex %s") % v_i) # if animall_properties.key_vertex_crease: # for v_i, vert in enumerate(data.vertices): # if not animall_properties.key_selected or vert.select: - # insert_key(vert, 'crease', group="Vertex %s" % v_i) + # insert_key(vert, 'crease', group=data_("Vertex %s") % v_i) if animall_properties.key_vertex_group: for v_i, vert in enumerate(data.vertices): if not animall_properties.key_selected or vert.select: for group in vert.groups: - insert_key(group, 'weight', group="Vertex %s" % v_i) + insert_key(group, 'weight', group=data_("Vertex %s") % v_i) if animall_properties.key_edge_bevel: for e_i, edge in enumerate(data.edges): if not animall_properties.key_selected or edge.select: - insert_key(edge, 'bevel_weight', group="Edge %s" % e_i) + insert_key(edge, 'bevel_weight', group=data_("Edge %s") % e_i) if animall_properties.key_edge_crease: for e_i, edge in enumerate(data.edges): if not animall_properties.key_selected or edge.select: - insert_key(edge, 'crease', group="Edge %s" % e_i) + insert_key(edge, 'crease', group=data_("Edge %s") % e_i) if animall_properties.key_material_index: for p_i, polygon in enumerate(data.polygons): if not animall_properties.key_selected or polygon.select: - insert_key(polygon, 'material_index', group="Face %s" % p_i) + insert_key(polygon, 'material_index', group=data_("Face %s") % p_i) if animall_properties.key_attribute: if data.attributes.active is not None: @@ -354,13 +355,13 @@ class ANIM_OT_insert_keyframe_animall(Operator): attribute_key = "vector" if attribute.domain == 'POINT': - group = "Vertex %s" + group = data_("Vertex %s") elif attribute.domain == 'EDGE': - group = "Edge %s" + group = data_("Edge %s") elif attribute.domain == 'FACE': - group = "Face %s" + group = data_("Face %s") elif attribute.domain == 'CORNER': - group = "Loop %s" + group = data_("Loop %s") for e_i, _attribute_data in enumerate(attribute.data): if (not animall_properties.key_selected @@ -375,14 +376,14 @@ class ANIM_OT_insert_keyframe_animall(Operator): if data.uv_layers.active is not None: for uv_i, uv in enumerate(data.uv_layers.active.data): if not animall_properties.key_selected or uv.select: - insert_key(uv, 'uv', group="UV layer %s" % uv_i) + insert_key(uv, 'uv', group=data_("UV layer %s") % uv_i) if animall_properties.key_shape_key: if obj.active_shape_key_index > 0: sk_name = obj.active_shape_key.name for v_i, vert in enumerate(obj.active_shape_key.data): if not animall_properties.key_selected or data.vertices[v_i].select: - insert_key(vert, 'co', group="%s Vertex %s" % (sk_name, v_i)) + insert_key(vert, 'co', group=data_("%s Vertex %s") % (sk_name, v_i)) elif obj.type in {'CURVE', 'SURFACE'}: # Shape key keys have to be inserted in object mode for curves... @@ -398,11 +399,11 @@ class ANIM_OT_insert_keyframe_animall(Operator): or CV.select_right_handle): if obj.active_shape_key_index > 0: CV = obj.active_shape_key.data[global_spline_index] - insert_key(CV, 'co', group="%s Spline %s CV %s" % (sk_name, s_i, v_i)) - insert_key(CV, 'handle_left', group="%s Spline %s CV %s" % (sk_name, s_i, v_i)) - insert_key(CV, 'handle_right', group="%s Spline %s CV %s" % (sk_name, s_i, v_i)) - insert_key(CV, 'radius', group="%s Spline %s CV %s" % (sk_name, s_i, v_i)) - insert_key(CV, 'tilt', group="%s Spline %s CV %s" % (sk_name, s_i, v_i)) + insert_key(CV, 'co', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i)) + insert_key(CV, 'handle_left', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i)) + insert_key(CV, 'handle_right', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i)) + insert_key(CV, 'radius', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i)) + insert_key(CV, 'tilt', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i)) global_spline_index += 1 elif spline.type in ('POLY', 'NURBS'): @@ -410,9 +411,9 @@ class ANIM_OT_insert_keyframe_animall(Operator): if not animall_properties.key_selected or CV.select: if obj.active_shape_key_index > 0: CV = obj.active_shape_key.data[global_spline_index] - insert_key(CV, 'co', group="%s Spline %s CV %s" % (sk_name, s_i, v_i)) - insert_key(CV, 'radius', group="%s Spline %s CV %s" % (sk_name, s_i, v_i)) - insert_key(CV, 'tilt', group="%s Spline %s CV %s" % (sk_name, s_i, v_i)) + insert_key(CV, 'co', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i)) + insert_key(CV, 'radius', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i)) + insert_key(CV, 'tilt', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i)) global_spline_index += 1 bpy.ops.object.mode_set(mode=mode) @@ -427,8 +428,7 @@ class ANIM_OT_delete_keyframe_animall(Operator): bl_description = "Delete a Keyframe" bl_options = {'REGISTER', 'UNDO'} - - def execute(op, context): + def execute(self, context): animall_properties = context.scene.animall_properties if context.mode == 'OBJECT': diff --git a/animation_animall/translations.py b/animation_animall/translations.py index f81dd7c2..03b77e0b 100644 --- a/animation_animall/translations.py +++ b/animation_animall/translations.py @@ -29,6 +29,13 @@ translations_tuple = ( ("fr_FR", "Choisir un nom pour la catégorie du panneau", (False, ())), ), + (("Operator", "Insert Key"), + (("bpy.types.ANIM_OT_insert_keyframe_animall", + "bpy.types.ANIM_OT_insert_keyframe_animall"), + ()), + ("fr_FR", "Insérer une clé", + (False, ())), + ), (("Operator", "Clear Animation"), (("bpy.types.ANIM_OT_clear_animation_animall",), ()), @@ -145,106 +152,206 @@ translations_tuple = ( (False, ())), ), (("*", "AnimAll"), - (("scripts/addons/animation_animall.py:142",), + (("scripts/addons/animation_animall/__init__.py:138", + "Add-on AnimAll info: name"), ()), - ("fr_FR", "", + ("fr_FR", "AnimAll", (False, ())), ), (("*", "Key:"), - (("scripts/addons/animation_animall.py:150",), + (("scripts/addons/animation_animall/__init__.py:146",), ()), ("fr_FR", "Insérer :", (False, ())), ), (("*", "Tab Category:"), - (("scripts/addons/animation_animall.py:658",), + (("scripts/addons/animation_animall/__init__.py:653",), ()), - ("fr_FR", "Catégorie d’onglet", + ("fr_FR", "Catégorie d’onglet :", + (False, ())), + ), + (("*", "Points"), + (("scripts/addons/animation_animall/__init__.py:152", + "scripts/addons/animation_animall/__init__.py:159", + "scripts/addons/animation_animall/__init__.py:188"), + ()), + ("fr_FR", "Points", (False, ())), ), - # (("*", "Points"), - # (("scripts/addons/animation_animall.py:156", - # "scripts/addons/animation_animall.py:163", - # "scripts/addons/animation_animall.py:192"), - # ()), - # ("fr_FR", "", - # (False, ())), - # ), (("*", "Others"), - (("scripts/addons/animation_animall.py:159", - "scripts/addons/animation_animall.py:175", - "scripts/addons/animation_animall.py:200"), + (("scripts/addons/animation_animall/__init__.py:155", + "scripts/addons/animation_animall/__init__.py:171", + "scripts/addons/animation_animall/__init__.py:196"), ()), ("fr_FR", "Autres", (False, ())), ), - # (("*", "Bevel"), - # (("scripts/addons/animation_animall.py:165", - # "scripts/addons/animation_animall.py:169"), - # ()), - # ("fr_FR", "", - # (False, ())), - # ), - # (("*", "Edges"), - # (("scripts/addons/animation_animall.py:168",), - # ()), - # ("fr_FR", "", - # (False, ())), - # ), - # (("*", "Crease"), - # (("scripts/addons/animation_animall.py:170",), - # ()), - # ("fr_FR", "", - # (False, ())), - # ), - # (("*", "Faces"), - # (("scripts/addons/animation_animall.py:172",), - # ()), - # ("fr_FR", "", - # (False, ())), - # ), + (("*", "Bevel"), + (("scripts/addons/animation_animall/__init__.py:161", + "scripts/addons/animation_animall/__init__.py:165"), + ()), + ("fr_FR", "Biseau", + (False, ())), + ), + (("*", "Edges"), + (("scripts/addons/animation_animall/__init__.py:164",), + ()), + ("fr_FR", "Arêtes", + (False, ())), + ), + (("*", "Crease"), + (("scripts/addons/animation_animall/__init__.py:166",), + ()), + ("fr_FR", "Plis", + (False, ())), + ), + (("*", "Faces"), + (("scripts/addons/animation_animall/__init__.py:168",), + ()), + ("fr_FR", "Faces", + (False, ())), + ), (("*", "\"Location\" and \"Shape Key\" are redundant?"), - (("scripts/addons/animation_animall.py:222",), + (("scripts/addons/animation_animall/__init__.py:218",), ()), ("fr_FR", "\"Position\" et \"Clé de forme\" sont redondants ?", (False, ())), ), (("*", "Splines"), - (("scripts/addons/animation_animall.py:197",), + (("scripts/addons/animation_animall/__init__.py:193",), ()), - ("fr_FR", "", + ("fr_FR", "Splines", (False, ())), ), (("*", "Maybe set \"%s\" to 1.0?"), - (("scripts/addons/animation_animall.py:213",), + (("scripts/addons/animation_animall/__init__.py:209", + "scripts/addons/animation_animall/__init__.py:209"), ()), ("fr_FR", "Essayez de mettre « %s » à 1.0 ?", (False, ())), ), (("*", "Cannot key on Basis Shape"), - (("scripts/addons/animation_animall.py:216",), + (("scripts/addons/animation_animall/__init__.py:212",), ()), ("fr_FR", "Impossible d’ajouter une clé sur la forme de base", (False, ())), ), (("*", "No active Shape Key"), - (("scripts/addons/animation_animall.py:219",), + (("scripts/addons/animation_animall/__init__.py:215",), ()), ("fr_FR", "Pas de clé de forme active", (False, ())), ), (("*", "Clear Animation could not be performed"), - (("scripts/addons/animation_animall.py:586",), + (("scripts/addons/animation_animall/__init__.py:581",), ()), ("fr_FR", "La suppression de l’animation n’a pas pu aboutir", (False, ())), ), (("*", "Object includes old-style vertex colors. Consider updating them."), - (("scripts/addons/animation_animall.py:186",), + (("scripts/addons/animation_animall/__init__.py:182",), ()), ("fr_FR", "L’objet contient des couleurs de sommets à l’ancien format. Veuillez les mettre à jour", (False, ())), ), + (("*", "Vertex %s"), + (("scripts/addons/animation_animall/__init__.py:358", + "scripts/addons/animation_animall/__init__.py:313", + "scripts/addons/animation_animall/__init__.py:318", + "scripts/addons/animation_animall/__init__.py:328"), + ()), + ("fr_FR", "Sommet %s", + (False, ())), + ), + (("*", "Edge %s"), + (("scripts/addons/animation_animall/__init__.py:360", + "scripts/addons/animation_animall/__init__.py:333", + "scripts/addons/animation_animall/__init__.py:338"), + ()), + ("fr_FR", "Arête %s", + (False, ())), + ), + (("*", "Point %s"), + (("scripts/addons/animation_animall/__init__.py:265",), + ()), + ("fr_FR", "Point %s", + (False, ())), + ), + (("*", "Spline %s"), + (("scripts/addons/animation_animall/__init__.py:273",), + ()), + ("fr_FR", "Spline %s", + (False, ())), + ), + (("*", "Face %s"), + (("scripts/addons/animation_animall/__init__.py:343", + "scripts/addons/animation_animall/__init__.py:362"), + ()), + ("fr_FR", "Face %s", + (False, ())), + ), + (("*", "%s Point %s"), + (("scripts/addons/animation_animall/__init__.py:260",), + ()), + ("fr_FR", "%s Point %s", + (False, ())), + ), + (("*", "Loop %s"), + (("scripts/addons/animation_animall/__init__.py:364",), + ()), + ("fr_FR", "Boucle %s", + (False, ())), + ), + (("*", "UV layer %s"), + (("scripts/addons/animation_animall/__init__.py:379",), + ()), + ("fr_FR", "Calque UV %s", + (False, ())), + ), + (("*", "%s Vertex %s"), + (("scripts/addons/animation_animall/__init__.py:386",), + ()), + ("fr_FR", "%s Sommet %s", + (False, ())), + ), + (("*", "Spline %s CV %s"), + (("scripts/addons/animation_animall/__init__.py:283", + "scripts/addons/animation_animall/__init__.py:284", + "scripts/addons/animation_animall/__init__.py:285", + "scripts/addons/animation_animall/__init__.py:288", + "scripts/addons/animation_animall/__init__.py:291", + "scripts/addons/animation_animall/__init__.py:297", + "scripts/addons/animation_animall/__init__.py:300", + "scripts/addons/animation_animall/__init__.py:303"), + ()), + ("fr_FR", "Spline %s Point %s", + (False, ())), + ), + (("*", "%s Spline %s CV %s"), + (("scripts/addons/animation_animall/__init__.py:402", + "scripts/addons/animation_animall/__init__.py:403", + "scripts/addons/animation_animall/__init__.py:404", + "scripts/addons/animation_animall/__init__.py:405", + "scripts/addons/animation_animall/__init__.py:406", + "scripts/addons/animation_animall/__init__.py:414", + "scripts/addons/animation_animall/__init__.py:415", + "scripts/addons/animation_animall/__init__.py:416"), + ()), + ("fr_FR", "%s Spline %s Point %s", + (False, ())), + ), + (("*", "3D View > Toolbox > Animation tab > AnimAll"), + (("Add-on AnimAll info: location",), + ()), + ("fr_FR", "Vue 3D > Panneau N > Onglet Animer > AnimAll", + (False, ())), + ), + (("*", "Allows animation of mesh, lattice, curve and surface data"), + (("Add-on AnimAll info: description",), + ()), + ("fr_FR", "Permet d’animer les données de maillages, de lattices, de courbes et de surfaces", + (False, ())), + ), ) translations_dict = {} -- cgit v1.2.3 From 3570274fe75f626de116c0de52d3b008dd0b9644 Mon Sep 17 00:00:00 2001 From: Ryan Inch Date: Tue, 30 Aug 2022 08:41:19 -0400 Subject: Collection Manager: Fix "Add/Remove nested from selection". Task: T69577 "Add/Remove nested from selection" was inverting the selection for the branch instead of adding or removing. This fixes it to behave similarly to toggling nested RTOs. --- object_collection_manager/__init__.py | 2 +- object_collection_manager/operator_utils.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/object_collection_manager/__init__.py b/object_collection_manager/__init__.py index fd2bd44b..7467fcbd 100644 --- a/object_collection_manager/__init__.py +++ b/object_collection_manager/__init__.py @@ -6,7 +6,7 @@ bl_info = { "name": "Collection Manager", "description": "Manage collections and their objects", "author": "Ryan Inch", - "version": (2, 24, 3), + "version": (2, 24, 4), "blender": (2, 80, 0), "location": "View3D - Object Mode (Shortcut - M)", "warning": '', # used for warning icon and text in addons panel diff --git a/object_collection_manager/operator_utils.py b/object_collection_manager/operator_utils.py index 51b4385d..4394cf3a 100644 --- a/object_collection_manager/operator_utils.py +++ b/object_collection_manager/operator_utils.py @@ -604,10 +604,10 @@ def select_collection_objects(is_master_collection, collection_name, replace, ne if replace: bpy.ops.object.select_all(action='DESELECT') - def select_objects(collection, selection_state): - if selection_state == None: - selection_state = get_move_selection().isdisjoint(collection.objects) + if selection_state == None: + selection_state = get_move_selection().isdisjoint(target_collection.objects) + def select_objects(collection, selection_state): for obj in collection.objects: try: obj.select_set(selection_state) -- cgit v1.2.3 From 2ec6aba72cd3f64bbf8a4844a976696be942900a Mon Sep 17 00:00:00 2001 From: Rick Astley Date: Mon, 5 Sep 2022 12:38:26 +0200 Subject: Update for "Import Images as Planes" Addon. Add more features for Material and ImageTexture settings: 1. The ability to change Interpolation Mode of the texture (linear, closest, cubic, smart). Before there was only linear as default. 2. The ability to change the extension parameter. 3. The ability to change a Blend Mode (Blend, Clip, Hashed). Before there was only Blend (or Opaque if "Use Alpha" is unchecked) as default. 4. In "Alpha Blend" case, added the "Show Backface" checkbox. 5. The ability to change the Shadow Mode (Clip, Hashed, Opaque) or disable casting shadows. Before there was only "Alpha Clip" as default. 6. The ability to enable Backface Culling. Reworked: 1. Compositing Nodes, Material Settings and Texture Settings are in different layouts now. 2. "Use Alpha" checkbox has moved from "Texture Setting" to "Material Settings". Differential Revision: https://developer.blender.org/D15744 --- io_import_images_as_planes.py | 98 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 85 insertions(+), 13 deletions(-) diff --git a/io_import_images_as_planes.py b/io_import_images_as_planes.py index 50a56238..ef63ec52 100644 --- a/io_import_images_as_planes.py +++ b/io_import_images_as_planes.py @@ -2,8 +2,8 @@ bl_info = { "name": "Import Images as Planes", - "author": "Florian Meyer (tstscr), mont29, matali, Ted Schundler (SpkyElctrc)", - "version": (3, 4, 0), + "author": "Florian Meyer (tstscr), mont29, matali, Ted Schundler (SpkyElctrc), mrbimax", + "version": (3, 5, 0), "blender": (2, 91, 0), "location": "File > Import > Images as Planes or Add > Mesh > Images as Planes", "description": "Imports images and creates planes with the appropriate aspect ratio. " @@ -718,6 +718,34 @@ class IMPORT_IMAGE_OT_to_plane(Operator, AddObjectHelper): name="Strength", min=0.0, default=1.0, soft_max=10.0, step=100, description="Brightness of Emission Texture") + use_transparency: BoolProperty( + name="Use Alpha", default=True, + description="Use alpha channel for transparency") + + BLEND_METHODS = ( + ('BLEND',"Blend","Render polygon transparent, depending on alpha channel of the texture"), + ('CLIP', "Clip","Use the alpha threshold to clip the visibility (binary visibility)"), + ('HASHED', "Hashed","Use noise to dither the binary visibility (works well with multi-samples)"), + ('OPAQUE', "Opaque","Render surface without transparency"), + ) + blend_method: EnumProperty(name="Blend Mode", items=BLEND_METHODS, default='BLEND', description="Blend Mode for Transparent Faces") + + SHADOW_METHODS = ( + ('CLIP', "Clip","Use the alpha threshold to clip the visibility (binary visibility)"), + ('HASHED', "Hashed","Use noise to dither the binary visibility (works well with multi-samples)"), + ('OPAQUE',"Opaque","Material will cast shadows without transparency"), + ('NONE',"None","Material will cast no shadow"), + ) + shadow_method: EnumProperty(name="Shadow Mode", items=SHADOW_METHODS, default='CLIP', description="Shadow mapping method") + + use_backface_culling: BoolProperty( + name="Backface Culling", default=False, + description="Use back face culling to hide the back side of faces") + + show_transparent_back: BoolProperty( + name="Show Backface", default=True, + description="Render multiple transparent layers (may introduce transparency sorting problems)") + overwrite_material: BoolProperty( name="Overwrite Material", default=True, description="Overwrite existing Material (based on material name)") @@ -729,9 +757,20 @@ class IMPORT_IMAGE_OT_to_plane(Operator, AddObjectHelper): # ------------------ # Properties - Image - use_transparency: BoolProperty( - name="Use Alpha", default=True, - description="Use alpha channel for transparency") + INTERPOLATION_MODES = ( + ('Linear', "Linear", "Linear interpolation"), + ('Closest', "Closest", "No interpolation (sample closest texel)"), + ('Cubic', "Cubic", "Cubic interpolation"), + ('Smart', "Smart", "Bicubic when magnifying, else bilinear (OSL only)"), + ) + interpolation: EnumProperty(name="Interpolation", items=INTERPOLATION_MODES, default='Linear', description="Texture interpolation") + + EXTENSION_MODES = ( + ('CLIP', "Clip", "Clip to image size and set exterior pixels as transparent"), + ('EXTEND', "Extend", "Extend by repeating edge pixels of the image"), + ('REPEAT', "Repeat", "Cause the image to repeat horizontally and vertically"), + ) + extension: EnumProperty(name="Extension", items=EXTENSION_MODES, default='CLIP', description="How the image is extrapolated past its original bounds") t = bpy.types.Image.bl_rna.properties["alpha_mode"] alpha_mode_items = tuple((e.identifier, e.name, e.description) for e in t.enum_items) @@ -766,27 +805,53 @@ class IMPORT_IMAGE_OT_to_plane(Operator, AddObjectHelper): box.label(text="Compositing Nodes:", icon='RENDERLAYERS') box.prop(self, "compositing_nodes") - + layout = self.layout + box = layout.box() box.label(text="Material Settings:", icon='MATERIAL') + box.label(text="Material Type") row = box.row() row.prop(self, 'shader', expand=True) if self.shader == 'EMISSION': box.prop(self, "emit_strength") + box.label(text="Blend Mode") + row = box.row() + row.prop(self, 'blend_method', expand=True) + if self.use_transparency and self.alpha_mode != "NONE" and self.blend_method == "OPAQUE": + box.label(text="'Opaque' does not support alpha", icon="ERROR") + if self.blend_method == 'BLEND': + row = box.row() + row.prop(self, "show_transparent_back") + + box.label(text="Shadow Mode") + row = box.row() + row.prop(self, 'shadow_method', expand=True) + + row = box.row() + row.prop(self, "use_backface_culling") + engine = context.scene.render.engine if engine not in ('CYCLES', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'): box.label(text="%s is not supported" % engine, icon='ERROR') box.prop(self, "overwrite_material") - + layout = self.layout + box = layout.box() box.label(text="Texture Settings:", icon='TEXTURE') + box.label(text="Interpolation") + row = box.row() + row.prop(self, 'interpolation', expand=True) + box.label(text="Extension") + row = box.row() + row.prop(self, 'extension', expand=True) row = box.row() row.prop(self, "use_transparency") - sub = row.row() - sub.active = self.use_transparency - sub.prop(self, "alpha_mode", text="") - box.prop(self, "use_auto_refresh") + if self.use_transparency: + sub = row.row() + sub.prop(self, "alpha_mode", text="") + row = box.row() + row.prop(self, "use_auto_refresh") def draw_spatial_config(self, context): # --- Spatial Properties: Position, Size and Orientation --- # @@ -970,6 +1035,8 @@ class IMPORT_IMAGE_OT_to_plane(Operator, AddObjectHelper): tex_image = node_tree.nodes.new('ShaderNodeTexImage') tex_image.image = img_spec.image tex_image.show_texture = True + tex_image.interpolation = self.interpolation + tex_image.extension = self.extension self.apply_texture_options(tex_image, img_spec) return tex_image @@ -985,8 +1052,13 @@ class IMPORT_IMAGE_OT_to_plane(Operator, AddObjectHelper): material = bpy.data.materials.new(name=name_compat) material.use_nodes = True - if self.use_transparency: - material.blend_method = 'BLEND' + + material.blend_method = self.blend_method + material.shadow_method = self.shadow_method + + material.use_backface_culling = self.use_backface_culling + material.show_transparent_back = self.show_transparent_back + node_tree = material.node_tree out_node = clean_node_tree(node_tree) -- cgit v1.2.3 From 8d63239a0ef2cb091e751365c7b8355e138ddb28 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 5 Sep 2022 10:57:22 -0300 Subject: Snap Utilities Line: use 'is_wire' instead of checking 'link_faces' Small micro optimization --- mesh_snap_utilities_line/op_line.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mesh_snap_utilities_line/op_line.py b/mesh_snap_utilities_line/op_line.py index 7dcb4a52..0444f0e2 100644 --- a/mesh_snap_utilities_line/op_line.py +++ b/mesh_snap_utilities_line/op_line.py @@ -51,10 +51,10 @@ def get_closest_edge(bm, point, dist): return r_edge -def get_loose_linked_edges(bmvert): - linked = [e for e in bmvert.link_edges if not e.link_faces] +def get_loose_linked_edges(vert): + linked = [e for e in vert.link_edges if e.is_wire] for e in linked: - linked += [le for v in e.verts if not v.link_faces for le in v.link_edges if le not in linked] + linked += [le for v in e.verts if v.is_wire for le in v.link_edges if le not in linked] return linked -- cgit v1.2.3 From 0cd92169d40ae1c7e103ff269e850eaf1b901646 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 5 Sep 2022 11:36:38 -0300 Subject: Fix T100807: Snap Utilities Line doesn't create faces for non-boundary edges --- mesh_snap_utilities_line/op_line.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/mesh_snap_utilities_line/op_line.py b/mesh_snap_utilities_line/op_line.py index 0444f0e2..eacc551b 100644 --- a/mesh_snap_utilities_line/op_line.py +++ b/mesh_snap_utilities_line/op_line.py @@ -170,8 +170,28 @@ def make_line(self, bm_geom, location): break ed_list.update(get_loose_linked_edges(v2)) + ed_list = list(ed_list) + + # WORKAROUND: `edgenet_fill` only works with loose edges or boundary + # edges, so remove the other edges and create temporary elements to + # replace them. + targetmap = {} + ed_new = [] + for edge in ed_list: + if not edge.is_wire and not edge.is_boundary: + v1, v2 = edge.verts + tmp_vert = bm.verts.new(v2.co) + e1 = bm.edges.new([v1, tmp_vert]) + e2 = bm.edges.new([tmp_vert, v2]) + ed_list.remove(edge) + ed_new.append(e1) + ed_new.append(e2) + targetmap[tmp_vert] = v2 + + bmesh.ops.edgenet_fill(bm, edges=ed_list + ed_new) + if targetmap: + bmesh.ops.weld_verts(bm, targetmap=targetmap) - bmesh.ops.edgenet_fill(bm, edges=list(ed_list)) update_edit_mesh = True # print('face created') -- cgit v1.2.3