# ***** BEGIN GPL LICENSE BLOCK ***** # # Copyright (C) 2021-2024 Robossembler LLC # # Created by Ilia Kurochkin (brothermechanic) # contact: brothermechanic@yandex.com # # This file is part of Robossembler Framework # project repo: https://gitlab.com/robossembler/framework # # Robossembler Framework 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 . # # ***** END GPL LICENSE BLOCK ***** # # coding: utf-8 ''' DESCRIPTION. Generate CG libs database for Robossembler Framework. ''' __version__ = '1.0' import logging import json import math import os import shutil import bpy from .utils.collection_tools import remove_collections_with_objects from .utils.cleanup_orphan_data import cleanup_orphan_data from .utils.object_transforms import apply_transforms from .material_generators import material_generator, black_material from .utils.object_converter import mesh_to_mesh from .export import dae, fbx, glb, ply import .export.obj as obj_export logger = logging.getLogger(__name__) def generate_libs_database(project_dir): ''' Generate Blender LIBS database from PARTs database. ''' parts_data_path = os.path.join(project_dir, 'parts.json') if not os.path.exists(parts_data_path): raise Exception('No parts database found! Check %s directory' % project_dir) with open(parts_data_path, encoding='utf-8') as data: parts_data = json.load(data) libs_data = [] # material libs materials_dir = os.path.join(project_dir, 'libs', 'materials') if os.path.exists(materials_dir): shutil.rmtree(materials_dir) os.makedirs(materials_dir) else: os.makedirs(materials_dir) for part in parts_data: bpy.ops.wm.read_homefile() obj = bpy.data.objects['Cube'] if not part.get('material_path'): continue material_name = os.path.splitext(os.path.basename(part['material_path']))[0] # do not regenerate exists material material_in_libs = False for lib in libs_data: if lib['type'] == 'MATERIAL' and lib['name'] == material_name: material_in_libs = True break if material_in_libs: continue # generate material mat = material_generator(obj, os.path.join(project_dir, part['material_path'])) bpy.data.materials[mat.name].use_fake_user = True remove_collections_with_objects() cleanup_orphan_data() bpy.data.materials.remove(bpy.data.materials['Dots Stroke']) bpy.ops.wm.previews_ensure() img = bpy.data.images.new(mat.name, *mat.preview.image_size, alpha=True) img.pixels.foreach_set(mat.preview.image_pixels_float) img_path = os.path.join(materials_dir, mat.name + '.png') img.save(filepath=img_path) blend_path = os.path.join(materials_dir, mat.name + '.blend') logger.info('Material stored as libs %s to %s.', mat.name, materials_dir) bpy.ops.wm.save_as_mainfile(filepath=blend_path, compress=True) libs_data.append( { 'type': mat.rna_type.name.upper(), 'name': mat.name, 'path': os.path.relpath(blend_path, project_dir), 'thumbnail': os.path.relpath(img_path, project_dir), } ) # object libs objects_dir = os.path.join(project_dir, 'libs', 'objects') if os.path.exists(objects_dir): shutil.rmtree(objects_dir) os.makedirs(objects_dir) else: os.makedirs(objects_dir) for part in parts_data: bpy.ops.wm.read_homefile() remove_collections_with_objects() cleanup_orphan_data() obj_collection = bpy.data.collections.new(part['name']) bpy.context.scene.collection.children.link(obj_collection) # set active to collection active_collection = bpy.context.view_layer.layer_collection.children[part['name']] bpy.context.view_layer.active_layer_collection = active_collection # import stl file bpy.ops.wm.stl_import( filepath=os.path.join(project_dir, part['part_path']), global_scale=0.001, use_facet_normal=False, forward_axis='Y', up_axis='Z') obj = bpy.data.objects.get(part['name']) if not obj: raise Exception('STL Import Fail for %s' % part['name']) apply_transforms(obj, scale=True) # setup material if part.get('material_path'): material_name = os.path.splitext(os.path.basename(part['material_path']))[0] for lib in libs_data: if lib['type'] == 'MATERIAL' and lib['name'] == material_name: lib_path = os.path.join(project_dir, lib['path']) bpy.ops.wm.link( filepath=lib_path, directory=os.path.join(lib_path, 'Material'), filename=lib['name'], relative_path=True, do_reuse_local_id=True) obj.data.materials.append(bpy.data.materials[lib['name']]) else: black_material(obj) with bpy.context.temp_override(selected_editable_objects=[obj]): bpy.ops.object.shade_smooth_by_angle(angle=math.radians(12)) obj.modifiers.new( type='WEIGHTED_NORMAL', name='weighted_normal').keep_sharp = True obj = mesh_to_mesh(obj) obj.asset_mark() obj.asset_generate_preview() img = bpy.data.images.new(part['name'], *obj.preview.image_size, alpha=True) img.pixels.foreach_set(obj.preview.image_pixels_float) img_path = os.path.join(objects_dir, part['name'] + '.png') img.save(filepath=img_path) obj.asset_clear() blend_path = os.path.join(objects_dir, part['name'] + '.blend') logger.info('Object stored as libs %s to %s.', part['name'], objects_dir) bpy.ops.wm.save_as_mainfile(filepath=blend_path, compress=True) libs_data.append( { 'type': obj.rna_type.name.upper(), 'name': part['name'], 'path': os.path.relpath(blend_path, project_dir), 'dae': os.path.relpath( dae.export(obj_name=part['name'], file_dir=objects_dir), project_dir), 'fbx': os.path.relpath( fbx.export(obj_name=part['name'], file_dir=objects_dir, axis_forward='-Z', axis_up='Y'), project_dir), 'glb': os.path.relpath( glb.export(obj_name=part['name'], file_dir=objects_dir), project_dir), 'ply': os.path.relpath( ply.export(obj_name=part['name'], file_dir=objects_dir), project_dir), 'obj': os.path.relpath( obj_export.export(obj_name=part['name'], file_dir=objects_dir), project_dir), 'thumbnail': os.path.relpath(img_path, project_dir), } ) # write db file libs_data_path = os.path.join(project_dir, 'libs.json') with open(libs_data_path, 'w', encoding='utf-8') as libs_data_file: json.dump(libs_data, libs_data_file, ensure_ascii=False, indent=4) logger.info('Database saved successfully to %s!', libs_data_path) return libs_data_path