286 lines
10 KiB
Python
286 lines
10 KiB
Python
import bpy
|
|
from bpy.utils import register_class, unregister_class
|
|
from bpy.types import Operator
|
|
from bpy.props import FloatProperty, EnumProperty, BoolProperty, StringProperty
|
|
|
|
from .utils import SBConstants, select_only_this
|
|
from .messages import print_message
|
|
from .bake_operators.common_bake_support import match_high_low_objects
|
|
|
|
def rearrange_uvs(obj, uv_name):
|
|
|
|
uvs = obj.data.uv_layers
|
|
orig_active_name = uvs.active.name
|
|
orig_active_render_name = [uv.name for uv in uvs if uv.active_render][0]
|
|
|
|
while uvs[0].name != uv_name:
|
|
|
|
#Move to bottom
|
|
name = uvs[0].name
|
|
uvs.active = uvs[0]
|
|
uvs.new()
|
|
uvs.remove(uvs[0])
|
|
uvs[-1].name = name
|
|
|
|
uvs.active = uvs[orig_active_name]
|
|
uvs[orig_active_render_name].active_render = True
|
|
|
|
def pack_uvs(self, context):
|
|
sbp = context.scene.SimpleBake_Props
|
|
|
|
bpy.ops.uv.select_all(action="SELECT")
|
|
|
|
#Always average - still relevant for pre-Blender 3.6
|
|
if sbp.new_uv_method == "CombineExisting" and self.average_islands:
|
|
bpy.ops.uv.average_islands_scale()
|
|
|
|
n = int(''.join(c for c in bpy.app.version_string if c.isdigit()))
|
|
if n >= 360:
|
|
bpy.ops.uv.pack_islands("EXEC_DEFAULT", udim_source=sbp.uvp_pack_to, rotate=sbp.uvp_rotate, rotate_method=sbp.uvp_rotation_method, scale=sbp.uvp_scale, merge_overlap=sbp.uvp_merge_overlapping, margin_method=sbp.uvp_margin_method, margin=sbp.uvpackmargin, pin=sbp.uvp_lock_pinned, pin_method=sbp.uvp_lock_method, shape_method=sbp.uvp_shape_method)
|
|
else: # Pre Blender 3.6
|
|
bpy.ops.uv.pack_islands(rotate=True, margin=sbp.uvpackmargin)
|
|
|
|
bpy.ops.object.mode_set(mode="OBJECT", toggle=False)
|
|
|
|
|
|
class SimpleBake_OT_Process_UVs(Operator):
|
|
"""Start the bake operation"""
|
|
bl_idname = "simplebake.process_uvs"
|
|
bl_description = "Prepare UV maps on the bake objects"
|
|
bl_label = "Prepare UVs"
|
|
|
|
def smart_project_to_atlas(self, context):
|
|
|
|
sbp = context.scene.SimpleBake_Props
|
|
|
|
print_message(context, "Smart projecting all objects to atlas map")
|
|
|
|
#Selection
|
|
bpy.ops.object.select_all(action="DESELECT")
|
|
for obj in self.bake_objects:
|
|
obj.select_set(state=True)
|
|
|
|
#Make sure we have an active object
|
|
if context.active_object == None:
|
|
context.view_layer.objects.active = self.bake_objects[0]
|
|
|
|
for obj in self.bake_objects:
|
|
if("SimpleBake" in obj.data.uv_layers):
|
|
obj.data.uv_layers.remove(obj.data.uv_layers["SimpleBake"])
|
|
|
|
obj.data.uv_layers.new(name="SimpleBake")
|
|
obj.data.uv_layers["SimpleBake"].active = True
|
|
|
|
bpy.ops.object.mode_set(mode="EDIT", toggle=False) #Enter edit mode
|
|
bpy.ops.mesh.reveal()
|
|
bpy.ops.mesh.select_all(action="SELECT")
|
|
|
|
#Unwrap
|
|
bpy.ops.uv.smart_project(angle_limit=self.angle_limit, island_margin=self.island_margin, area_weight=self.area_weight, correct_aspect=self.uvcorrectaspect, scale_to_bounds=self.scale_to_bounds)
|
|
|
|
#Packing
|
|
#pack_uvs(self)
|
|
|
|
bpy.ops.object.mode_set(mode="OBJECT", toggle=False)
|
|
return
|
|
|
|
def combine_existing_to_atlas(self, context):
|
|
|
|
sbp = context.scene.SimpleBake_Props
|
|
|
|
print_message(context, "Combining existing UVs into atlas map")
|
|
|
|
#Selection
|
|
# bpy.ops.object.select_all(action="DESELECT")
|
|
|
|
for obj in bpy.context.selected_objects:
|
|
obj.select_set(False)
|
|
|
|
for obj in self.bake_objects:
|
|
obj.select_set(state=True)
|
|
|
|
#Make sure we have an active object
|
|
if context.active_object == None:
|
|
context.view_layer.objects.active = self.bake_objects[0]
|
|
|
|
for obj in self.bake_objects:
|
|
if("GENERATED_SimpleBake_Atlas" in obj.data.uv_layers):
|
|
obj.data.uv_layers.remove(obj.data.uv_layers["GENERATED_SimpleBake_Atlas"])
|
|
|
|
u = obj.data.uv_layers.new(name="GENERATED_SimpleBake_Atlas")
|
|
u.active = True
|
|
|
|
#With everything selected, pack into one big map
|
|
bpy.ops.object.mode_set(mode="EDIT", toggle=False)
|
|
bpy.ops.mesh.reveal()
|
|
|
|
bpy.ops.mesh.select_all(action="SELECT")
|
|
|
|
pack_uvs(self, context)
|
|
|
|
bpy.ops.object.mode_set(mode="OBJECT", toggle=False)
|
|
return
|
|
|
|
|
|
def smart_project_individual_objects(self, context):
|
|
|
|
print_message(context, "Smart projecting each object individually")
|
|
|
|
for obj in self.bake_objects:
|
|
|
|
select_only_this(context, obj)
|
|
|
|
if("SimpleBake" in obj.data.uv_layers):
|
|
obj.data.uv_layers.remove(obj.data.uv_layers["SimpleBake"])
|
|
obj.data.uv_layers.new(name="SimpleBake")
|
|
obj.data.uv_layers["SimpleBake"].active = True
|
|
|
|
bpy.ops.object.mode_set(mode="EDIT", toggle=False)
|
|
|
|
bpy.ops.mesh.reveal()
|
|
bpy.ops.mesh.select_all(action="SELECT")
|
|
|
|
bpy.ops.uv.smart_project(angle_limit=self.angle_limit, island_margin=self.island_margin, area_weight=self.area_weight, correct_aspect=self.uvcorrectaspect, scale_to_bounds=self.scale_to_bounds)
|
|
|
|
bpy.ops.object.mode_set(mode="OBJECT", toggle=False)
|
|
|
|
return
|
|
|
|
|
|
def per_mat_pack_uvs_to_bounds(self, context):
|
|
|
|
sbp = context.scene.SimpleBake_Props
|
|
print_message(context, "Packing UVs for each material into new UV map")
|
|
|
|
bpy.ops.object.select_all(action="DESELECT")
|
|
|
|
for obj in self.bake_objects:
|
|
|
|
# Copy existing UV data into a fresh SimpleBake layer
|
|
source_layer = obj.data.uv_layers.active
|
|
if "SimpleBake" in obj.data.uv_layers:
|
|
obj.data.uv_layers.remove(obj.data.uv_layers["SimpleBake"])
|
|
new_layer = obj.data.uv_layers.new(name="SimpleBake")
|
|
if source_layer:
|
|
for i, item in enumerate(source_layer.data):
|
|
new_layer.data[i].uv = item.uv.copy()
|
|
new_layer.active = True
|
|
|
|
select_only_this(context, obj)
|
|
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
|
|
bpy.ops.mesh.reveal()
|
|
|
|
# Get enabled materials — strip SBM_/SBW_ prefix for comparison
|
|
enabled_mat_names = {
|
|
e.mat_name for e in sbp.mat_bake_list
|
|
if e.obj_name == obj.name and e.enabled
|
|
}
|
|
|
|
i = 0
|
|
for slot in obj.material_slots:
|
|
if slot.material is None:
|
|
i += 1
|
|
continue
|
|
mat_name = slot.material.name
|
|
if mat_name.startswith("SBW_"): mat_name = mat_name[4:]
|
|
if mat_name.startswith("SBM_"): mat_name = mat_name[4:]
|
|
if enabled_mat_names and mat_name not in enabled_mat_names:
|
|
i += 1
|
|
continue
|
|
obj.active_material_index = i
|
|
bpy.ops.mesh.select_all(action="DESELECT")
|
|
bpy.ops.object.material_slot_select()
|
|
bpy.ops.uv.pack_islands(scale=True, margin=self.island_margin)
|
|
i += 1
|
|
|
|
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
|
|
|
|
return
|
|
|
|
def check_prefer_sb_maps(self, context):
|
|
if self.prefer_sb_uv_maps:
|
|
print_message(context, "Preferring existing UV maps called SimpleBake. Setting them to active")
|
|
for obj in self.bake_objects:
|
|
if("SimpleBake" in obj.data.uv_layers):
|
|
obj.data.uv_layers["SimpleBake"].active = True
|
|
return
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
sbp = context.scene.SimpleBake_Props
|
|
|
|
#Pull what we need from the panel
|
|
self.island_margin = sbp.unwrapmargin
|
|
self.uvcorrectaspect = sbp.uvcorrectaspect
|
|
self.angle_limit = sbp.angle_limit
|
|
self.area_weight = sbp.area_weight
|
|
self.scale_to_bounds = sbp.scale_to_bounds
|
|
self.average_islands = sbp.average_uv_size
|
|
self.prefer_sb_uv_maps = sbp.prefer_existing_sbmap
|
|
self.new_uv_option = sbp.new_uv_option
|
|
self.new_uv_method = sbp.new_uv_method
|
|
self.expand_mat_uvs = sbp.expand_mat_uvs
|
|
self.uvpackmargin = sbp.uvpackmargin
|
|
|
|
|
|
#Set the bake objects
|
|
if (sbp.selected_s2a or sbp.cycles_s2a) and sbp.s2a_opmode=="automatch":
|
|
from .bake_operators.auto_match_operators import SimpleBake_OT_AutoMatch_Operation as bo
|
|
hl_matches = bo.hl_matches
|
|
self.bake_objects = [context.scene.objects[name] for name in hl_matches.keys()]
|
|
|
|
elif sbp.selected_s2a: #Covering decals or standard
|
|
self.bake_objects =[sbp.targetobj]
|
|
elif sbp.cycles_s2a:
|
|
self.bake_objects =[sbp.targetobj_cycles]
|
|
else:
|
|
self.bake_objects = [i.obj_point for i in sbp.objects_list]
|
|
|
|
|
|
#Decide what we are actually doing
|
|
if sbp.tex_per_mat and self.expand_mat_uvs:
|
|
self.per_mat_pack_uvs_to_bounds(context)
|
|
elif self.new_uv_option and len(self.bake_objects)==1:
|
|
self.smart_project_individual_objects(context)
|
|
elif self.new_uv_option and self.new_uv_method == SBConstants.NEWUV_SMART_INDIVIDUAL:
|
|
self.smart_project_individual_objects(context)
|
|
elif self.new_uv_option and self.new_uv_method == SBConstants.NEWUV_SMART_ATLAS:
|
|
self.smart_project_to_atlas(context)
|
|
elif self.new_uv_option and self.new_uv_method == SBConstants.NEWUV_COMBINE_EXISTING:
|
|
self.combine_existing_to_atlas(context)
|
|
elif not self.new_uv_option:
|
|
print_message(context, "We are working with existing UV maps")
|
|
self.check_prefer_sb_maps(context)
|
|
else:
|
|
print_message(context, "Something went wrong processing UVs")
|
|
|
|
|
|
for obj in self.bake_objects:
|
|
obj["SB_uv_used_for_bake"] = obj.data.uv_layers.active.name
|
|
|
|
if self.new_uv_option and sbp.move_new_uvs_to_top:
|
|
rearrange_uvs(obj, obj["SB_uv_used_for_bake"])
|
|
|
|
|
|
|
|
|
|
|
|
return {'FINISHED'}
|
|
|
|
classes = ([
|
|
SimpleBake_OT_Process_UVs#,
|
|
#SimpleBake_OT_Pack_UVs
|
|
])
|
|
|
|
def register():
|
|
|
|
global classes
|
|
for cls in classes:
|
|
register_class(cls)
|
|
|
|
def unregister():
|
|
global classes
|
|
for cls in classes:
|
|
unregister_class(cls)
|