[Blender] Baking optimization

This commit is contained in:
brothermechanic 2023-10-16 12:21:34 +00:00 committed by Igor Brylyov
parent 90945c0430
commit 25c9cbfbe9
6 changed files with 182 additions and 13 deletions

View file

@ -26,6 +26,7 @@ logging.basicConfig(level=logging.INFO)
def export_decorator(func):
def wrapper(**kwargs):
bpy.ops.object.select_all(action='DESELECT')
# add defaults
kwargs.setdefault('path', '//')
kwargs.setdefault('subdir', '')

View file

@ -0,0 +1,78 @@
# -*- 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.
Basic mesh processing for asset pipeline.
'''
__version__ = '0.1'
import logging
import bpy
import math
from blender.utils.object_relations import parenting
from blender.utils.remove_collections import remove_collections
from blender.utils.mesh_tools import collect_less_volume_objs
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
# COLLECTIONS NAMIG CONVENTION
parts_col_name = 'Parts'
lcs_col_name = 'LCS'
hierarchy_col_name = 'Hierarchy'
midpoly_col_name = 'Midpoly'
lowpoly_col_name = 'Lowpoly'
# LCS POINT'S SUFFIXES CONVENTION
inlet = '_in'
outlet = '_out'
root = '_root'
# CG ASSETS SUFFIXES CONVENTION
hightpoly = '_hp'
midpoly = 'mp'
lowpoly = '_lp'
render = '_render'
def hightpoly_collections_to_midpoly(part_names):
''' Convert part's collecttions to single objects. '''
for part_name in part_names:
midpoly_name = '_'.join((part_name, midpoly))
midpoly_mesh = bpy.data.meshes.new(midpoly_name)
midpoly_obj = bpy.data.objects.new(midpoly_name, midpoly_mesh)
bpy.context.view_layer.update()
part_inlet = bpy.data.objects.get('{}{}'.format(part_name, inlet))
midpoly_obj.matrix_world = part_inlet.matrix_world.copy()
parenting(part_inlet, midpoly_obj)
midpoly_parts_col = bpy.data.collections['_'.join((parts_col_name, midpoly))]
midpoly_parts_col.objects.link(midpoly_obj)
for col in midpoly_parts_col.children.keys():
if part_name in col:
bpy.ops.object.select_all(action='DESELECT')
exclude_objs = collect_less_volume_objs(
bpy.data.collections[col].objects, min_volume=2.0e-06)
for obj in bpy.data.collections[col].objects:
if obj not in exclude_objs:
obj.select_set(state=True)
midpoly_obj.select_set(state=True)
bpy.context.view_layer.objects.active = midpoly_obj
bpy.ops.object.join()
bpy.ops.object.shade_smooth(use_auto_smooth=True)
break
midpoly_parts_col.name = midpoly_col_name
for col in midpoly_parts_col.children.keys():
remove_collections(col)
return logger.info('Setup of %s midpoly meshes is finished!', len(part_names))

View file

@ -28,6 +28,7 @@ def uv_unwrap(obj_names, angle_limit=30):
''' UV unwrapping and UV packing processing '''
for obj_name in obj_names:
obj = bpy.data.objects[obj_name]
bpy.ops.object.select_all(action='DESELECT')
obj.select_set(True)
bpy.context.view_layer.objects.active = obj
bpy.ops.object.mode_set(mode='EDIT')

View file

@ -0,0 +1,57 @@
# 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.
Various collection tools.
'''
__version__ = '0.1'
import bpy
from collections import defaultdict
def copy_objects(from_col, to_col, linked, double_lut):
'''Function copying objects from collection to collection.'''
for obj in from_col.objects:
double = obj.copy()
if not linked and obj.data:
double.data = double.data.copy()
to_col.objects.link(double)
double_lut[obj] = double
return True
def copy_collections_recursive(collection, suffix='copy', linked=False):
'''Function recursive copying collection.'''
double_lut = defaultdict(lambda: None)
parent = [p for p in (bpy.data.collections[:] + [bpy.context.scene.collection])
if collection.name in p.children.keys()][0]
def _copy(parent, collection, suffix, linked=False):
'''Function copying collection.'''
clone_collection = bpy.data.collections.new(
'_'.join((collection.name, suffix))
)
copy_objects(collection, clone_collection, linked, double_lut)
for _collection in collection.children:
_copy(clone_collection, _collection, suffix, linked)
parent.children.link(clone_collection)
_copy(parent, collection, suffix, linked)
for obj, double in tuple(double_lut.items()):
parent = double_lut[obj.parent]
if parent:
double.parent = parent
return True

View file

@ -16,6 +16,7 @@ Various mesh tools for Edit Mode.
'''
__version__ = '0.1'
import bpy
import bmesh
from math import radians
@ -81,3 +82,22 @@ def select_stratched_edges(me, edge_length_limit=0.002):
edge_max.select_set(True)
bmesh.update_edit_mesh(me)
return me
def collect_less_volume_objs(objs: list, min_volume):
''' Separate selection for less volume objects. '''
less_volume_objs = []
for obj in objs:
bpy.ops.object.select_all(action='DESELECT')
if obj.type != 'MESH':
continue
# requed for bmesh
obj.hide_set(False)
obj.select_set(state=True)
if obj.type == 'MESH':
bpy.ops.object.mode_set(mode='EDIT')
bm = bmesh.from_edit_mesh(obj.data)
if bm.calc_volume() < min_volume:
less_volume_objs.append(obj)
bpy.ops.object.mode_set(mode='OBJECT')
return less_volume_objs

View file

@ -14,10 +14,12 @@ from itertools import zip_longest
from blender.utils.remove_collections import remove_collections
from blender.utils.cleanup_orphan_data import cleanup_orphan_data
from blender.utils.collection_tools import copy_collections_recursive
from utils.cmd_proc import cmd_proc
from blender.import_cad.build_blender_scene import json_to_blend
from blender.processing.restruct_hierarchy_by_lcs import restruct_hierarchy
from blender.processing.highpoly_setup import setup_meshes
from blender.processing.midpoly_setup import hightpoly_collections_to_midpoly
from blender.processing.lowpoly_setup import parts_to_shells
from blender.processing.uv_setup import uv_unwrap
from blender.export.dae import export_dae
@ -59,6 +61,7 @@ outlet = '_out'
root = '_root'
# CG ASSETS SUFFIXES CONVENTION
hightpoly = '_hp'
midpoly = 'mp'
lowpoly = '_lp'
render = '_render'
@ -68,6 +71,7 @@ def cg_pipeline(**kwargs):
blend_path = kwargs.pop('blend_path', None)
mesh_export_path = kwargs.pop('mesh_export_path', None)
config = kwargs.pop('config', None)
# prepare blend file
remove_collections()
@ -88,7 +92,7 @@ def cg_pipeline(**kwargs):
if imported_objects['objs_lcs']:
restruct_hierarchy(imported_objects['objs_lcs'])
# save blender scene
# save import in blender scene
if blend_path is not None:
if not os.path.isdir(os.path.dirname(blend_path)):
os.makedirs(os.path.dirname(blend_path))
@ -105,11 +109,18 @@ def cg_pipeline(**kwargs):
for lcs_name in imported_objects['objs_lcs']
if lcs_name.endswith(inlet)]
# prepare lowpoly
lowpoly_objs = parts_to_shells(part_names)
uv_unwrap(lowpoly_objs)
# prepare midpoly
copy_collections_recursive(
bpy.data.collections[parts_col_name], suffix=midpoly
)
hightpoly_collections_to_midpoly(part_names)
# save blender scene
# prepare lowpoly
lowpoly_obj_names = parts_to_shells(part_names)
uv_unwrap(lowpoly_obj_names)
# save lowpoly in blender scene
if blend_path is not None:
if not os.path.isdir(os.path.dirname(blend_path)):
os.makedirs(os.path.dirname(blend_path))
@ -118,29 +129,30 @@ def cg_pipeline(**kwargs):
# export object meshes and urdf
to_urdf = collections.defaultdict(list)
if lowpoly_objs:
export_objs = lowpoly_objs
if lowpoly_obj_names:
export_obj_names = lowpoly_obj_names
else:
export_objs = sum([imported_objects['objs_foreground'],
export_obj_names = sum([imported_objects['objs_foreground'],
imported_objects['objs_background']], [])
link = {}
for export_obj in export_objs:
for export_obj_name in export_obj_names:
link_prop = {}
if mesh_export_path is not None:
link_prop['visual'] = export_dae(
obj_name=export_obj, path=mesh_export_path, subdir='visual')
obj_name=export_obj_name, path=mesh_export_path, subdir='visual')
link_prop['collision'] = export_stl(
obj_name=export_obj, path=mesh_export_path, subdir='collision')
obj_name=export_obj_name, path=mesh_export_path, subdir='collision')
link[export_obj] = link_prop
link[export_obj_name] = link_prop
to_urdf['links'].append(link)
config = {'sequences': [['cube1', 'cube2', 'cube3', 'cube4'], ['cube2', 'cube1', 'cube4', 'cube3']]}
#config = {'sequences': [['cube1', 'cube2', 'cube3', 'cube4'], ['cube2', 'cube1', 'cube4', 'cube3']]}
if config:
for sequence in config['sequences']:
joint = {}
# TODO collect pairs 0_1, 1_2, 2_3, 3_4, ...
for pair in zip_longest(sequence[0::2], sequence[1::2]):
joint_prop = {}
if pair[1]: