204 lines
7.9 KiB
Python
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
|