Add UV margin setting.

It's a little hacky, but mostly works.
This commit is contained in:
Nathan Vegdahl 2022-04-09 13:20:31 -07:00
parent e65525da82
commit d7aa7cc8ca
3 changed files with 42 additions and 41 deletions

View File

@ -36,6 +36,7 @@ from .node_groups import \
ensure_footage_group, \ ensure_footage_group, \
ensure_camera_project_group, \ ensure_camera_project_group, \
ensure_feathered_square_group ensure_feathered_square_group
from .uv_utils import leftmost_u
MAIN_NODE_NAME = "Compify Footage" MAIN_NODE_NAME = "Compify Footage"
BAKE_IMAGE_NODE_NAME = "Baked Lighting" BAKE_IMAGE_NODE_NAME = "Baked Lighting"
@ -99,6 +100,7 @@ class CompifyPanel(bpy.types.Panel):
layout.separator(factor=1.0) layout.separator(factor=1.0)
layout.use_property_split = True layout.use_property_split = True
layout.prop(context.scene.compify_config, "bake_uv_margin")
layout.prop(context.scene.compify_config, "bake_image_res") layout.prop(context.scene.compify_config, "bake_image_res")
layout.separator(factor=1.0) layout.separator(factor=1.0)
@ -301,13 +303,29 @@ class CompifyPrepScene(bpy.types.Operator):
bpy.ops.mesh.select_all(action='SELECT') bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.uv.smart_project( bpy.ops.uv.smart_project(
angle_limit=(math.pi/180)*60, # 60 degrees angle_limit=(math.pi/180)*60, # 60 degrees
island_margin=0.005, island_margin=0.001,
area_weight=0.0, area_weight=0.0,
correct_aspect=False, correct_aspect=False,
scale_to_bounds=True, scale_to_bounds=False,
) )
bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='OBJECT')
# We have to do the UV island margins twice, because Blender's
# `island_margin` is stupid beyond belief and corresponds to
# nothing absolute that we can depend on. So what we're doing
# here is saying, "Hey, what was the actual margin achieved
# with `island_margin=0.001`? Okay, now let's redo it based on
# that result." It's still not 100% precise even with this,
# but with a bit of buffer it's close enough.
actual_margin = leftmost_u(context.selected_objects, UV_LAYER_NAME)
actual_margin_pixels = actual_margin * context.scene.compify_config.bake_image_res
target_margin_with_buffer = context.scene.compify_config.bake_uv_margin * (5.0 / 4.0)
correction_factor = target_margin_with_buffer / actual_margin_pixels
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.uv.select_all(action='SELECT')
bpy.ops.uv.pack_islands(rotate=False, margin = 0.001 * correction_factor)
bpy.ops.object.mode_set(mode='OBJECT')
return {'FINISHED'} return {'FINISHED'}
@ -333,7 +351,8 @@ class CompifyBake(bpy.types.Operator):
and context.scene.compify_config.footage != None \ and context.scene.compify_config.footage != None \
and context.scene.compify_config.camera != None \ and context.scene.compify_config.camera != None \
and context.scene.compify_config.geo_collection != None \ and context.scene.compify_config.geo_collection != None \
and len(context.scene.compify_config.geo_collection.all_objects) > 0 and len(context.scene.compify_config.geo_collection.all_objects) > 0 \
and compify_mat_name(context) in bpy.data.materials
def execute(self, context): def execute(self, context):
# Clear operator fields. Not strictly necessary, since they # Clear operator fields. Not strictly necessary, since they
@ -421,7 +440,7 @@ class CompifyBake(bpy.types.Operator):
# filepath='', # filepath='',
# width=512, # width=512,
# height=512, # height=512,
margin=4, margin=context.scene.compify_config.bake_uv_margin,
margin_type='EXTEND', margin_type='EXTEND',
use_selected_to_active=False, use_selected_to_active=False,
max_ray_distance=0.0, max_ray_distance=0.0,
@ -534,6 +553,15 @@ class CompifyFootageConfig(bpy.types.PropertyGroup):
type=bpy.types.Collection, type=bpy.types.Collection,
name="Footage Lights Collection", name="Footage Lights Collection",
) )
bake_uv_margin: bpy.props.IntProperty(
name="Bake UV Margin",
subtype='PIXEL',
options=set(), # Not animatable.
default=4,
min=0,
max=2**16,
soft_max=32,
)
bake_image_res: bpy.props.IntProperty( bake_image_res: bpy.props.IntProperty(
name="Bake Resolution", name="Bake Resolution",
subtype='PIXEL', subtype='PIXEL',

View File

@ -1,37 +0,0 @@
from math import inf
from bpy_extras.mesh_utils import mesh_linked_uv_islands
def uv_island_bbox_area_sum_multi_object(mesh_objects, uv_layer_name):
area_sum = 0.0
for obj in mesh_objects:
area_sum += uv_island_bbox_area_sum(obj.data, uv_layer_name)
return area_sum
def uv_island_bbox_area_sum(mesh, uv_layer_name):
""" Returns the sum of the area of the bounding boxes of all the UV
islands in the given mesh for the given uv layer.
"""
uvs = mesh.uv_layers[uv_layer_name].data
islands = mesh_linked_uv_islands(mesh)
bbox_area_sum = 0.0
for island in islands:
# Calculate the island's bounding box.
min_u = inf
max_u = -inf
min_v = inf
max_v = -inf
for face_idx in island:
for loop_idx in mesh.polygons[face_idx].loop_indices:
uv = uvs[mesh.loops[loop_idx].vertex_index].uv
min_u = min(min_u, uv[0])
max_u = max(max_u, uv[0])
min_v = min(min_v, uv[1])
max_v = max(max_v, uv[1])
# Add it's area to the sum.
bbox_area_sum += ((max_u - min_u) * (max_v - min_v))**0.5
return bbox_area_sum

10
uv_utils.py Normal file
View File

@ -0,0 +1,10 @@
from math import inf
def leftmost_u(mesh_objects, uv_layer_name):
leftmost = inf
for obj in mesh_objects:
uvs = obj.data.uv_layers[uv_layer_name].data
for uv in uvs:
leftmost = min(leftmost, uv.uv[0])
return leftmost