framework/cg/blender/processing/restruct_hierarchy_by_lcs.py
2023-12-01 11:34:37 +03:00

162 lines
5.9 KiB
Python

# coding: utf-8
# Copyright (C) 2023 Ilia Kurochkin <brothermechanic@yandex.com>
#
# 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.
'''
DESCRIPTION.
Reorganization and restructuring of assembly structure
based on LCS point objects.
'''
__version__ = '0.2'
import logging
import bpy
import mathutils
from blender.utils.object_relations import (parenting,
unparenting)
from blender.utils.object_transforms import round_transforms
from blender.utils.collection_tools import unlink_from_collections
logger = logging.getLogger(__name__)
def restruct_hierarchy(lcs_names, parts_sequence=None, **cg_config):
''' Execute restructurisation. '''
main_locators = [obj for obj in bpy.data.objects if not obj.parent]
assert len(main_locators) == 1, (
'Scene should have only one root parent locator!')
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])
if name.endswith(cg_config['lcs_outlet']):
lcs_outlet_objects.append(bpy.data.objects[name])
if len(lcs_inlet_objects) > 1:
assert parts_sequence, (
'Parts sequence do not assign! Process stopped!')
for locator in main_locators[0].children:
assert locator.name in parts_sequence, (
'Can not find {} in "parts_sequence" config!'
.format(locator.name))
first_part_obj = bpy.data.objects[parts_sequence[0]]
elif len(lcs_inlet_objects) == 1:
first_part_obj = lcs_inlet_objects[0].parent
else:
# TODO
first_part_obj = None
assert lcs_inlet_objects, (
'Scene do not contain any inlet lcs! Process stopped!')
# create root lcs by parts sequence
root_lcs = None
for lcs in first_part_obj.children:
if lcs.name not in lcs_names:
continue
if lcs.name.endswith(cg_config['lcs_outlet']):
continue
root_lcs_name = cg_config['lcs_root']
root_lcs = bpy.data.objects.new(root_lcs_name, None)
root_lcs.empty_display_type = 'ARROWS'
root_lcs.empty_display_size = 0.15
root_lcs.show_in_front = True
root_lcs.location = lcs.location
root_lcs.rotation_euler = lcs.rotation_euler
root_lcs.parent = lcs.parent
bpy.data.collections[cg_config['lcs_col_name']].objects.link(root_lcs)
logger.info('Root Inlet LCS object created!')
unparenting(root_lcs)
round_transforms(root_lcs)
parenting(root_lcs, main_locators[0])
# retree_by lcs
for lcs in lcs_inlet_objects:
locator = lcs.parent
unparenting(lcs)
round_transforms(lcs)
if locator:
if locator.parent:
unparenting(locator)
parenting(lcs, locator)
parenting(root_lcs, lcs)
for lcs in lcs_outlet_objects:
unparenting(lcs)
round_transforms(lcs)
parenting(
lcs_inlet_objects[lcs_inlet_objects.index(bpy.data.objects[
'{}_{}'.format(lcs.name.rpartition('_')[0], cg_config['lcs_inlet'])
])],
lcs)
# reset transforms for root_lcs
root_lcs.matrix_world = mathutils.Matrix()
# lcs collections
part_names = []
for lcs in root_lcs.children:
# remove unmarked parts
if lcs not in lcs_inlet_objects:
for obj in lcs.children_recursive:
bpy.data.objects.remove(obj, do_unlink=True)
bpy.data.objects.remove(lcs, do_unlink=True)
continue
# collect part names
part_name = None
for locator in lcs.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.children_recursive:
# outlet lcs objects are already in place, don't move it
if obj in lcs_outlet_objects:
continue
unlink_from_collections(obj)
part_col.objects.link(obj)
# parts assembling TODO clones
if len(lcs_inlet_objects) > 1:
for idx, part_name in enumerate(parts_sequence):
# for clones
if part_name not in part_names:
continue
lcs_in = bpy.data.objects[part_name].parent
constraint = lcs_in.constraints.new(type='COPY_TRANSFORMS')
if idx == 0:
constraint.target = root_lcs
continue
# if asm pair exists
if bpy.data.objects.get(parts_sequence[idx - 1]):
lcs_target = [
lcs_out
for lcs_out in bpy.data.objects[parts_sequence[idx - 1]].parent.children
if lcs_out in lcs_outlet_objects][0]
constraint.target = lcs_target
else:
constraint.target = root_lcs
constraint.enabled = False
# for reseet transforms when exporting
for lcs in lcs_outlet_objects:
constraint = lcs.constraints.new(type='COPY_TRANSFORMS')
constraint.target = root_lcs
constraint.enabled = False
logger.info('Restructuring pipeline by LCS finished!')
return part_names