framework/cg/blender/processing/restruct_hierarchy_by_lcs.py

172 lines
5.7 KiB
Python

# -*- coding: utf-8 -*-
# Copyright (C) 2023 Ilia Kurochkin <brothermechanic@gmail.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.1'
import logging
import math
import bpy
from mathutils import Matrix
from blender.utils.object_relations import (parenting,
unparenting)
from blender.utils.object_transforms import round_transforms
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
# COLLECTIONS NAMIG CONVENTION
parts_col_name = 'Parts'
lcs_col_name = 'LCS'
hierarchy_col_name = 'Hierarchy'
lowpoly_col_name = 'Lowpoly'
# LCS POINT'S SUFFIXES CONVENTION
inlet = '_in'
outlet = '_out'
root = '_root'
# CG ASSETS SUFFIXES CONVENTION
hightpoly = '_hp'
lowpoly = '_lp'
render = '_render'
def retree_by_lcs(lcs_objects, root_lcs):
''' Organizing project structure based on LCS. '''
for lcs in lcs_objects:
locator = lcs.parent
if lcs.name.endswith(inlet):
unparenting(lcs)
round_transforms(lcs)
if locator.parent:
unparenting(locator)
parenting(lcs, locator)
parenting(root_lcs, lcs)
for lcs in lcs_objects:
if lcs.name.endswith(outlet):
unparenting(lcs)
round_transforms(lcs)
parenting(
lcs_objects[lcs_objects.index(
bpy.data.objects[
'{}{}'.format(lcs.name.split(outlet)[0], inlet)])],
lcs)
root_lcs.matrix_world = Matrix()
return lcs_objects
def closest_lcs(lcs_objects):
''' Finding closest outlet to inlet LCS. '''
target_dists = {}
for target in lcs_objects:
if target.name.endswith(inlet):
dists = {}
for lcs in lcs_objects:
if lcs.name.endswith(outlet):
dist = math.dist(
target.matrix_world.translation,
lcs.matrix_world.translation)
dists[lcs.name] = dist
min_dist = min(dists.values())
if min_dist < 0.01:
min_lcs = [k for k, v in dists.items() if v == min_dist][0]
target_dists[target.name] = min_lcs
return target_dists
def lcs_constrainting(lcs_objects, root_lcs):
''' Placing inlet right on outlet LCS. '''
closests = closest_lcs(lcs_objects)
for lcs in lcs_objects:
if lcs.name in closests:
constraint = lcs.constraints.new(type='COPY_TRANSFORMS')
constraint.target = bpy.data.objects[closests[lcs.name]]
if lcs.name.endswith(outlet):
constraint = lcs.constraints.new(type='COPY_TRANSFORMS')
constraint.target = root_lcs
constraint.enabled = False
for lcs in lcs_objects:
if len(lcs.constraints) == 0:
constraint = lcs.constraints.new(type='COPY_TRANSFORMS')
constraint.target = root_lcs
constraint.enabled = False
return lcs_objects
def unlink_from_col(obj):
''' Unlinking object from all collections. '''
for col in bpy.data.collections:
if obj.name in col.objects:
col.objects.unlink(obj)
return obj
def lcs_collections(root_lcs, lcs_objects):
''' Create LCS based hierarchy. '''
for lcs in root_lcs.children:
lcs_col = bpy.data.collections.new(
'{}{}'.format(lcs.name.split(inlet)[0], hightpoly))
bpy.data.collections[parts_col_name].children.link(lcs_col)
for obj in lcs.children_recursive:
unlink_from_col(obj)
lcs_col.objects.link(obj)
if lcs not in lcs_objects:
unlink_from_col(lcs)
lcs_col.objects.link(lcs)
return root_lcs.children
def restruct_hierarchy(lcs_names):
''' Execute restructurisation. '''
#lcs_objects = bpy.data.collections[lcs_col_name].objects
lcs_objects = []
root_lcs = None
if lcs_names:
for obj_name in lcs_names:
if obj_name.endswith(root):
root_lcs = bpy.data.objects[obj_name]
lcs_objects.append(bpy.data.objects[obj_name])
main_locators = [obj for obj in bpy.data.objects if not obj.parent]
if len(main_locators) > 1:
logger.info('Scene has several main (root) locators! '
'This may cause an error!')
if root_lcs:
lcs_objects = [lcs for lcs in lcs_objects if lcs != root_lcs]
root_locator = root_lcs.parent
unparenting(root_lcs)
round_transforms(root_lcs)
unparenting(root_locator)
parenting(root_lcs, root_locator)
parenting(root_lcs, main_locators[0])
retree_by_lcs(lcs_objects, root_lcs)
lcs_constrainting(lcs_objects, root_lcs)
lcs_collections(root_lcs, lcs_objects)
# remove unused for now collection
bpy.data.collections.remove(bpy.data.collections[hierarchy_col_name])
return logger.info('Restructuring pipeline finished!')
else:
return logger.info('Lost root LCS object!')
else:
return logger.info('Restructuring pipeline canceled!')