From e02bab244d5200f9a83f309d53210257d3046f81 Mon Sep 17 00:00:00 2001 From: brothermechanic Date: Thu, 29 Feb 2024 11:51:30 +0300 Subject: [PATCH] Blender Addon: entity manager --- .../scripts/addons/Robossembler/__init__.py | 97 ++++++++++++++++--- .../io_entity_manager/__init__.py | 88 +++++++++++++++++ 2 files changed, 171 insertions(+), 14 deletions(-) create mode 100644 cg/blender/scripts/addons/Robossembler/io_entity_manager/__init__.py diff --git a/cg/blender/scripts/addons/Robossembler/__init__.py b/cg/blender/scripts/addons/Robossembler/__init__.py index 0f46f8f..1c212fa 100644 --- a/cg/blender/scripts/addons/Robossembler/__init__.py +++ b/cg/blender/scripts/addons/Robossembler/__init__.py @@ -42,6 +42,7 @@ from bpy.props import ( from .io_scene_json import export_json from .io_anim_ros2bag import set_animation_data +from .io_entity_manager import switch_3d_entities bl_info = { 'name': 'Robossembler Tools', @@ -60,7 +61,7 @@ bl_info = { class addon_Properties(PropertyGroup): engine: EnumProperty( - name='Physics Engine', + name='Engine', description='Selest Target Engine', items=[('BULLET', 'Bullet', ''), ('ODE', 'O D E', ''), @@ -69,14 +70,39 @@ class addon_Properties(PropertyGroup): ] ) - conf_file_path: StringProperty( - name='Config File Path', - description='Input/output config file path', + entity: EnumProperty( + name='Entity', + description='Selest 3d Entity', + items=[('hp', 'Highpoly', ''), + ('mp', 'Modpoly', ''), + ('lp', 'Lowpoly', '') + ] + ) + + json_file_path: StringProperty( + name='File Path', + description='Input/output Json file path', default='', maxlen=1023, subtype='FILE_PATH' ) + ros2bag_file_path: StringProperty( + name='File Path', + description='Input Ros2Bag file path', + default='', + maxlen=1023, + subtype='FILE_PATH' + ) + + refs_file_path: StringProperty( + name='Dir Path', + description='References library file path', + default='', + maxlen=1023, + subtype='DIR_PATH' + ) + class RobossemblerPanel1(Panel): ''' Robossembler UI''' @@ -90,13 +116,13 @@ class RobossemblerPanel1(Panel): prop = context.scene.robossembler_properties layout = self.layout - layout.prop(prop, 'conf_file_path') + layout.prop(prop, 'json_file_path') layout.prop(prop, 'engine') col = layout.column() col.alert = True col.scale_y = 2.0 - col.operator('export.scene_config', + col.operator('scene.export_json', icon='WORLD_DATA', text='Export Scene') @@ -113,27 +139,50 @@ class RobossemblerPanel2(Panel): prop = context.scene.robossembler_properties layout = self.layout - layout.prop(prop, 'conf_file_path') + layout.prop(prop, 'ros2bag_file_path') col = layout.column() col.alert = True col.scale_y = 2.0 - col.operator('export.scene_config', + col.operator('scene.import_ros2bag', icon='ACTION', text='Import Animation') +class RobossemblerPanel3(Panel): + ''' Robossembler UI''' + bl_idname = 'ROBOSSEMBLER_PT_ENTITY_MANAGER' + bl_label = 'Switch 3d Entities' + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = 'Robossembler' + + def draw(self, context): + prop = context.scene.robossembler_properties + + layout = self.layout + layout.prop(prop, 'refs_file_path') + layout.prop(prop, 'entity') + + col = layout.column() + col.alert = True + col.scale_y = 2.0 + col.operator('scene.manage_entities', + icon='ASSET_MANAGER', + text='Switch Entities') + + class RobossemblerOperator1(Operator): '''Tooltip''' - bl_idname = 'export.scene_config' + bl_idname = 'scene.export_json' bl_label = '' - bl_description = 'Export scene liks to json config.' + bl_description = 'Export scene liks to json config operator.' bl_options = {'REGISTER', 'UNDO'} def execute(self, context): prop = context.scene.robossembler_properties - file_path = os.path.realpath(bpy.path.abspath((prop.conf_file_path))) + file_path = os.path.realpath(bpy.path.abspath((prop.json_file_path))) physics_engine = prop.engine export_json(context, file_path, physics_engine) @@ -142,25 +191,45 @@ class RobossemblerOperator1(Operator): class RobossemblerOperator2(Operator): '''Tooltip''' - bl_idname = 'export.scene_config' + bl_idname = 'scene.import_ros2bag' bl_label = '' - bl_description = 'Export scene liks to json config.' + bl_description = 'Import Ros2Bag animation to scene and apply it to liks.' bl_options = {'REGISTER', 'UNDO'} def execute(self, context): prop = context.scene.robossembler_properties - file_path = os.path.realpath(bpy.path.abspath((prop.conf_file_path))) + file_path = os.path.realpath(bpy.path.abspath((prop.ros2bag_file_path))) set_animation_data(context, file_path) return {'FINISHED'} +class RobossemblerOperator3(Operator): + '''Tooltip''' + bl_idname = 'scene.manage_entities' + bl_label = '' + bl_description = 'Switch visual 3d entities.' + bl_options = {'REGISTER', 'UNDO'} + + def execute(self, context): + prop = context.scene.robossembler_properties + + file_path = os.path.realpath(bpy.path.abspath((prop.refs_file_path))) + print('1'*10, file_path) + entity = prop.entity + switch_3d_entities(context, file_path) + + return {'FINISHED'} + + classes = ( RobossemblerPanel1, RobossemblerPanel2, + RobossemblerPanel3, RobossemblerOperator1, RobossemblerOperator2, + RobossemblerOperator3, addon_Properties) diff --git a/cg/blender/scripts/addons/Robossembler/io_entity_manager/__init__.py b/cg/blender/scripts/addons/Robossembler/io_entity_manager/__init__.py new file mode 100644 index 0000000..00cce18 --- /dev/null +++ b/cg/blender/scripts/addons/Robossembler/io_entity_manager/__init__.py @@ -0,0 +1,88 @@ +# coding: utf-8 +''' +Copyright (C) 2024 brothermechanic@yandex.com + +Created by brothermechanic + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +''' + +import logging +import os +import bpy + +logger = logging.getLogger(__name__) + + +def unlink_from_collections(context, obj): + ''' Unlinking object from all collections. ''' + for col in list(bpy.data.collections) + [context.scene.collection]: + if obj.name in col.objects: + col.objects.unlink(obj) + return obj + + +def link_library(lib_path, lib_type, lib_name): + ''' ''' + bpy.ops.wm.link( + filepath=lib_path, + directory=os.path.join(lib_path, lib_type), + filename=lib_name, + relative_path=True, + do_reuse_local_id=True, + autoselect=True, + instance_collections=True, + instance_object_data=True + ) + return bpy.data.objects[lib_name] + + +def switch_3d_entities(context, lib_dir): + ''' ''' + assert bpy.data.collections.get('visual'), 'No visual collection!' + entities_from = bpy.data.collections['visual'].objects + assert os.path.isdir(lib_dir), 'No libs dir {}!'.format(lib_dir) + lib_files = os.listdir(lib_dir) + + hp_col = bpy.data.collections.new('Parts') + context.scene.collection.children.link(hp_col) + for entity_from in entities_from: + for lib_file in lib_files: + if '{}_hp.blend'.format(entity_from.name) != lib_file: + continue + + entity_to = link_library( + lib_path=os.path.join(lib_dir, lib_file), + lib_type='Collection', + lib_name='{}_hp'.format(entity_from.name)) + + entity_to.empty_display_type = 'ARROWS' + entity_to.empty_display_size = 0.5 + + unlink_from_collections(context, entity_to) + hp_col.objects.link(entity_to) + + entity_to.location = entity_from.location + entity_from.rotation_mode = entity_to.rotation_mode = 'QUATERNION' + entity_to.rotation_quaternion = entity_from.rotation_quaternion + entity_to.scale = entity_from.scale + + entity_to.parent = entity_from.parent + + logger.info('Entity %s changed to %s!', entity_from.name, entity_to.name) + + bpy.data.collections['visual'].hide_render = True + bpy.data.collections['visual'].hide_viewport = True + + return True