"""
Blender Baking Handlers
This module contains handlers for texture baking operations in Blender.
Includes normal map baking and ambient occlusion baking.
"""
import bpy
import os
import time
import tempfile
def bake_normals(high_poly, low_poly, map_size, space, cage_object, max_ray_distance):
"""Bake normal map from high-poly to low-poly"""
try:
start_time = time.time()
# Find objects
high_obj = bpy.data.objects.get(high_poly)
low_obj = bpy.data.objects.get(low_poly)
if not high_obj:
return {"error": f"High-poly object '{high_poly}' not found"}
if not low_obj:
return {"error": f"Low-poly object '{low_poly}' not found"}
# Check for UVs on low-poly
if not low_obj.data.uv_layers:
return {"error": f"Low-poly object '{low_poly}' has no UV coordinates. Unwrap UVs first."}
# Create textures directory
blend_dir = bpy.path.abspath("//")
if blend_dir == "":
blend_dir = tempfile.gettempdir()
textures_dir = os.path.join(blend_dir, "textures")
os.makedirs(textures_dir, exist_ok=True)
# Create image for baking
image_name = f"{low_poly}_normal"
image = bpy.data.images.new(image_name, width=map_size, height=map_size, alpha=False)
# Setup material for low-poly if needed
if not low_obj.data.materials:
mat = bpy.data.materials.new(name=f"{low_poly}_Material")
mat.use_nodes = True
low_obj.data.materials.append(mat)
mat = low_obj.data.materials[0]
mat.use_nodes = True
nodes = mat.node_tree.nodes
# Add image texture node for baking
tex_node = nodes.new('ShaderNodeTexImage')
tex_node.image = image
nodes.active = tex_node
# Setup bake settings
bpy.context.scene.render.engine = 'CYCLES'
bpy.context.scene.cycles.bake_type = 'NORMAL'
bpy.context.scene.render.bake.use_selected_to_active = True
bpy.context.scene.render.bake.normal_space = space
bpy.context.scene.render.bake.max_ray_distance = max_ray_distance
# Select objects for baking
bpy.ops.object.select_all(action='DESELECT')
high_obj.select_set(True)
low_obj.select_set(True)
bpy.context.view_layer.objects.active = low_obj
# Bake
bpy.ops.object.bake(type='NORMAL')
# Save image
file_path = os.path.join(textures_dir, f"{image_name}.png")
image.filepath_raw = file_path
image.file_format = 'PNG'
image.save()
bake_time = time.time() - start_time
return {
"success": True,
"file_path": file_path,
"bake_time": bake_time
}
except Exception as e:
return {"error": f"Failed to bake normals: {str(e)}"}
def bake_ambient_occlusion(target, map_size, samples):
"""Bake ambient occlusion map"""
try:
start_time = time.time()
# Find target object
target_obj = bpy.data.objects.get(target)
if not target_obj:
return {"error": f"Target object '{target}' not found"}
# Check for UVs
if not target_obj.data.uv_layers:
return {"error": f"Target object '{target}' has no UV coordinates. Unwrap UVs first."}
# Create textures directory
blend_dir = bpy.path.abspath("//")
if blend_dir == "":
blend_dir = tempfile.gettempdir()
textures_dir = os.path.join(blend_dir, "textures")
os.makedirs(textures_dir, exist_ok=True)
# Create image for baking
image_name = f"{target}_ao"
image = bpy.data.images.new(image_name, width=map_size, height=map_size, alpha=False)
# Setup material if needed
if not target_obj.data.materials:
mat = bpy.data.materials.new(name=f"{target}_Material")
mat.use_nodes = True
target_obj.data.materials.append(mat)
mat = target_obj.data.materials[0]
mat.use_nodes = True
nodes = mat.node_tree.nodes
# Add image texture node for baking
tex_node = nodes.new('ShaderNodeTexImage')
tex_node.image = image
nodes.active = tex_node
# Setup bake settings
bpy.context.scene.render.engine = 'CYCLES'
bpy.context.scene.cycles.bake_type = 'AO'
bpy.context.scene.cycles.samples = samples
# Select object for baking
bpy.ops.object.select_all(action='DESELECT')
target_obj.select_set(True)
bpy.context.view_layer.objects.active = target_obj
# Bake
bpy.ops.object.bake(type='AO')
# Save image
file_path = os.path.join(textures_dir, f"{image_name}.png")
image.filepath_raw = file_path
image.file_format = 'PNG'
image.save()
bake_time = time.time() - start_time
return {
"success": True,
"file_path": file_path,
"bake_time": bake_time
}
except Exception as e:
return {"error": f"Failed to bake ambient occlusion: {str(e)}"}