From 48ce69086097d90dbc98f98b7197a0a286bac39c Mon Sep 17 00:00:00 2001 From: "kaosat.dev" Date: Wed, 14 Aug 2024 14:34:12 +0200 Subject: [PATCH] feat(Blender): * added ability to clear/reset settings to default * a lot of improvements to QOL ui features * fixed issue with editing scenes being tagged as Level scenes * fixed mode switching issues * added save & reset of camera settings * improved selection logic * various minor tweaks * better naming & cleanups --- tools/blenvy/__init__.py | 3 +- tools/blenvy/add_ons/auto_export/settings.py | 14 +- .../add_ons/bevy_components/settings.py | 11 +- tools/blenvy/add_ons/bevy_components/utils.py | 1 - tools/blenvy/core/blenvy_manager.py | 34 +++- tools/blenvy/core/helpers_collections.py | 3 - tools/blenvy/core/operators.py | 21 ++- tools/blenvy/core/ui/menus_and_shortcuts.py | 168 ++++++++---------- tools/blenvy/core/ui/ui.py | 7 +- tools/blenvy/settings.py | 8 + 10 files changed, 160 insertions(+), 110 deletions(-) diff --git a/tools/blenvy/__init__.py b/tools/blenvy/__init__.py index 63449d8..6c6abad 100644 --- a/tools/blenvy/__init__.py +++ b/tools/blenvy/__init__.py @@ -49,7 +49,7 @@ from .blueprints.operators import BLENVY_OT_blueprint_select # blenvy core from .core.blenvy_manager import BlenvyManager -from .core.operators import BLENVY_OT_configuration_switch, BLENVY_OT_tooling_switch +from .core.operators import BLENVY_OT_tooling_switch, BLENVY_OT_configuration_switch, BLENVY_OT_configuration_reset from .core.ui.ui import (BLENVY_PT_SidePanel) from .core.ui.scenes_list import BLENVY_OT_scenes_list_actions from .core.ui.menus_and_shortcuts import BLENVY_OT_ui_blueprint_create, BLENVY_OT_ui_blueprint_edit_start, BLENVY_OT_ui_blueprint_edit_end, BLENVY_OT_ui_blueprint_create_or_edit, edit_or_create_blueprint_menu @@ -112,6 +112,7 @@ classes = [ BlenvyManager, BLENVY_OT_tooling_switch, BLENVY_OT_configuration_switch, + BLENVY_OT_configuration_reset, Asset, AssetsRegistry, diff --git a/tools/blenvy/add_ons/auto_export/settings.py b/tools/blenvy/add_ons/auto_export/settings.py index 42c9070..80abc12 100644 --- a/tools/blenvy/add_ons/auto_export/settings.py +++ b/tools/blenvy/add_ons/auto_export/settings.py @@ -1,7 +1,7 @@ import bpy from bpy_types import (PropertyGroup) -from bpy.props import (EnumProperty, PointerProperty, StringProperty, BoolProperty, CollectionProperty, IntProperty) -from ...settings import load_settings, upsert_settings, generate_complete_settings_dict +from bpy.props import (EnumProperty, BoolProperty) +from ...settings import load_settings, upsert_settings, generate_complete_settings_dict, clear_settings # list of settings we do NOT want to save settings_black_list = ['settings_save_enabled', 'dry_run'] @@ -128,3 +128,13 @@ class AutoExportSettings(PropertyGroup): self.settings_save_enabled = True + def reset_settings(self): + for property_name in self.bl_rna.properties.keys(): + if property_name not in ["name", "rna_type"]: + self.property_unset(property_name) + + # clear the stored settings + clear_settings(".blenvy_export_settings") + clear_settings(".blenvy_export_settings_previous") + clear_settings(".blenvy_gltf_settings_previous") + clear_settings(".blenvy.project_serialized_previous") diff --git a/tools/blenvy/add_ons/bevy_components/settings.py b/tools/blenvy/add_ons/bevy_components/settings.py index c698425..d007ed6 100644 --- a/tools/blenvy/add_ons/bevy_components/settings.py +++ b/tools/blenvy/add_ons/bevy_components/settings.py @@ -1,8 +1,8 @@ import os import bpy from bpy_types import (PropertyGroup) -from bpy.props import (EnumProperty, PointerProperty, StringProperty, BoolProperty, CollectionProperty, FloatProperty) -from ...settings import load_settings, upsert_settings, generate_complete_settings_dict +from bpy.props import (StringProperty, BoolProperty, FloatProperty) +from ...settings import load_settings, upsert_settings, generate_complete_settings_dict, clear_settings from .propGroups.prop_groups import generate_propertyGroups_for_components from .components.metadata import ensure_metadata_for_all_items @@ -154,3 +154,10 @@ class ComponentsSettings(PropertyGroup): except:pass self.settings_save_enabled = True + + def reset_settings(self): + for property_name in self.bl_rna.properties.keys(): + if property_name not in ["name", "rna_type"]: + self.property_unset(property_name) + # clear the stored settings + clear_settings(".blenvy_components_settings") diff --git a/tools/blenvy/add_ons/bevy_components/utils.py b/tools/blenvy/add_ons/bevy_components/utils.py index ff2be3d..12b659c 100644 --- a/tools/blenvy/add_ons/bevy_components/utils.py +++ b/tools/blenvy/add_ons/bevy_components/utils.py @@ -101,7 +101,6 @@ class BLENVY_OT_item_select(Operator): select_area(context=context, area_name="OBJECT") elif self.item_type == 'COLLECTION': - print("selecting collection") collection = bpy.data.collections[self.target_name] scene_of_collection = get_collection_scene(collection) if self.override_scene_name == "" else bpy.data.scenes.get(self.override_scene_name, None) if scene_of_collection is not None: diff --git a/tools/blenvy/core/blenvy_manager.py b/tools/blenvy/core/blenvy_manager.py index 62db0bf..49aa1c5 100644 --- a/tools/blenvy/core/blenvy_manager.py +++ b/tools/blenvy/core/blenvy_manager.py @@ -1,8 +1,8 @@ import os import bpy from bpy_types import (PropertyGroup) -from bpy.props import (BoolProperty, EnumProperty, PointerProperty, StringProperty, CollectionProperty, IntProperty) -from ..settings import upsert_settings, load_settings, generate_complete_settings_dict +from bpy.props import (BoolProperty, EnumProperty, PointerProperty, StringProperty, CollectionProperty, IntProperty, FloatProperty, FloatVectorProperty) +from ..settings import upsert_settings, load_settings, generate_complete_settings_dict, clear_settings from ..add_ons.auto_export.settings import AutoExportSettings from ..add_ons.bevy_components.settings import ComponentsSettings @@ -154,11 +154,22 @@ class BlenvyManager(PropertyGroup): edit_blueprint_previous_mode: StringProperty( name="edit_blueprint_previous_mode", - description="previous blenvy mode before starting editing the current blueprint", + description="previous blenvy mode before starting editing the current Blueprint", default="", update=save_settings )# type: ignore + edit_blueprint_previous_view_distance: FloatProperty( + name="edit_blueprint_previous_view_distance", + description="previous view distance before focusing in on the specific Blueprint to edit", + default=0.0, + )# type: ignore + + edit_blueprint_previous_view_position: FloatVectorProperty( + name="edit_blueprint_previous_view_position", + description="previous view position before focusing in on the specific Blueprint to edit", + )# type: ignore + # sub ones auto_export: PointerProperty(type=AutoExportSettings) # type: ignore components: PointerProperty(type=ComponentsSettings) # type: ignore @@ -195,7 +206,7 @@ class BlenvyManager(PropertyGroup): ('Level', 'Level','Level scene'), ('Library', 'Library', 'Library scene'), ), - default='None' + default='None' ) @classmethod @@ -228,3 +239,18 @@ class BlenvyManager(PropertyGroup): for scene in bpy.data.scenes: self.scenes_to_scene_names[scene] = scene.name + + def reset_settings(self): + for property_name in self.bl_rna.properties.keys(): + if property_name not in ["name", "rna_type"]: + self.property_unset(property_name) + + # now reset auto_export settings + self.auto_export.reset_settings() + + # now reset component settings + self.components.reset_settings() + + # clear the stored settings + clear_settings(".blenvy_common_settings") + clear_settings(".blenvy_common_settings_previous") diff --git a/tools/blenvy/core/helpers_collections.py b/tools/blenvy/core/helpers_collections.py index ef4d7e5..82e50ad 100644 --- a/tools/blenvy/core/helpers_collections.py +++ b/tools/blenvy/core/helpers_collections.py @@ -17,10 +17,7 @@ def recurLayerCollection(layerColl, collName): return found def set_active_collection(scene, collection_name): - print("set active collection", scene, collection_name) layer_collection = scene.view_layers['ViewLayer'].layer_collection layerColl = recurLayerCollection(layer_collection, collection_name) - - print("layerColl", layerColl) # set active collection to the collection bpy.context.view_layer.active_layer_collection = layerColl diff --git a/tools/blenvy/core/operators.py b/tools/blenvy/core/operators.py index ad545b8..4a8f224 100644 --- a/tools/blenvy/core/operators.py +++ b/tools/blenvy/core/operators.py @@ -1,6 +1,8 @@ from bpy_types import (Operator) from bpy.props import (EnumProperty) +from ..settings import clear_settings + class BLENVY_OT_tooling_switch(Operator): """Switch blenvy tooling""" bl_idname = "bevy.tooling_switch" @@ -26,8 +28,6 @@ class BLENVY_OT_tooling_switch(Operator): context.window_manager.blenvy.mode = self.tool return {'FINISHED'} - - class BLENVY_OT_configuration_switch(Operator): """Switch tooling configuration""" bl_idname = "bevy.config_switch" @@ -49,4 +49,21 @@ class BLENVY_OT_configuration_switch(Operator): def execute(self, context): context.window_manager.blenvy.config_mode = self.tool return {'FINISHED'} + + +class BLENVY_OT_configuration_reset(Operator): + """Reset all blenvy settings to default""" + bl_idname = "bevy.config_reset" + bl_label = "Clear stored setting & reset configuration to default" + #bl_options = {} + + def execute(self, context): + print("reset configuration") + blenvy = context.window_manager.blenvy + try: + blenvy.reset_settings() + except Exception as error: + self.report({"ERROR"}, f"Failed to reset settings: {error}") + return {"CANCELLED"} + return {'FINISHED'} \ No newline at end of file diff --git a/tools/blenvy/core/ui/menus_and_shortcuts.py b/tools/blenvy/core/ui/menus_and_shortcuts.py index 8cfb3af..33d02c0 100644 --- a/tools/blenvy/core/ui/menus_and_shortcuts.py +++ b/tools/blenvy/core/ui/menus_and_shortcuts.py @@ -16,69 +16,92 @@ from ..blenvy_manager import BlenvyManager - or alternative: sub menu to choose instance creation or not - [x] save & restore blenvy mode - [x] add a contextual shortcut to easilly jump in/out of editing mode -- [ ] if editing is already in progress close it first - - [ ] do this for the wrapper +- [x] if editing is already in progress close it first + - [x] do this for the wrapper - [x] do this for the shortcut - - [ ] move the "close first" logic to inside create & edit operators + - [x] move the "close first" logic to inside create & edit operators - [ ] also allow triggering editing from library scene with collection selected => requires checking if collection is a blueprint's collection - [x] do not go in create mode if there is an object (not a collection !) selected -- [ ] save & reset camera +- [x] save & reset camera """ def edit_or_create_blueprint_menu(self, context): - if bpy.context.active_object and bpy.context.active_object.instance_collection: - self.layout.operator(BLENVY_OT_ui_blueprint_edit_start.bl_idname) + blenvy = context.window_manager.blenvy # type: BlenvyManager + selected_objects = context.selected_objects + selected_object = selected_objects[0] if len(selected_objects) > 0 else None + text = "" + if selected_object is not None and selected_object.instance_collection: + if blenvy.edit_blueprint_current_scene != "": # if there is already editing in progress, close it first + text = "Exit editing previous Blueprint and start editing Blueprint" + else: + text = "Start editing Blueprint" else: - blenvy = context.window_manager.blenvy # type: BlenvyManager prev_scene = bpy.data.scenes.get(blenvy.edit_blueprint_previous_scene) if prev_scene is not None: - self.layout.operator(BLENVY_OT_ui_blueprint_edit_end.bl_idname) + text = "Exit editing Blueprint" else: - self.layout.operator(BLENVY_OT_ui_blueprint_create.bl_idname) - + if len(selected_objects) == 0: # do not go into creation mode if any object was selected + if blenvy.edit_blueprint_current_scene != "": # if there is already editing in progress, close it first + text = "Exit editing previous Blueprint and start editing new Blueprint" + else: + text = "Create & start editing Blueprint" + + self.layout.operator(BLENVY_OT_ui_blueprint_create_or_edit.bl_idname, text=text) + +# for camera save & reset +def find_area(): + try: + for a in bpy.data.window_managers[0].windows[0].screen.areas: + if a.type == "VIEW_3D": + return a + return None + except: + return None + def find_viewport_camera(): - def find_area(): - try: - for a in bpy.data.window_managers[0].windows[0].screen.areas: - if a.type == "VIEW_3D": - return a - return None - except: - return None - area = find_area() - if area is None: - print("area not find") + return None else: # print(dir(area)) - r3d = area.spaces[0].region_3d - view_mat = r3d.view_matrix - print("view matrix: ", view_mat) + region_3D = area.spaces[0].region_3d + view_mat = region_3D.view_matrix loc, rot, sca = view_mat.decompose() - print("location xyz: ", loc) + """print("location xyz: ", loc) print("rotation wxyz: ", rot) print("scale xyz: ", sca) print("") - print("view_distance: ", r3d.view_distance) - print("view_location: ", r3d.view_location) - print("view_rotation: ", r3d.view_rotation) - print("view_camera_zoom: ", r3d.view_camera_zoom) - print("view_distance: ", r3d.view_distance) - print("view_camera_offset: ", r3d.view_camera_offset) + print("view_distance: ", region_3D.view_distance) + print("view_location: ", region_3D.view_location) + print("view_rotation: ", region_3D.view_rotation) + print("view_camera_zoom: ", region_3D.view_camera_zoom) + print("view_camera_offset: ", region_3D.view_camera_offset)""" + return (region_3D.view_distance, region_3D.view_location) +def set_viewport_camera(view_distance, view_location): + area = find_area() + if area is None or view_distance == 0.0: + return None + else: + region_3D = area.spaces[0].region_3d + region_3D.view_distance = view_distance + region_3D.view_location = view_location + class BLENVY_OT_ui_blueprint_create_or_edit(bpy.types.Operator): - """Create Blueprint in a new Scene""" + """Create/Edit start/stop Blueprint in a new Scene""" bl_idname = "window_manager.blenvy_blueprint_shortcut" bl_label = "Edit Blueprint" bl_options = {"REGISTER", "UNDO"} def execute(self, context): blenvy = context.window_manager.blenvy # type: BlenvyManager - if bpy.context.active_object and bpy.context.active_object.instance_collection: + selected_objects = context.selected_objects + selected_object = selected_objects[0] if len(selected_objects) > 0 else None + + if selected_object is not None and selected_object.instance_collection: if blenvy.edit_blueprint_current_scene != "": # if there is already editing in progress, close it first bpy.ops.window_manager.blenvy_exit_edit_blueprint() bpy.ops.window_manager.blenvy_blueprint_edit_start() @@ -88,7 +111,7 @@ class BLENVY_OT_ui_blueprint_create_or_edit(bpy.types.Operator): if prev_scene is not None: bpy.ops.window_manager.blenvy_exit_edit_blueprint() else: - if len(context.selected_objects) == 0: # do not go into creation mode if any object was selected + if len(selected_objects) == 0: # do not go into creation mode if any object was selected if blenvy.edit_blueprint_current_scene != "": # if there is already editing in progress, close it first bpy.ops.window_manager.blenvy_exit_edit_blueprint() bpy.ops.window_manager.blenvy_create_blueprint() @@ -118,12 +141,11 @@ class BLENVY_OT_ui_blueprint_create(bpy.types.Operator): if len(blenvy.library_scenes_names) > 0: library_scene_name = blenvy.library_scenes_names[0] else: - bpy.data.scenes.new(library_scene_name) + scene = bpy.data.scenes.new(library_scene_name) + scene.blenvy_scene_type = "Library" # automatically add it to the library : find library scene, if any, if not, create it bpy.data.scenes[library_scene_name].collection.children.link(collection) - - # create an instance of the blueprint ONLY the current scene we are in is a level scene if context.scene.blenvy_scene_type == 'Level': @@ -143,6 +165,7 @@ class BLENVY_OT_ui_blueprint_create(bpy.types.Operator): new_scene = bpy.context.scene new_scene.name = scene_name bpy.context.window.scene = new_scene + new_scene.blenvy_scene_type = 'None' new_scene.collection.children.link(collection) @@ -187,59 +210,21 @@ class BLENVY_OT_ui_blueprint_edit_start(bpy.types.Operator): new_scene.name = scene_name bpy.context.window.scene = new_scene new_scene.collection.children.link(collection) - + new_scene.blenvy_scene_type = 'None' # flag the current temporary scene as an active edit blenvy.edit_blueprint_current_scene = scene_name - - - # Assuming you want to focus on the objects from the linked collection - # Switch to the new scene context - - """blenvy.edit_collection_world_texture = "checker" - if blenvy.edit_collection_world_texture != "none": - world = bpy.data.worlds.new(bpy.context.scene.name) - new_scene.world = world - world.use_nodes = True - tree = world.node_tree - - if blenvy.edit_collection_world_texture in ["checker", "checker_view"]: - checker_texture = tree.nodes.new("ShaderNodeTexChecker") - checker_texture.inputs["Scale"].default_value = 20 - checker_texture.location = Vector((-250, 0)) - if blenvy.edit_collection_world_texture == "checker_view": - coord = tree.nodes.new("ShaderNodeTexCoord") - coord.location = Vector((-500, 0)) - for op in coord.outputs: - op.hide = True - tree.links.new(coord.outputs["Window"], checker_texture.inputs["Vector"]) - tree.links.new(checker_texture.outputs["Color"], tree.nodes["Background"].inputs["Color"]) - elif blenvy.edit_collection_world_texture == "gray": - tree.nodes["Background"].inputs["Color"].default_value = (.3, .3, .3, 1)""" - + # deselect all objects then select the first object in new scene bpy.ops.object.select_all(action='DESELECT') - # find the root object - if len(collection.objects) > 0 : - root_obj = collection.objects[0] - while root_obj.parent: - root_obj = root_obj.parent - - # select object and children - new_scene.objects[root_obj.name].select_set(True) - # def select_children(parent): - # for child in parent.children: - # child.select_set(True) - # select_children(child) # Recursively select further descendants - # select_children(root_obj); - - # Select the view layer and view the selected objects - bpy.context.view_layer.objects.active = new_scene.objects[root_obj.name] - bpy.context.view_layer.active_layer_collection = bpy.context.view_layer.layer_collection.children[collection.name] - - # zoom to selected - bpy.ops.view3d.view_selected() - + # backup view distance + view_distance, view_location = find_viewport_camera() + if view_distance is not None: + blenvy.edit_blueprint_previous_view_distance = view_distance + blenvy.edit_blueprint_previous_view_position = view_location + # focus on objects in the temp scene + bpy.ops.view3d.view_all() + # now that the 3d view has been adapted, we select what we actually need: the collection/blueprint bpy.ops.blenvy.select_item(target_name=collection.name, item_type="COLLECTION", override_scene_name=scene_name) @@ -260,10 +245,10 @@ class BLENVY_OT_ui_blueprint_edit_end(bpy.types.Operator): # we are done editing the blueprint, reset settings to the way they were before blenvy.edit_blueprint_previous_scene = "" blenvy.mode = blenvy.edit_blueprint_previous_mode - + set_viewport_camera(blenvy.edit_blueprint_previous_view_distance, blenvy.edit_blueprint_previous_view_position) if prev_scene is None: - print("No scene to return to") + self.report({"WARNING"}, "No scene to return to") return {'CANCELLED'} if blenvy.edit_blueprint_current_scene != "": active_edit_scene = bpy.data.scenes.get(blenvy.edit_blueprint_current_scene, None) @@ -278,17 +263,12 @@ class BLENVY_OT_ui_blueprint_edit_end(bpy.types.Operator): bpy.context.window.scene = prev_scene except: bpy.context.window.scene = prev_scene - """if current_scene.name.startswith("EDITING:"): - bpy.data.scenes.remove(bpy.context.scene) - bpy.context.window.scene = prev_scene""" - blenvy.edit_blueprint_current_scene = "" + blenvy.edit_blueprint_current_scene = "" else: blenvy.edit_blueprint_current_scene = "" - print("Not in temp scene") + self.report({"WARNING"}, "Not currently in Blueprint editing scene") return {'CANCELLED'} - - return {'FINISHED'} diff --git a/tools/blenvy/core/ui/ui.py b/tools/blenvy/core/ui/ui.py index 5bf36eb..830f78d 100644 --- a/tools/blenvy/core/ui/ui.py +++ b/tools/blenvy/core/ui/ui.py @@ -182,4 +182,9 @@ def draw_common_settings_ui(layout, settings): remove_operator.action = 'REMOVE' remove_operator.scene_type = 'LEVEL' remove_operator.scene_name = scene.name - col.separator() \ No newline at end of file + col.separator() + + # reset settings + row = section.row() + row.label(text="Reset settings") + row.operator("bevy.config_reset") \ No newline at end of file diff --git a/tools/blenvy/settings.py b/tools/blenvy/settings.py index 87ac93e..ed06363 100644 --- a/tools/blenvy/settings.py +++ b/tools/blenvy/settings.py @@ -25,6 +25,14 @@ def load_settings(name): return None return None +def clear_settings(name): + texts = bpy.data.texts + stored_settings = texts.get(name, None) + if stored_settings is not None: + stored_settings.clear() + texts.remove(texts[name], do_unlink=True) + + # given the input (actual) settings, filters out any invalid/useless params & params that are equal to defaults def generate_complete_settings_dict(settings, presets, ignore_list=[], preset_defaults=True):