mirror of
https://github.com/kaosat-dev/Blender_bevy_components_workflow.git
synced 2024-12-22 15:44:11 +00:00
feat(Blender): a few QOL improvements to the workflow
* context menu + shortcut + operators to quickly edit /and/or create new blueprints in a seperate scene * boilerplate based on the awesome work by slyedoc * "one keyboard shortcut" workflow: edit if blueprint instance is selected, create if not, stop editing if editing is already in progress * updated list of contributors
This commit is contained in:
parent
7e71e30187
commit
566120d073
@ -112,6 +112,8 @@ Thanks to all the contributors helping out with this project ! Big kudos to you,
|
||||
* [killercup](https://github.com/killercup)
|
||||
* [janhohenheim](https://github.com/janhohenheim)
|
||||
* [BUGO07](https://github.com/BUGO07)
|
||||
* [ChristopherBiscardi](https://github.com/ChristopherBiscardi)
|
||||
* [slyedoc](https://github.com/slyedoc)
|
||||
|
||||
## License
|
||||
|
||||
|
@ -52,6 +52,7 @@ from .core.blenvy_manager import BlenvyManager
|
||||
from .core.operators import BLENVY_OT_configuration_switch, BLENVY_OT_tooling_switch
|
||||
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
|
||||
from .assets.assets_folder_browser import BLENVY_OT_assets_paths_browse
|
||||
|
||||
|
||||
@ -126,6 +127,11 @@ classes = [
|
||||
BlueprintsRegistry,
|
||||
BLENVY_OT_blueprint_select,
|
||||
BLENVY_PT_blueprints_panel,
|
||||
|
||||
BLENVY_OT_ui_blueprint_create,
|
||||
BLENVY_OT_ui_blueprint_edit_start,
|
||||
BLENVY_OT_ui_blueprint_edit_end,
|
||||
BLENVY_OT_ui_blueprint_create_or_edit
|
||||
]
|
||||
|
||||
|
||||
@ -139,40 +145,37 @@ def post_save(scene, depsgraph):
|
||||
|
||||
@persistent
|
||||
def post_load(file_name):
|
||||
print("POST LOAD")
|
||||
blenvy = bpy.context.window_manager.blenvy
|
||||
if blenvy is not None:
|
||||
blenvy.load_settings()
|
||||
|
||||
def init_keymaps():
|
||||
window_manager = bpy.context.window_manager
|
||||
if window_manager.keyconfigs.addon:
|
||||
km = window_manager.keyconfigs.addon.keymaps.new(name='3D View', space_type='VIEW_3D')
|
||||
kmi = [
|
||||
km.keymap_items.new(BLENVY_OT_ui_blueprint_create_or_edit.bl_idname, "F", "PRESS", shift=True),
|
||||
]
|
||||
return km, kmi
|
||||
|
||||
addon_keymaps = []
|
||||
|
||||
def register():
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
bpy.app.handlers.load_post.append(post_load)
|
||||
# for some reason, adding these directly to the tracker class in register() do not work reliably
|
||||
bpy.app.handlers.depsgraph_update_post.append(post_update)
|
||||
bpy.app.handlers.save_post.append(post_save)
|
||||
|
||||
bpy.types.VIEW3D_MT_object.append(edit_or_create_blueprint_menu)
|
||||
bpy.types.VIEW3D_MT_object_context_menu.append(edit_or_create_blueprint_menu)
|
||||
|
||||
""" handle = object()
|
||||
|
||||
subscribe_to = bpy.types.Scene, "name" #
|
||||
|
||||
def notify_test(context):
|
||||
#if (context.scene.type == 'MESH'):
|
||||
print("Renamed", dir(context), context.scenes)
|
||||
|
||||
bpy.msgbus.subscribe_rna(
|
||||
key=subscribe_to,
|
||||
owner=bpy,
|
||||
args=(bpy.context,),
|
||||
notify=notify_test,
|
||||
)"""
|
||||
|
||||
|
||||
#bpy.msgbus.publish_rna(key=subscribe_to)
|
||||
|
||||
|
||||
if not bpy.app.background:
|
||||
km, kmi = init_keymaps()
|
||||
for k in kmi:
|
||||
k.active = True
|
||||
addon_keymaps.append((km, k))
|
||||
|
||||
|
||||
def unregister():
|
||||
@ -181,3 +184,12 @@ def unregister():
|
||||
bpy.app.handlers.load_post.remove(post_load)
|
||||
bpy.app.handlers.depsgraph_update_post.remove(post_update)
|
||||
bpy.app.handlers.save_post.remove(post_save)
|
||||
|
||||
|
||||
for km, kmi in addon_keymaps:
|
||||
km.keymap_items.remove(kmi)
|
||||
addon_keymaps.clear()
|
||||
|
||||
|
||||
bpy.types.VIEW3D_MT_object.remove(edit_or_create_blueprint_menu)
|
||||
bpy.types.VIEW3D_MT_object_context_menu.remove(edit_or_create_blueprint_menu)
|
||||
|
@ -34,7 +34,6 @@ def get_object_scene(object):
|
||||
return scenes_of_object[0]
|
||||
return None
|
||||
|
||||
|
||||
def get_mesh_object(mesh):
|
||||
for object in bpy.data.objects:
|
||||
if isinstance(object.data, bpy.types.Mesh) and mesh.name == object.data.name:
|
||||
@ -69,6 +68,12 @@ class BLENVY_OT_item_select(Operator):
|
||||
description="target to select's name ",
|
||||
) # type: ignore
|
||||
|
||||
override_scene_name: StringProperty(
|
||||
name="override scene name",
|
||||
description="use this to override the scene selection mecanism",
|
||||
default=""
|
||||
) # type: ignore
|
||||
|
||||
|
||||
@classmethod
|
||||
def register(cls):
|
||||
@ -96,8 +101,9 @@ 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)
|
||||
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:
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
bpy.context.window.scene = scene_of_collection
|
||||
@ -105,7 +111,7 @@ class BLENVY_OT_item_select(Operator):
|
||||
context.window_manager.blenvy_item_selected_ids = json.dumps({"name": collection.name, "type": self.item_type})
|
||||
set_active_collection(bpy.context.window.scene, collection.name)
|
||||
|
||||
select_area(context=context, area_name="COLLECTION")
|
||||
#select_area(context=context, area_name="COLLECTION")
|
||||
|
||||
elif self.item_type == 'MESH':
|
||||
mesh = bpy.data.meshes[self.target_name]
|
||||
|
@ -137,6 +137,28 @@ class BlenvyManager(PropertyGroup):
|
||||
get=lambda self: os.path.abspath(os.path.join(os.path.dirname(bpy.data.filepath), self.project_root_path, self.assets_path, self.animations_path))
|
||||
) # type: ignore
|
||||
|
||||
# Edit blueprint settings
|
||||
edit_blueprint_previous_scene: StringProperty(
|
||||
name="edit_blueprint_previous_scene",
|
||||
description="name of the scene we started from for editing the current Blueprint",
|
||||
default="",
|
||||
update=save_settings
|
||||
)# type: ignore
|
||||
|
||||
edit_blueprint_current_scene: StringProperty(
|
||||
name="edit_blueprint_current_scene",
|
||||
description="name of the scene where we are currently editing a Blueprint in",
|
||||
default="",
|
||||
update=save_settings
|
||||
)# type: ignore
|
||||
|
||||
edit_blueprint_previous_mode: StringProperty(
|
||||
name="edit_blueprint_previous_mode",
|
||||
description="previous blenvy mode before starting editing the current blueprint",
|
||||
default="",
|
||||
update=save_settings
|
||||
)# type: ignore
|
||||
|
||||
# sub ones
|
||||
auto_export: PointerProperty(type=AutoExportSettings) # type: ignore
|
||||
components: PointerProperty(type=ComponentsSettings) # type: ignore
|
||||
|
@ -17,7 +17,10 @@ def recurLayerCollection(layerColl, collName):
|
||||
return found
|
||||
|
||||
def set_active_collection(scene, collection_name):
|
||||
layer_collection = bpy.data.scenes[scene.name].view_layers['ViewLayer'].layer_collection
|
||||
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
|
||||
|
225
tools/blenvy/core/ui/menus_and_shortcuts.py
Normal file
225
tools/blenvy/core/ui/menus_and_shortcuts.py
Normal file
@ -0,0 +1,225 @@
|
||||
import bpy
|
||||
import json
|
||||
from mathutils import Vector
|
||||
|
||||
from ..helpers_collections import set_active_collection
|
||||
from ..blenvy_manager import BlenvyManager
|
||||
|
||||
""" This file contains quality of life operators/menus/shortcuts to make working with blueprints more pleasant
|
||||
* based on the excellent work by slyedoc: https://github.com/slyedoc/bevy_sly_blender/tree/4223cc0ff86255f82bb555ffc8eddf65e91aa636
|
||||
|
||||
- [ ] detect editing in progress
|
||||
- [x] select collection instead of objects
|
||||
- [ ] if current scene (before edit)
|
||||
- is library: do not create instance
|
||||
- is main scene: create instance
|
||||
- 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
|
||||
- [ ] 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)
|
||||
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)
|
||||
else:
|
||||
self.layout.operator(BLENVY_OT_ui_blueprint_create.bl_idname)
|
||||
|
||||
|
||||
class BLENVY_OT_ui_blueprint_create_or_edit(bpy.types.Operator):
|
||||
"""Create 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:
|
||||
bpy.ops.window_manager.blenvy_edit_blueprinrt()
|
||||
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:
|
||||
bpy.ops.window_manager.blenvy_exit_edit_blueprint()
|
||||
else:
|
||||
bpy.ops.window_manager.blenvy_create_blueprint()
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
class BLENVY_OT_ui_blueprint_create(bpy.types.Operator):
|
||||
"""Create Blueprint in a new Scene"""
|
||||
bl_idname = "window_manager.blenvy_create_blueprint"
|
||||
bl_label = "Create Blueprint"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
blenvy = context.window_manager.blenvy # type: BlenvyManager
|
||||
|
||||
# Store original Blenvy setting
|
||||
blenvy.edit_blueprint_previous_scene = bpy.context.scene.name
|
||||
blenvy.edit_blueprint_previous_mode = blenvy.mode
|
||||
|
||||
# set mode to components
|
||||
blenvy.mode = "COMPONENTS"
|
||||
|
||||
blueprint_name = "new_blueprint"
|
||||
collection = bpy.data.collections.new(blueprint_name)
|
||||
|
||||
library_scene_name = "Library"
|
||||
if len(blenvy.library_scenes_names) > 0:
|
||||
library_scene_name = blenvy.library_scenes_names[0]
|
||||
else:
|
||||
bpy.data.scenes.new(library_scene_name)
|
||||
# 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
|
||||
source_collection = collection
|
||||
instance_obj = bpy.data.objects.new(
|
||||
name=collection.name,
|
||||
object_data=None
|
||||
)
|
||||
instance_obj.instance_collection = source_collection
|
||||
instance_obj.instance_type = 'COLLECTION'
|
||||
parent_collection = bpy.context.view_layer.active_layer_collection
|
||||
parent_collection.collection.objects.link(instance_obj)
|
||||
|
||||
# now open the temporary scene
|
||||
scene_name = f"temp:{blueprint_name}"
|
||||
bpy.ops.scene.new(type="EMPTY")
|
||||
new_scene = bpy.context.scene
|
||||
new_scene.name = scene_name
|
||||
bpy.context.window.scene = new_scene
|
||||
|
||||
|
||||
new_scene.collection.children.link(collection)
|
||||
|
||||
# deselect all objects then select the first object in new scene
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
|
||||
# zoom to selected
|
||||
bpy.ops.view3d.view_selected()
|
||||
# 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)
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
class BLENVY_OT_ui_blueprint_edit_start(bpy.types.Operator):
|
||||
"""Edit the Blueprint referenced by this Blueprint Instance in a new Scene"""
|
||||
bl_idname = "window_manager.blenvy_edit_blueprinrt"
|
||||
bl_label = "Start Editing Blueprint"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
blenvy = context.window_manager.blenvy # type: BlenvyManager
|
||||
collection = bpy.context.active_object.instance_collection
|
||||
|
||||
# Store original Blenvy setting
|
||||
blenvy.edit_blueprint_previous_scene = bpy.context.scene.name
|
||||
blenvy.edit_blueprint_previous_mode = blenvy.mode
|
||||
|
||||
# set mode to components
|
||||
blenvy.mode = "COMPONENTS"
|
||||
|
||||
if not collection:
|
||||
print("Active item is not a Blueprint/Collection instance")
|
||||
self.report({"WARNING"}, "Active item is not a Blueprint/Collection instance")
|
||||
return {"CANCELLED"}
|
||||
|
||||
scene_name = f"temp:{collection.name}"
|
||||
bpy.ops.scene.new(type="EMPTY")
|
||||
new_scene = bpy.context.scene
|
||||
new_scene.name = scene_name
|
||||
bpy.context.window.scene = new_scene
|
||||
new_scene.collection.children.link(collection)
|
||||
|
||||
# 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()
|
||||
|
||||
# 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)
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
class BLENVY_OT_ui_blueprint_edit_end(bpy.types.Operator):
|
||||
bl_idname = "window_manager.blenvy_exit_edit_blueprint"
|
||||
bl_label = "Done editing blueprint"
|
||||
bl_options = {"UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
blenvy = context.window_manager.blenvy # type: BlenvyManager
|
||||
# = context.window_manager.bevy # type: BlenvyManager
|
||||
|
||||
current_scene = bpy.context.scene
|
||||
prev_scene = bpy.data.scenes.get(blenvy.edit_blueprint_previous_scene)
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
if prev_scene is None:
|
||||
print("No scene to return to")
|
||||
return {'CANCELLED'}
|
||||
|
||||
if current_scene.name.startswith("temp:"):
|
||||
bpy.data.scenes.remove(bpy.context.scene)
|
||||
bpy.context.window.scene = prev_scene
|
||||
else:
|
||||
#if
|
||||
print("Not in temp scene")
|
||||
return {'CANCELLED'}
|
||||
|
||||
return {'FINISHED'}
|
||||
|
Loading…
Reference in New Issue
Block a user