diff --git a/cg/blender/import_cad/build_blender_scene.py b/cg/blender/import_cad/build_blender_scene.py index 25e7c8d..1e50c3e 100644 --- a/cg/blender/import_cad/build_blender_scene.py +++ b/cg/blender/import_cad/build_blender_scene.py @@ -36,12 +36,10 @@ scene_scale = 0.001 blackbody_mat_name = 'Robossembler_Black_Body' -def json_to_blend(js_data, **cg_config): +def json_to_blend(js_data): ''' Reads JSON data and creates Blender scene ''' - lcs_collection = bpy.data.collections.new(cg_config['lcs_col_name']) - bpy.context.scene.collection.children.link(lcs_collection) - parts_collection = bpy.data.collections.new(cg_config['parts_col_name']) - bpy.context.scene.collection.children.link(parts_collection) + render_collection = bpy.data.collections.new('Render') + bpy.context.scene.collection.children.link(render_collection) fc_file = list(js_data.keys())[0] @@ -52,7 +50,7 @@ def json_to_blend(js_data, **cg_config): if js_data[fc_file][js_obj]['type'] == 'LCS': if not js_data[fc_file][js_obj].get('Robossembler_SocketFlow'): - #if not js_obj.endswith(cg_config['lcs_inlet']) and not js_obj.endswith(cg_config['lcs_outlet']): + # TODO test logger.info('LCS %s is not defined!', js_obj) continue bobj = bpy.data.objects.new(js_obj, None) @@ -63,7 +61,7 @@ def json_to_blend(js_data, **cg_config): if 'Robossembler' not in attr: continue bobj[attr] = js_data[fc_file][js_obj][attr] - lcs_collection.objects.link(bobj) + render_collection.objects.link(bobj) imported_objects['objs_lcs'].append(bobj.name) elif js_data[fc_file][js_obj]['type'] == 'PART': @@ -77,7 +75,7 @@ def json_to_blend(js_data, **cg_config): bmesh.from_pydata(verts, edges, faces) bmesh.update() bobj = bpy.data.objects.new(js_obj, bmesh) - parts_collection.objects.link(bobj) + render_collection.objects.link(bobj) if bobj: fc_placement(bobj, @@ -92,7 +90,7 @@ def json_to_blend(js_data, **cg_config): hierarchy_objs = hierarchy_list( bobj, js_data[fc_file][js_obj]['hierarchy'], scene_scale) for hierarchy_obj in hierarchy_objs: - parts_collection.objects.link(hierarchy_obj) + render_collection.objects.link(hierarchy_obj) imported_objects['objs_hierarchy'].append(hierarchy_obj.name) # one material for the whole object diff --git a/cg/blender/processing/midpoly_setup.py b/cg/blender/processing/midpoly_setup.py index 109354f..afdb8ac 100644 --- a/cg/blender/processing/midpoly_setup.py +++ b/cg/blender/processing/midpoly_setup.py @@ -14,6 +14,7 @@ DESCRIPTION. Generte object from collection of objects. ''' +# DEPRECATED __version__ = '0.1' import logging diff --git a/cg/blender/processing/highpoly_setup.py b/cg/blender/processing/render_assets.py similarity index 93% rename from cg/blender/processing/highpoly_setup.py rename to cg/blender/processing/render_assets.py index 10a4edb..4193829 100644 --- a/cg/blender/processing/highpoly_setup.py +++ b/cg/blender/processing/render_assets.py @@ -12,9 +12,9 @@ # GNU General Public License for more details. ''' DESCRIPTION. -Basic mesh processing for asset pipeline. +Mesh processing for original tesselated assets for render. ''' -__version__ = '0.2' +__version__ = '0.3' import logging import bpy @@ -25,7 +25,7 @@ logger = logging.getLogger(__name__) def setup_meshes(obj_names, cleanup=False, sharpness=False, shading=False): ''' Setup raw meshes list after importing ''' - logger.info('Hightpoly meshes setup launched...') + logger.info('Render assets setup launched...') fixed_obj_names = [] for obj_name in obj_names: if not bpy.data.objects.get(obj_name): @@ -71,4 +71,4 @@ def setup_meshes(obj_names, cleanup=False, sharpness=False, shading=False): fixed_obj_names.append(obj_name) - return logger.info('Setup of %s hightpoly meshes is finished!', len(fixed_obj_names)) + return logger.info('Setup of %s meshes is finished!', len(fixed_obj_names)) diff --git a/cg/blender/processing/restruct_hierarchy.py b/cg/blender/processing/restruct_hierarchy.py index c0ff400..facc325 100644 --- a/cg/blender/processing/restruct_hierarchy.py +++ b/cg/blender/processing/restruct_hierarchy.py @@ -28,17 +28,24 @@ from blender.utils.collection_tools import unlink_from_collections logger = logging.getLogger(__name__) -def hierarchy_assembly(lcs_names, parts_sequence, **cg_config): +def hierarchy_assembly(lcs_names, parts_sequence): ''' Hierarchy by LCS and Parts Assembling Sequence. ''' # collect scene hierarchy start info main_locators = [obj for obj in bpy.data.objects if not obj.parent] lcs_inlet_objects = [] lcs_outlet_objects = [] - for name in lcs_names: - if name.endswith(cg_config['lcs_inlet']): - lcs_inlet_objects.append(bpy.data.objects[name]) + for lcs_name in lcs_names: + lcs_obj = bpy.data.objects[lcs_name] + if (lcs_obj.get('Robossembler_SocketFlow') == 'inlet' + and lcs_obj.get('Robossembler_DefaultOrigin') + ): + lcs_inlet_objects.append(lcs_obj) else: - lcs_outlet_objects.append(bpy.data.objects[name]) + lcs_outlet_objects.append(lcs_obj) + + if not lcs_inlet_objects: + raise Exception('No LCS Inlet objects found!') + # get main_locator main_locator = None for locator in main_locators: @@ -55,18 +62,20 @@ def hierarchy_assembly(lcs_names, parts_sequence, **cg_config): if not bpy.data.objects.get(part): return logger.error('%s part object not found!', part) - first_part_obj = bpy.data.objects[parts_sequence[0]] - # create root lcs by parts sequence + first_part_obj = bpy.data.objects[parts_sequence[0]] root_lcs = None for lcs_inlet in first_part_obj.children: # drop non lcs objs if lcs_inlet.name not in lcs_names: continue - # drop non inlet objs - if lcs_inlet.name.endswith(cg_config['lcs_outlet']): + # drop non DefaultOrigins + lcs_obj = bpy.data.objects[lcs_name] + if not (lcs_obj.get('Robossembler_SocketFlow') == 'inlet' + and lcs_obj.get('Robossembler_DefaultOrigin') + ): continue - root_lcs_name = cg_config['lcs_root'] + root_lcs_name = 'root' root_lcs = bpy.data.objects.new(root_lcs_name, None) root_lcs.empty_display_type = 'ARROWS' root_lcs.empty_display_size = 0.15 @@ -74,19 +83,18 @@ def hierarchy_assembly(lcs_names, parts_sequence, **cg_config): root_lcs.location = lcs_inlet.location root_lcs.rotation_euler = lcs_inlet.rotation_euler root_lcs.parent = lcs_inlet.parent - bpy.data.collections[cg_config['lcs_col_name']].objects.link(root_lcs) + bpy.context.scene.collection.objects.link(root_lcs) logger.info('Root Inlet LCS object created!') - unparenting(root_lcs) round_transforms(root_lcs) parenting(root_lcs, main_locator) # retree_by lcs for lcs_inlet_obj in lcs_inlet_objects: - # lcs inlet + # lcs inlet as main parent parent_locator = lcs_inlet_obj.parent if not parent_locator: - return logger.error('LCS %s should have a parent', lcs_inlet_obj.name) + raise Exception('LCS %s should have a parent!', lcs_inlet_obj.name) unparenting(lcs_inlet_obj) round_transforms(lcs_inlet_obj) if parent_locator: @@ -94,11 +102,10 @@ def hierarchy_assembly(lcs_names, parts_sequence, **cg_config): unparenting(parent_locator) parenting(lcs_inlet_obj, parent_locator) parenting(root_lcs, lcs_inlet_obj) - # lcs outlet - lcs_outlet = '{}_{}'.format( - lcs_inlet_obj.name.rpartition('_')[0], cg_config['lcs_outlet']) - if bpy.data.objects.get(lcs_outlet): - lcs_outlet_obj = bpy.data.objects[lcs_outlet] + # lcs outlet parent to lcs inlet + for lcs_outlet_obj in lcs_inlet_obj.children_recursive: + if lcs_outlet_obj.name not in lcs_names: + continue unparenting(lcs_outlet_obj) round_transforms(lcs_outlet_obj) parenting(lcs_inlet_obj, lcs_outlet_obj) @@ -118,20 +125,21 @@ def hierarchy_assembly(lcs_names, parts_sequence, **cg_config): # collect part names part_name = None for locator in lcs_inlet_obj.children: - if locator not in lcs_outlet_objects: - part_name = locator.name - part_names.append(part_name) - # pack parts to collections - part_col = bpy.data.collections.new('{}_{}'.format( - part_name, cg_config['hightpoly'])) - bpy.data.collections[cg_config['parts_col_name']].children.link(part_col) - for obj in lcs_inlet_obj.children_recursive: - # outlet lcs objects are already in place, don't move it - if obj in lcs_outlet_objects: + if locator in lcs_outlet_objects: continue + part_name = locator.name + part_names.append(part_name) + # pack parts to collections + part_col = bpy.data.collections.new(f'{part_name}') + bpy.data.collections['Render'].children.link(part_col) + for obj in lcs_inlet_obj.children_recursive: unlink_from_collections(obj) part_col.objects.link(obj) + unlink_from_collections(lcs_inlet_obj) + part_col.objects.link(lcs_inlet_obj) + # TODO DEPRECATED + """ # parts assembling for idx, part_name in enumerate(parts_sequence): # TODO clones for clones @@ -160,63 +168,73 @@ def hierarchy_assembly(lcs_names, parts_sequence, **cg_config): constraint = lcs.constraints.new(type='COPY_TRANSFORMS') constraint.target = root_lcs constraint.enabled = False + """ logger.info('Restructuring assembly pipeline finished!') return part_names -def hierarchy_separated_parts(lcs_names, **cg_config): +def hierarchy_separated_parts(lcs_names): ''' Restructuring pipeline as separated parts. ''' # collect scene hierarchy start info lcs_inlet_objects = [] lcs_outlet_objects = [] - for name in lcs_names: - if name.endswith(cg_config['lcs_inlet']): - lcs_inlet_objects.append(bpy.data.objects[name]) + + for lcs_name in lcs_names: + lcs_obj = bpy.data.objects[lcs_name] + if (lcs_obj.get('Robossembler_SocketFlow') == 'inlet' + and lcs_obj.get('Robossembler_DefaultOrigin') + ): + lcs_inlet_objects.append(lcs_obj) else: - lcs_outlet_objects.append(bpy.data.objects[name]) + lcs_outlet_objects.append(lcs_obj) + + if not lcs_inlet_objects: + raise Exception('No LCS Inlet objects found!') # retree_by lcs part_names = [] for lcs_inlet_obj in lcs_inlet_objects: - # outlet lcs + # lcs inlet as main parent parent_locator = lcs_inlet_obj.parent if not parent_locator: - return logger.error('LCS %s should have a parent', lcs_inlet_obj.name) + raise Exception('LCS %s should have a parent!', lcs_inlet_obj.name) unparenting(lcs_inlet_obj) round_transforms(lcs_inlet_obj) if parent_locator: if parent_locator.parent: unparenting(parent_locator) parenting(lcs_inlet_obj, parent_locator) - # lcs outlet - lcs_outlet = '{}_{}'.format( - lcs_inlet_obj.name.rpartition('_')[0], cg_config['lcs_outlet']) - if bpy.data.objects.get(lcs_outlet): - lcs_outlet_obj = bpy.data.objects[lcs_outlet] + # lcs outlet parent to lcs inlet + for lcs_outlet_obj in lcs_inlet_obj.children_recursive: + if lcs_outlet_obj.name not in lcs_names: + continue unparenting(lcs_outlet_obj) round_transforms(lcs_outlet_obj) parenting(lcs_inlet_obj, lcs_outlet_obj) + # reset transforms for inlet_lcs lcs_inlet_obj.matrix_world = mathutils.Matrix() + # pack parts to collections part_name = None for locator in lcs_inlet_obj.children: if locator not in lcs_outlet_objects: part_name = locator.name part_names.append(part_name) - part_col = bpy.data.collections.new('{}_{}'.format( - part_name, cg_config['hightpoly'])) - bpy.data.collections[cg_config['parts_col_name']].children.link(part_col) + part_col = bpy.data.collections.new(f'{part_name}') + bpy.data.collections['Render'].children.link(part_col) for obj in lcs_inlet_obj.children_recursive: unlink_from_collections(obj) part_col.objects.link(obj) + unlink_from_collections(lcs_inlet_obj) + part_col.objects.link(lcs_inlet_obj) # remove unmarked objects marked_objs = sum( [lcs_inlet_obj.children_recursive for lcs_inlet_obj in lcs_inlet_objects], []) - parts_col_objs = bpy.data.collections[cg_config['parts_col_name']].objects + parts_col_objs = bpy.data.collections['Render'].objects unmarked_objs = list(set(parts_col_objs) - set(marked_objs)) if unmarked_objs: removed_objs = list(map(bpy.data.objects.remove, unmarked_objs)) @@ -227,7 +245,7 @@ def hierarchy_separated_parts(lcs_names, **cg_config): return part_names -def hierarchy_single_part(**cg_config): +def hierarchy_mono_part(): ''' Restructuring pipeline as single part. ''' # collect scene hierarchy start info main_locators = [obj for obj in bpy.data.objects if not obj.parent] @@ -237,9 +255,8 @@ def hierarchy_single_part(**cg_config): for main_locator in main_locators: part_name = main_locator.name part_names.append(part_name) - part_col = bpy.data.collections.new('{}_{}'.format( - part_name, cg_config['hightpoly'])) - bpy.data.collections[cg_config['parts_col_name']].children.link(part_col) + part_col = bpy.data.collections.new(f'{part_name}') + bpy.data.collections['Render'].children.link(part_col) for obj in main_locator.children_recursive: unlink_from_collections(obj) part_col.objects.link(obj) diff --git a/cg/blender/processing/lowpoly_setup.py b/cg/blender/processing/visual_assets.py similarity index 98% rename from cg/blender/processing/lowpoly_setup.py rename to cg/blender/processing/visual_assets.py index 2dc47bc..5f742ec 100644 --- a/cg/blender/processing/lowpoly_setup.py +++ b/cg/blender/processing/visual_assets.py @@ -12,9 +12,9 @@ # GNU General Public License for more details. ''' DESCRIPTION. -Create lowpoly shells from parts collections. +Retopology visual assets for simulation pipeline. ''' -__version__ = '0.1' +__version__ = '0.3' import logging import bpy diff --git a/cg/pipeline/cg_pipeline.py b/cg/pipeline/cg_pipeline.py index 410df9d..0fff600 100644 --- a/cg/pipeline/cg_pipeline.py +++ b/cg/pipeline/cg_pipeline.py @@ -19,10 +19,10 @@ from utils.cmd_proc import cmd_proc from blender.import_cad.build_blender_scene import json_to_blend from blender.processing.restruct_hierarchy import (hierarchy_assembly, hierarchy_separated_parts, - hierarchy_single_part) -from blender.processing.highpoly_setup import setup_meshes + hierarchy_mono_part) +from blender.processing.render_assets import setup_meshes from blender.processing.midpoly_setup import hightpoly_collections_to_midpoly -from blender.processing.lowpoly_setup import parts_to_shells +from blender.processing.visual_assets import parts_to_shells from blender.processing.uv_setup import uv_unwrap from blender.texturing.bake_submitter import bw_bake from blender.texturing.composing import compose_baked_textures @@ -45,21 +45,22 @@ freecad_bin = 'freecadcmd' blender_bin = 'blender' # TODO WEBAPP -cg_config = { - 'lcs_col_name': 'LCS', +cg_config = {} +""" 'parts_col_name': 'Parts', 'midpoly_col_name': 'Midpoly', - 'lowpoly_col_name': 'Lowpoly', + 'visual_col_name': 'Lowpoly', 'lcs_inlet': 'in', 'lcs_outlet': 'out', 'lcs_root': 'root', - 'hightpoly': 'hp', + 'render': 'hp', 'midpoly': 'mp', - 'lowpoly': 'lp', + 'visual': 'lp', 'render': 'render', } +""" -defined_pipeline_list = ['cad', 'highpoly', 'midpoly', 'lowpoly', 'baking', 'export'] +defined_pipeline_list = ['cad', 'render', 'visual', 'collision', 'baking', 'export'] def cg_pipeline(**kwargs): @@ -95,7 +96,7 @@ def cg_pipeline(**kwargs): '--', **kwargs ).split('FreeCAD ')[0] - ), **cg_config + ) ) # Save original cad setup as blender scene @@ -107,53 +108,54 @@ def cg_pipeline(**kwargs): bpy.ops.wm.save_as_mainfile(filepath=blend_file) return blend_file - # 2) prepare highpoly (depend of cad_objects['objs_lcs'] and parts_sequence) + # 2) prepare render (depend of cad_objects['objs_lcs'] and parts_sequence) # ______________________________ if parts_sequence_path and os.path.isfile(parts_sequence_path): with open(parts_sequence_path, 'r', encoding='utf-8') as sequence_file: parts_sequence = json.load(sequence_file) else: parts_sequence = None - lcs_inlet_objects = [ - inlet for inlet in cad_objects['objs_lcs'] - if inlet.endswith(cg_config['lcs_inlet'])] + lcs_inlets = [ + lcs_name for lcs_name in cad_objects['objs_lcs'] + if (bpy.data.objects[lcs_name].get('Robossembler_SocketFlow') == 'inlet' + and bpy.data.objects[lcs_name].get('Robossembler_DefaultOrigin'))] # input cases # 1 case - if parts_sequence and len(lcs_inlet_objects) > 1: + if parts_sequence and len(lcs_inlets) > 1: logger.info('Parts assembling sequence and LCS points found! ' 'Launch "hierarchy_assembly" restructuring pipeline.') part_names = hierarchy_assembly( - cad_objects['objs_lcs'], parts_sequence, **cg_config) + cad_objects['objs_lcs'], parts_sequence) # 2 case - elif parts_sequence and len(lcs_inlet_objects) < 2: + elif parts_sequence and len(lcs_inlets) < 2: return logger.error('Assembly do not have enough LCS points!') # 3 case - elif not parts_sequence and lcs_inlet_objects: + elif not parts_sequence and lcs_inlets: logger.info('Parts assembling sequence not found! ' 'Launch "hierarchy_separated_parts" restructuring pipeline.') part_names = hierarchy_separated_parts( - cad_objects['objs_lcs'], **cg_config) + cad_objects['objs_lcs']) # 4 case - elif not parts_sequence and not lcs_inlet_objects: + elif not parts_sequence and not lcs_inlets: logger.info('Parts assembling sequence and LCS points not found! ' - 'Launch "hierarchy_single_part" restructuring pipeline.') - part_names = hierarchy_single_part(**cg_config) + 'Launch "hierarchy_mono_part" restructuring pipeline.') + part_names = hierarchy_mono_part() if not part_names: return logger.error('Can not generate parts!') - # setup highpolys with materials only + # setup renders with materials only if cad_objects['objs_foreground']: setup_meshes(cad_objects['objs_foreground'], sharpness=True, shading=True) - # setup all highpolys + # setup all renders else: setup_meshes(cad_objects['objs_background'], sharpness=True, shading=True) - # Save highpoly setup as blender scene - if kwargs['pipeline_type'] == 'highpoly': + # Save render setup as blender scene + if kwargs['pipeline_type'] == 'render': blend_file = os.path.join( blend_path, '{}_{}.blend'.format(assembly_name, kwargs['pipeline_type']) @@ -181,19 +183,19 @@ def cg_pipeline(**kwargs): logger.info('%s midpoly objects ready!', len(midpoly_obj_names)) return blend_file - # 4) prepare lowpoly + # 4) prepare visual # ______________________________ - lowpoly_obj_names = parts_to_shells(part_names, **cg_config) - uv_unwrap(lowpoly_obj_names) + visual_obj_names = parts_to_shells(part_names, **cg_config) + uv_unwrap(visual_obj_names) - # Save lowpoly setup as blender scene - if kwargs['pipeline_type'] == 'lowpoly': + # Save visual setup as blender scene + if kwargs['pipeline_type'] == 'visual': blend_file = os.path.join( blend_path, '{}_{}.blend'.format(assembly_name, kwargs['pipeline_type']) ).replace('\\', '/') bpy.ops.wm.save_as_mainfile(filepath=blend_file) - logger.info('%s lowpoly objects ready!', len(lowpoly_obj_names)) + logger.info('%s visual objects ready!', len(visual_obj_names)) return blend_file # 5) bake textures @@ -202,7 +204,7 @@ def cg_pipeline(**kwargs): logger.info('Baking pipeline has been canceled!') else: textures_path = os.path.join(blend_path, 'textures').replace('\\', '/') - bake_paths = bw_bake(lowpoly_obj_names, + bake_paths = bw_bake(visual_obj_names, textures_path, kwargs['textures_resolution'], **cg_config) @@ -218,7 +220,7 @@ def cg_pipeline(**kwargs): for bake_path in bake_paths: shutil.rmtree(bake_path) bpy.ops.wm.open_mainfile(filepath=blend_file) - assign_pbr_material(lowpoly_obj_names, textures_path) + assign_pbr_material(visual_obj_names, textures_path) bpy.ops.file.make_paths_relative() # Save baking setup as blender scene @@ -228,7 +230,7 @@ def cg_pipeline(**kwargs): '{}_{}.blend'.format(assembly_name, kwargs['pipeline_type']) ).replace('\\', '/') bpy.ops.wm.save_as_mainfile(filepath=blend_file) - logger.info('%s lowpoly objects baked!', len(lowpoly_obj_names)) + logger.info('%s visual objects baked!', len(visual_obj_names)) return blend_file # 6 export object meshes @@ -244,7 +246,7 @@ def cg_pipeline(**kwargs): for part_name in part_names: export_fbx( - obj_name=f'{part_name}_{cg_config["lowpoly"]}', + obj_name=f'{part_name}_{cg_config["visual"]}', path=os.path.join(export_path, part_name, 'meshes').replace('\\', '/')) export_ply( obj_name=f'{part_name}_{cg_config["midpoly"]}', @@ -253,7 +255,7 @@ def cg_pipeline(**kwargs): obj_name=f'{part_name}_{cg_config["midpoly"]}', path=os.path.join(export_path, part_name, 'meshes').replace('\\', '/')) export_stl( - obj_name=f'{part_name}_{cg_config["lowpoly"]}', + obj_name=f'{part_name}_{cg_config["visual"]}', path=os.path.join(export_path, part_name, 'meshes').replace('\\', '/')) logger.info('%s parts exported!', len(part_names)) @@ -317,7 +319,7 @@ if __name__ == '__main__': parser.add_argument( '--pipeline_type', type=str, - help='Set pipeline type: "cad", "highpoly", "midpoly", "lowpoly", "baking", "export"', + help='Set pipeline type: "cad", "render","visual", "collision", "baking", "export"', default='export', required=False )