[Blender 3.6] UV Packer

This commit is contained in:
brothermechanic 2023-09-12 09:07:30 +00:00 committed by Igor Brylyov
parent 80e3c58913
commit 78e31ea49c
18 changed files with 635 additions and 347 deletions

View file

@ -35,10 +35,10 @@ logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
# COLLECTIONS NAMIG CONVENTION
part_col_name = 'Part'
part_col_name = 'Parts'
lcs_col_name = 'LCS'
hierarchy_col_name = 'Hierarchy'
lowpoly_col_name = 'Lowpoly Parts'
lowpoly_col_name = 'Lowpoly'
# LCS POINT'S SUFFIXES CONVENTION
inlet = '_in'
outlet = '_out'
@ -91,8 +91,6 @@ def json_to_blend(js_data):
bmesh.update()
bobj = bpy.data.objects.new(js_obj, bmesh)
part_collection.objects.link(bobj)
else:
logger.info('%s has not mesh data!', js_obj)
if bobj:
fc_placement(bobj,
@ -142,5 +140,5 @@ def json_to_blend(js_data):
# TODO
# update do not dork
logger.info('Imported %s objects without errors', len(bobjs))
logger.info('Generated %s objects without errors', len(bobjs))
return bobjs_for_render

View file

@ -17,7 +17,6 @@ Basic mesh processing for asset pipeline.
__version__ = '0.2'
import logging
import sys
import bpy
import math
@ -51,7 +50,7 @@ def setup_meshes(bobjs, cleanup=False, sharpness=False, shading=False):
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.mesh.select_mode(type='EDGE')
bpy.ops.mesh.edges_select_sharp( sharpness=math.radians(12) )
bpy.ops.mesh.edges_select_sharp(sharpness=math.radians(12))
bpy.ops.mesh.mark_sharp()
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.uv.smart_project()

View file

@ -0,0 +1,140 @@
# -*- 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.
Create lowpoly shells from parts collections.
'''
__version__ = '0.1'
import logging
import bpy
from blender.utils.generative_modifiers import shell_remesher
from blender.utils.object_converter import mesh_to_mesh
from blender.utils.object_relations import parenting
from blender.utils.mesh_tools import select_peaks, select_stratched_edges
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 parts_to_shells(hightpoly_part_names):
''' Create lowpoly shells from parts collections. '''
logger.info('Lowpoly shells creation launched...')
lowpoly_col = bpy.data.collections.new(lowpoly_col_name)
bpy.context.scene.collection.children.link(lowpoly_col)
for part_name in hightpoly_part_names:
# generate lowpoly objects from part collections
lowpoly_name = ('{}{}'.format(part_name, lowpoly))
lowpoly_mesh = bpy.data.meshes.new(lowpoly_name)
lowpoly_obj = bpy.data.objects.new(lowpoly_name, lowpoly_mesh)
bpy.context.view_layer.update()
part_inlet = bpy.data.objects.get('{}{}'.format(part_name, inlet))
lowpoly_obj.matrix_world = part_inlet.matrix_world.copy()
parenting(part_inlet, lowpoly_obj)
lowpoly_col.objects.link(lowpoly_obj)
shell_remesher(lowpoly_obj, 'remesh_nodes', 'robossembler')
part_col = bpy.data.collections[('{}{}'.format(part_name, hightpoly))]
lowpoly_obj.modifiers['remesh_nodes']['Input_0'] = part_col
remesh_voxel = lowpoly_obj.modifiers.new('remesh_voxel', type='REMESH')
remesh_voxel.mode = 'VOXEL'
remesh_voxel.voxel_size = 0.001
remesh_sharp = lowpoly_obj.modifiers.new('remesh_sharp', type='REMESH')
remesh_sharp.mode = 'SHARP'
remesh_sharp.octree_depth = 7
decimate = lowpoly_obj.modifiers.new('decimate', type='DECIMATE')
decimate.decimate_type = 'COLLAPSE'
decimate.ratio = 0.1
# apply all modifiers to mesh
parenting(part_inlet, mesh_to_mesh(lowpoly_obj))
# fix non_manifold shape
for lowpoly_obj in lowpoly_col.objects:
bpy.ops.object.select_all(action='DESELECT')
lowpoly_obj.select_set(state=True)
bpy.context.view_layer.objects.active = lowpoly_obj
bpy.ops.object.mode_set(mode='EDIT')
# pass 1
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')
select_peaks(lowpoly_obj.data)
bpy.ops.mesh.select_non_manifold()
bpy.ops.mesh.dissolve_mode(use_verts=True, use_boundary_tear=False)
bpy.ops.mesh.delete(type='VERT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.quads_convert_to_tris(quad_method='BEAUTY', ngon_method='BEAUTY')
# pass 2
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')
select_peaks(lowpoly_obj.data)
bpy.ops.mesh.select_non_manifold()
bpy.ops.mesh.dissolve_mode(use_verts=True, use_boundary_tear=False)
bpy.ops.mesh.delete(type='VERT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.quads_convert_to_tris(quad_method='BEAUTY', ngon_method='BEAUTY')
# pass 3
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')
select_peaks(lowpoly_obj.data)
bpy.ops.mesh.select_non_manifold()
bpy.ops.mesh.dissolve_mode(use_verts=True, use_boundary_tear=False)
bpy.ops.mesh.delete(type='VERT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.quads_convert_to_tris(quad_method='BEAUTY', ngon_method='BEAUTY')
# pass 4
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT')
select_peaks(lowpoly_obj.data)
bpy.ops.mesh.select_non_manifold()
bpy.ops.mesh.dissolve_mode(use_verts=True, use_boundary_tear=False)
bpy.ops.mesh.delete(type='VERT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.quads_convert_to_tris(quad_method='BEAUTY', ngon_method='BEAUTY')
# pass 5
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.mesh.select_mode(type='EDGE')
select_stratched_edges(lowpoly_obj.data)
bpy.ops.mesh.dissolve_mode(use_verts=True)
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.quads_convert_to_tris(quad_method='BEAUTY', ngon_method='BEAUTY')
bpy.ops.mesh.normals_make_consistent()
# final
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.mesh.select_mode(type='FACE')
bpy.ops.object.mode_set(mode='OBJECT')
logger.info('Lowpoly shells created successfully!')
return lowpoly_col.objects

View file

@ -31,10 +31,10 @@ logging.basicConfig(level=logging.INFO)
# COLLECTIONS NAMIG CONVENTION
parts_col_name = 'Import Parts'
lcs_col_name = 'Import LCS'
hierarchy_col_name = 'Import Hierarchy'
lowpoly_col_name = 'Lowpoly Parts'
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'

View file

@ -0,0 +1,64 @@
# 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.
UV unwrapping and UV packing processing.
'''
__version__ = '0.1'
import logging
import math
import bpy
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
def uv_unwrap(objs, angle_limit=30):
''' UV unwrapping and UV packing processing '''
for obj in objs:
obj.select_set(True)
bpy.context.view_layer.objects.active = obj
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
# unwrapping
bpy.ops.uv.smart_project(angle_limit=math.radians(angle_limit))
# packing
bpy.ops.uv.pack_islands(udim_source='CLOSEST_UDIM',
rotate=True,
rotate_method='ANY',
scale=True,
merge_overlap=False,
margin_method='ADD',
margin=(1 / 256),
pin=False,
pin_method='LOCKED',
shape_method='CONCAVE')
bpy.ops.uv.pack_islands(udim_source='CLOSEST_UDIM',
rotate=True,
rotate_method='ANY',
scale=True,
merge_overlap=False,
margin_method='ADD',
margin=(1 / 256),
pin=False,
pin_method='LOCKED',
shape_method='CONCAVE')
bpy.ops.object.mode_set(mode='OBJECT')
obj.select_set(False)
logger.info('UV unwrapping and UV packing processing done successfully!')
return objs

View file

@ -1,86 +0,0 @@
# -*- 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.
Create lowpoly shells from parts collections.
'''
__version__ = '0.1'
import logging
import sys
import bpy
import math
from blender.utils.generative_modifiers import shell_remesher
from blender.utils.object_converter import convert_mesh_to_mesh
from blender.utils.object_relations import parenting
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
# COLLECTIONS NAMIG CONVENTION
parts_col_name = 'Import Parts'
lcs_col_name = 'Import LCS'
hierarchy_col_name = 'Import Hierarchy'
lowpoly_col_name = 'Lowpoly Parts'
# LCS POINT'S SUFFIXES CONVENTION
inlet = '_in'
outlet = '_out'
root = '_root'
# CG ASSETS SUFFIXES CONVENTION
hightpoly = '_hp'
lowpoly = '_lp'
render = '_render'
def parts_to_shells(hightpoly_part_names):
''' Create lowpoly shells from parts collections. '''
logger.info('Lowpoly shells creation launched...')
lowpoly_col = bpy.data.collections.new(lowpoly_col_name)
bpy.context.scene.collection.children.link(lowpoly_col)
for part_name in hightpoly_part_names:
# generate lowpoly objects from part collections
lowpoly_name = ('{}{}'.format(part_name, lowpoly))
lowpoly_mesh = bpy.data.meshes.new(lowpoly_name)
lowpoly_obj = bpy.data.objects.new(lowpoly_name, lowpoly_mesh)
bpy.context.view_layer.update()
part_inlet = bpy.data.objects.get('{}{}'.format(part_name, inlet))
lowpoly_obj.matrix_world = part_inlet.matrix_world.copy()
parenting(part_inlet, lowpoly_obj)
lowpoly_col.objects.link(lowpoly_obj)
shell_remesher(lowpoly_obj, 'remesh_nodes', 'robossembler')
part_col = bpy.data.collections[('{}{}'.format(part_name, hightpoly))]
lowpoly_obj.modifiers['remesh_nodes']['Input_0'] = part_col
remesh_voxel = lowpoly_obj.modifiers.new('remesh_voxel', type='REMESH')
remesh_voxel.mode = 'VOXEL'
remesh_voxel.voxel_size = 0.001
remesh_sharp = lowpoly_obj.modifiers.new('remesh_sharp', type='REMESH')
remesh_sharp.mode = 'SHARP'
remesh_sharp.octree_depth = 7
decimate = lowpoly_obj.modifiers.new('decimate', type='DECIMATE')
decimate.decimate_type = 'COLLAPSE'
decimate.ratio = 0.1
# apply all modifiers to mesh
parenting(part_inlet, convert_mesh_to_mesh(lowpoly_obj))
logger.info('Lowpoly shells created successfully!')
return lowpoly_col.objects

View file

@ -2,6 +2,7 @@
"""
DESCRIPTION.
This script setup render settings for reduce rendertime!
DEPRECATED
"""
__version__ = "0.1"

View file

@ -0,0 +1,83 @@
# -*- 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 mesh tools for Edit Mode.
'''
__version__ = '0.1'
import bmesh
from math import radians
def select_peaks(me, peak_limit_angle=60, peak_accuracy_angle=10):
''' Select sharp vertices that stand alone. '''
bm = bmesh.from_edit_mesh(me)
def is_sharp(vert, eps=radians(peak_limit_angle)):
sharps = []
face_before = None
for face in vert.link_faces:
if face_before:
face_angle = face.normal.angle(face_before.normal)
if face_angle > radians(peak_accuracy_angle):
angle = vert.normal.angle(face.normal)
if angle > eps:
sharps.append(angle)
face_before = face
return (
(len(sharps) + 1) == len(vert.link_faces)
or (len(sharps) + 2) == len(vert.link_faces)
)
def non_single(vert):
for edge in vert.link_edges:
if edge.other_vert(vert).select:
return False
return True
for v in bm.verts:
v.select_set(
is_sharp(v)
)
for v in bm.verts:
if v.select:
v.select_set(
non_single(v)
)
bmesh.update_edit_mesh(me)
return me
def select_zero_faces(me):
''' Select very small faces. '''
bm = bmesh.from_edit_mesh(me)
[f.select_set(True) for f in bm.faces if f.calc_area() < 1e-7]
bmesh.update_edit_mesh(me)
return me
def select_stratched_edges(me, edge_length_limit=0.002):
''' Select very stratched edges of small faces. '''
bm = bmesh.from_edit_mesh(me)
faces_stratched = [f for f in bm.faces if f.calc_area() < 1e-6]
for face in faces_stratched:
edges_lengths = {e: e.calc_length() for e in face.edges}
edge_max_length = max(edges_lengths.values())
if edge_max_length > edge_length_limit:
edge_max = [k for k, v in edges_lengths.items() if v == edge_max_length][0]
edge_max.select_set(True)
bmesh.update_edit_mesh(me)
return me

View file

@ -19,7 +19,7 @@ __version__ = '0.1'
import bpy
def convert_mesh_to_mesh(obj):
def mesh_to_mesh(obj):
''' Convert all deformers and modifiers of object to it's mesh. '''
if obj and obj.type == 'MESH':
deg = bpy.context.evaluated_depsgraph_get()