[Blender] Baking optimization
This commit is contained in:
parent
90945c0430
commit
25c9cbfbe9
6 changed files with 182 additions and 13 deletions
|
@ -26,6 +26,7 @@ logging.basicConfig(level=logging.INFO)
|
||||||
def export_decorator(func):
|
def export_decorator(func):
|
||||||
|
|
||||||
def wrapper(**kwargs):
|
def wrapper(**kwargs):
|
||||||
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
# add defaults
|
# add defaults
|
||||||
kwargs.setdefault('path', '//')
|
kwargs.setdefault('path', '//')
|
||||||
kwargs.setdefault('subdir', '')
|
kwargs.setdefault('subdir', '')
|
||||||
|
|
78
cg/blender/processing/midpoly_setup.py
Normal file
78
cg/blender/processing/midpoly_setup.py
Normal 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))
|
|
@ -28,6 +28,7 @@ def uv_unwrap(obj_names, angle_limit=30):
|
||||||
''' UV unwrapping and UV packing processing '''
|
''' UV unwrapping and UV packing processing '''
|
||||||
for obj_name in obj_names:
|
for obj_name in obj_names:
|
||||||
obj = bpy.data.objects[obj_name]
|
obj = bpy.data.objects[obj_name]
|
||||||
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
obj.select_set(True)
|
obj.select_set(True)
|
||||||
bpy.context.view_layer.objects.active = obj
|
bpy.context.view_layer.objects.active = obj
|
||||||
bpy.ops.object.mode_set(mode='EDIT')
|
bpy.ops.object.mode_set(mode='EDIT')
|
||||||
|
|
57
cg/blender/utils/collection_tools.py
Normal file
57
cg/blender/utils/collection_tools.py
Normal 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
|
|
@ -16,6 +16,7 @@ Various mesh tools for Edit Mode.
|
||||||
'''
|
'''
|
||||||
__version__ = '0.1'
|
__version__ = '0.1'
|
||||||
|
|
||||||
|
import bpy
|
||||||
import bmesh
|
import bmesh
|
||||||
from math import radians
|
from math import radians
|
||||||
|
|
||||||
|
@ -81,3 +82,22 @@ def select_stratched_edges(me, edge_length_limit=0.002):
|
||||||
edge_max.select_set(True)
|
edge_max.select_set(True)
|
||||||
bmesh.update_edit_mesh(me)
|
bmesh.update_edit_mesh(me)
|
||||||
return 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
|
||||||
|
|
|
@ -14,10 +14,12 @@ from itertools import zip_longest
|
||||||
|
|
||||||
from blender.utils.remove_collections import remove_collections
|
from blender.utils.remove_collections import remove_collections
|
||||||
from blender.utils.cleanup_orphan_data import cleanup_orphan_data
|
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 utils.cmd_proc import cmd_proc
|
||||||
from blender.import_cad.build_blender_scene import json_to_blend
|
from blender.import_cad.build_blender_scene import json_to_blend
|
||||||
from blender.processing.restruct_hierarchy_by_lcs import restruct_hierarchy
|
from blender.processing.restruct_hierarchy_by_lcs import restruct_hierarchy
|
||||||
from blender.processing.highpoly_setup import setup_meshes
|
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.lowpoly_setup import parts_to_shells
|
||||||
from blender.processing.uv_setup import uv_unwrap
|
from blender.processing.uv_setup import uv_unwrap
|
||||||
from blender.export.dae import export_dae
|
from blender.export.dae import export_dae
|
||||||
|
@ -59,6 +61,7 @@ outlet = '_out'
|
||||||
root = '_root'
|
root = '_root'
|
||||||
# CG ASSETS SUFFIXES CONVENTION
|
# CG ASSETS SUFFIXES CONVENTION
|
||||||
hightpoly = '_hp'
|
hightpoly = '_hp'
|
||||||
|
midpoly = 'mp'
|
||||||
lowpoly = '_lp'
|
lowpoly = '_lp'
|
||||||
render = '_render'
|
render = '_render'
|
||||||
|
|
||||||
|
@ -68,6 +71,7 @@ def cg_pipeline(**kwargs):
|
||||||
|
|
||||||
blend_path = kwargs.pop('blend_path', None)
|
blend_path = kwargs.pop('blend_path', None)
|
||||||
mesh_export_path = kwargs.pop('mesh_export_path', None)
|
mesh_export_path = kwargs.pop('mesh_export_path', None)
|
||||||
|
config = kwargs.pop('config', None)
|
||||||
|
|
||||||
# prepare blend file
|
# prepare blend file
|
||||||
remove_collections()
|
remove_collections()
|
||||||
|
@ -88,7 +92,7 @@ def cg_pipeline(**kwargs):
|
||||||
if imported_objects['objs_lcs']:
|
if imported_objects['objs_lcs']:
|
||||||
restruct_hierarchy(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 blend_path is not None:
|
||||||
if not os.path.isdir(os.path.dirname(blend_path)):
|
if not os.path.isdir(os.path.dirname(blend_path)):
|
||||||
os.makedirs(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']
|
for lcs_name in imported_objects['objs_lcs']
|
||||||
if lcs_name.endswith(inlet)]
|
if lcs_name.endswith(inlet)]
|
||||||
|
|
||||||
# prepare lowpoly
|
# prepare midpoly
|
||||||
lowpoly_objs = parts_to_shells(part_names)
|
copy_collections_recursive(
|
||||||
uv_unwrap(lowpoly_objs)
|
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 blend_path is not None:
|
||||||
if not os.path.isdir(os.path.dirname(blend_path)):
|
if not os.path.isdir(os.path.dirname(blend_path)):
|
||||||
os.makedirs(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
|
# export object meshes and urdf
|
||||||
to_urdf = collections.defaultdict(list)
|
to_urdf = collections.defaultdict(list)
|
||||||
|
|
||||||
if lowpoly_objs:
|
if lowpoly_obj_names:
|
||||||
export_objs = lowpoly_objs
|
export_obj_names = lowpoly_obj_names
|
||||||
else:
|
else:
|
||||||
export_objs = sum([imported_objects['objs_foreground'],
|
export_obj_names = sum([imported_objects['objs_foreground'],
|
||||||
imported_objects['objs_background']], [])
|
imported_objects['objs_background']], [])
|
||||||
|
|
||||||
link = {}
|
link = {}
|
||||||
for export_obj in export_objs:
|
for export_obj_name in export_obj_names:
|
||||||
link_prop = {}
|
link_prop = {}
|
||||||
if mesh_export_path is not None:
|
if mesh_export_path is not None:
|
||||||
link_prop['visual'] = export_dae(
|
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(
|
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)
|
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:
|
if config:
|
||||||
for sequence in config['sequences']:
|
for sequence in config['sequences']:
|
||||||
joint = {}
|
joint = {}
|
||||||
|
# TODO collect pairs 0_1, 1_2, 2_3, 3_4, ...
|
||||||
for pair in zip_longest(sequence[0::2], sequence[1::2]):
|
for pair in zip_longest(sequence[0::2], sequence[1::2]):
|
||||||
joint_prop = {}
|
joint_prop = {}
|
||||||
if pair[1]:
|
if pair[1]:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue