Files
blender-portable-repo/scripts/addons/SimpleBake/starting_checks.py
T
2026-07-16 12:41:53 -06:00

821 lines
37 KiB
Python

import bpy
from bpy.types import Operator
from bpy.utils import register_class, unregister_class
from .utils import show_message_box, clean_file_name, SBConstants, specials_selection_to_list, pbr_selections_to_list, blender_refresh, get_cached_udim_tiles
from .messages import print_message
from .ui.objects_list import refresh_bake_objects_list
from pathlib import Path
from .utils import can_write_to_location
from .bake_operators.pbr_bake_support_operators import has_node_groups, convertable_shaders
from . import __package__ as base_package
def check_mat_valid_for_displacement(mat_name, obj_name, messages):
mat = bpy.data.materials.get(mat_name)
if mat == None:
return True
node_tree = mat.node_tree
nodes = node_tree.nodes
valid = False
#groups = [n.name for n in this_node_set if n.bl_idname == "ShaderNodeGroup"]
#Find our MO node
mo = [n for n in nodes if n.bl_idname == "ShaderNodeOutputMaterial" and n.is_active_output]
assert len(mo)==1, "Couldn't find the Material Output node"
mo = mo[0]
#Empty is OK
if len(mo.inputs["Displacement"].links)==0:
valid = True
if len(mo.inputs["Displacement"].links)>0 and not valid:
from_node = mo.inputs["Displacement"].links[0].from_node
#Check if this is straightforward
if from_node.bl_idname in ["ShaderNodeDisplacement", "ShaderNodeVectorDisplacement"]:
valid = True
#Maybe it's in a node group
elif from_node.bl_idname == "ShaderNodeGroup":
#Recursive search for the nodes we want
trees = [from_node.node_tree]
for tree in trees:
for node in tree.nodes:
if node.bl_idname == "ShaderNodeGroup":
trees.append(node.node_tree)
continue
if node.bl_idname in ["ShaderNodeDisplacement", "ShaderNodeVectorDisplacement"]:
if len(node.outputs[0].links)>0:
if node.outputs[0].links[0].to_node.bl_idname == "NodeGroupOutput":
valid = True
if valid:
return True
else:
messages.append(f"ERROR: Baking Displacement map, but material \"{mat_name}\" on object \"{obj_name}\" has an")
messages.append("invalid node plugged into the Material Output displacement socket.")
messages.append("SimpleBake can only bake a single Displacement node or Vector Displacement node")
messages.append("connected to the Displacement socket of the active Material Output node.")
messages.append("If such a node is inside a node group, it must be connected directly to the Group Output node.")
return False
def find_shader_node_editor():
found_space = False
for screen in bpy.data.screens:
for area in screen.areas:
if area.type == 'NODE_EDITOR':
for space in area.spaces:
if space.type == 'NODE_EDITOR' and space.tree_type == 'ShaderNodeTree':
found_space = True
break
if found_space:
break
if found_space:
break
return found_space
def check_for_connected_viewer_node(context, mat):
sbp = context.scene.SimpleBake_Props
mat.use_nodes = True
node_tree = mat.node_tree
nodes = node_tree.nodes
#onode = find_onode(node_tree)
#Get all nodes with label "Viewer"
viewer_nodes = [n for n in nodes if n.label == "Viewer"]
#Check if any of those viewer nodes are connected to the Material Output
for n in viewer_nodes:
#if n.name == onode.inputs[0].links[0].from_node.name:
if len(n.outputs[0].links)>1:
if n.outputs[0].links[0].to_node.type == "OUTPUT_MATERIAL":
return True
return False
def check_mats_valid_for_pbr(context, mat):
prefs = context.preferences.addons[base_package].preferences
skip = prefs.skip_pbr_nodes_check
allowed_list = convertable_shaders + [
"ShaderNodeBsdfPrincipled", "ShaderNodeMixShader",
"ShaderNodeEmission", "NodeReroute", "ShaderNodeGroup",
"NodeGroupInput", "NodeGroupOutput"]
if skip:
print_message(context, "Skipping check for invalid PBR nodes")
return []
sbp = context.scene.SimpleBake_Props
node_sets = [mat.node_tree.nodes]
invalid_node_names = []
i = 0
while i < len(node_sets):
this_node_set = node_sets[i]
#Find any node groups, and add their node_set to the list
groups = [n.name for n in this_node_set if n.bl_idname == "ShaderNodeGroup"]
for n_name in groups:
if(n:=this_node_set.get(n_name)):
if hasattr(n, "node_tree"):
node_tree = n.node_tree
if hasattr(node_tree, "nodes"):
node_sets.append(n.node_tree.nodes)
#Now check for offending nodes in this node set
offending = [n for n in this_node_set if
len(n.outputs)>0 and
n.outputs[0].type == "SHADER" and
len(n.outputs[0].links) > 0 and
n.bl_idname not in allowed_list]
for o in offending:
invalid_node_names.append(o.name)
#Second case, the shader to RGB node
offending = ([n for n in this_node_set if
len(n.outputs)>0 and
len(n.outputs[0].links) > 0 and
n.bl_idname == "ShaderNodeShaderToRGB"
])
for o in offending:
invalid_node_names.append(o.name)
i+=1
return invalid_node_names
def ensure_valid_material_config(context, obj):
sbp = context.scene.SimpleBake_Props
if "SimpleBake_Placeholder" in bpy.data.materials:
mat = bpy.data.materials["SimpleBake_Placeholder"]
else:
mat = bpy.data.materials.new("SimpleBake_Placeholder")
bpy.data.materials["SimpleBake_Placeholder"].use_nodes = True
# Assign it to object
if len(obj.material_slots) > 0:
#Assign it to every empty slot
for slot in obj.material_slots:
if slot.material == None:
slot.material = mat
else:
# no slots
obj.data.materials.append(mat)
#All materials must use nodes
for slot in obj.material_slots:
mat = slot.material
if mat.use_nodes == False:
mat.use_nodes = True
return True
def ensure_target_object_bake_visibility(obj):
if obj == None:
return
obj.hide_viewport = False
obj.hide_set(False)
obj.hide_select = False
obj.hide_render = False
class SimpleBake_OT_Starting_Checks(Operator):
"""Perform pre-bake starting checks"""
bl_idname = "simplebake.starting_checks"
bl_description = "Perform bake starting checks"
bl_label = "Go"
def universal_checks(self, context):
sbp = context.scene.SimpleBake_Props
print_message(context, "Universal pre-bake checks")
bake_objects = self.bake_objects
targetobj = self.targetobj
targetobj_cycles = self.targetobj_cycles
messages = []
#Universal Checks
if(context.mode != "OBJECT"):
messages.append("ERROR: Not in object mode")
show_message_box(context, messages, "Errors occured", icon = 'ERROR')
return {'CANCELLED'}
#If exporting, check we can write to the folder given
if sbp.save_bakes_external or sbp.save_obj_external:
path = Path(bpy.path.abspath(sbp.export_path))
#path = path.parent
if not can_write_to_location(path):
messages.append(f"ERROR: Blender reports that it cannot write to the Export Path you have")
messages.append(f'specified under Export settings')
messages.append(f'CANNOT WRITE TO: "{bpy.path.abspath(str(path))}"')
messages.append(f"Try specifying a different export path")
show_message_box(context, messages, "Errors occured", icon = 'ERROR')
return {'CANCELLED'}
#Check no cp textures rely on bakes that are no longer enabled
if sbp.global_mode == SBConstants.PBR:
pbr_bakes = pbr_selections_to_list(context)
special_bakes = specials_selection_to_list(context)
bakes = pbr_bakes + special_bakes
bakes.append("none")
def cp_channel_invalid(value):
"""Return True if this channel value can't be satisfied by the current bake settings."""
if value == "none":
return False
if "||" in value:
# Per-material value — only valid when tex_per_mat is on
if not sbp.tex_per_mat:
return True
bake_type = value.split("||", 1)[0]
else:
bake_type = value
return bake_type not in bakes
def cp_display(value):
"""Human-readable form of a channel value, safe for the | -delimited message box."""
return value.replace("||", " - ")
for cpt in sbp.cp_list:
if cp_channel_invalid(cpt.R):
messages.append(f"ERROR: Channel packed texture \"{cpt.name}\" depends on {cp_display(cpt.R)}, but you are no longer baking it")
if cp_channel_invalid(cpt.G):
messages.append(f"ERROR: Channel packed texture \"{cpt.name}\" depends on {cp_display(cpt.G)}, but you are no longer baking it")
if cp_channel_invalid(cpt.B):
messages.append(f"ERROR: Channel packed texture \"{cpt.name}\" depends on {cp_display(cpt.B)}, but you are no longer baking it")
if cp_channel_invalid(cpt.A):
messages.append(f"ERROR: Channel packed texture \"{cpt.name}\" depends on {cp_display(cpt.A)}, but you are no longer baking it")
messages = list(set(messages)) #Remove duplicates
if len(messages) >0:
show_message_box(context, messages, "Errors occured", icon = 'ERROR')
return {'CANCELLED'}
#Is anything seleccted at all for bake?
if len(bake_objects) == 0:
messages.append("ERROR: Nothing in the bake objects list!")
show_message_box(context, messages, "Errors occured", icon = 'ERROR')
return {'CANCELLED'}
#Check everything selected (or target) is mesh
l = [bpy.data.objects.get(name) for name in bake_objects]
l = [o for o in l if o!=None and o.type != "MESH"]
if len(l)>0:
([messages.append(f"ERROR: Object '{obj.name}' is not mesh") for obj in l])
if sbp.selected_s2a and targetobj != None and targetobj.type != "MESH":
messages.append(f"ERROR: Object '{targetobj.name}' (your target object) is not mesh")
if sbp.cycles_s2a and targetobj_cycles != None and targetobj_cycles.type != "MESH":
messages.append(f"ERROR: Object '{targetobj.name}' (your target object) is not mesh")
if len(messages) > 0:
show_message_box(context, messages, "Errors occured", icon = 'ERROR')
return {'CANCELLED'}
#if sbp.save_bakes_external or sbp.save_obj_external:
#if sbp.save_external_folder != clean_file_name(sbp.save_external_folder):
#message_items = ["ERROR: Folder name can only contain characters that",
#"are valid for the external file system"]
#show_message_box(context, message_items, "ERROR", icon = "ERROR")
#return {'CANCELLED'}
#Ensure valid material config
objs = [bpy.data.objects.get(name) for name in bake_objects]
objs = [o for o in objs if o!=None]
for obj in objs:
ensure_valid_material_config(context, obj)
if targetobj!=None: ensure_valid_material_config(context, targetobj)
if targetobj_cycles!=None: ensure_valid_material_config(context, targetobj_cycles)
#Check object visibility
if len([o for o in context.view_layer.objects if o.type=="MESH"])==0:
messages.append("There must be at least one mesh object visible in the current view layer")
show_message_box(context, messages, "Errors occured", icon = 'ERROR')
return {'CANCELLED'}
if sbp.selected_s2a:
ensure_target_object_bake_visibility(sbp.targetobj)
if sbp.cycles_s2a:
ensure_target_object_bake_visibility(sbp.targetobj_cycles)
obj_test_list = bake_objects.copy()
if sbp.selected_s2a and sbp.targetobj != None:
obj_test_list.append(sbp.targetobj.name)
if sbp.cycles_s2a and sbp.targetobj_cycles != None:
obj_test_list.append(sbp.targetobj_cycles.name)
for obj_name in obj_test_list:
if (o:=bpy.data.objects.get(obj_name)):
if o.hide_viewport == True:
messages.append(f"Object '{o.name}' is hidden in viewport (monitor icon in outliner)")
if o.hide_render == True:
messages.append(f"Object '{o.name}' is hidden for render (camera icon in outliner)")
if o.hide_get() == True:
messages.append(f"Object '{o.name}' is hidden in viewport eye (eye icon in outliner)")
if o.hide_select == True:
messages.append(f"Object '{o.name}' is hidden for selection (arrow icon in outliner)")
if len(messages)>0:
show_message_box(context, messages, "Errors occured", icon = 'ERROR')
return {'CANCELLED'}
#None of the objects can have zero faces
for obj_name in bake_objects:
o = bpy.data.objects.get(obj_name)
if o!= None and len(o.data.polygons) < 1: messages.append(f"ERROR: Object '{o.name}' has no faces")
if sbp.selected_s2a and targetobj != None:
if len(targetobj.data.polygons) < 1: messages.append(f"ERROR: Object '{targetobj.name}' has no faces")
if sbp.cycles_s2a and targetobj_cycles != None:
if len(targetobj_cycles.data.polygons) < 1: messages.append(f"ERROR: Object '{targetobj_cycles.name}' has no faces")
if len(messages)>0:
show_message_box(context, messages, "Errors occured", icon = 'ERROR')
return {'CANCELLED'}
#Check for viewer nodes still connected
for obj_name in bake_objects:
if (o:=bpy.data.objects.get(obj_name)):
for slot in o.material_slots:
mat = slot.material
if mat != None: #It'll get a placeholder material later on if it's none
if check_for_connected_viewer_node(context, mat):
messages.append(f"ERROR: Material '{mat.name}' on object '{o.name}' has a Viewer node connected to the Material Output")
if len(messages)>0:
show_message_box(context, messages, "Errors occured", icon = 'ERROR')
return {'CANCELLED'}
#glTF
if sbp.create_glTF_node:
if sbp.glTF_selection == SBConstants.AO and not sbp.selected_ao:
messages.append(f"ERROR: You have selected AO for glTF settings (in the 'Other Settings' section), but you aren't baking AO")
if sbp.glTF_selection == SBConstants.LIGHTMAP and not sbp.selected_lightmap:
messages.append(f"ERROR: You have selected Lightmap for glTF settings (in the 'Other Settings' section), but you aren't baking Lightmap")
if len(messages)>0:
show_message_box(context, messages, "Errors occured", icon = 'ERROR')
return {'CANCELLED'}
prefs = context.preferences.addons[__package__].preferences
for obj_name in bake_objects:
if (o:=bpy.data.objects.get(obj_name)):
if o.name != clean_file_name(o.name) and sbp.save_bakes_external and "%OBJ%" in prefs.img_name_format:
messages.append(f"ERROR: You are trying to save external images, but object with name \"{o.name}\" contains invalid characters for saving externally.")
show_message_box(context, messages, "Errors occured", icon = 'ERROR')
return {'CANCELLED'}
#Merged bake checks
if sbp.merged_bake and sbp.merged_bake_name == "":
messages.append(f"ERROR: You are baking multiple objects to one texture set, but the texture name is blank")
if (sbp.merged_bake_name != clean_file_name(sbp.merged_bake_name)) and sbp.save_bakes_external:
messages.append(f"ERROR: The texture name you inputted for baking multiple objects to one texture set (\"{sbp.merged_bake_name}\") contains invalid characters for saving externally.")
if sbp.merged_bake:
if sbp.selected_s2a or sbp.cycles_s2a:
if sbp.s2a_opmode!="automatch":
messages.append("You can't use the Bake Multiple Objects to One Texture Set option when baking to target")
#Export bakes checkes
if (sbp.save_bakes_external or sbp.save_obj_external) and sbp.export_path == "":
messages.append(f"You are saving your bakes and/or mesh externally, but your "
"Export Path (under the Export settings section) is blank")
if len(messages)>0:
show_message_box(context, messages, "Errors occured", icon = 'ERROR')
return {'CANCELLED'}
# Background bake + UDIMs requires external save to be enabled
if sbp.bgbake == "bg" and not sbp.save_bakes_external:
if sbp.selected_s2a:
udim_check_objects = [targetobj.name] if targetobj else []
elif sbp.cycles_s2a:
udim_check_objects = [targetobj_cycles.name] if targetobj_cycles else []
else:
udim_check_objects = list(bake_objects)
for obj_name in udim_check_objects:
result = get_cached_udim_tiles(context, obj_name, threshold=0.001)
if result["total_tiles"] > 1:
messages.append("ERROR: Bake objects have UDIMs. Background baking with UDIMs")
messages.append('requires "Export Bakes" to be enabled in the Export settings.')
show_message_box(context, messages, "Errors occurred", icon='ERROR')
return {'CANCELLED'}
return {'FINISHED'}
def pbr_no_s2a_checks(self, context):
sbp = context.scene.SimpleBake_Props
print_message(context, "PBR no S2A pre-bake checks")
bake_objects = self.bake_objects
targetobj = self.targetobj
targetobj_cycles = self.targetobj_cycles
messages = []
#Check if we have a shader node editor to use_nodes
ngs = False
for obj_name in bake_objects:
if (o:=bpy.data.objects.get(obj_name)):
for slot in o.material_slots:
if slot.material != None:
ngs = has_node_groups(context, slot.material.name)
break
if ngs:
break
if ngs and not find_shader_node_editor():
messages.append(f"Some or all of your materials have node groups. To process node groups, SimpleBake needs there")
messages.append(f"to be a Shader Node Editor open somewhere in one of your Blender workspaces. You don't seem to have")
messages.append(f"one open anywhere! Open a Shader Node Editor (as if you were editing a material) somewhere in one of")
messages.append(f"your Blender workspaces, then try again")
show_message_box(context, messages, "Errors occured", icon = 'ERROR')
return {'CANCELLED'}
#If baking displacement, do all materials meet the rules
if sbp.selected_displacement:
mats = {}
for obj_name in bake_objects:
if (o:=bpy.data.objects.get(obj_name)):
for slot in o.material_slots:
if slot.material != None:
mats[obj_name] = slot.material.name
for m in mats:
if not check_mat_valid_for_displacement(mats[m], m, messages):
show_message_box(context, messages, "Errors occured", "ERROR")
return {'CANCELLED'}
for obj_name in bake_objects:
if not (o:=bpy.data.objects.get(obj_name)):
break
#Are UVs OK?
generating_uvs = sbp.new_uv_option or (sbp.tex_per_mat and sbp.expand_mat_uvs)
if not generating_uvs and len(o.data.uv_layers) == 0:
messages.append(f"ERROR: Object {o.name} has no UVs, and you aren't generating new ones")
if sbp.new_uv_option and sbp.new_uv_method=='CombineExisting' and len(o.data.uv_layers) == 0:
messages.append(f"ERROR: You are combining existing UVs, but object {o.name} has no UVs")
#Do all materials have valid PBR config?
for slot in o.material_slots:
mat = slot.material
result = check_mats_valid_for_pbr(context, mat)
warn = False
if len(result) > 0:
for node_name in result:
messages.append(f"ERROR: Node '{node_name}' in material '{mat.name}' on object '{o.name}' is not valid for PBR bake. Principled BSDFs and/or Emission only!")
warn = True
if warn:
messages.append("NOTE: There is an option to disable this check in the SimpleBake addon preferences, but it is not recommended. Baking PBR with nodes that are not PBR compatible may give unexpected results, Black bakes or even CRASHES!")
#Checked for invalid nodes, now let's check for just absent any useful nodes:
else:
#Do all materials have at least one material output node?
for slot in o.material_slots:
mat = slot.material
mos = [node for node in mat.node_tree.nodes if node.bl_idname == "ShaderNodeOutputMaterial"\
and len(node.inputs[0].links)>0]
if len(mos) == 0:
messages.append(f"ERROR: Material '{mat.name}' on object '{o.name}' is broken. Must have at least one Material Output node that's plugged in!")
if len(messages)>0:
show_message_box(context, messages, "Errors occured", icon = 'ERROR')
return {'CANCELLED'}
return {'FINISHED'}
def pbr_s2a_checks(self, context):
sbp = context.scene.SimpleBake_Props
print_message(context, "PBR S2A pre-bake checks")
bake_objects = self.bake_objects
targetobj = self.targetobj
targetobj_cycles = self.targetobj_cycles
messages = []
ngs = False
for obj_name in bake_objects:
if(o:=bpy.data.objects.get(obj_name)):
for slot in o.material_slots:
if slot.material != None:
ngs = has_node_groups(context, slot.material.name)
break
if ngs:
break
if ngs and not find_shader_node_editor():
messages.append(f"Some or all of your materials have node groups. To process node groups, SimpleBake needs there")
messages.append(f"to be a Shader Node Editor open somewhere in one of your Blender workspaces. You don't seem to have")
messages.append(f"one open anywhere! Open a Shader Node Editor (as if you were editing a material) somewhere in one of")
messages.append(f"your Blender workspaces, then try again")
show_message_box(context, messages, "Errors occured", icon = 'ERROR')
return {'CANCELLED'}
#Do all materials have valid PBR config?
warn = False
for obj_name in bake_objects:
if(o:=bpy.data.objects.get(obj_name)):
for slot in o.material_slots:
mat = slot.material
result = check_mats_valid_for_pbr(context, mat)
if len(result) > 0:
for node_name in result:
messages.append(f"ERROR: Node '{node_name}' in material '{mat.name}' on object '{o.name}' is not valid for PBR bake. Principled BSDFs and/or Emission only!")
warn = True
if warn:
messages.append("NOTE: There is an option to disable this check in the SimpleBake addon preferences, but it is not recommended. Baking PBR with nodes that are not PBR compatible may give unexpected results, Black bakes or even CRASHES!")
#Checked for invalid nodes, now let's check for just absent any useful nodes:
else:
#Do all materials have at least one material output node?
for obj_name in bake_objects:
if (o:=bpy.data.objects.get(obj_name)):
for slot in o.material_slots:
mat = slot.material
mos = [node for node in mat.node_tree.nodes if node.bl_idname == "ShaderNodeOutputMaterial"\
and len(node.inputs[0].links)>0]
if len(mos) == 0:
messages.append(f"ERROR: Material '{mat.name}' on object '{o.name}' is broken. Must have at least one Material Output node that's plugged in!")
#Checks for baking displacement
for obj_name in bake_objects:
if (o:=bpy.data.objects.get(obj_name)):
for slot in o.material_slots:
if slot.material!=None:
mat = slot.material
if not check_mat_valid_for_displacement(mat.name, o.name, messages):
show_message_box(context, messages, "Errors occured", "ERROR")
return {'CANCELLED'}
if len(messages)>0:
show_message_box(context, messages, "Errors occured", icon = 'ERROR')
return {'CANCELLED'}
#From this point onward, we only care about the target object
#-------------------------------------------------------------------------
#Do we have a target object?
if targetobj == None:
messages.append("ERROR: You are trying to bake to a target object with PBR Bake, but you have not selected one in the SimpleBake panel")
show_message_box(context, messages, "Errors occured", "ERROR")
return {'CANCELLED'}
#If declas, need to do the displacement check again as the target object materials get baked
for slot in targetobj.material_slots:
if slot.material!=None:
mat = slot.material
if not check_mat_valid_for_displacement(mat.name, targetobj.name, messages):
show_message_box(context, messages, "Errors occured", "ERROR")
return {'CANCELLED'}
#Have we got more selected than just the target object?
if len(bake_objects) == 1 and bake_objects[0] == targetobj.name:
messages.append("ERROR: You are trying to bake to a target object, but the only object you have selected is your target")
show_message_box(context, messages, "Errors occured", "ERROR")
return {'CANCELLED'}
#If the target object is in the bake objects list, remove it now
i = sbp.objects_list.find(targetobj.name)
if i>-1: sbp.objects_list.remove(i)
#Are UVs OK?
if sbp.new_uv_option == False and len(targetobj.data.uv_layers) == 0:
messages.append(f"ERROR: Object {targetobj.name} has no UVs, and you aren't generating new ones")
show_message_box(context, messages, "Errors occured", "ERROR")
return {'CANCELLED'}
#All existing materials must use nodes
for slot in targetobj.material_slots:
if slot.material != None:
if not slot.material.use_nodes:
slot.material.use_nodes = True
return {'FINISHED'}
def cyclesbake_no_s2a_checks(self, context):
sbp = context.scene.SimpleBake_Props
print_message(context, "CyclesBake no S2A pre-bake checks")
bake_objects = self.bake_objects
targetobj = self.targetobj
targetobj_cycles = self.targetobj_cycles
messages = []
for obj_name in bake_objects:
if (o:=bpy.data.objects.get(obj_name)):
#Are UVs OK?
if not sbp.tex_per_mat:
if sbp.new_uv_option == False and len(o.data.uv_layers) == 0:
messages.append(f"ERROR: Object {o.name} has no UVs, and you aren't generating new ones")
show_message_box(context, messages, "Errors occured", "ERROR")
return {'CANCELLED'}
if sbp.new_uv_option and sbp.new_uv_method=='CombineExisting' and len(o.data.uv_layers) == 0:
messages.append(f"ERROR: You are combining existing UVs, but object {o.name} has no UVs")
show_message_box(context, messages, "Errors occured", "ERROR")
return {'CANCELLED'}
else:
if sbp.expand_mat_uvs == False and len(o.data.uv_layers) == 0:
messages.append(f"ERROR: Object {o.name} has no UVs, and you aren't generating new ones")
show_message_box(context, messages, "Errors occured", "ERROR")
return {'CANCELLED'}
return {'FINISHED'}
def cyclesbake_s2a_checks(self, context):
sbp = context.scene.SimpleBake_Props
print_message(context, "CyclesBake S2A pre-bake checks")
bake_objects = self.bake_objects
targetobj = self.targetobj
targetobj_cycles = self.targetobj_cycles
messages = []
#Do we actually have a target object?
if targetobj_cycles == None:
messages.append(f"ERROR: You are trying to bake to target with CyclesBake, but there is no target object selected on the SimpleBake panel")
show_message_box(context, messages, "Errors occured", "ERROR")
return {'CANCELLED'}
#Have we got more selected than just the target object?
elif len(bake_objects) == 1 and bake_objects[0] == targetobj_cycles.name:
messages.append("ERROR: You are trying to bake selected to active with CyclesBake, but the only object you have selected is your active (target) object")
show_message_box(context, messages, "Errors occured", "ERROR")
return {'CANCELLED'}
#If the target object is in the bake objects list, remove it now
i = sbp.objects_list.find(targetobj_cycles.name)
if i>-1: sbp.objects_list.remove(i)
#Are UVs OK?
elif not sbp.new_uv_option and len(targetobj_cycles.data.uv_layers) == 0:
messages.append(f"ERROR: Object {targetobj_cycles.name} has no UVs, and you aren't generating new ones")
show_message_box(context, messages, "Errors occured", "ERROR")
return {'CANCELLED'}
return {'FINISHED'}
def specials_checks(self, context):
sbp = context.scene.SimpleBake_Props
print_message(context, "Specials pre-bake checks")
bake_objects = self.bake_objects
targetobj = self.targetobj
targetobj_cycles = self.targetobj_cycles
messages = []
if sbp.selected_col_vertex:
if not sbp.selected_s2a and not sbp.cycles_s2a:
for obj_name in bake_objects:
if (o:=bpy.data.objects.get(obj_name)):
if len(o.data.color_attributes) == 0:
messages.append(f"You are trying to bake the active vertex colours, but object {o.name} doesn't have vertex colours")
show_message_box(context, messages, "Errors occured", "ERROR")
return {'CANCELLED'}
if sbp.cycles_s2a or sbp.selected_s2a:
if sbp.selected_s2a: target_obj = sbp.targetobj
else: target_obj = sbp.targetobj_cycles
target_obj_name = target_obj.name
obj_names = [o.obj_point.name for o in sbp.objects_list]
if target_obj_name in obj_names:
obj_names.remove(target_obj_name)
for o_name in obj_names:
if(o:=context.scene.objects.get(o_name)):
if len(o.data.color_attributes) == 0:
messages.append(f"You are trying to bake the active vertex colours, but object {o.name} doesn't have vertex colours")
if len(messages)>0:
show_message_box(context, messages, "Errors occured", "ERROR")
return {'CANCELLED'}
return {'FINISHED'}
def image_sequence_checks(self,context):
sbp = context.scene.SimpleBake_Props
print_message(context, "Image sequence pre-bake checks")
messages = []
if not sbp.image_sequence_start_frame < sbp.image_sequence_end_frame:
messages.append("When baking an image sequence, the starting frame must be before the end frame. Change this in the Other Settings section of SimpleBake")
show_message_box(context, messages, "Errors occured", "ERROR")
return {'CANCELLED'}
return {'FINISHED'}
def execute(self, context):
blender_refresh()
sbp = context.scene.SimpleBake_Props
refresh_bake_objects_list(context)
#disable_impossible(context)
#sbp.bake_done = False
#Get objects
self.bake_objects = [i.name for i in sbp.objects_list]
self.targetobj = sbp.targetobj
self.targetobj_cycles = sbp.targetobj_cycles
print_message(context, "Starting checks operator")
#Universal checks
r = self.universal_checks(context)
if 'CANCELLED' in r: return{'CANCELLED'}
#PBR Bake Checks - No S2A
if sbp.global_mode == SBConstants.PBR and not sbp.selected_s2a:
r = self.pbr_no_s2a_checks(context)
if 'CANCELLED' in r: return{'CANCELLED'}
#PBR Bake - S2A
if sbp.global_mode == SBConstants.PBR and sbp.selected_s2a:
r = self.pbr_s2a_checks(context)
if 'CANCELLED' in r: return{'CANCELLED'}
#If decals, need to do a normal PBR check on the target object
if sbp.s2a_opmode == "decals":
self.bake_objects= [sbp.targetobj.name]
r = self.pbr_no_s2a_checks(context)
if 'CANCELLED' in r: return{'CANCELLED'}
#CyclesBake - no S2A
if sbp.global_mode == SBConstants.CYCLESBAKE and not sbp.cycles_s2a:
r = self.cyclesbake_no_s2a_checks(context)
if 'CANCELLED' in r: return{'CANCELLED'}
#CyclesBake - S2A
if sbp.global_mode == SBConstants.CYCLESBAKE and sbp.cycles_s2a:
r = self.cyclesbake_s2a_checks(context)
if 'CANCELLED' in r: return{'CANCELLED'}
#Specials
if len(specials_selection_to_list(context)) >0:
r = self.specials_checks(context)
if 'CANCELLED' in r: return{'CANCELLED'}
#Image sequence
if sbp.image_sequence_enabled:
r = self.image_sequence_checks(context)
if 'CANCELLED' in r: return{'CANCELLED'}
#Fix that weird context incorrect error
#Selected object at start must be something and must be mesh
#Find a nominal object that is a mesh and in the current view layer
if context.active_object == None or context.active_object.type != "MESH":
nominal = None
for o in context.view_layer.objects:
if o.type == "MESH":
nominal = o
break
assert(nominal!=None)
bpy.ops.simplebake.select_only_this(object_name=nominal.name)
#Guess everything was OK
return {'FINISHED'}
"""
#Specials Bake
#CAGE OBJECT BROKEN? CHECK IF NOT NONE AND, IF NOT, FLIP THE SWITdisable_impossibleCH TO USE CAGE
"""
classes = ([
SimpleBake_OT_Starting_Checks
])
def register():
global classes
for cls in classes:
register_class(cls)
def unregister():
global classes
for cls in classes:
unregister_class(cls)