From 7cdde96d329d6221090d1f23ea70a7b90c2b9739 Mon Sep 17 00:00:00 2001 From: Vladimir Spivak Date: Sat, 12 Mar 2022 13:42:19 +0200 Subject: Fix T96342: Add Curve Extra Objects addon fails when adding Curvy Curve --- add_curve_extra_objects/add_curve_curly.py | 6 +++--- add_curve_extra_objects/beveltaper_curve.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/add_curve_extra_objects/add_curve_curly.py b/add_curve_extra_objects/add_curve_curly.py index 0111aaf6..af02f115 100644 --- a/add_curve_extra_objects/add_curve_curly.py +++ b/add_curve_extra_objects/add_curve_curly.py @@ -5,7 +5,7 @@ bl_info = { "name": "Curly Curves", "author": "Cmomoney", - "version": (1, 1, 9), + "version": (1, 2, 0), "blender": (2, 80, 0), "location": "View3D > Add > Curve > Curly Curve", "description": "Adds a new Curly Curve", @@ -402,7 +402,7 @@ def make_curve(self, context, verts, lh, rh): for p in range(len(verts)): c = 0 newSpline = Curve.data.splines.new(type='BEZIER') # newSpline - newSpline.bezier_points.add(len(verts[p]) / 3 - 1) + newSpline.bezier_points.add(int(len(verts[p]) / 3 - 1)) newSpline.bezier_points.foreach_set('co', verts[p]) for bp in newSpline.bezier_points: @@ -424,7 +424,7 @@ def make_curve(self, context, verts, lh, rh): for p in range(len(verts)): c = 0 newSpline = dataCurve.splines.new(type='BEZIER') # newSpline - newSpline.bezier_points.add(len(verts[p]) / 3 - 1) + newSpline.bezier_points.add(int(len(verts[p]) / 3 - 1)) newSpline.bezier_points.foreach_set('co', verts[p]) for bp in newSpline.bezier_points: diff --git a/add_curve_extra_objects/beveltaper_curve.py b/add_curve_extra_objects/beveltaper_curve.py index 80c01b3a..798b1fbb 100644 --- a/add_curve_extra_objects/beveltaper_curve.py +++ b/add_curve_extra_objects/beveltaper_curve.py @@ -5,7 +5,7 @@ bl_info = { "name": "Bevel/Taper Curve", "author": "Cmomoney", - "version": (1, 2), + "version": (1, 2, 1), "blender": (2, 80, 0), "location": "View3D > Object > Bevel/Taper", "description": "Adds bevel and/or taper curve to active curve", @@ -204,7 +204,7 @@ def make_curve(self, context, verts, lh, rh): c = 0 spline = curve_data.splines.new(type='BEZIER') spline.use_cyclic_u = True - spline.bezier_points.add(len(verts[p]) / 3 - 1) + spline.bezier_points.add(int(len(verts[p]) / 3 - 1)) spline.bezier_points.foreach_set('co', verts[p]) for bp in spline.bezier_points: -- cgit v1.2.3 From 5838e260366aebd1a1b014bcd84ad05728739005 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sun, 13 Mar 2022 11:17:33 +0100 Subject: glTF exporter: Add glTF settings node in shader menu This menu (in Add > Output) will be displayed only if activated in addon preferences. The node is sed for AO export. This is a shortcut, avoiding user to create it him/herself --- io_scene_gltf2/__init__.py | 25 ++++++++++- .../blender/com/gltf2_blender_material_helpers.py | 9 ++++ io_scene_gltf2/blender/com/gltf2_blender_ui.py | 49 ++++++++++++++++++++++ .../imp/gltf2_blender_pbrMetallicRoughness.py | 9 +--- 4 files changed, 83 insertions(+), 9 deletions(-) create mode 100644 io_scene_gltf2/blender/com/gltf2_blender_ui.py diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index b290f946..7c39ac3f 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, 2, 9), + "version": (3, 2, 10), 'blender': (3, 1, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', @@ -1171,6 +1171,20 @@ class ImportGLTF2(Operator, ImportHelper): self.loglevel = logging.NOTSET +class GLTF_AddonPreferences(bpy.types.AddonPreferences): + bl_idname = __package__ + + settings_node_ui : bpy.props.BoolProperty( + default= False, + description="Displays glTF Settings node in Shader Editor (Menu Add > Ouput)" + ) + + + def draw(self, context): + layout = self.layout + row = layout.row() + row.prop(self, "settings_node_ui", text="Shader Editor Add-ons") + def menu_func_import(self, context): self.layout.operator(ImportGLTF2.bl_idname, text='glTF 2.0 (.glb/.gltf)') @@ -1188,21 +1202,26 @@ classes = ( GLTF_PT_export_animation_skinning, GLTF_PT_export_user_extensions, ImportGLTF2, - GLTF_PT_import_user_extensions + GLTF_PT_import_user_extensions, + GLTF_AddonPreferences ) def register(): + import io_scene_gltf2.blender.com.gltf2_blender_ui as blender_ui for c in classes: bpy.utils.register_class(c) # bpy.utils.register_module(__name__) + blender_ui.register() + # add to the export / import menu bpy.types.TOPBAR_MT_file_export.append(menu_func_export) bpy.types.TOPBAR_MT_file_import.append(menu_func_import) def unregister(): + import io_scene_gltf2.blender.com.gltf2_blender_ui as blender_ui for c in classes: bpy.utils.unregister_class(c) for f in exporter_extension_panel_unregister_functors: @@ -1213,6 +1232,8 @@ 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_material_helpers.py b/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py index f3d7122e..7b90b0a3 100755 --- a/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py +++ b/io_scene_gltf2/blender/com/gltf2_blender_material_helpers.py @@ -1,6 +1,15 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2018-2021 The glTF-Blender-IO authors. +import bpy def get_gltf_node_name(): return "glTF Settings" + +def create_settings_group(name): + gltf_node_group = bpy.data.node_groups.new(name, 'ShaderNodeTree') + gltf_node_group.inputs.new("NodeSocketFloat", "Occlusion") + 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 diff --git a/io_scene_gltf2/blender/com/gltf2_blender_ui.py b/io_scene_gltf2/blender/com/gltf2_blender_ui.py new file mode 100644 index 00000000..59c364fb --- /dev/null +++ b/io_scene_gltf2/blender/com/gltf2_blender_ui.py @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2018-2022 The glTF-Blender-IO authors. + +import bpy +from ..com.gltf2_blender_material_helpers import get_gltf_node_name, create_settings_group + +def create_gltf_ao_group(operator, group_name): + + # create a new group + gltf_ao_group = bpy.data.node_groups.new(group_name, "ShaderNodeTree") + + return gltf_ao_group + +class NODE_OT_GLTF_SETTINGS(bpy.types.Operator): + bl_idname = "node.gltf_settings_node_operator" + bl_label = "glTF Settings" + + + @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_settings_node_name = get_gltf_node_name() + if gltf_settings_node_name in bpy.data.node_groups: + my_group = bpy.data.node_groups[get_gltf_node_name()] + else: + my_group = create_settings_group(gltf_settings_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_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") + + +def register(): + bpy.utils.register_class(NODE_OT_GLTF_SETTINGS) + bpy.types.NODE_MT_category_SH_NEW_OUTPUT.append(add_gltf_settings_to_menu) + +def unregister(): + bpy.utils.unregister_class(NODE_OT_GLTF_SETTINGS) diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py b/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py index 1780998c..a5d10a7a 100755 --- a/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py +++ b/io_scene_gltf2/blender/imp/gltf2_blender_pbrMetallicRoughness.py @@ -3,7 +3,7 @@ import bpy from ...io.com.gltf2_io import TextureInfo, MaterialPBRMetallicRoughness -from ..com.gltf2_blender_material_helpers import get_gltf_node_name +from ..com.gltf2_blender_material_helpers import get_gltf_node_name, create_settings_group from .gltf2_blender_texture import texture from .gltf2_blender_KHR_materials_clearcoat import \ clearcoat, clearcoat_roughness, clearcoat_normal @@ -552,16 +552,11 @@ def make_settings_node(mh): node.node_tree = get_settings_group() return node - def get_settings_group(): gltf_node_group_name = get_gltf_node_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 = bpy.data.node_groups.new(gltf_node_group_name, 'ShaderNodeTree') - gltf_node_group.inputs.new("NodeSocketFloat", "Occlusion") - gltf_node_group.nodes.new('NodeGroupOutput') - gltf_node_group_input = gltf_node_group.nodes.new('NodeGroupInput') - gltf_node_group_input.location = -200, 0 + gltf_node_group = create_settings_group(gltf_node_group_name) return gltf_node_group -- cgit v1.2.3 From 5a557e72fc1e63802837b385a303b5dd6e44f284 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sun, 13 Mar 2022 11:25:33 +0100 Subject: glTF exporter: export armature without skined mesh as skin in glTF files At import, this will be defined as armature/joints instead of empties --- io_scene_gltf2/__init__.py | 2 +- io_scene_gltf2/blender/exp/gltf2_blender_export.py | 3 +++ io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py | 9 +++++++++ io_scene_gltf2/blender/exp/gltf2_blender_gltf2_exporter.py | 4 ++++ 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 7c39ac3f..a9bf80b5 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, 2, 10), + "version": (3, 2, 11), 'blender': (3, 1, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_export.py b/io_scene_gltf2/blender/exp/gltf2_blender_export.py index ddcad91d..539c6b74 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_export.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_export.py @@ -65,6 +65,8 @@ def __export(export_settings): def __gather_gltf(exporter, export_settings): active_scene_idx, scenes, animations = gltf2_blender_gather.gather_gltf2(export_settings) + unused_skins = export_settings['vtree'].get_unused_skins() + if export_settings['gltf_draco_mesh_compression']: gltf2_io_draco_compression_extension.encode_scene_primitives(scenes, export_settings) exporter.add_draco_extension() @@ -75,6 +77,7 @@ def __gather_gltf(exporter, export_settings): exporter.add_scene(scene, idx==active_scene_idx) for animation in animations: exporter.add_animation(animation) + exporter.traverse_unused_skins(unused_skins) def __create_buffer(exporter, export_settings): 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 d3edd50a..549c4ed0 100644 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py @@ -372,3 +372,12 @@ class VExportTree: if len(candidates) > 0: n.armature = candidates[0].uuid del n.armature_needed + + def get_unused_skins(self): + from .gltf2_blender_gather_skins import gather_skin + skins = [] + for n in [n for n in self.nodes.values() if n.blender_type == VExportNode.ARMATURE]: + if len([m for m in self.nodes.values() if m.keep_tag is True and m.blender_type == VExportNode.OBJECT and m.armature == n.uuid]) == 0: + skin = gather_skin(n.uuid, self.export_settings) + skins.append(skin) + return skins diff --git a/io_scene_gltf2/blender/exp/gltf2_blender_gltf2_exporter.py b/io_scene_gltf2/blender/exp/gltf2_blender_gltf2_exporter.py index 9761e81c..574693d7 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gltf2_exporter.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gltf2_exporter.py @@ -174,6 +174,10 @@ class GlTF2Exporter: if active: self.__gltf.scene = scene_num + def traverse_unused_skins(self, skins): + for s in skins: + self.__traverse(s) + def add_animation(self, animation: gltf2_io.Animation): """ Add an animation to the glTF. -- cgit v1.2.3 From 43477e00ce44cf561a38365d35c769010392c102 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sun, 13 Mar 2022 11:28:02 +0100 Subject: glTF exporter: add filter hook for user extensions. You can change filter tag to change default filtering --- io_scene_gltf2/__init__.py | 2 +- io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index a9bf80b5..369090e5 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, 2, 11), + "version": (3, 2, 12), 'blender': (3, 1, 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 549c4ed0..643cbea0 100644 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_tree.py @@ -5,6 +5,7 @@ import bpy import uuid from . import gltf2_blender_export_keys +from io_scene_gltf2.io.exp.gltf2_io_user_extensions import export_user_extensions from mathutils import Quaternion, Matrix class VExportNode: @@ -255,6 +256,7 @@ class VExportTree: def filter(self): self.filter_tag() + export_user_extensions('gather_tree_filter_tag_hook', self.export_settings, self) self.filter_perform() -- cgit v1.2.3 From 8653db3808888a79bd83b92e33d314bbb9d873c2 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sun, 13 Mar 2022 11:32:48 +0100 Subject: glTF exporter: add a hook for user extension to change json encoding This allows user to use no indent in json, for example Here is an example of hook: ``` def gather_gltf_encoded_hook(self, gltf_format, sort_order, export_settings): gltf_format.indent = None gltf_format.separators = (',', ':') ``` --- io_scene_gltf2/__init__.py | 2 +- io_scene_gltf2/io/exp/gltf2_io_export.py | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index 369090e5..b87af39a 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, 2, 12), + "version": (3, 2, 13), 'blender': (3, 1, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', diff --git a/io_scene_gltf2/io/exp/gltf2_io_export.py b/io_scene_gltf2/io/exp/gltf2_io_export.py index 49cc4a78..aee74ba9 100755 --- a/io_scene_gltf2/io/exp/gltf2_io_export.py +++ b/io_scene_gltf2/io/exp/gltf2_io_export.py @@ -7,6 +7,7 @@ import json import struct +from io_scene_gltf2.io.exp.gltf2_io_user_extensions import export_user_extensions # # Globals @@ -19,13 +20,18 @@ from collections import OrderedDict def save_gltf(gltf, export_settings, encoder, glb_buffer): - indent = None - separators = (',', ':') + # Use a class here, to be able to pass data by reference to hook (to be able to change them inside hook) + class GlTF_format: + def __init__(self, indent, separators): + self.indent = indent + self.separators = separators + + gltf_format = GlTF_format(None, (',', ':')) if export_settings['gltf_format'] != 'GLB': - indent = 4 + gltf_format.indent = 4 # The comma is typically followed by a newline, so no trailing whitespace is needed on it. - separators = (',', ' : ') + gltf_format.separators = (',', ' : ') sort_order = [ "asset", @@ -48,8 +54,11 @@ def save_gltf(gltf, export_settings, encoder, glb_buffer): "samplers", "buffers" ] + + export_user_extensions('gather_gltf_encoded_hook', export_settings, gltf_format, sort_order) + gltf_ordered = OrderedDict(sorted(gltf.items(), key=lambda item: sort_order.index(item[0]))) - gltf_encoded = json.dumps(gltf_ordered, indent=indent, separators=separators, cls=encoder, allow_nan=False) + gltf_encoded = json.dumps(gltf_ordered, indent=gltf_format.indent, separators=gltf_format.separators, cls=encoder, allow_nan=False) # -- cgit v1.2.3 From 4d8b2dc95f98493f44d51ace3712bf895686c670 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sun, 13 Mar 2022 11:39:29 +0100 Subject: glTF exporter: remove back compatibility of export_selection -> use_selection This is now more than a year old. Removing the back compatibility. Use can of course set again the value in UI at export --- io_scene_gltf2/__init__.py | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index b87af39a..dcef27b1 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, 2, 13), + "version": (3, 2, 14), 'blender': (3, 1, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', @@ -287,13 +287,6 @@ class ExportGLTF2_Base: default=False ) - # keep it for compatibility (for now) - export_selected: BoolProperty( - name='Selected Objects', - description='Export selected objects only', - default=False - ) - use_selection: BoolProperty( name='Selected Objects', description='Export selected objects only', @@ -462,11 +455,6 @@ class ExportGLTF2_Base: self.will_save_settings = False if settings: try: - if 'export_selected' in settings.keys(): # Back compatibility for export_selected --> use_selection - setattr(self, "use_selection", settings['export_selected']) - settings["use_selection"] = settings['export_selected'] - del settings['export_selected'] - print("export_selected is now renamed use_selection, and will be deleted in a few release") for (k, v) in settings.items(): setattr(self, k, v) self.will_save_settings = True @@ -503,8 +491,6 @@ class ExportGLTF2_Base: x: getattr(self, x) for x in dir(all_props) if (x.startswith("export_") or x in exceptional) and all_props.get(x) is not None } - if 'export_selected' in export_props.keys(): - del export_props['export_selected'] # Do not save this property, only here for backward compatibility context.scene[self.scene_key] = export_props def execute(self, context): @@ -554,18 +540,12 @@ class ExportGLTF2_Base: export_settings['gltf_colors'] = self.export_colors export_settings['gltf_cameras'] = self.export_cameras - # compatibility after renaming export_selected to use_selection - if self.export_selected is True: - self.report({"WARNING"}, "export_selected is now renamed use_selection, and will be deleted in a few release") - export_settings['gltf_selected'] = self.export_selected - else: - export_settings['gltf_selected'] = self.use_selection export_settings['gltf_visible'] = self.use_visible export_settings['gltf_renderable'] = self.use_renderable export_settings['gltf_active_collection'] = self.use_active_collection - # export_settings['gltf_selected'] = self.use_selection This can be uncomment when removing compatibility of export_selected + export_settings['gltf_selected'] = self.use_selection export_settings['gltf_layers'] = True # self.export_layers export_settings['gltf_extras'] = self.export_extras export_settings['gltf_yup'] = self.export_yup -- cgit v1.2.3 From 3a299965833c65ae76a324a5540bcf5b31a3b669 Mon Sep 17 00:00:00 2001 From: Julien Duroure Date: Sun, 13 Mar 2022 11:44:02 +0100 Subject: glTF exporter: Manage active UVMap correclty, when there is no UVMap used in node tree --- io_scene_gltf2/__init__.py | 2 +- .../blender/exp/gltf2_blender_gather_materials.py | 160 +++++++++++++++++---- ...nder_gather_materials_pbr_metallic_roughness.py | 22 ++- .../exp/gltf2_blender_gather_materials_unlit.py | 5 +- .../blender/exp/gltf2_blender_gather_primitives.py | 10 +- .../exp/gltf2_blender_gather_texture_info.py | 14 +- 6 files changed, 170 insertions(+), 43 deletions(-) diff --git a/io_scene_gltf2/__init__.py b/io_scene_gltf2/__init__.py index dcef27b1..fd0687b9 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, 2, 14), + "version": (3, 2, 15), 'blender': (3, 1, 0), 'location': 'File > Import-Export', 'description': 'Import-Export as glTF 2.0', 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 04129996..402e06fa 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials.py @@ -1,6 +1,7 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright 2018-2021 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 @@ -17,14 +18,17 @@ from io_scene_gltf2.io.exp.gltf2_io_user_extensions import export_user_extension from io_scene_gltf2.io.com.gltf2_io_debug import print_console @cached -def get_material_cache_key(blender_material, export_settings): +def get_material_cache_key(blender_material, active_uvmap_index, export_settings): # Use id of material # Do not use bpy.types that can be unhashable # Do not use material name, that can be not unique (when linked) - return ((id(blender_material),)) + return ( + (id(blender_material),), + (active_uvmap_index,) + ) @cached_by_key(key=get_material_cache_key) -def gather_material(blender_material, export_settings): +def gather_material(blender_material, active_uvmap_index, export_settings): """ Gather the material used by the blender primitive. @@ -35,26 +39,79 @@ def gather_material(blender_material, export_settings): if not __filter_material(blender_material, export_settings): return None - mat_unlit = __gather_material_unlit(blender_material, export_settings) + mat_unlit = __gather_material_unlit(blender_material, active_uvmap_index, export_settings) if mat_unlit is not None: return mat_unlit orm_texture = __gather_orm_texture(blender_material, export_settings) - material = gltf2_io.Material( + emissive_texture, uvmap_actives_emissive_texture = __gather_emissive_texture(blender_material, export_settings) + extensions, uvmap_actives_extensions = __gather_extensions(blender_material, 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) + + 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_texture=__gather_emissive_texture(blender_material, export_settings), - extensions=__gather_extensions(blender_material, export_settings), + emissive_texture=emissive_texture, + extensions=extensions, extras=__gather_extras(blender_material, export_settings), name=__gather_name(blender_material, export_settings), - normal_texture=__gather_normal_texture(blender_material, export_settings), - occlusion_texture=__gather_occlusion_texture(blender_material, orm_texture, export_settings), - pbr_metallic_roughness=__gather_pbr_metallic_roughness(blender_material, orm_texture, export_settings) + normal_texture=normal_texture, + occlusion_texture=occlusion_texture, + pbr_metallic_roughness=pbr_metallic_roughness ) + + # merge all uvmap_actives + uvmap_actives = [] + if uvmap_actives_emissive_texture: + uvmap_actives.extend(uvmap_actives_emissive_texture) + if uvmap_actives_extensions: + uvmap_actives.extend(uvmap_actives_extensions) + if uvmap_actives_normal_texture: + uvmap_actives.extend(uvmap_actives_normal_texture) + if uvmap_actives_occlusion_texture: + uvmap_actives.extend(uvmap_actives_occlusion_texture) + if uvmap_actives_pbr_metallic_roughness: + uvmap_actives.extend(uvmap_actives_pbr_metallic_roughness) + + # Because some part of material are shared (eg pbr_metallic_roughness), we must copy the material + # Texture must be shared, but not TextureInfo + material = deepcopy(base_material) + __get_new_material_texture_shared(base_material, material) + + active_uvmap_index = active_uvmap_index if active_uvmap_index != 0 else None + + for tex in uvmap_actives: + if tex == "emissiveTexture": + material.emissive_texture.tex_coord = active_uvmap_index + elif tex == "normalTexture": + material.normal_texture.tex_coord = active_uvmap_index + elif tex == "occlusionTexture": + material.occlusion_texture.tex_coord = active_uvmap_index + elif tex == "baseColorTexture": + material.pbr_metallic_roughness.base_color_texture.tex_coord = active_uvmap_index + elif tex == "metallicRoughnessTexture": + material.pbr_metallic_roughness.metallic_roughness_texture.tex_coord = active_uvmap_index + elif tex == "clearcoatTexture": + material.extensions["KHR_materials_clearcoat"].extension['clearcoatTexture'].tex_coord = active_uvmap_index + elif tex == "clearcoatRoughnessTexture": + material.extensions["KHR_materials_clearcoat"].extension['clearcoatRoughnessTexture'].tex_coord = active_uvmap_index + elif tex == "clearcoatNormalTexture": #TODO not tested yet + 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 + + # If material is not using active UVMap, we need to return the same material, + # Even if multiples meshes are using different active UVMap + if len(uvmap_actives) == 0 and active_uvmap_index != -1: + material = gather_material(blender_material, -1, export_settings) + + # If emissive is set, from an emissive node (not PBR) # We need to set manually default values for # pbr_metallic_roughness.baseColor @@ -80,6 +137,25 @@ def gather_material(blender_material, export_settings): # 'material'] + ' not found. Please assign glTF 2.0 material or enable Blinn-Phong material in export.') +def __get_new_material_texture_shared(base, node): + if node is None: + return + if callable(node) is True: + return + if node.__str__().startswith('__'): + return + if type(node) in [gltf2_io.TextureInfo, gltf2_io.MaterialOcclusionTextureInfoClass, gltf2_io.MaterialNormalTextureInfoClass]: + node.index = base.index + else: + if hasattr(node, '__dict__'): + for attr, value in node.__dict__.items(): + __get_new_material_texture_shared(getattr(base, attr), value) + else: + # For extensions (on a dict) + if type(node).__name__ == 'dict': + for i in node.keys(): + __get_new_material_texture_shared(base[i], node[i]) + def __filter_material(blender_material, export_settings): return export_settings[gltf2_blender_export_keys.MATERIALS] @@ -155,17 +231,20 @@ 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") - return gltf2_blender_gather_texture_info.gather_texture_info(emissive, (emissive,), export_settings) + 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 __gather_extensions(blender_material, export_settings): extensions = {} # KHR_materials_clearcoat + actives_uvmaps = [] - clearcoat_extension = __gather_clearcoat_extension(blender_material, export_settings) + clearcoat_extension, use_actives_uvmap_clearcoat = __gather_clearcoat_extension(blender_material, export_settings) if clearcoat_extension: extensions["KHR_materials_clearcoat"] = clearcoat_extension + actives_uvmaps.extend(use_actives_uvmap_clearcoat) # KHR_materials_transmission @@ -173,7 +252,7 @@ def __gather_extensions(blender_material, export_settings): if transmission_extension: extensions["KHR_materials_transmission"] = transmission_extension - return extensions if extensions else None + return extensions, actives_uvmaps if extensions else None def __gather_extras(blender_material, export_settings): @@ -190,10 +269,11 @@ 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") - return 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) + return normal_texture, ["normalTexture"] if use_active_uvmap_normal else None def __gather_orm_texture(blender_material, export_settings): @@ -231,7 +311,7 @@ def __gather_orm_texture(blender_material, export_settings): return None # Double-check this will past the filter in texture_info - info = 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 @@ -241,10 +321,11 @@ 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") - return 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) + return occlusion_texture, ["occlusionTexture"] if use_active_uvmap_occlusion else None def __gather_pbr_metallic_roughness(blender_material, orm_texture, export_settings): @@ -284,7 +365,7 @@ def __gather_clearcoat_extension(blender_material, export_settings): clearcoat_enabled = True if not clearcoat_enabled: - return None + 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 @@ -302,28 +383,38 @@ def __gather_clearcoat_extension(blender_material, export_settings): 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_extension['clearcoatTexture'] = gltf2_blender_gather_texture_info.gather_texture_info( + 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_extension['clearcoatRoughnessTexture'] = gltf2_blender_gather_texture_info.gather_texture_info( + 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_extension['clearcoatNormalTexture'] = gltf2_blender_gather_texture_info.gather_material_normal_texture_info_class( + 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) + return Extension('KHR_materials_clearcoat', clearcoat_extension, False), use_actives_uvmaps def __gather_transmission_extension(blender_material, export_settings): transmission_enabled = False @@ -350,7 +441,7 @@ def __gather_transmission_extension(blender_material, export_settings): transmission_slots = (transmission_socket,) if len(transmission_slots) > 0: - combined_texture = gltf2_blender_gather_texture_info.gather_texture_info( + combined_texture, use_active_uvmap = gltf2_blender_gather_texture_info.gather_texture_info( transmission_socket, transmission_slots, export_settings, @@ -358,17 +449,19 @@ def __gather_transmission_extension(blender_material, export_settings): if has_transmission_texture: transmission_extension['transmissionTexture'] = combined_texture - return Extension('KHR_materials_transmission', transmission_extension, False) + return Extension('KHR_materials_transmission', transmission_extension, False), ["transmissionTexture"] if use_active_uvmap else [] -def __gather_material_unlit(blender_material, export_settings): +def __gather_material_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) if info is None: return None - material = gltf2_io.Material( + base_color_texture, use_active_uvmap = gltf2_unlit.gather_base_color_texture(info, export_settings) + + 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), @@ -382,7 +475,7 @@ def __gather_material_unlit(blender_material, export_settings): pbr_metallic_roughness=gltf2_io.MaterialPBRMetallicRoughness( base_color_factor=gltf2_unlit.gather_base_color_factor(info, export_settings), - base_color_texture=gltf2_unlit.gather_base_color_texture(info, export_settings), + base_color_texture=base_color_texture, metallic_factor=0.0, roughness_factor=0.9, metallic_roughness_texture=None, @@ -391,6 +484,19 @@ def __gather_material_unlit(blender_material, export_settings): ) ) + if use_active_uvmap is not None: + # Because some part of material are shared (eg pbr_metallic_roughness), we must copy the material + # Texture must be shared, but not TextureInfo + material = deepcopy(base_material) + __get_new_material_texture_shared(base_material, material) + material.pbr_metallic_roughness.base_color_texture.tex_coord = active_uvmap_index + elif use_active_uvmap is None and active_uvmap_index != -1: + # If material is not using active UVMap, we need to return the same material, + # Even if multiples meshes are using different active UVMap + material = gather_material(blender_material, -1, export_settings) + else: + material = base_material + export_user_extensions('gather_material_unlit_hook', export_settings, material, blender_material) return material 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 9395aa43..0b40ffd6 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 @@ -13,21 +13,31 @@ from io_scene_gltf2.io.exp.gltf2_io_user_extensions import export_user_extension @cached def gather_material_pbr_metallic_roughness(blender_material, orm_texture, export_settings): if not __filter_pbr_material(blender_material, export_settings): - return None + 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) material = gltf2_io.MaterialPBRMetallicRoughness( base_color_factor=__gather_base_color_factor(blender_material, export_settings), - base_color_texture=__gather_base_color_texture(blender_material, export_settings), + base_color_texture=base_color_texture, extensions=__gather_extensions(blender_material, export_settings), extras=__gather_extras(blender_material, export_settings), metallic_factor=__gather_metallic_factor(blender_material, export_settings), - metallic_roughness_texture=__gather_metallic_roughness_texture(blender_material, orm_texture, export_settings), + metallic_roughness_texture=metallic_roughness_texture, roughness_factor=__gather_roughness_factor(blender_material, export_settings) ) + # merge all use_active_uvmap infos + uvmap_actives = [] + if use_active_uvmap_base_color_texture is True: + uvmap_actives.append("baseColorTexture") + if use_active_uvmap_metallic_roughness_texture is True: + uvmap_actives.append("metallicRoughnessTexture") + export_user_extensions('gather_material_pbr_metallic_roughness_hook', export_settings, material, blender_material, orm_texture) - return material + return material, uvmap_actives def __filter_pbr_material(blender_material, export_settings): @@ -82,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 + return None, None return gltf2_blender_gather_texture_info.gather_texture_info(inputs[0], inputs, export_settings) @@ -118,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 + return 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_unlit.py b/io_scene_gltf2/blender/exp/gltf2_blender_gather_materials_unlit.py index 2645f1f7..e104b7f1 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 @@ -127,9 +127,10 @@ 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. - return 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, ) - return None + return unlit_texture, ["unlitTexture"] if unlit_use_active_uvmap else None + return None, None 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 9e5ce648..367c30f5 100755 --- a/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitives.py +++ b/io_scene_gltf2/blender/exp/gltf2_blender_gather_primitives.py @@ -57,6 +57,13 @@ def gather_primitives( """ primitives = [] + # retrieve active render UVMap + active_uvmap_idx = 0 + for i in range(len(blender_mesh.uv_layers)): + if blender_mesh.uv_layers[i].active_render is True: + active_uvmap_idx = i + break + blender_primitives = __gather_cache_primitives(blender_mesh, uuid_for_skined_data, vertex_groups, modifiers, export_settings) @@ -73,7 +80,8 @@ def gather_primitives( if mat is not None: material = gltf2_blender_gather_materials.gather_material( mat, - export_settings, + active_uvmap_idx, + export_settings ) primitive = gltf2_io.MeshPrimitive( 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 2618973b..15b101ad 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 @@ -36,15 +36,15 @@ def __gather_texture_info_helper( kind: str, export_settings): if not __filter_texture_info(primary_socket, blender_shader_sockets, export_settings): - return None + return None, None - tex_transform, tex_coord = __gather_texture_transform_and_tex_coord(primary_socket, export_settings) + tex_transform, tex_coord, use_active_uvmap = __gather_texture_transform_and_tex_coord(primary_socket, 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), - 'tex_coord': tex_coord, + 'tex_coord': tex_coord } if kind == 'DEFAULT': @@ -59,11 +59,11 @@ def __gather_texture_info_helper( texture_info = gltf2_io.MaterialOcclusionTextureInfoClass(**fields) if texture_info.index is None: - return None + return None, None export_user_extensions('gather_texture_info_hook', export_settings, texture_info, blender_shader_sockets) - return texture_info + return texture_info, use_active_uvmap def __filter_texture_info(primary_socket, blender_shader_sockets, export_settings): @@ -151,15 +151,17 @@ def __gather_texture_transform_and_tex_coord(primary_socket, export_settings): node = previous_node(node.inputs['Vector']) texcoord_idx = 0 + use_active_uvmap = True if node and node.type == 'UVMAP' and node.uv_map: # Try to gather map index. for blender_mesh in bpy.data.meshes: i = blender_mesh.uv_layers.find(node.uv_map) if i >= 0: texcoord_idx = i + use_active_uvmap = False break - return texture_transform, texcoord_idx or None + return texture_transform, texcoord_idx or None, use_active_uvmap def __get_tex_from_socket(socket): -- cgit v1.2.3 From 82d7d6b8bdec8dfc47c5366607f40b8ff219a718 Mon Sep 17 00:00:00 2001 From: Pullusb Date: Mon, 14 Mar 2022 19:51:31 +0100 Subject: GPencil Tools: Fix timeline scrub snap Due to API change, timeline scrub snapping crashed when setting scene frame --- greasepencil_tools/__init__.py | 2 +- greasepencil_tools/timeline_scrub.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/greasepencil_tools/__init__.py b/greasepencil_tools/__init__.py index 18b808c4..0205362b 100644 --- a/greasepencil_tools/__init__.py +++ b/greasepencil_tools/__init__.py @@ -4,7 +4,7 @@ bl_info = { "name": "Grease Pencil Tools", "description": "Extra tools for Grease Pencil", "author": "Samuel Bernou, Antonio Vazquez, Daniel Martinez Lara, Matias Mendiola", -"version": (1, 5, 6), +"version": (1, 5, 7), "blender": (2, 91, 0), "location": "Sidebar > Grease Pencil > Grease Pencil Tools", "warning": "", diff --git a/greasepencil_tools/timeline_scrub.py b/greasepencil_tools/timeline_scrub.py index 8f058ed9..eaccab99 100644 --- a/greasepencil_tools/timeline_scrub.py +++ b/greasepencil_tools/timeline_scrub.py @@ -21,13 +21,13 @@ from bpy.props import (BoolProperty, EnumProperty) -def nearest(array, value): +def nearest(array, value) -> int: ''' Get a numpy array and a target value Return closest val found in array to passed value ''' idx = (np.abs(array - value)).argmin() - return array[idx] + return int(array[idx]) def draw_callback_px(self, context): -- cgit v1.2.3 From 80c14a2adc06c92620471b226a64a3540718dd71 Mon Sep 17 00:00:00 2001 From: Pullusb Date: Mon, 14 Mar 2022 22:53:28 +0100 Subject: GPencil Tools: Added mirror flip New button in menu to flip view horizontally within camera by inverting x-scale --- greasepencil_tools/__init__.py | 5 ++++- greasepencil_tools/draw_tools.py | 23 +++++++++++++++++++++++ greasepencil_tools/ui_panels.py | 15 ++++++++++++--- 3 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 greasepencil_tools/draw_tools.py diff --git a/greasepencil_tools/__init__.py b/greasepencil_tools/__init__.py index 0205362b..980493ed 100644 --- a/greasepencil_tools/__init__.py +++ b/greasepencil_tools/__init__.py @@ -4,7 +4,7 @@ bl_info = { "name": "Grease Pencil Tools", "description": "Extra tools for Grease Pencil", "author": "Samuel Bernou, Antonio Vazquez, Daniel Martinez Lara, Matias Mendiola", -"version": (1, 5, 7), +"version": (1, 6, 0), "blender": (2, 91, 0), "location": "Sidebar > Grease Pencil > Grease Pencil Tools", "warning": "", @@ -20,6 +20,7 @@ from . import (prefs, line_reshape, rotate_canvas, timeline_scrub, + draw_tools, import_brush_pack, ui_panels, ) @@ -32,6 +33,7 @@ def register(): box_deform.register() line_reshape.register() rotate_canvas.register() + draw_tools.register() import_brush_pack.register() ui_panels.register() @@ -43,6 +45,7 @@ def unregister(): return ui_panels.unregister() import_brush_pack.unregister() + draw_tools.unregister() rotate_canvas.unregister() box_deform.unregister() line_reshape.unregister() diff --git a/greasepencil_tools/draw_tools.py b/greasepencil_tools/draw_tools.py new file mode 100644 index 00000000..6d2cf3a9 --- /dev/null +++ b/greasepencil_tools/draw_tools.py @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +import bpy + +class GP_OT_camera_flip_x(bpy.types.Operator): + bl_idname = "gp.camera_flip_x" + bl_label = "Camera Flip X" + bl_description = "Invert active camera scale.x to flip view horizontally" + bl_options = {"REGISTER"} + + @classmethod + def poll(cls, context): + return context.space_data.region_3d.view_perspective == 'CAMERA' + + def execute(self, context): + context.scene.camera.scale.x *= -1 + return {"FINISHED"} + +def register(): + bpy.utils.register_class(GP_OT_camera_flip_x) + +def unregister(): + bpy.utils.unregister_class(GP_OT_camera_flip_x) diff --git a/greasepencil_tools/ui_panels.py b/greasepencil_tools/ui_panels.py index ff3900de..2e8a50f1 100644 --- a/greasepencil_tools/ui_panels.py +++ b/greasepencil_tools/ui_panels.py @@ -20,15 +20,24 @@ class GP_PT_sidebarPanel(bpy.types.Panel): layout.operator('gp.straight_stroke', icon ="CURVE_PATH")# IPO_LINEAR - # Expose Native view operators - # if context.scene.camera: + # Expose native view operators row = layout.row(align=True) - row.operator('view3d.zoom_camera_1_to_1', text = 'Zoom 1:1', icon = 'ZOOM_PREVIOUS')# FULLSCREEN_EXIT? + row.operator('view3d.zoom_camera_1_to_1', text = 'Zoom 1:1', icon = 'ZOOM_PREVIOUS') # FULLSCREEN_EXIT row.operator('view3d.view_center_camera', text = 'Zoom Fit', icon = 'FULLSCREEN_ENTER') + + # Rotation save/load row = layout.row(align=True) row.operator('view3d.rotate_canvas_reset', text = 'Reset Rotation', icon = 'FILE_REFRESH') row.operator('view3d.rotate_canvas_set', text = 'Save Rotation', icon = 'DRIVER_ROTATIONAL_DIFFERENCE') + # View flip + if context.scene.camera and context.scene.camera.scale.x < 0: + row = layout.row(align=True) + row.operator('gp.camera_flip_x', text = 'Camera Mirror Flip', icon = 'MOD_MIRROR') + row.label(text='', icon='LOOP_BACK') + else: + layout.operator('gp.camera_flip_x', text = 'Camera Mirror Flip', icon = 'MOD_MIRROR') + def menu_boxdeform_entry(self, context): """Transform shortcut to append in existing menu""" -- cgit v1.2.3 From 777653f7d6322a3a651c5ca95ba9f458165b9b48 Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Tue, 15 Mar 2022 19:59:41 +0900 Subject: VR: Fix camera rotation when added from landmark --- viewport_vr_preview/operators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/viewport_vr_preview/operators.py b/viewport_vr_preview/operators.py index 69100762..dc2dd4b9 100644 --- a/viewport_vr_preview/operators.py +++ b/viewport_vr_preview/operators.py @@ -189,7 +189,7 @@ class VIEW3D_OT_add_camera_from_vr_landmark(Operator): scene.collection.objects.link(new_cam) angle = lm.base_pose_angle new_cam.location = lm.base_pose_location - new_cam.rotation_euler = (math.pi, 0, angle) + new_cam.rotation_euler = (math.pi / 2, 0, angle) return {'FINISHED'} -- cgit v1.2.3