rcg-pipeline/rcg_pipeline/libs.py

204 lines
7.9 KiB
Python

# ***** 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 <https://www.gnu.org/licenses/>.
#
# ***** 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