feat(Blenvy): added support for Bevy components to meshes & materials

* added a much improved "get current selection in outliner" helper function
 * added MESH and MATERIAL variants to the type enums
 * added metadata to both Mesh & Material types
 * various related tweaks & improvements
This commit is contained in:
kaosat.dev 2024-06-21 12:15:41 +02:00
parent dafd408bd1
commit c162c33575
7 changed files with 70 additions and 54 deletions

View File

@ -42,6 +42,8 @@ class BLENVY_OT_component_list_actions(Operator):
items=( items=(
('OBJECT', "Object", ""), ('OBJECT', "Object", ""),
('COLLECTION', "Collection", ""), ('COLLECTION', "Collection", ""),
('MESH', "Mesh", ""),
('MATERIAL', "Material", ""),
), ),
default="OBJECT" default="OBJECT"
) # type: ignore ) # type: ignore

View File

@ -37,6 +37,8 @@ class BLENVY_OT_component_map_actions(Operator):
items=( items=(
('OBJECT', "Object", ""), ('OBJECT', "Object", ""),
('COLLECTION', "Collection", ""), ('COLLECTION', "Collection", ""),
('MESH', "Mesh", ""),
('MATERIAL', "Material", ""),
), ),
default="OBJECT" default="OBJECT"
) # type: ignore ) # type: ignore

View File

@ -61,14 +61,18 @@ class ComponentsMeta(PropertyGroup):
@classmethod @classmethod
def register(cls): def register(cls):
# you can add components to both objects & collections # you can add components to both objects , collections , meshes & materials
bpy.types.Object.components_meta = PointerProperty(type=ComponentsMeta) bpy.types.Object.components_meta = PointerProperty(type=ComponentsMeta)
bpy.types.Collection.components_meta = PointerProperty(type=ComponentsMeta) bpy.types.Collection.components_meta = PointerProperty(type=ComponentsMeta)
bpy.types.Mesh.components_meta = PointerProperty(type=ComponentsMeta)
bpy.types.Material.components_meta = PointerProperty(type=ComponentsMeta)
@classmethod @classmethod
def unregister(cls): def unregister(cls):
del bpy.types.Object.components_meta del bpy.types.Object.components_meta
del bpy.types.Collection.components_meta del bpy.types.Collection.components_meta
del bpy.types.Mesh.components_meta
del bpy.types.Material.components_meta
# remove no longer valid metadata from item # remove no longer valid metadata from item
def cleanup_invalid_metadata(item): def cleanup_invalid_metadata(item):

View File

@ -6,7 +6,7 @@ from bpy.props import (StringProperty, EnumProperty)
from .metadata import add_component_from_custom_property, add_component_to_item, apply_customProperty_values_to_item_propertyGroups, apply_propertyGroup_values_to_item_customProperties, apply_propertyGroup_values_to_item_customProperties_for_component, copy_propertyGroup_values_to_another_item, get_bevy_component_value_by_long_name, get_bevy_components, is_bevy_component_in_item, remove_component_from_item, rename_component, toggle_component from .metadata import add_component_from_custom_property, add_component_to_item, apply_customProperty_values_to_item_propertyGroups, apply_propertyGroup_values_to_item_customProperties, apply_propertyGroup_values_to_item_customProperties_for_component, copy_propertyGroup_values_to_another_item, get_bevy_component_value_by_long_name, get_bevy_components, is_bevy_component_in_item, remove_component_from_item, rename_component, toggle_component
from ..utils import get_selected_object_or_collection from ..utils import get_item_by_type, get_selected_item
class BLENVY_OT_component_add(Operator): class BLENVY_OT_component_add(Operator):
"""Add Bevy component to object/collection""" """Add Bevy component to object/collection"""
@ -20,7 +20,7 @@ class BLENVY_OT_component_add(Operator):
) # type: ignore ) # type: ignore
def execute(self, context): def execute(self, context):
target = get_selected_object_or_collection(context) target = get_selected_item(context)
print("adding component ", self.component_type, "to target '"+target.name+"'") print("adding component ", self.component_type, "to target '"+target.name+"'")
has_component_type = self.component_type != "" has_component_type = self.component_type != ""
@ -53,6 +53,8 @@ class BLENVY_OT_component_copy(Operator):
items=( items=(
('OBJECT', "Object", ""), ('OBJECT', "Object", ""),
('COLLECTION', "Collection", ""), ('COLLECTION', "Collection", ""),
('MESH', "Mesh", ""),
('MATERIAL', "Material", ""),
), ),
default="OBJECT" default="OBJECT"
) # type: ignore ) # type: ignore
@ -90,12 +92,14 @@ class BLENVY_OT_component_paste(Operator):
def execute(self, context): def execute(self, context):
source_item_name = context.window_manager.copied_source_item_name source_item_name = context.window_manager.copied_source_item_name
source_item_type = context.window_manager.copied_source_item_type source_item_type = context.window_manager.copied_source_item_type
if source_item_type == 'Object': source_item = get_item_by_type(source_item_type, source_item_name)
print("HEEEERRE", source_item_name, source_item_type, source_item)
"""if source_item_type == 'Object':
source_item = bpy.data.objects.get(source_item_name, None) source_item = bpy.data.objects.get(source_item_name, None)
elif source_item_type == 'Collection': elif source_item_type == 'Collection':
source_item = bpy.data.collections.get(source_item_name, None) source_item = bpy.data.collections.get(source_item_name, None)"""
if source_item == None: if source_item is None:
self.report({"ERROR"}, "The source object to copy a component from does not exist") self.report({"ERROR"}, "The source object to copy a component from does not exist")
else: else:
component_name = context.window_manager.copied_source_component_name component_name = context.window_manager.copied_source_component_name
@ -105,7 +109,7 @@ class BLENVY_OT_component_paste(Operator):
else: else:
print("pasting component to item:", source_item, "component name:", str(component_name), "component value:" + str(component_value)) print("pasting component to item:", source_item, "component name:", str(component_name), "component value:" + str(component_value))
registry = context.window_manager.components_registry registry = context.window_manager.components_registry
target_item = get_selected_object_or_collection(context) target_item = get_selected_item(context)
copy_propertyGroup_values_to_another_item(source_item, target_item, component_name, registry) copy_propertyGroup_values_to_another_item(source_item, target_item, component_name, registry)
return {'FINISHED'} return {'FINISHED'}
@ -133,6 +137,8 @@ class BLENVY_OT_component_remove(Operator):
items=( items=(
('OBJECT', "Object", ""), ('OBJECT', "Object", ""),
('COLLECTION', "Collection", ""), ('COLLECTION', "Collection", ""),
('MESH', "Mesh", ""),
('MATERIAL', "Material", ""),
), ),
default="OBJECT" default="OBJECT"
) # type: ignore ) # type: ignore
@ -142,10 +148,7 @@ class BLENVY_OT_component_remove(Operator):
if self.item_name == "": if self.item_name == "":
self.report({"ERROR"}, "The target to remove ("+ self.component_name +") from does not exist") self.report({"ERROR"}, "The target to remove ("+ self.component_name +") from does not exist")
else: else:
if self.item_type == 'OBJECT': target = get_item_by_type(self.item_type, self.item_name)
target = bpy.data.objects[self.item_name]
elif self.item_type == 'COLLECTION':
target = bpy.data.collections[self.item_name]
print("removing component ", self.component_name, "from object '"+target.name+"'") print("removing component ", self.component_name, "from object '"+target.name+"'")
@ -260,11 +263,7 @@ class BLENVY_OT_component_rename_component(Operator):
if original_name != '' and target_name != '' and original_name != target_name and len(target_items) > 0: if original_name != '' and target_name != '' and original_name != target_name and len(target_items) > 0:
for index, item_data in enumerate(target_items): for index, item_data in enumerate(target_items):
[item_name, item_type] = item_data [item_name, item_type] = item_data
item = None item = get_item_by_type(item_type, item_name)
if item_type == "OBJECT":
item = bpy.data.objects[item_name]
elif item_type == "COLLECTION":
item = bpy.data.collections[item_name]
if item and original_name in get_bevy_components(item) or original_name in item: if item and original_name in get_bevy_components(item) or original_name in item:
try: try:
@ -367,7 +366,7 @@ class BLENVY_OT_component_toggle_visibility(Operator):
) # type: ignore ) # type: ignore
def execute(self, context): def execute(self, context):
target = get_selected_object_or_collection(context) target = get_selected_item(context)
toggle_component(target, self.component_name) toggle_component(target, self.component_name)
return {'FINISHED'} return {'FINISHED'}

View File

@ -1,7 +1,7 @@
import json import json
import bpy import bpy
from ..utils import get_selection_type from ..utils import get_selected_item, get_selection_type
from .metadata import do_item_custom_properties_have_missing_metadata, get_bevy_components from .metadata import do_item_custom_properties_have_missing_metadata, get_bevy_components
@ -176,22 +176,17 @@ class BLENVY_PT_components_panel(bpy.types.Panel):
layout = self.layout layout = self.layout
name = "" name = ""
target_type = "" target_type = ""
object = next(iter(context.selected_objects), None) selected_item = get_selected_item(context)
collection = context.collection target_type = get_selection_type(selected_item)
if object is not None: name = selected_item.name if selected_item is not None else ''
name = object.name
target_type = "Object"
elif collection is not None:
name = collection.name
target_type = "Collection"
# name = context.object.name if context.object is not None else '' # name = context.object.name if context.object is not None else ''
layout.label(text=f"Components for {name} ({target_type})") layout.label(text=f"Components for {name} ({target_type})")
#print("object", context.object, "active", context.active_object, "objects", context.selected_objects) #print("object", context.object, "active", context.active_object, "objects", context.selected_objects)
def draw(self, context): def draw(self, context):
object = next(iter(context.selected_objects), None) selected_item = get_selected_item(context)
collection = context.collection
layout = self.layout layout = self.layout
# we get & load our component registry # we get & load our component registry
@ -199,10 +194,8 @@ class BLENVY_PT_components_panel(bpy.types.Panel):
selected_component = bpy.context.window_manager.blenvy.components.component_selector selected_component = bpy.context.window_manager.blenvy.components.component_selector
registry_has_type_infos = registry.has_type_infos() registry_has_type_infos = registry.has_type_infos()
if object is not None: if selected_item is not None:
draw_component_ui(layout, object, registry, selected_component, registry_has_type_infos, context) draw_component_ui(layout, selected_item, registry, selected_component, registry_has_type_infos, context)
elif collection is not None:
draw_component_ui(layout, collection, registry, selected_component, registry_has_type_infos, context)
else: else:
layout.label(text ="Select an object to edit its components") layout.label(text ="Select an object to edit its components")

View File

@ -4,13 +4,13 @@ import bpy
from .conversions_from_prop_group import property_group_value_to_custom_property_value from .conversions_from_prop_group import property_group_value_to_custom_property_value
from .process_component import process_component from .process_component import process_component
from .utils import update_calback_helper from .utils import update_calback_helper
from ..utils import get_selected_object_or_collection from ..utils import get_selected_item
## main callback function, fired whenever any property changes, no matter the nesting level ## main callback function, fired whenever any property changes, no matter the nesting level
def update_component(self, context, definition, component_name): def update_component(self, context, definition, component_name):
registry = bpy.context.window_manager.components_registry registry = bpy.context.window_manager.components_registry
current_object_or_collection = get_selected_object_or_collection(context) current_object_or_collection = get_selected_item(context)
update_disabled = current_object_or_collection["__disable__update"] if "__disable__update" in current_object_or_collection else False update_disabled = current_object_or_collection["__disable__update"] if "__disable__update" in current_object_or_collection else False
update_disabled = registry.disable_all_object_updates or update_disabled # global settings update_disabled = registry.disable_all_object_updates or update_disabled # global settings
if update_disabled: if update_disabled:

View File

@ -22,6 +22,8 @@ class BLENVY_OT_item_select(Operator):
items=( items=(
('OBJECT', "Object", ""), ('OBJECT', "Object", ""),
('COLLECTION', "Collection", ""), ('COLLECTION', "Collection", ""),
('MESH', "Mesh", ""),
('MATERIAL', "Material", ""),
), ),
default="OBJECT" default="OBJECT"
) # type: ignore ) # type: ignore
@ -53,19 +55,38 @@ class BLENVY_OT_item_select(Operator):
return {'FINISHED'} return {'FINISHED'}
#FIXME: does not work if object is hidden !! def get_selected_item(context):
def get_selected_object_or_collection(context): selection = None
target = None
object = next(iter(context.selected_objects), None) #print("original context", context)
collection = context.collection def get_outliner_area():
if object is not None: if bpy.context.area.type!='OUTLINER':
target = object for area in bpy.context.screen.areas:
elif collection is not None: if area.type == 'OUTLINER':
target = collection return area
return target return None
area = get_outliner_area()
if area is not None:
region = next(region for region in area.regions if region.type == "WINDOW")
with bpy.context.temp_override(area=area, region=region):
#print("overriden context", bpy.context)
for obj in bpy.context.selected_ids:
pass#print(f"Selected: {obj.name} - {type(obj)}")
selection = bpy.context.selected_ids[len(bpy.context.selected_ids) - 1] if len(bpy.context.selected_ids)>0 else None #next(iter(bpy.context.selected_ids), None)
print("selection", f"Selected: {selection.name} - {type(selection)}")
#print("SELECTIONS", context.selected_objects)
return selection
def get_selection_type(selection): def get_selection_type(selection):
#print("bla mesh", isinstance(selection, bpy.types.Mesh), "bli bli", selection.type)
if isinstance(selection, bpy.types.Material):
return 'MATERIAL'
if isinstance(selection, bpy.types.Mesh):
return 'MESH'
if isinstance(selection, bpy.types.Object): if isinstance(selection, bpy.types.Object):
return 'OBJECT' return 'OBJECT'
if isinstance(selection, bpy.types.Collection): if isinstance(selection, bpy.types.Collection):
@ -76,18 +97,13 @@ def get_item_by_type(item_type, item_name):
if item_type == 'OBJECT': if item_type == 'OBJECT':
item = bpy.data.objects[item_name] item = bpy.data.objects[item_name]
elif item_type == 'COLLECTION': elif item_type == 'COLLECTION':
print("item NAME", item_name)
item = bpy.data.collections[item_name] item = bpy.data.collections[item_name]
elif item_type == "MESH":
item = bpy.data.meshes[item_name]
elif item_type == 'MATERIAL':
item = bpy.data.materials[item_name]
return item return item
def get_selected_object_or_collection_by_item_type(context, item_type):
item = None
if item_type == 'OBJECT':
object = next(iter(context.selected_objects), None)
return object
elif item_type == 'COLLECTION':
return context.collection
def add_component_to_ui_list(self, context, _): def add_component_to_ui_list(self, context, _):
items = [] items = []
type_infos = context.window_manager.components_registry.type_infos type_infos = context.window_manager.components_registry.type_infos