1823 lines
76 KiB
Python
1823 lines
76 KiB
Python
import bpy
|
|
import os
|
|
from pathlib import Path
|
|
from bpy.utils import register_class, unregister_class
|
|
from bpy.types import Panel, Operator
|
|
|
|
from ..utils import SBConstants, pbr_selections_to_list, specials_selection_to_list, is_blend_saved, get_cached_udim_tiles
|
|
from .objects_list import SIMPLEBAKE_UL_Objects_List
|
|
from .channel_packing_list import SIMPLEBAKE_UL_CPTexList
|
|
from ..background_and_progress import BackgroundBakeTasks, BakeInProgress
|
|
from .panel_helpers import check_for_render_inactive_modifiers, check_for_convertible_shaders, check_for_auto_cage, check_for_viewport_inactive_modifiers
|
|
try:
|
|
from ..auto_update import VersionControl
|
|
except:
|
|
VersionControl = None
|
|
|
|
from ..messages import print_message
|
|
from .. import __package__ as base_package
|
|
|
|
space = ""
|
|
region = ""
|
|
context = ""
|
|
cat = ""
|
|
TOGGLES = True
|
|
ALIGN = False
|
|
SCALE = 1.3
|
|
|
|
def read_settings():
|
|
global space, region, context, cat, TOGGLES, ALIGN, SCALE
|
|
|
|
p = Path(bpy.utils.script_path_user())
|
|
p = p.parents[1]
|
|
p = p / "data" / "SimpleBake" / "settings"
|
|
|
|
filename = "panel_location.txt"
|
|
content = ""
|
|
try:
|
|
with open(str(p / filename), 'r') as file:
|
|
content = file.read()
|
|
except Exception as e:
|
|
content = "renderpanel"
|
|
|
|
if content == "renderpanel":
|
|
space = 'PROPERTIES'
|
|
region = 'WINDOW'
|
|
context = "render"
|
|
cat = ""
|
|
else:
|
|
space = 'VIEW_3D'
|
|
region = 'UI'
|
|
context = ""
|
|
cat = 'SimpleBake'
|
|
|
|
filename = "toggles.txt"
|
|
try:
|
|
with open(str(p / filename), 'r') as file:
|
|
content = file.read()
|
|
except Exception as e:
|
|
content = "toggles"
|
|
|
|
TOGGLES = True if content == "toggles" else False
|
|
|
|
read_settings()
|
|
|
|
|
|
def monkey_tip_formatter(text, max_length=32):
|
|
words = text.split()
|
|
segments = []
|
|
current_segment = ""
|
|
|
|
for word in words:
|
|
if len(current_segment) + len(word) + 1 > max_length:
|
|
segments.append(current_segment)
|
|
current_segment = word
|
|
else:
|
|
if current_segment:
|
|
current_segment += " "
|
|
current_segment += word
|
|
|
|
if current_segment:
|
|
segments.append(current_segment)
|
|
|
|
return segments
|
|
|
|
|
|
def monkey_tip(context, message_lines, box):
|
|
sbp = context.scene.SimpleBake_Props
|
|
row = box.row(align=ALIGN)
|
|
row.alert = True
|
|
row.prop(sbp, "showtips", icon="TRIA_DOWN" if sbp.showtips else "TRIA_RIGHT", icon_only=True, emboss=False)
|
|
row.label(text=f'{"Tip" if sbp.showtips else "Tip available"}', icon="MONKEY")
|
|
row.alignment = 'CENTER'
|
|
|
|
if sbp.showtips:
|
|
for line in message_lines:
|
|
row = box.row(align=ALIGN)
|
|
row.alignment = 'CENTER'
|
|
row.scale_y = 0.5
|
|
row.label(text=line)
|
|
|
|
|
|
def decals_tips(context, box):
|
|
message_lines = ["PBR DECALS MODE"]
|
|
message_lines.extend(monkey_tip_formatter(
|
|
"Add the decal objects to the list, and specify your target "
|
|
"object in the Target Object box. If using the 'Copy objects and "
|
|
"apply bakes' option, a suitable decals material will be created"))
|
|
monkey_tip(context, message_lines, box)
|
|
box.row(align=ALIGN).label(text="")
|
|
|
|
|
|
def standard_s2a_tips(context, box):
|
|
message_lines = ["STANDARD BAKE TO TARGET MODE"]
|
|
message_lines.extend(monkey_tip_formatter(
|
|
"Add source objects to the list and specify the "
|
|
"target object in the target box"))
|
|
monkey_tip(context, message_lines, box)
|
|
|
|
|
|
def auto_match_high_low_tips(context, box):
|
|
sbp = context.scene.SimpleBake_Props
|
|
if sbp.auto_match_mode == "name":
|
|
message_lines = ["AUTO MATCH HIGH/LOW", "NAME MODE"]
|
|
message_lines.extend(monkey_tip_formatter(
|
|
"Only objects with \"_high\" as a suffix to their name can be added to the list. "
|
|
"Each object's icon indicates if a corresponding \"_low\" object for baking "
|
|
"can be found in the scene. Cage objects are also auto detected with the "
|
|
"suffix \"_cage\""))
|
|
else:
|
|
message_lines = ["AUTO MATCH HIGH/LOW", "POSITION MODE"]
|
|
message_lines.extend(monkey_tip_formatter(
|
|
"Add your high poly objects to the list "
|
|
"Low poly detected by position (<0.5 BU) "
|
|
"Each object's icon indicates if a "
|
|
"corresponding low poly object for baking "
|
|
"can be found in the scene"))
|
|
monkey_tip(context, message_lines, box)
|
|
box.row(align=ALIGN).label(text="")
|
|
|
|
|
|
def denoise_options(box):
|
|
C = bpy.context
|
|
sbp = C.scene.SimpleBake_Props
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.prop(sbp, "rundenoise", text="Denoise (Compositor)", toggle=TOGGLES)
|
|
if not sbp.save_bakes_external:
|
|
row.enabled = False
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.prop(C.scene.cycles, "use_denoising", text="Denoise (Cycles)", toggle=TOGGLES)
|
|
|
|
if C.scene.cycles.use_denoising:
|
|
box.row(align=ALIGN).prop(C.scene.cycles, "denoiser")
|
|
box.row(align=ALIGN).prop(C.scene.cycles, "denoising_input_passes")
|
|
box.row(align=ALIGN).prop(C.scene.cycles, "denoising_prefilter")
|
|
|
|
|
|
def disable_row_if_image_sequence(context, row, box):
|
|
# Image sequence baking is driven by a modal operator
|
|
# (SimpleBake_OT_Image_Sequence) which needs Blender's event loop.
|
|
# Background baking launches a headless subprocess and exits, so the
|
|
# two modes are architecturally incompatible — disable the row here.
|
|
sbp = context.scene.SimpleBake_Props
|
|
|
|
if sbp.image_sequence_enabled:
|
|
row.enabled = False
|
|
for t in ("Image sequence selected", "Unfortunately, this can only be", "baked in the foreground"):
|
|
r = box.row(align=ALIGN)
|
|
r.alert = True
|
|
r.alignment = 'CENTER'
|
|
r.scale_y = 0.5
|
|
r.label(text=t)
|
|
return True
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Layout helper functions
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def prop_row(box, data, prop, enabled=True, small_row=False, **kwargs):
|
|
"""Single standard-scale prop row. Returns the row so callers can set .enabled."""
|
|
|
|
row = box.row(align=ALIGN)
|
|
|
|
element = None
|
|
if small_row:
|
|
row.scale_y = 1
|
|
row.use_property_split = True
|
|
row.use_property_decorate = False
|
|
element = row
|
|
else:
|
|
row.scale_y = SCALE
|
|
element = row
|
|
|
|
element.enabled = enabled
|
|
element.prop(data, prop, toggle=TOGGLES, **kwargs)
|
|
return row
|
|
|
|
|
|
def two_prop_row(box, data, prop_left, prop_right, label_left=None, label_right=None):
|
|
"""Two props side-by-side in columns. Returns (row, col_left, col_right)."""
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
col_l = row.column()
|
|
col_l.prop(data, prop_left, toggle=TOGGLES,
|
|
**({"text": label_left} if label_left is not None else {}))
|
|
col_r = row.column()
|
|
col_r.prop(data, prop_right, toggle=TOGGLES,
|
|
**({"text": label_right} if label_right is not None else {}))
|
|
return row, col_l, col_r
|
|
|
|
|
|
def preview_eye(layout, enabled, bake_type):
|
|
"""Eye icon button for bake type preview. Greyed out when enabled=False."""
|
|
col = layout.column()
|
|
col.enabled = enabled
|
|
col.operator("simplebake.preview_bake_type", text="", icon="HIDE_OFF").bake_type = bake_type
|
|
|
|
|
|
def two_prop_row_preview(box, sbp, prop_left, prop_right, type_left, type_right):
|
|
"""Two props side-by-side with a preview eye button after each. Returns (row, col_left, col_right)."""
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
col_l = row.column()
|
|
col_l.prop(sbp, prop_left, toggle=TOGGLES)
|
|
preview_eye(row, getattr(sbp, prop_left), type_left)
|
|
col_r = row.column()
|
|
col_r.prop(sbp, prop_right, toggle=TOGGLES)
|
|
preview_eye(row, getattr(sbp, prop_right), type_right)
|
|
return row, col_l, col_r
|
|
|
|
|
|
def section_header(box, data, show_prop, label, icon_only=False, section_icon='NONE'):
|
|
"""Collapsible section header. Returns the row."""
|
|
row = box.row(align=ALIGN)
|
|
tria = "TRIA_DOWN" if getattr(data, show_prop) else "TRIA_RIGHT"
|
|
row.prop(data, show_prop, text="", icon=tria, icon_only=True, emboss=False)
|
|
row.prop(data, show_prop, text=label, icon=section_icon, emboss=False)
|
|
return row
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Main panel class
|
|
class SIMPLEBAKE_PT_main_panel(Panel):
|
|
bl_label = "SimpleBake"
|
|
bl_space_type = f"{space}"
|
|
bl_region_type = f"{region}"
|
|
bl_context = f"{context}"
|
|
bl_category = f"{cat}"
|
|
|
|
# -----------------------------------------------------------------------
|
|
def draw_version_info(self, context, layout):
|
|
if VersionControl is None:
|
|
row = layout.row(align=ALIGN)
|
|
row.alignment = 'CENTER'
|
|
row.scale_y = 0.6
|
|
row.label(text="DEMO MODE")
|
|
return
|
|
|
|
prefs = context.preferences.addons[base_package].preferences
|
|
|
|
def print_installed_ver():
|
|
row = layout.row(align=ALIGN)
|
|
row.alignment = 'CENTER'
|
|
row.scale_y = 0.6
|
|
row.label(text=f"Installed version: {VersionControl.installed_version_str}")
|
|
|
|
if prefs.no_update_check:
|
|
print_installed_ver()
|
|
row = layout.row(align=ALIGN)
|
|
row.alert = True
|
|
row.alignment = 'CENTER'
|
|
row.scale_y = 0.6
|
|
row.label(text="(Update checking disabled)")
|
|
return
|
|
|
|
if VersionControl.was_error:
|
|
for text, icon in (("Simplebake wasn't able to check", "ERROR"), ("for updates", 'NONE')):
|
|
row = layout.row(align=ALIGN)
|
|
row.alignment = 'CENTER'
|
|
row.scale_y = 0.6
|
|
if icon != 'NONE':
|
|
row.label(text=text, icon=icon)
|
|
else:
|
|
row.label(text=text)
|
|
print_installed_ver()
|
|
return
|
|
|
|
if not VersionControl.at_current:
|
|
for text, alert in (
|
|
("", True),
|
|
("Newer version of SimpleBake available", True),
|
|
("Update automatically in addon preferences", True),
|
|
):
|
|
row = layout.row(align=ALIGN)
|
|
row.alignment = 'CENTER'
|
|
row.scale_y = 0.6
|
|
row.alert = alert
|
|
row.label(text=text, icon="MOD_WAVE" if text == "" else 'NONE')
|
|
print_installed_ver()
|
|
row = layout.row(align=ALIGN)
|
|
row.alignment = 'CENTER'
|
|
row.scale_y = 0.6
|
|
row.label(text=f"Available Version: {VersionControl.current_version_str}")
|
|
else:
|
|
row = layout.row(align=ALIGN)
|
|
row.alignment = 'CENTER'
|
|
row.scale_y = 0.6
|
|
row.label(text="", icon="CHECKMARK")
|
|
row = layout.row(align=ALIGN)
|
|
row.alignment = 'CENTER'
|
|
row.scale_y = 0.6
|
|
row.label(text="SimpleBake is up-to-date")
|
|
print_installed_ver()
|
|
|
|
# -----------------------------------------------------------------------
|
|
def draw_global_mode_buttons(self, context, box):
|
|
sbp = context.scene.SimpleBake_Props
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = 1.5
|
|
row.prop(sbp, "global_mode", icon="SETTINGS", expand=True)
|
|
row = box.row(align=ALIGN)
|
|
row.operator("simplebake.panel_hide_all", icon='PROP_OFF', text="Hide all")
|
|
row.operator("simplebake.panel_show_all", icon='PROP_ON', text="Show all")
|
|
|
|
# -----------------------------------------------------------------------
|
|
def draw_presets(self, context, box):
|
|
sbp = context.scene.SimpleBake_Props
|
|
section_header(box, sbp, "presets_show", "Settings presets", section_icon='PRESET')
|
|
|
|
if not sbp.presets_show:
|
|
return
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.alignment = "CENTER"
|
|
row.label(text="Global Presets", icon="WORLD_DATA")
|
|
|
|
row = box.row(align=ALIGN)
|
|
col = row.column()
|
|
col.template_list("SIMPLEBAKE_UL_Presets_List", "Presets List", sbp,
|
|
"presets_list", sbp, "presets_list_index")
|
|
col = row.column()
|
|
col.operator("simplebake.preset_refresh", text="", icon="FILE_REFRESH")
|
|
col.operator("simplebake.preset_load", text="", icon="CHECKMARK")
|
|
col.operator("simplebake.preset_delete", text="", icon="CANCEL")
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.prop(sbp, "preset_name")
|
|
row.operator("simplebake.preset_save", text="", icon="FUND")
|
|
|
|
prop_row(box, sbp, "preset_load_clear_obj")
|
|
box.row(align=ALIGN).label(text="")
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.alignment = "CENTER"
|
|
row.label(text="Blend File Presets", icon="FILE_BLEND")
|
|
|
|
row = box.row(align=ALIGN)
|
|
col = row.column()
|
|
col.template_list("SIMPLEBAKE_UL_Local_Presets_List", "Local Presets List", sbp,
|
|
"local_presets_list", sbp, "local_presets_list_index")
|
|
col = row.column()
|
|
col.operator("simplebake.local_preset_refresh", text="", icon="FILE_REFRESH")
|
|
col.operator("simplebake.local_preset_load", text="", icon="CHECKMARK")
|
|
col.operator("simplebake.local_preset_delete", text="", icon="CANCEL")
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.prop(sbp, "local_preset_name")
|
|
row.operator("simplebake.local_preset_save", text="", icon="FUND")
|
|
|
|
# -----------------------------------------------------------------------
|
|
def draw_objects_list(self, context, box):
|
|
sbp = context.scene.SimpleBake_Props
|
|
section_header(box, sbp, "bake_objects_show", "Bake objects", section_icon='OUTLINER_OB_MESH')
|
|
|
|
if not sbp.bake_objects_show:
|
|
return
|
|
|
|
row = box.row(align=ALIGN)
|
|
col = row.column()
|
|
col.template_list("SIMPLEBAKE_UL_Objects_List", "Bake Objects List", sbp,
|
|
"objects_list", sbp, "objects_list_index")
|
|
col = row.column()
|
|
col.scale_x = 0.32
|
|
col.operator("simplebake.highlight_bake_objects", text="", icon="LIGHT_SPOT")
|
|
col.operator("simplebake.remove_highlight", text="", icon="SHADING_BBOX")
|
|
col.prop(sbp, "highlight_col", text="")
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.operator('simplebake.add_bake_object', text='Add', icon="PRESET_NEW")
|
|
row.operator('simplebake.remove_bake_object', text='Remove', icon="CANCEL")
|
|
row.operator('simplebake.clear_bake_objects_list', text='Clear', icon="MATPLANE")
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.operator('simplebake.move_bake_object_list', text='Up', icon="TRIA_UP").direction = "UP"
|
|
row.operator('simplebake.move_bake_object_list', text='Down', icon="TRIA_DOWN").direction = "DOWN"
|
|
row.operator('simplebake.refresh_bake_object_list', text='Refresh', icon="FILE_REFRESH")
|
|
|
|
if sbp.highlight_on:
|
|
monkey_tip(context, monkey_tip_formatter(
|
|
"Objects are highlighted when viewport set to \"Solid\" shading. "
|
|
"Must be re-enabled after Blender restart"), box)
|
|
|
|
if sbp.global_mode == SBConstants.PBR:
|
|
row, col_l, col_r = two_prop_row(box, sbp, "selected_s2a", "isolate_objects",
|
|
"Bake to target", "Isolate objects")
|
|
if sbp.tex_per_mat:
|
|
col_l.enabled = False
|
|
if sbp.join_objs_to_proxy:
|
|
col_r.enabled = False
|
|
if sbp.selected_s2a:
|
|
self._draw_s2a_pbr(context, box, sbp)
|
|
|
|
if sbp.global_mode == SBConstants.CYCLESBAKE:
|
|
row, col_l, col_r = two_prop_row(box, sbp, "cycles_s2a", "isolate_objects",
|
|
"Bake to target", "Isolate objects")
|
|
if sbp.tex_per_mat:
|
|
col_l.enabled = False
|
|
if sbp.join_objs_to_proxy:
|
|
col_r.enabled = False
|
|
if sbp.cycles_s2a:
|
|
self._draw_s2a_cycles(context, box, sbp)
|
|
|
|
if check_for_render_inactive_modifiers(context):
|
|
monkey_tip(context, [
|
|
"One or more selected objects",
|
|
"has a modifier enabled for",
|
|
"render (and so baking), but disabled in",
|
|
"viewport. May cause unexpected results"
|
|
], box)
|
|
|
|
if check_for_viewport_inactive_modifiers(context):
|
|
monkey_tip(context, [
|
|
"One or more selected objects",
|
|
"has a modifier enabled in the",
|
|
"viewport, but disabled for",
|
|
"render (and so baking).",
|
|
"May cause unexpected results"
|
|
], box)
|
|
|
|
def _draw_s2a_pbr(self, context, box, sbp):
|
|
row = box.row(align=ALIGN)
|
|
row.alignment = "RIGHT"
|
|
row.prop(sbp, "s2a_opmode")
|
|
|
|
if sbp.s2a_opmode in ["single", "decals"]:
|
|
row = box.row(align=ALIGN)
|
|
split = row.split(factor=0.25)
|
|
split.alignment = "LEFT"
|
|
split.label(text="Target")
|
|
split.prop(sbp, "targetobj", text="")
|
|
|
|
row = box.row(align=ALIGN)
|
|
split = row.split(factor=0.25)
|
|
split.alignment = "LEFT"
|
|
split.label(text="Cage (optional)")
|
|
split.prop(context.scene.render.bake, "cage_object", text="")
|
|
##TODO - Move---------------------------------------------------------------------------------
|
|
row.enabled = False if check_for_auto_cage(context) else True
|
|
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.operator("simplebake.auto_cage", icon='ADD', text="Auto-generate cage")
|
|
row.operator("simplebake.remove_auto_cage", icon='REMOVE', text="Remove auto-generated cage")
|
|
|
|
if context.scene.render.bake.cage_object is not None:
|
|
if 'SB_AUTO_CAGE' in context.scene.render.bake.cage_object.modifiers:
|
|
prop_row(box, sbp, "auto_cage_extrusion", text="Auto generated cage extrusion")
|
|
|
|
if sbp.s2a_opmode == "decals":
|
|
decals_tips(context, box)
|
|
if sbp.s2a_opmode == "single":
|
|
standard_s2a_tips(context, box)
|
|
|
|
if sbp.s2a_opmode == "automatch":
|
|
row = box.row(align=ALIGN)
|
|
row.alignment = "RIGHT"
|
|
row.prop(sbp, "auto_match_mode")
|
|
auto_match_high_low_tips(context, box)
|
|
|
|
self._draw_cage_settings(context, box, sbp)
|
|
|
|
def _draw_s2a_cycles(self, context, box, sbp):
|
|
row = box.row(align=ALIGN)
|
|
row.alignment = "RIGHT"
|
|
row.prop(sbp, "s2a_opmode")
|
|
|
|
if sbp.s2a_opmode in ["single"]:
|
|
row = box.row(align=ALIGN)
|
|
split = row.split(factor=0.25)
|
|
split.alignment = "LEFT"
|
|
split.label(text="Target")
|
|
split.prop(sbp, "targetobj_cycles", text="")
|
|
|
|
row = box.row(align=ALIGN)
|
|
split = row.split(factor=0.25)
|
|
split.alignment = "LEFT"
|
|
split.label(text="Cage (optional)")
|
|
split.prop(context.scene.render.bake, "cage_object", text="")
|
|
row.enabled = False if check_for_auto_cage(context) else True
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.operator("simplebake.auto_cage", icon='ADD', text="Auto-generate cage")
|
|
row.operator("simplebake.remove_auto_cage", icon='REMOVE', text="Remove auto-generated cage")
|
|
|
|
if context.scene.render.bake.cage_object is not None:
|
|
if 'SB_AUTO_CAGE' in context.scene.render.bake.cage_object.modifiers:
|
|
prop_row(box, sbp, "auto_cage_extrusion", text="Auto generated cage extrusion")
|
|
|
|
standard_s2a_tips(context, box)
|
|
|
|
if sbp.s2a_opmode in ["automatch"]:
|
|
row = box.row(align=ALIGN)
|
|
row.alignment = "RIGHT"
|
|
row.prop(sbp, "auto_match_mode")
|
|
auto_match_high_low_tips(context, box)
|
|
|
|
self._draw_cage_settings(context, box, sbp)
|
|
|
|
def _draw_cage_settings(self, context, box, sbp):
|
|
cage_obj = context.scene.render.bake.cage_object
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.alignment = "RIGHT"
|
|
row.prop(sbp, "cage_smooth_hard")
|
|
if cage_obj is not None:
|
|
row.enabled = False
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.use_property_split = True
|
|
row.prop(sbp, "cage_extrusion")
|
|
if cage_obj is not None:
|
|
row.enabled = False
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.use_property_split = True
|
|
row.prop(sbp, "ray_distance")
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.use_property_split = True
|
|
row.prop(sbp, "cage_and_ray_multiplier")
|
|
|
|
# -----------------------------------------------------------------------
|
|
def draw_materials_section(self, context, box):
|
|
sbp = context.scene.SimpleBake_Props
|
|
|
|
section_header(box, sbp, "materials_show", "Materials", section_icon='MATERIAL')
|
|
|
|
|
|
if not sbp.materials_show:
|
|
return
|
|
|
|
if not sbp.objects_list:
|
|
box.label(text="No objects in the bake objects list", icon="INFO")
|
|
return
|
|
|
|
monkey_tip(context, monkey_tip_formatter(
|
|
"Each object's materials are listed below. Use the checkboxes to "
|
|
"include or exclude individual materials from the bake. Excluded "
|
|
"materials will be skipped — no texture will be created for them."), box)
|
|
|
|
row = box.row()
|
|
row.scale_y = SCALE
|
|
row.operator("simplebake.refresh_bake_object_list", text="Refresh Materials", icon="FILE_REFRESH")
|
|
|
|
# tex_per_mat and expand_mat_uvs toggles
|
|
prop_row(box, sbp, "tex_per_mat", enabled=not sbp.merged_bake and not sbp.selected_s2a and not sbp.cycles_s2a, text="Texture per material")
|
|
|
|
|
|
box.separator(factor=0.5)
|
|
|
|
row = box.row(align=True)
|
|
row.scale_y = SCALE
|
|
row.operator("simplebake.materials_expand_all", text="Expand All", icon="TRIA_DOWN")
|
|
row.operator("simplebake.materials_collapse_all", text="Collapse All", icon="TRIA_RIGHT")
|
|
|
|
# Per-object material lists
|
|
for item in sbp.objects_list:
|
|
if item.obj_point is None:
|
|
continue
|
|
obj = item.obj_point
|
|
|
|
# Arrow + plain label — label is always left-aligned and unaffected by toggle mode
|
|
row = box.row(align=True)
|
|
row.scale_y = SCALE
|
|
icon = "TRIA_DOWN" if item.expanded else "TRIA_RIGHT"
|
|
row.prop(item, "expanded", text="", icon=icon, emboss=False)
|
|
row.label(text=obj.name, icon="OBJECT_DATA")
|
|
|
|
if item.expanded:
|
|
mat_entries = [e for e in sbp.mat_bake_list if e.obj_name == obj.name]
|
|
if not mat_entries:
|
|
box.label(text="No materials", icon="INFO")
|
|
else:
|
|
col = box.column(align=True)
|
|
col.scale_y = 0.9
|
|
for entry in mat_entries:
|
|
row2 = col.row(align=True)
|
|
# Checkbox style — clearer as an include/exclude control than a toggle
|
|
row2.prop(entry, "enabled", text=entry.mat_name, icon="MATERIAL")
|
|
|
|
# -----------------------------------------------------------------------
|
|
def draw_pbr_settings(self, context, box):
|
|
sbp = context.scene.SimpleBake_Props
|
|
section_header(box, sbp, "pbr_settings_show", "PBR Bakes", section_icon='NODE_MATERIAL')
|
|
|
|
if not sbp.pbr_settings_show:
|
|
return
|
|
|
|
if sbp.preview_active:
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.alert = True
|
|
row.operator("simplebake.restore_preview", icon="LOOP_BACK")
|
|
|
|
two_prop_row_preview(box, sbp, "selected_col", "selected_metal",
|
|
SBConstants.PBR_DIFFUSE, SBConstants.PBR_METAL)
|
|
|
|
# Roughness / Normal — Normal has no preview
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
col_l = row.column()
|
|
col_l.prop(sbp, "selected_rough", toggle=TOGGLES)
|
|
preview_eye(row, sbp.selected_rough, sbp.rough_glossy_switch)
|
|
col_r = row.column()
|
|
col_r.prop(sbp, "selected_normal", toggle=TOGGLES)
|
|
# Placeholder to match the preview eye column width on other rows
|
|
col_placeholder = row.column()
|
|
col_placeholder.label(text="", icon="BLANK1")
|
|
|
|
# Transmission row — right col disabled on Blender 4+
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
col_l = row.column()
|
|
col_l.prop(sbp, "selected_trans", toggle=TOGGLES)
|
|
preview_eye(row, sbp.selected_trans, SBConstants.PBR_TRANSMISSION)
|
|
col_r = row.column()
|
|
t = " " if bpy.app.version >= (4, 0, 0) else "Transmission Roughness"
|
|
col_r.prop(sbp, "selected_transrough", toggle=TOGGLES, text=t)
|
|
if bpy.app.version >= (4, 0, 0):
|
|
col_r.enabled = False
|
|
preview_eye(row, sbp.selected_transrough and bpy.app.version < (4, 0, 0), SBConstants.PBR_TRANSMISSION_ROUGH)
|
|
|
|
two_prop_row_preview(box, sbp, "selected_clearcoat", "selected_clearcoat_rough",
|
|
SBConstants.PBR_CLEARCOAT, sbp.ccrough_glossy_switch)
|
|
two_prop_row_preview(box, sbp, "selected_emission", "selected_emission_strength",
|
|
SBConstants.PBR_EMISSION, SBConstants.PBR_EMISSION_STRENGTH)
|
|
|
|
# Specular / Alpha — alpha disabled for decals
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
col_l = row.column()
|
|
col_l.prop(sbp, "selected_specular", toggle=TOGGLES)
|
|
preview_eye(row, sbp.selected_specular, SBConstants.PBR_SPECULAR)
|
|
col_r = row.column()
|
|
col_r.prop(sbp, "selected_alpha", toggle=TOGGLES)
|
|
if sbp.selected_s2a and sbp.s2a_opmode == "decals":
|
|
col_r.enabled = False
|
|
preview_eye(row, sbp.selected_alpha and not (sbp.selected_s2a and sbp.s2a_opmode == "decals"), SBConstants.PBR_ALPHA)
|
|
|
|
two_prop_row_preview(box, sbp, "selected_sss", "selected_sss_scale",
|
|
SBConstants.PBR_SSS, SBConstants.PBR_SSS_SCALE)
|
|
two_prop_row_preview(box, sbp, "selected_bump", "selected_displacement",
|
|
SBConstants.PBR_BUMP, SBConstants.PBR_DISPLACEMENT)
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = 0.2
|
|
row.label(text="")
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.operator("simplebake.selectall_pbr", icon='ADD')
|
|
row.operator("simplebake.selectnone_pbr", icon='REMOVE')
|
|
row.operator("simplebake.autodetect_pbr_channels", icon='LIGHT', text="Attempt to auto-detect")
|
|
|
|
# Extra options
|
|
#All extra options need us to be saving externally
|
|
|
|
enabled = sbp.save_bakes_external
|
|
if sbp.selected_col or sbp.selected_rough or sbp.selected_normal:
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = 0.5
|
|
row.label(text="Extra Options:")
|
|
if not sbp.save_bakes_external:
|
|
monkey_tip(context, [
|
|
"These options are greyed out",
|
|
"because you are not exporting",
|
|
"your bakes externally"
|
|
], box)
|
|
|
|
if sbp.selected_col and sbp.s2a_opmode != "decals":
|
|
prop_row(box, sbp, "multiply_diffuse_ao", enabled=enabled, small_row=True, text="Diffuse")
|
|
if sbp.multiply_diffuse_ao == "diffusexao":
|
|
prop_row(box, sbp, "multiply_diffuse_ao_percent", small_row=True)
|
|
|
|
if sbp.selected_rough:
|
|
prop_row(box, sbp, "rough_glossy_switch", enabled=enabled, small_row=True, text="Rough/Gloss")
|
|
|
|
if sbp.selected_clearcoat_rough:
|
|
prop_row(box, sbp, "ccrough_glossy_switch", enabled=enabled, small_row=True, text="CC Rough/Gloss")
|
|
|
|
if sbp.selected_normal:
|
|
prop_row(box, sbp, "normal_format_switch", enabled=enabled, small_row=True, text="Normal Format")
|
|
prop_row(box, context.scene.render.bake, "normal_space", enabled=enabled, text="Normal Space", small_row=True)
|
|
prop_row(box, context.scene.render.bake, "normal_r", enabled=enabled, text="Swizzle R", small_row=True)
|
|
prop_row(box, context.scene.render.bake, "normal_g", enabled=enabled, text="G", small_row=True)
|
|
prop_row(box, context.scene.render.bake, "normal_b", enabled=enabled, text="B", small_row=True)
|
|
if context.scene.render.bake.normal_space == 'OBJECT':
|
|
prop_row(box, context.scene.render.bake, "margin_type", enabled=enabled, text="Normal Margin", small_row=True)
|
|
|
|
|
|
|
|
if check_for_convertible_shaders(context) and not sbp.preview_active:
|
|
monkey_tip(context, monkey_tip_formatter(
|
|
"WARNING: Some materials on your bake objects use non-PBR shader nodes "
|
|
"(e.g., Diffuse, Glossy). SimpleBake will TRY to match them for PBR, but "
|
|
"results may vary. It's recommended to use the 'Attempt to auto-detect' button, "
|
|
"as you may not expect the PBR maps needed after conversion. You will always get "
|
|
"the best results from reworking your materials to be based around the Principled BSDF"
|
|
), box)
|
|
|
|
# -----------------------------------------------------------------------
|
|
def draw_aov_bakes(self, context, box):
|
|
sbp = context.scene.SimpleBake_Props
|
|
section_header(box, sbp, "aov_settings_show", "AOV Bakes", section_icon='RENDERLAYERS')
|
|
|
|
if not sbp.aov_settings_show:
|
|
return
|
|
|
|
row = box.row()
|
|
row.scale_y = SCALE
|
|
row.operator("simplebake.refresh_aov_list", text="Refresh AOVs", icon="FILE_REFRESH")
|
|
|
|
if len(sbp.aov_items) == 0:
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.label(text="No AOV Output nodes found in your bake object(s)' materials'")
|
|
else:
|
|
for item in sbp.aov_items:
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
name = item.name if item.name != "" else "Untitled"
|
|
row.prop(item, "enabled", text=name, toggle=TOGGLES)
|
|
row.prop(item, "cs")
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = 0.1
|
|
row.label(text=f"(Object: \"{item.object_name}\", Material: \"{item.mat_name}\")")
|
|
|
|
monkey_tip(context, monkey_tip_formatter(
|
|
"You can use AOV Output Nodes in your materials to capture specific outputs and "
|
|
"effectively create custom bake maps. "
|
|
"Whatever you connect to an AOV Output Node's input will be baked as a separate map."
|
|
), box)
|
|
|
|
# -----------------------------------------------------------------------
|
|
def draw_cyclesbake_settings(self, context, box):
|
|
sbp = context.scene.SimpleBake_Props
|
|
section_header(box, sbp, "cyclesbake_settings_show", "CyclesBake", section_icon='BLENDER')
|
|
|
|
if not sbp.cyclesbake_settings_show:
|
|
return
|
|
|
|
cscene = context.scene.cycles
|
|
cbk = context.scene.render.bake
|
|
|
|
box.row(align=ALIGN).prop(cscene, "bake_type")
|
|
box.row(align=ALIGN).prop(context.scene.render.bake, "view_from")
|
|
|
|
if cscene.bake_type == 'NORMAL':
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
col = row.column()
|
|
col.prop(cbk, "normal_space", text="Space")
|
|
sub = col.column()
|
|
sub.prop(cbk, "normal_r", text="Swizzle R")
|
|
sub.prop(cbk, "normal_g", text="Swizzle G")
|
|
sub.prop(cbk, "normal_b", text="Swizzle B")
|
|
|
|
elif cscene.bake_type == 'COMBINED':
|
|
two_prop_row(box, cbk, "use_pass_direct", "use_pass_indirect")
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
col = row.column()
|
|
col.active = cbk.use_pass_direct or cbk.use_pass_indirect
|
|
two_prop_row(box, cbk, "use_pass_diffuse", "use_pass_glossy")
|
|
two_prop_row(box, cbk, "use_pass_transmission", "use_pass_emit")
|
|
|
|
elif cscene.bake_type in {'DIFFUSE', 'GLOSSY', 'TRANSMISSION'}:
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.column().prop(cbk, "use_pass_direct", toggle=TOGGLES)
|
|
row.column().prop(cbk, "use_pass_indirect", toggle=TOGGLES)
|
|
row.column().prop(cbk, "use_pass_color", toggle=TOGGLES)
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.column().prop(context.scene.cycles, "samples")
|
|
|
|
if sbp.global_mode == SBConstants.CYCLESBAKE and cscene.bake_type != "NORMAL":
|
|
box.row(align=ALIGN).prop(sbp, "cyclesbake_cs")
|
|
|
|
denoise_options(box)
|
|
|
|
# -----------------------------------------------------------------------
|
|
def draw_specials_settings(self, context, box):
|
|
sbp = context.scene.SimpleBake_Props
|
|
section_header(box, sbp, "specials_show", "Special bakes", section_icon='EXPERIMENTAL')
|
|
need_denoise_options = False
|
|
|
|
if not sbp.specials_show:
|
|
return
|
|
|
|
if sbp.preview_active:
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.alert = True
|
|
row.operator("simplebake.restore_preview", icon="LOOP_BACK")
|
|
|
|
# Colour ID / Vertex Colour
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
col_l = row.column()
|
|
col_l.prop(sbp, "selected_col_mats", toggle=TOGGLES)
|
|
if sbp.selected_s2a or sbp.cycles_s2a:
|
|
col_l.enabled = False
|
|
preview_eye(row, sbp.selected_col_mats and not (sbp.selected_s2a or sbp.cycles_s2a), SBConstants.COLOURID)
|
|
col_r = row.column()
|
|
col_r.prop(sbp, "selected_col_vertex", toggle=TOGGLES)
|
|
preview_eye(row, sbp.selected_col_vertex, SBConstants.VERTEXCOL)
|
|
|
|
two_prop_row_preview(box, sbp, "selected_curvature", "selected_thickness",
|
|
SBConstants.CURVATURE, SBConstants.THICKNESS)
|
|
|
|
# AO / Lightmap
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
col_l = row.column()
|
|
col_l.prop(sbp, "selected_ao", toggle=TOGGLES)
|
|
if sbp.global_mode == SBConstants.PBR and sbp.multiply_diffuse_ao == "diffusexao":
|
|
col_l.enabled = False
|
|
preview_eye(row, sbp.selected_ao and not (sbp.global_mode == SBConstants.PBR and sbp.multiply_diffuse_ao == "diffusexao"), SBConstants.AO)
|
|
col_r = row.column()
|
|
col_r.prop(sbp, "selected_lightmap", toggle=TOGGLES)
|
|
if sbp.selected_s2a or sbp.cycles_s2a:
|
|
col_r.enabled = False
|
|
preview_eye(row, sbp.selected_lightmap and not (sbp.selected_s2a or sbp.cycles_s2a), SBConstants.LIGHTMAP)
|
|
|
|
if sbp.selected_ao or sbp.selected_thickness or sbp.selected_lightmap:
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = 0.5
|
|
row.label(text="Extra options:")
|
|
|
|
if sbp.selected_ao or sbp.selected_thickness:
|
|
box.row(align=ALIGN).prop(sbp, "ao_sample_count")
|
|
if sbp.global_mode == SBConstants.PBR:
|
|
need_denoise_options = True
|
|
|
|
if sbp.selected_lightmap and sbp.global_mode == SBConstants.PBR:
|
|
prop_row(box, context.scene.cycles, "samples", text="Lightmap Sample Count")
|
|
need_denoise_options = True
|
|
|
|
if sbp.selected_lightmap:
|
|
row = prop_row(box, sbp, "lightmap_apply_colman", text="Lightmap apply colour management")
|
|
if not sbp.save_bakes_external:
|
|
row.enabled = False
|
|
|
|
if need_denoise_options:
|
|
box.row(align=ALIGN)
|
|
denoise_options(box)
|
|
|
|
# Tips
|
|
if (sbp.selected_lightmap or sbp.selected_ao or sbp.selected_thickness) and sbp.global_mode == SBConstants.PBR:
|
|
monkey_tip(context, monkey_tip_formatter(
|
|
f"Sample count and denoise are not normally relevant for PBR Bake, but they are for "
|
|
f"{SBConstants.LIGHTMAP}, {SBConstants.AO} and {SBConstants.THICKNESS}"
|
|
), box)
|
|
|
|
if sbp.selected_lightmap and sbp.global_mode == SBConstants.CYCLESBAKE:
|
|
monkey_tip(context, monkey_tip_formatter(
|
|
"Lightmap will have sample count and denoise settings that you have set for CyclesBake above"
|
|
), box)
|
|
|
|
if (sbp.selected_ao or sbp.selected_thickness) and sbp.global_mode == SBConstants.CYCLESBAKE:
|
|
monkey_tip(context, [
|
|
"AO and thickness will have",
|
|
"denoise settings that",
|
|
"you have set for CyclesBake above"
|
|
], box)
|
|
|
|
if sbp.selected_s2a or sbp.cycles_s2a:
|
|
monkey_tip(context, ["Lightmap is unavailable when", "baking to target object for now"], box)
|
|
|
|
monkey_tip(context, monkey_tip_formatter(
|
|
"Use the eye icon next to a specials bake type to preview it. "
|
|
"You can then edit the material directly (e.g. adjust curvature strength). "
|
|
"Your changes will be used for all bakes of that type in this file."
|
|
), box)
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.operator("simplebake.reset_specials_materials", icon="TRASH")
|
|
|
|
# -----------------------------------------------------------------------
|
|
def draw_texture_settings(self, context, box):
|
|
sbp = context.scene.SimpleBake_Props
|
|
section_header(box, sbp, "textures_show", "Texture settings", section_icon='TEXTURE')
|
|
|
|
if not sbp.textures_show:
|
|
return
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = 0.5
|
|
row.label(text="Bake at:")
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.prop(sbp, "imgwidth")
|
|
row.prop(sbp, "imgheight")
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.operator("simplebake.decrease_texture_res", icon="TRIA_DOWN")
|
|
row.operator("simplebake.increase_texture_res", icon="TRIA_UP")
|
|
|
|
box.row(align=ALIGN)
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = 0.5
|
|
row.label(text="Output at:")
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.prop(sbp, "outputwidth")
|
|
row.prop(sbp, "outputheight")
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.operator("simplebake.decrease_output_res", icon="TRIA_DOWN")
|
|
row.operator("simplebake.increase_output_res", icon="TRIA_UP")
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.alignment = "RIGHT"
|
|
row.prop(context.scene.render.bake, "margin", text="Bake Margin")
|
|
|
|
try:
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.alignment = "RIGHT"
|
|
row.prop(context.scene.render.bake, "margin_type", text="Margin Type")
|
|
except:
|
|
pass
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.prop(sbp, "texture_bg_col", text="Texture BG Colour")
|
|
|
|
if (sbp.texture_bg_col[3] < 1.0) and sbp.save_bakes_external:
|
|
monkey_tip(context, monkey_tip_formatter(
|
|
"NOTE: Normally, for 'non-color' textures (e.g. metalness, roughness etc), the custom background "
|
|
"colour will be lost when exporting, as these are saved as single-channel greyscale. "
|
|
"However, as you have set an alpha value below 1.0, ALL textures will be exported as RGBA and the "
|
|
"background colour + alpha will be preserved."
|
|
), box)
|
|
|
|
row, col_l, col_r = two_prop_row(box, sbp, "everything32bitfloat", "clear_image",
|
|
label_right="Clear image before bake")
|
|
if sbp.export_file_format == "JPEG" or not sbp.clear_image:
|
|
col_l.enabled = False
|
|
|
|
row = prop_row(box, sbp, "merged_bake")
|
|
if sbp.tex_per_mat:
|
|
row.enabled = False
|
|
|
|
if sbp.merged_bake:
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.column().prop(sbp, "merged_bake_name", text="Texture set name")
|
|
row.column().prop(sbp, "join_objs_to_proxy", text="Join bake objects", toggle=TOGGLES)
|
|
|
|
if sbp.join_objs_to_proxy:
|
|
monkey_tip(context, monkey_tip_formatter(
|
|
"WARNING: When using the option to join objects before baking, any materials "
|
|
"that rely on 'Generated' (or 'Object') texture coordinates may appear "
|
|
"differently after the join. This can cause baked textures to look incorrect. "
|
|
"Consider disabling this option if your materials use these coordinates. "
|
|
"Also note that the 'Isolate Objects' option will not function when objects are "
|
|
"joined, as individual objects cannot be hidden from each other once combined."
|
|
), box)
|
|
|
|
if len(sbp.objects_list) < 2 and sbp.merged_bake or \
|
|
((sbp.selected_s2a or sbp.cycles_s2a) and sbp.s2a_opmode != "automatch"):
|
|
monkey_tip(context, [
|
|
"You are baking multiple objects to one",
|
|
"texture set, but have fewer then 2",
|
|
"in the bake list. This is OK, but",
|
|
"might not be what you wanted"
|
|
], box)
|
|
|
|
row = box.row(align=ALIGN)
|
|
col = row.column()
|
|
col.scale_y = SCALE
|
|
col.prop(sbp, "do_aa", text="Anti-aliasing", toggle=TOGGLES)
|
|
if not sbp.save_bakes_external:
|
|
col.enabled = False
|
|
row.column().scale_y = SCALE # spacer column
|
|
|
|
if sbp.do_aa:
|
|
box.row(align=ALIGN).prop(sbp, "aa_threshold", text="AA Threshold")
|
|
box.row(align=ALIGN).prop(sbp, "aa_contrast_limit", text="AA Contrast limit")
|
|
box.row(align=ALIGN).prop(sbp, "aa_corner_radius", text="AA Corner radius")
|
|
|
|
# -----------------------------------------------------------------------
|
|
def draw_export_settings(self, context, box):
|
|
sbp = context.scene.SimpleBake_Props
|
|
section_header(box, sbp, "export_show", "Export settings", section_icon='EXPORT')
|
|
|
|
if not sbp.export_show:
|
|
return
|
|
|
|
if not is_blend_saved():
|
|
box.row(align=ALIGN).label(text="Unavailable - Blend file not saved")
|
|
return
|
|
|
|
_, _, col_obj = two_prop_row(box, sbp, "save_bakes_external", "save_obj_external")
|
|
col_obj.enabled = sbp.save_bakes_external
|
|
|
|
if sbp.save_bakes_external or sbp.save_obj_external:
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
col = row.column()
|
|
col.prop(sbp, "export_path", text="Export Path")
|
|
row.column().operator("simplebake.export_path_to_blend_location", text="", icon="UV_SYNC_SELECT")
|
|
prop_row(box, sbp, "export_folder_per_object")
|
|
|
|
if sbp.save_bakes_external:
|
|
if sbp.global_mode == SBConstants.PBR:
|
|
row = prop_row(box, sbp, "apply_col_man_to_col")
|
|
if not sbp.selected_col:
|
|
row.enabled = False
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
if sbp.export_file_format not in ("OPEN_EXR", "JPEG", "TARGA"):
|
|
row.prop(sbp, "everything_16bit", toggle=TOGGLES)
|
|
|
|
if sbp.global_mode == SBConstants.CYCLESBAKE:
|
|
row = prop_row(box, sbp, "export_cycles_col_space")
|
|
if context.scene.cycles.bake_type == "NORMAL":
|
|
row.enabled = False
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
col = row.column()
|
|
col.prop(sbp, "export_file_format", text="Format")
|
|
if sbp.export_file_format == "OPEN_EXR":
|
|
row.column().prop(sbp, "exr_codec_export")
|
|
if sbp.export_file_format == "JPEG":
|
|
row.column().prop(sbp, "jpeg_quality", slider=True)
|
|
|
|
prop_row(box, sbp, "keep_internal_after_export")
|
|
|
|
if sbp.save_obj_external:
|
|
prop_row(box, sbp, "export_mesh_individual_or_combined", text="Mesh objects")
|
|
if sbp.export_mesh_individual_or_combined == "combined":
|
|
prop_row(box, sbp, "merge_export_obj", text="Combine objects within export file")
|
|
prop_row(box, sbp, "export_format")
|
|
prop_row(box, sbp, "apply_mods_on_mesh_export")
|
|
if sbp.export_format == "fbx":
|
|
prop_row(box, sbp, "apply_transformation")
|
|
if sbp.export_mesh_individual_or_combined == "combined":
|
|
prop_row(box, sbp, "mesh_export_name", text="Mesh name")
|
|
prop_row(box, sbp, "export_mesh_preset_name")
|
|
|
|
if sbp.everything32bitfloat and not sbp.save_bakes_external:
|
|
monkey_tip(context, [
|
|
"You are baking in 32bit float",
|
|
"but not exporting externally.",
|
|
"You may want to export to EXR",
|
|
"to preserve your 32bit image(s)"
|
|
], box)
|
|
|
|
if sbp.export_file_format == "OPEN_EXR" and sbp.save_bakes_external:
|
|
monkey_tip(context, [
|
|
"Note: EXR files cannot be exported",
|
|
"with colour management settings.",
|
|
"EXR doesn't support them. Even",
|
|
"Blender defaults will be ignored"
|
|
], box)
|
|
|
|
# -----------------------------------------------------------------------
|
|
def draw_uv_settings(self, context, box):
|
|
|
|
sbp = context.scene.SimpleBake_Props
|
|
|
|
|
|
section_header(box, sbp, "uv_show", "UV settings", section_icon='UV_DATA')
|
|
|
|
if sbp.uv_show:
|
|
|
|
#Tex per mat is a special case with just one option
|
|
if sbp.tex_per_mat:
|
|
prop_row(box, sbp, "expand_mat_uvs")
|
|
|
|
|
|
# row = box.row(align=ALIGN)
|
|
# row.scale_y = SCALE
|
|
# if sbp.new_uv_option:
|
|
# row.label(text="Going to create new UVs")
|
|
# else:
|
|
# row.label(text="Will use existing UVs")
|
|
|
|
if not sbp.tex_per_mat:
|
|
prop_row(box, sbp, "new_uv_option")
|
|
|
|
if not sbp.new_uv_option:
|
|
prop_row(box, sbp, "auto_detect_udims")
|
|
|
|
if sbp.auto_detect_udims:
|
|
message_lines = monkey_tip_formatter("UDIM tiles are detected when UVs extend beyond standard UV space. Objects with and without UDIM tiles can be baked together. \
|
|
When using 'Multiple objects to one texture set,' if any object has UDIM tiles, all objects will be baked to that UDIM set")
|
|
monkey_tip(context, message_lines, box)
|
|
|
|
if sbp.selected_s2a and sbp.targetobj is not None:
|
|
udim_objs = [sbp.targetobj.name]
|
|
elif sbp.selected_s2a and sbp.targetobj_cycles is not None:
|
|
udim_objs = [sbp.targetobj_cycles.name]
|
|
else:
|
|
udim_objs = [i.name for i in sbp.objects_list]
|
|
|
|
udim_n = 0
|
|
for obj_name in udim_objs:
|
|
try:
|
|
r = get_cached_udim_tiles(context, obj_name, threshold=0.001)
|
|
if r["bbox_tiles"] > udim_n:
|
|
udim_n = r["bbox_tiles"]
|
|
except:
|
|
pass
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.label(text=f"UDIM tiles to bake: {udim_n}")
|
|
|
|
#-----------------------------------
|
|
def multi_object_options(box):
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
#row.alignment = "RIGHT"
|
|
row.prop(sbp, "new_uv_method")
|
|
if sbp.new_uv_method in ["SmartUVProject_Atlas", "SmartUVProject_Individual"]:
|
|
row = box.row(align=ALIGN)
|
|
row.alignment = "RIGHT"
|
|
row.scale_y=SCALE
|
|
row.prop(sbp, "unwrapmargin")
|
|
prop_row(box, sbp, "uvcorrectaspect")
|
|
prop_row(box, sbp, "angle_limit")
|
|
two_prop_row(box, sbp, "area_weight", "scale_to_bounds")
|
|
else: #Combine
|
|
row = box.row(align=ALIGN)
|
|
row.alignment = "RIGHT"
|
|
|
|
#Advanced UV packing options
|
|
n = int(''.join(c for c in bpy.app.version_string if c.isdigit()))
|
|
if sbp.new_uv_method in ["CombineExisting"] and n >= 360:
|
|
|
|
row=box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.prop(sbp, "average_uv_size", text="Average UV island size")
|
|
|
|
two_prop_row(box, sbp, "uvp_rotate", "uvp_scale")
|
|
|
|
two_prop_row(box, sbp, "uvp_lock_pinned", "uvp_merge_overlapping")
|
|
|
|
row=box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
col=row.column()
|
|
col.prop(sbp, "uvpackmargin", toggle=TOGGLES)
|
|
|
|
row=box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
col=row.column()
|
|
col.prop(sbp, "uvp_shape_method", toggle=TOGGLES)
|
|
|
|
row=box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
col=row.column()
|
|
col.prop(sbp, "uvp_rotation_method", toggle=TOGGLES)
|
|
|
|
row=box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
col=row.column()
|
|
col.prop(sbp, "uvp_margin_method", toggle=TOGGLES)
|
|
|
|
row=box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
col=row.column()
|
|
col.prop(sbp, "uvp_lock_method", toggle=TOGGLES)
|
|
|
|
row=box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
col=row.column()
|
|
col.prop(sbp, "uvp_pack_to", toggle=TOGGLES)
|
|
|
|
|
|
def single_object_options(box):
|
|
#One object selected
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y=SCALE
|
|
row.alignment = "RIGHT"
|
|
row.label(text="Smart UV Project: ")
|
|
row.prop(sbp, "unwrapmargin", toggle=TOGGLES)
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y=SCALE
|
|
row.alignment = "RIGHT"
|
|
row.prop(sbp, "uvcorrectaspect")
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y=SCALE
|
|
row.alignment = "RIGHT"
|
|
row.prop(sbp, "angle_limit")
|
|
two_prop_row(box, sbp, "area_weight", "scale_to_bounds")
|
|
#-----------------------------------
|
|
|
|
#Print the options if requesting new UVs
|
|
if sbp.new_uv_option:
|
|
objects = [obj.name for obj in sbp.objects_list]
|
|
viable_high_low = SIMPLEBAKE_UL_Objects_List.viable_high_low_bakes
|
|
if sbp.expand_mat_uvs:
|
|
#No options
|
|
pass
|
|
elif (sbp.selected_s2a or sbp.cycles_s2a) and sbp.s2a_opmode=="automatch" and len(viable_high_low) >1:
|
|
multi_object_options(box)
|
|
elif (sbp.selected_s2a or sbp.cycles_s2a) and sbp.s2a_opmode=="automatch" and len(viable_high_low) ==1:
|
|
single_object_options(box)
|
|
elif (sbp.selected_s2a or sbp.cycles_s2a) and sbp.s2a_opmode=="decals":
|
|
single_object_options(box)
|
|
|
|
elif sbp.selected_s2a and sbp.targetobj!=None:
|
|
single_object_options(box)
|
|
elif sbp.cycles_s2a and sbp.targetobj_cycles!=None:
|
|
single_object_options(box)
|
|
|
|
elif len(objects) >1 and not (sbp.selected_s2a or sbp.cycles_s2a):
|
|
multi_object_options(box)
|
|
elif len(objects) ==1:
|
|
single_object_options(box)
|
|
|
|
else:
|
|
row = box.row(align=ALIGN)
|
|
row.label(text="No objects selected for bake")
|
|
|
|
|
|
if not sbp.new_uv_option:
|
|
two_prop_row(box, sbp, "prefer_existing_sbmap", "restore_orig_uv_map",
|
|
label_left="Prefer existing UVs called SimpleBake", label_right="Restore original UVs")
|
|
|
|
if sbp.new_uv_option:
|
|
two_prop_row(box, sbp, "move_new_uvs_to_top", "restore_orig_uv_map",
|
|
label_right="Restore original UVs")
|
|
|
|
#List for when we are using existing UVs
|
|
if not sbp.new_uv_option or (sbp.new_uv_option and sbp.new_uv_method == 'CombineExisting') or sbp.expand_mat_uvs:
|
|
|
|
row=box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
if (sbp.new_uv_option and sbp.new_uv_method == 'CombineExisting'):
|
|
row.label(text="We are combining existing UVs:", icon="MOD_BOOLEAN")
|
|
else:
|
|
row.label(text="We are baking from existing UVs:", icon="UV_VERTEXSEL")
|
|
|
|
row=box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.operator("simplebake.sync_uv_list", text="Refresh UVs list", icon='FILE_REFRESH')
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.template_list(
|
|
"SIMPLEBAKE_UL_UVItems", "", # UIList type and list_id
|
|
sbp, "uv_items", # data ptr + collection prop
|
|
sbp, "uv_items_index", # active data ptr + index prop
|
|
rows=6
|
|
)
|
|
|
|
row=box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.prop(sbp, "available_uv_maps", text="Available UV Maps")
|
|
row.operator("simplebake.uv_set_all_name", icon='OUTLINER_DATA_MESH')
|
|
|
|
#------------------------------------Tips---------------------------------------------------------
|
|
|
|
if sbp.new_uv_option and sbp.restore_orig_uv_map and not sbp.copy_and_apply:
|
|
#Tip alert
|
|
message_lines = [
|
|
"Generating new UVs, but restoring originals",
|
|
"after bake. Baked textures won't align with the",
|
|
"original UVs. Manually change active UV map ",
|
|
"after bake"
|
|
]
|
|
monkey_tip(context, message_lines, box)
|
|
|
|
|
|
if sbp.new_uv_option and sbp.new_uv_method == "SmartUVProject_Individual" and sbp.merged_bake:
|
|
#Tip alert
|
|
message_lines = [
|
|
"Current settings will unwrap objects",
|
|
"individually but bake to one texture set",
|
|
"Bakes will be on top of each other!"
|
|
]
|
|
monkey_tip(context, message_lines, box)
|
|
|
|
|
|
if not sbp.new_uv_option and sbp.merged_bake:
|
|
#Tip alert
|
|
message_lines = monkey_tip_formatter(
|
|
"ALERT: You are baking multiple objects to one texture \
|
|
set with existing UVs. You will need to manually \
|
|
make sure those UVs don't overlap!"
|
|
)
|
|
monkey_tip(context, message_lines, box)
|
|
|
|
|
|
if sbp.new_uv_option and not sbp.save_obj_external and not sbp.copy_and_apply and sbp.bgbake == "bg":
|
|
#Tip alert
|
|
message_lines = [
|
|
"You are baking in background with new UVs, but",
|
|
"not exporting FBX or using 'Copy Objects and Apply Bakes'",
|
|
"You will recieve the baked textures on import, but you will",
|
|
"have no access to an object with the new UV map!"
|
|
]
|
|
monkey_tip(context, message_lines, box)
|
|
|
|
# -----------------------------------------------------------------------
|
|
def draw_other_settings(self, context, box):
|
|
sbp = context.scene.SimpleBake_Props
|
|
section_header(box, sbp, "other_show", "Other settings", section_icon='SETTINGS')
|
|
|
|
if not sbp.other_show:
|
|
return
|
|
|
|
prop_row(box, sbp, "batch_name")
|
|
prop_row(box, sbp, "replace_previous_bakes")
|
|
prop_row(box, sbp, "image_sequence_enabled")
|
|
|
|
if sbp.image_sequence_enabled:
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.prop(sbp, "image_sequence_start_frame")
|
|
row.prop(sbp, "image_sequence_end_frame")
|
|
monkey_tip(context, monkey_tip_formatter(
|
|
"NOTE: If you haven't added %FRAME% to your image format "
|
|
"string in the SimpleBake addon preferences, it will "
|
|
"be added automatically to the end of the names of your baked images"
|
|
), box)
|
|
|
|
dt = False
|
|
s2a = sbp.selected_s2a or sbp.cycles_s2a
|
|
t = "Copy target object and apply bakes" if s2a else "Copy objects and apply bakes"
|
|
t = f"{t} (after import)" if sbp.bgbake == "bg" else t
|
|
row = prop_row(box, sbp, "copy_and_apply", text=t)
|
|
if not sbp.keep_internal_after_export:
|
|
dt = True
|
|
row.enabled = False
|
|
|
|
if sbp.copy_and_apply:
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
col = row.column()
|
|
t = "Hide source objects after bake"
|
|
t = f"{t} (after import)" if sbp.bgbake == "bg" else t
|
|
col.prop(sbp, "hide_source_objects", text=t, toggle=TOGGLES)
|
|
if sbp.selected_s2a or sbp.cycles_s2a:
|
|
t2 = "Hide cage object after bake"
|
|
t2 = f"{t2} (after import)" if sbp.bgbake == "bg" else t2
|
|
row.column().prop(sbp, "hide_cage_object", text=t2, toggle=TOGGLES)
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
col = row.column()
|
|
col.prop(sbp, "create_glTF_node", toggle=TOGGLES)
|
|
if sbp.create_glTF_node:
|
|
row.column().prop(sbp, "glTF_selection", text="", toggle=TOGGLES)
|
|
|
|
if sbp.global_mode == SBConstants.CYCLESBAKE:
|
|
prop_row(box, sbp, "cyclesbake_copy_and_apply_mat_format", text="Material")
|
|
|
|
t = "Apply bakes to target object" if s2a else "Apply bakes to original objects"
|
|
row = prop_row(box, sbp, "apply_bakes_to_original", text=t)
|
|
if sbp.bgbake == "bg" or not sbp.keep_internal_after_export or sbp.tex_per_mat:
|
|
dt = True
|
|
row.enabled = False
|
|
|
|
if dt:
|
|
if sbp.tex_per_mat:
|
|
t = "'Apply bakes to target object' is not available when Texture Per Material is enabled" if s2a else "'Apply bakes to original objects' is not available when Texture Per Material is enabled"
|
|
monkey_tip(context, monkey_tip_formatter(
|
|
t
|
|
), box)
|
|
elif not sbp.keep_internal_after_export:
|
|
copy_apply_label = "'Copy target object and apply bakes'" if s2a else "'Copy objects and apply bakes'"
|
|
apply_label = "'Apply bakes to target object'" if s2a else "'Apply bakes to original objects'"
|
|
monkey_tip(context, monkey_tip_formatter(
|
|
f"{copy_apply_label} and {apply_label} aren't "
|
|
"available as you have chosen not to keep bakes internally after export. "
|
|
"Turn that back on under 'Export Settings' to use these options"
|
|
), box)
|
|
|
|
if sbp.apply_bakes_to_original:
|
|
if s2a:
|
|
monkey_tip(context, [
|
|
"Be careful when applying bakes to",
|
|
"the target object. Original materials won't",
|
|
"be deleted, BUT they will have no users",
|
|
"and may be purged on next save"
|
|
], box)
|
|
else:
|
|
monkey_tip(context, [
|
|
"Be careful when applying bakes to",
|
|
"original objects. Original materials won't",
|
|
"be deleted, BUT they will have no users",
|
|
"and may be purged on next save"
|
|
], box)
|
|
|
|
if sbp.apply_bakes_to_original and sbp.restore_orig_uv_map:
|
|
monkey_tip(context, [
|
|
"You are restoring your original UVs",
|
|
"after bake, but also applying the baked",
|
|
"textures to original objects. The textures",
|
|
"may not look right with original UVs"
|
|
], box)
|
|
|
|
row, col_l, col_r = two_prop_row(box, sbp, "no_force_32bit_normals", "boosted_sample_count")
|
|
if sbp.global_mode != SBConstants.PBR:
|
|
col_r.enabled = False
|
|
|
|
prop_row(box, sbp, "bw_to_rgb")
|
|
|
|
if context.preferences.addons["cycles"].preferences.has_active_device():
|
|
box.row(align=ALIGN).prop(context.scene.cycles, "device")
|
|
else:
|
|
box.row(align=ALIGN).label(text="No valid GPU device in Blender Preferences. Using CPU.")
|
|
|
|
if (context.preferences.addons["cycles"].preferences.compute_device_type == "OPTIX" and
|
|
context.preferences.addons["cycles"].preferences.has_active_device()):
|
|
monkey_tip(context, [
|
|
"Other users have reported problems baking",
|
|
"with GPU and OptiX. This is a Blender issue",
|
|
"If you encounter problems bake with CPU"
|
|
], box)
|
|
|
|
if (sbp.global_mode == SBConstants.PBR and sbp.copy_and_apply and
|
|
len(pbr_selections_to_list(context)) == 0 and
|
|
len(specials_selection_to_list(context)) > 0):
|
|
monkey_tip(context, [
|
|
"You are baking only special maps (no primary)",
|
|
"while using 'Copy objects and apply bakes'",
|
|
"Special maps will be in the new object(s)",
|
|
"material(s), but disconnected"
|
|
], box)
|
|
|
|
# -----------------------------------------------------------------------
|
|
def draw_channel_packing(self, context, box):
|
|
sbp = context.scene.SimpleBake_Props
|
|
section_header(box, sbp, "channelpacking_show", "Channel packing", section_icon='NODE_COMPOSITING')
|
|
|
|
if not sbp.channelpacking_show:
|
|
return
|
|
|
|
if not is_blend_saved():
|
|
box.row(align=ALIGN).label(text="Unavailable - Blend file not saved")
|
|
return
|
|
|
|
if not sbp.save_bakes_external:
|
|
box.row(align=ALIGN).label(text="Unavailable - You must be exporting your bakes")
|
|
return
|
|
|
|
row = box.row(align=ALIGN)
|
|
col = row.column()
|
|
col.template_list("SIMPLEBAKE_UL_CPTexList", "CP Textures List", sbp,
|
|
"cp_list", sbp, "cp_list_index")
|
|
col = row.column()
|
|
col.operator("simplebake.cptex_delete", text="", icon="CANCEL")
|
|
col.operator("simplebake.cptex_set_defaults", text="", icon="FILE_REFRESH")
|
|
|
|
# Name field and RGBA channel selectors always shown (needed to create or edit entries)
|
|
prop_row(box, sbp, "cp_name")
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
cp_file_format = sbp.channelpackfileformat
|
|
row.prop(sbp, "channelpackfileformat", text="Format")
|
|
if cp_file_format == "OPEN_EXR":
|
|
row.prop(sbp, "exr_codec_cpts")
|
|
if cp_file_format == "PNG":
|
|
row.prop(sbp, "png_compression", text="Compression")
|
|
|
|
for channel, prop in (("R", "cptex_R"), ("G", "cptex_G"),
|
|
("B", "cptex_B"), ("A", "cptex_A")):
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = 0.7
|
|
row.prop(sbp, prop, text=channel)
|
|
|
|
# Save/update button logic: check if cp_name already exists in the list
|
|
cp_list = sbp.cp_list
|
|
current_name = sbp.cp_name
|
|
if current_name in cp_list:
|
|
# Editing an existing saved entry — show update button only if something changed
|
|
index = cp_list.find(current_name)
|
|
cpt = cp_list[index]
|
|
if (cpt.R != sbp.cptex_R or cpt.G != sbp.cptex_G or
|
|
cpt.B != sbp.cptex_B or cpt.A != sbp.cptex_A or
|
|
cpt.file_format != sbp.channelpackfileformat or
|
|
cpt.exr_codec != sbp.exr_codec_cpts or
|
|
cpt.png_compression != sbp.png_compression):
|
|
row = box.row(align=ALIGN)
|
|
row.alert = True
|
|
row.operator("simplebake.cptex_add", text=f"Update {current_name} (!!not saved!!)", icon="ADD")
|
|
else:
|
|
row = box.row(align=ALIGN)
|
|
row.label(text=f"Editing {current_name}")
|
|
row.alignment = 'CENTER'
|
|
else:
|
|
# New entry
|
|
row = box.row(align=ALIGN)
|
|
row.alert = True
|
|
row.operator("simplebake.cptex_add", text="Add new (!!not saved!!)", icon="ADD")
|
|
|
|
if sbp.cptex_R == "" or sbp.cptex_G == "" or sbp.cptex_B == "" or sbp.cptex_A == "":
|
|
row.enabled = False
|
|
|
|
row = prop_row(box, sbp, "del_cptex_components")
|
|
if sbp.copy_and_apply or sbp.apply_bakes_to_original:
|
|
row.enabled = False
|
|
|
|
# -----------------------------------------------------------------------
|
|
def draw_admin_settings(self, context, box):
|
|
sbp = context.scene.SimpleBake_Props
|
|
section_header(box, sbp, "admin_settings_show", "Utilities", section_icon='TOOL_SETTINGS')
|
|
|
|
if not sbp.admin_settings_show:
|
|
return
|
|
|
|
for op_id, label in (
|
|
("simplebake.purge_settings", "x-- SETTINGS --x"),
|
|
("simplebake.restorematerials", "x-- MATERIALS --x"),
|
|
("simplebake.purge_images", "X-- IMAGES --X"),
|
|
("simplebake.purge_objects", "X-- OBJECTS --X"),
|
|
):
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.alert = True
|
|
row.operator(op_id, text=label, icon="ERROR")
|
|
|
|
prop_row(box, sbp, "tex_override")
|
|
if sbp.tex_override:
|
|
prop_row(box, sbp, "tex_override_tex")
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.label(text="Find and replace")
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.prop(sbp, "findreplace_find", toggle=TOGGLES)
|
|
row.prop(sbp, "findreplace_replace", toggle=TOGGLES)
|
|
prop_row(box, sbp, "findreplace_type")
|
|
prop_row(box, sbp, "limit_findandreplace_to_sb", text="Limit to SB data")
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.operator("simplebake.findandreplace", text="Find and replace", icon="VIEWZOOM")
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = SCALE
|
|
row.operator("simplebake.copy_settings_clipboard", icon='COPYDOWN')
|
|
row.operator("simplebake.paste_settings_clipboard", icon='PASTEDOWN')
|
|
|
|
# -----------------------------------------------------------------------
|
|
def draw_bake_buttons(self, context, box):
|
|
|
|
sbp = context.scene.SimpleBake_Props
|
|
n = 0
|
|
if sbp.selected_s2a and sbp.targetobj!=None: objs = [sbp.targetobj.name]
|
|
elif sbp.selected_s2a and sbp.targetobj_cycles!=None: objs = [sbp.targetobj_cycles.name]
|
|
else: objs = [i.name for i in sbp.objects_list]
|
|
|
|
for obj_name in objs:
|
|
try:
|
|
result = get_cached_udim_tiles(context, obj_name, threshold=0.001)
|
|
if result["bbox_tiles"] > n:
|
|
n = result["bbox_tiles"]
|
|
except:
|
|
pass
|
|
|
|
if n>20 and sbp.auto_detect_udims:
|
|
def alert_row(text, icon=None):
|
|
row = box.row(align=ALIGN)
|
|
row.alignment = 'CENTER'
|
|
row.scale_y = 0.6
|
|
row.alert = True
|
|
row.label(text=text, **({"icon": icon} if icon else {}))
|
|
|
|
alert_row(f"WARNING: You are about to bake at least {n} UDIM tiles!!!", icon="ERROR")
|
|
alert_row("This is high. If intentional, that's fine.")
|
|
alert_row("However, it often means UVs are not suitable for baking.")
|
|
alert_row("In the worst case, Blender may run out of memory or crash.")
|
|
alert_row("PLEASE CHECK YOUR UV MAPS!")
|
|
|
|
|
|
# Determine operator, icon, text and flags
|
|
bg = sbp.bgbake == "bg"
|
|
s2a = sbp.selected_s2a or sbp.cycles_s2a
|
|
disable_if_is = False
|
|
bg_name = False
|
|
|
|
if s2a and sbp.s2a_opmode == "decals":
|
|
if bg:
|
|
op, icon, text = "simplebake.bake_operation_decals_background", 'RENDER_RESULT', "Bake! (Decals) (Background)"
|
|
bg_name = disable_if_is = True
|
|
else:
|
|
op, icon, text = "simplebake.bake_operation_decals", 'RENDER_RESULT', "Bake! (Decals)"
|
|
|
|
elif s2a and sbp.s2a_opmode == "automatch":
|
|
if bg:
|
|
op, icon, text = "simplebake.automatch_operation_background", 'RENDER_RESULT', "Bake! (Auto Match) (Background)"
|
|
bg_name = disable_if_is = True
|
|
else:
|
|
op, icon, text = "simplebake.automatch_operation", 'RENDER_RESULT', "Bake! (Auto Match)"
|
|
|
|
elif sbp.global_mode == SBConstants.PBR and not sbp.selected_s2a:
|
|
if bg:
|
|
op, icon, text = "simplebake.bake_operation_pbr_background", 'RENDER_RESULT', "Bake! (in background)"
|
|
bg_name = disable_if_is = True
|
|
else:
|
|
op, icon, text = "simplebake.bake_operation_pbr", 'RENDER_RESULT', "Bake!"
|
|
|
|
elif sbp.global_mode == SBConstants.PBR and sbp.selected_s2a:
|
|
if bg:
|
|
op, icon, text = "simplebake.bake_operation_pbrs2a_background", 'RENDER_RESULT', "Bake! (in background)"
|
|
bg_name = disable_if_is = True
|
|
else:
|
|
op, icon, text = "simplebake.bake_operation_pbrs2a", 'RENDER_RESULT', "Bake! (to target)"
|
|
|
|
elif sbp.global_mode == SBConstants.CYCLESBAKE and not sbp.cycles_s2a:
|
|
if bg:
|
|
op, icon, text = "simplebake.bake_operation_cyclesbake_background", 'RENDER_RESULT', "Bake! (in background)"
|
|
bg_name = True
|
|
else:
|
|
op, icon, text = "simplebake.bake_operation_cyclesbake", 'RENDER_RESULT', "Bake!"
|
|
|
|
elif sbp.global_mode == SBConstants.CYCLESBAKE and sbp.cycles_s2a:
|
|
if bg:
|
|
op, icon, text = "simplebake.bake_operation_cyclesbake_s2a_background", 'RENDER_RESULT', "Bake! (in background)"
|
|
bg_name = disable_if_is = True
|
|
else:
|
|
op, icon, text = "simplebake.bake_operation_cyclesbake_s2a", 'RENDER_RESULT', "Bake! (to target)"
|
|
|
|
else:
|
|
op, icon, text = "simplebake.bake_operation_pbr", 'RENDER_RESULT', "Bake!"
|
|
|
|
# FG/BG switch — always shown at the bottom of the bake box
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = 1.5
|
|
row.prop(sbp, "bgbake", expand=True)
|
|
|
|
if bg_name:
|
|
prop_row(box, sbp, "bgbake_name")
|
|
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = 2
|
|
# Sequence bakes require the image_sequence modal operator (see note
|
|
# in disable_row_if_image_sequence). Background mode is mutually
|
|
# exclusive and is already blocked by disable_row_if_image_sequence.
|
|
if sbp.image_sequence_enabled and not bg:
|
|
row.operator("simplebake.image_sequence", icon='RENDER_RESULT', text="Bake, image sequence").cmd = op
|
|
else:
|
|
row.operator(op, icon=icon, text=text)
|
|
|
|
if BakeInProgress.is_baking:
|
|
row.enabled = False
|
|
if disable_if_is: disable_row_if_image_sequence(context, row, box)
|
|
|
|
if space == 'VIEW_3D':
|
|
monkey_tip(context, monkey_tip_formatter(
|
|
"WARNING: Based on my testing, Blender crashes WAY more when SimpleBake "
|
|
"is displayed in the N-Panel. I haven't been able to find out why. "
|
|
"Use with extreme caution!"
|
|
), box)
|
|
|
|
# -----------------------------------------------------------------------
|
|
def draw_fg_bake_status(self, context, box):
|
|
sbp = context.scene.SimpleBake_Props
|
|
for text, alert in (("FOREGROUND BAKE", True), ("IN PROGRESS", True), ("Please wait", False)):
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = 0.5
|
|
row.alignment = 'CENTER'
|
|
row.alert = alert
|
|
row.label(text=text)
|
|
row = box.row(align=ALIGN)
|
|
row.alignment = 'CENTER'
|
|
row.label(text=f"{sbp.percent_complete}% complete")
|
|
|
|
# -----------------------------------------------------------------------
|
|
def draw_background_bakes(self, context, box):
|
|
sbp = context.scene.SimpleBake_Props
|
|
active = BackgroundBakeTasks.active_tasks
|
|
queued = BackgroundBakeTasks.queued_tasks
|
|
completed = BackgroundBakeTasks.completed_tasks
|
|
|
|
section_header(box, sbp, "bg_status_show", "Background bakes", section_icon='RENDER_ANIMATION')
|
|
|
|
if not sbp.bg_status_show:
|
|
return
|
|
|
|
box.row(align=ALIGN).label(text="Completed:")
|
|
for bgt in completed:
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = 0.7
|
|
row.label(text=f"{bgt.name} - finished", icon="GHOST_ENABLED")
|
|
row.operator("simplebake.import_bgbake", icon='IMPORT', text="").pid = str(bgt.pid)
|
|
row.operator("simplebake.discard_bgbake", icon="CANCEL", text="").pid = str(bgt.pid)
|
|
|
|
box.row(align=ALIGN).label(text="Active:")
|
|
for bgt in active:
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = 0.7
|
|
row.label(text=f"{bgt.name} - baking in progress {BackgroundBakeTasks.get_bgbake_status(bgt)}%",
|
|
icon="GHOST_DISABLED")
|
|
row.operator("simplebake.kill_active_bgbake", icon="CANCEL", text="").pid = str(bgt.pid)
|
|
|
|
box.row(align=ALIGN).label(text="Queued:")
|
|
for bgt in queued:
|
|
row = box.row(align=ALIGN)
|
|
row.scale_y = 0.7
|
|
row.label(text=f"{bgt.name} - queued", icon="GHOST_DISABLED")
|
|
row.operator("simplebake.delete_queued_bgbake", icon='CANCEL', text="").id = bgt.id
|
|
|
|
# -----------------------------------------------------------------------
|
|
def draw_sketchfab_upload(self, context, box):
|
|
box.row(align=ALIGN).operator("simplebake.sketchfab_upload", icon='IMPORT', text="Sketchfab Upload")
|
|
|
|
# -----------------------------------------------------------------------
|
|
def draw(self, context):
|
|
sbp = context.scene.SimpleBake_Props
|
|
layout = self.layout
|
|
|
|
self.draw_version_info(context, layout)
|
|
|
|
if (not BakeInProgress.is_baking) or space == 'VIEW_3D':
|
|
box = layout.box()
|
|
self.draw_global_mode_buttons(context, box)
|
|
|
|
box = layout.box()
|
|
self.draw_presets(context, box)
|
|
|
|
box = layout.box()
|
|
self.draw_objects_list(context, box)
|
|
|
|
box = layout.box()
|
|
self.draw_materials_section(context, box)
|
|
|
|
if sbp.global_mode == SBConstants.PBR:
|
|
box = layout.box()
|
|
self.draw_pbr_settings(context, box)
|
|
|
|
box = layout.box()
|
|
self.draw_aov_bakes(context, box)
|
|
|
|
if sbp.global_mode == SBConstants.CYCLESBAKE:
|
|
box = layout.box()
|
|
self.draw_cyclesbake_settings(context, box)
|
|
|
|
box = layout.box()
|
|
self.draw_specials_settings(context, box)
|
|
|
|
box = layout.box()
|
|
self.draw_texture_settings(context, box)
|
|
|
|
box = layout.box()
|
|
self.draw_export_settings(context, box)
|
|
|
|
box = layout.box()
|
|
self.draw_uv_settings(context, box)
|
|
|
|
box = layout.box()
|
|
self.draw_other_settings(context, box)
|
|
|
|
if sbp.global_mode == SBConstants.PBR:
|
|
box = layout.box()
|
|
self.draw_channel_packing(context, box)
|
|
|
|
box = layout.box()
|
|
self.draw_admin_settings(context, box)
|
|
|
|
box = layout.box()
|
|
self.draw_bake_buttons(context, box)
|
|
|
|
else:
|
|
box = layout.box()
|
|
self.draw_fg_bake_status(context, box)
|
|
|
|
box = layout.box()
|
|
self.draw_background_bakes(context, box)
|
|
|
|
prefs = context.preferences.addons[base_package].preferences
|
|
if prefs.apikey != "":
|
|
box = layout.box()
|
|
self.draw_sketchfab_upload(context, box)
|
|
|
|
|
|
classes = ([SIMPLEBAKE_PT_main_panel])
|
|
|
|
|
|
def register():
|
|
global classes
|
|
for cls in classes:
|
|
register_class(cls)
|
|
|
|
|
|
def unregister():
|
|
global classes
|
|
for cls in classes:
|
|
unregister_class(cls)
|