Files
blender-portable-repo/extensions/blender_org/sequenced_bake/sequenced_bake.py
T
2026-03-17 14:30:01 -06:00

1160 lines
46 KiB
Python

# This file is part of Sequence Bake.
#
# Sequence Bake is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or any later version.
#
# Sequence Bake is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Sequence Bake. If not, see <http://www.gnu.org/licenses/>.
import bpy
import os
import re
from bpy.types import (
Operator,
Panel,
Node,
NodeSocket,
PropertyGroup,
)
class SequencedBakeProperties(PropertyGroup):
sequenced_bake_output_path: bpy.props.StringProperty(
name="",
default="",
subtype='DIR_PATH',
description='Define the output path for the rendered images'
)
sequenced_bake_width: bpy.props.IntProperty(
name="Width",
description='The width of the baked image',
default=1024,
min=1,
max=8192
)
sequenced_bake_height: bpy.props.IntProperty(
name="Height",
description='The height of the baked image',
default=1024,
min=1,
max=8192
)
sequenced_bake_image_format: bpy.props.EnumProperty(
name="",
description="Choose the image format",
items=[
("PNG", "PNG", "Save as PNG"),
("JPEG", "JPEG", "Save as JPEG"),
("BMP", "BMP", "Save as BMP"),
("TIFF", "TIFF", "Save as TIFF"),
("TGA", "TGA", "Save as TGA"),
("EXR", "OpenEXR", "Save as OpenEXR"),
("HDR", "Radiance HDR", "Save as Radiance HDR"),
("CINEON", "Cineon", "Save as Cineon"),
("DPX", "DPX", "Save as DPX")
],
default="PNG"
)
sequence_is_alpha: bpy.props.BoolProperty(
name="Use Alpha",
description="Use alpha channel in the generated material maps",
default=False
)
sequence_use_float: bpy.props.BoolProperty(
name="32-bit Float",
description="Create image with 32-bit floating-point bit depth\nControls the internal precision of the image during creation and baking.",
default=False
)
sequence_clear_baked_maps: bpy.props.BoolProperty(
name="Clear Baked Maps",
description="Clears the baked maps from blenders image viewer list",
default=True
)
sequenced_bake_normal: bpy.props.BoolProperty(
name="Normal",
description='Enable to bake the normal map for the selected objects active material',
default=False
)
# Normal map options.
normal_map_space: bpy.props.EnumProperty(
name="Space",
description="Normal map coordinate space",
items=[
('OBJECT', "Object", "Use object space for the normal map"),
('TANGENT', "Tangent", "Use tangent space for the normal map")
],
default='TANGENT'
)
normal_map_red_channel: bpy.props.EnumProperty(
name="R",
description="Swizzle for the R channel",
items=[
("POS_X", '+X', "Positive X axis"),
("POS_Y", '+Y', "Positive Y axis"),
("POS_Z", '+Z', "Positive Z axis"),
("NEG_X", '-X', "Negative X axis"),
("NEG_Y", '-Y', "Negative Y axis"),
("NEG_Z", '-Z', "Negative Z axis"),
],
default="POS_X"
)
normal_map_green_channel: bpy.props.EnumProperty(
name="G",
description="Swizzle for the G channel",
items=[
("POS_X", '+X', "Positive X axis"),
("POS_Y", '+Y', "Positive Y axis"),
("POS_Z", '+Z', "Positive Z axis"),
("NEG_X", '-X', "Negative X axis"),
("NEG_Y", '-Y', "Negative Y axis"),
("NEG_Z", '-Z', "Negative Z axis"),
],
default="POS_Y"
)
normal_map_blue_channel: bpy.props.EnumProperty(
name="B",
description="Swizzle for the B channel",
items=[
("POS_X", '+X', "Positive X axis"),
("POS_Y", '+Y', "Positive Y axis"),
("POS_Z", '+Z', "Positive Z axis"),
("NEG_X", '-X', "Negative X axis"),
("NEG_Y", '-Y', "Negative Y axis"),
("NEG_Z", '-Z', "Negative Z axis"),
],
default="POS_Z"
)
sequenced_bake_roughness: bpy.props.BoolProperty(
name="Roughness",
description='Enable to bake the roughness map for the selected objects active material',
default=False
)
sequenced_bake_glossy: bpy.props.BoolProperty(
name="Glossy",
description='Enable to bake the glossy map for the selected objects active material',
default=False
)
sequenced_bake_emission: bpy.props.BoolProperty(
name="Emission",
description='Enable to bake the emissive map for the selected objects active material',
default=False
)
sequenced_bake_ambient_occlusion: bpy.props.BoolProperty(
name="Ambient Occlusion",
description='Enable to bake the ambient occlusion map for the selected objects active material',
default=False
)
sequenced_bake_shadow: bpy.props.BoolProperty(
name="Shadow",
description='Enable to bake the shadow map for the selected objects active material',
default=False
)
sequenced_bake_position: bpy.props.BoolProperty(
name="Position",
description='Enable to bake the position map for the selected objects active material',
default=False
)
sequenced_bake_uv: bpy.props.BoolProperty(
name="UV",
description='Enable to bake the UV map for the selected objects active material',
default=False
)
sequenced_bake_environment: bpy.props.BoolProperty(
name="Environment",
description='Enable to bake the environment map for the selected objects active material',
default=False
)
sequenced_bake_diffuse: bpy.props.BoolProperty(
name="Diffuse",
description='Enable to bake the deffuse map for the selected objects active material',
default=False
)
sequenced_bake_transmission: bpy.props.BoolProperty(
name="Transmission",
description='Enable to bake the transmission map for the selected objects active material',
default=False
)
sequenced_bake_combined: bpy.props.BoolProperty(
name="Combined",
description='Enable to bake the combined map for the selected objects active material',
default=False
)
sequenced_bake_metallic: bpy.props.BoolProperty(
name="Metallic",
description='Enable to bake the metallic map for the selected objects active material',
default=False
)
# Lighting options.
diffuse_lighting_direct: bpy.props.BoolProperty(
name="Direct",
description="Include direct lighting in the bake",
default=True
)
diffuse_lighting_indirect: bpy.props.BoolProperty(
name="Indirect",
description="Include indirect lighting in the bake",
default=True
)
diffuse_lighting_color: bpy.props.BoolProperty(
name="Color",
description="Include color lighting contributions in the bake",
default=True
)
glossy_lighting_direct: bpy.props.BoolProperty(
name="Direct",
description="Include direct lighting in the bake",
default=True
)
glossy_lighting_indirect: bpy.props.BoolProperty(
name="Indirect",
description="Include indirect lighting in the bake",
default=True
)
glossy_lighting_color: bpy.props.BoolProperty(
name="Color",
description="Include color lighting contributions in the bake",
default=True
)
transmission_lighting_direct: bpy.props.BoolProperty(
name="Direct",
description="Include direct lighting in the bake",
default=True
)
transmission_lighting_indirect: bpy.props.BoolProperty(
name="Indirect",
description="Include indirect lighting in the bake",
default=True
)
transmission_lighting_color: bpy.props.BoolProperty(
name="Color",
description="Include color lighting contributions in the bake",
default=True
)
combined_lighting_direct: bpy.props.BoolProperty(
name="Direct",
description="Include direct lighting in the bake",
default=True
)
combined_lighting_indirect: bpy.props.BoolProperty(
name="Indirect",
description="Include indirect lighting in the bake",
default=True
)
combined_lighting_color: bpy.props.BoolProperty(
name="Color",
description="Include color lighting contributions in the bake",
default=True
)
combined_contribution_deffuse: bpy.props.BoolProperty(
name="Diffuse",
description="Include diffuse contributions in the bake",
default=True
)
combined_contribution_glossy: bpy.props.BoolProperty(
name="Glossy",
description="Include glossy contributions in the bake",
default=True
)
combined_contribution_transmission: bpy.props.BoolProperty(
name="Transmission",
description="Include transmission contributions in the bake",
default=True
)
combined_contribution_emit: bpy.props.BoolProperty(
name="Emit",
description="Include emission contributions in the bake",
default=True
)
# Color Management options.
# Display Device
display_device: bpy.props.EnumProperty(
name="Display Device",
description="Select the display device",
items=[
('sRGB', 'sRGB', ''),
('Display P3', 'Display P3', ''),
('Rec.1886', 'Rec.1886', ''),
('Rec.2020', 'Rec.2020', '')
],
default='sRGB'
)
# View Transform
view_transform: bpy.props.EnumProperty(
name="View Transform",
description="Select the view transform",
items=[
('Standard', 'Standard', ''),
('Khronos PBR Neutral', 'Khronos PBR Neutral', ''),
('AgX', 'AgX', ''),
('Filmic', 'Filmic', ''),
('Filmic Log', 'Filmic Log', ''),
('False Color', 'False Color', ''),
('Raw', 'Raw', '')
],
default='AgX'
)
# Look
look: bpy.props.EnumProperty(
name="Look",
description="Select the look",
items=[
('None', 'None', ''),
('Punchy', 'Punchy', ''),
('Greyscale', 'Greyscale', ''),
('Very High Contrast', 'Very High Contrast', ''),
('High Contrast', 'High Contrast', ''),
('Medium High Contrast', 'Medium High Contrast', ''),
('Base Contrast', 'Base Contrast', ''),
('Medium Low Contrast', 'Medium Low Contrast', ''),
('Low Contrast', 'Low Contrast', ''),
('Very Low Contrast', 'Very Low Contrast', '')
],
default='None'
)
# Exposure
exposure: bpy.props.FloatProperty(
name="Exposure",
description="Adjust the exposure level",
default=0.0,
min=-10.0,
max=10.0
)
# Gamma
gamma: bpy.props.FloatProperty(
name="Gamma",
description="Adjust the gamma level",
default=1.0,
min=0.0,
max=5.0
)
# Sequencer
sequencer: bpy.props.EnumProperty(
name="Sequencer",
description="Select the sequencer color space",
items=[
('ACES2065-1', 'ACES2065-1', ''),
('ACEScg', 'ACEScg', ''),
('AgX Base_Display_P3', 'AgX Base Display P3', ''),
('AgX Base_Rec_1886', 'AgX Base Rec.1886', ''),
('AgX Base_Rec_2020', 'AgX Base Rec.2020', ''),
('AgX Base_sRGB', 'AgX Base sRGB', ''),
('AgX Log', 'AgX Log', ''),
('Display P3', 'Display P3', ''),
('Filmic Log', 'Filmic Log', ''),
('Filmic sRGB', 'Filmic sRGB', ''),
('Khronos PBR Neutral sRGB', 'Khronos PBR Neutral sRGB', ''),
('Linear CIE XYZ D65', 'Linear CIE-XYZ D65', ''),
('Linear CIE XYZ E', 'Linear CIE-XYZ E', ''),
('Linear DCI P3 D65', 'Linear DCI-P3 D65', ''),
('Linear FilmLight E Gamut', 'Linear FilmLight E-Gamut', ''),
('Linear Rec.2020', 'Linear Rec.2020', ''),
('Linear Rec.709', 'Linear Rec.709', ''),
('Non-Color', 'Non-Color', ''),
('Rec.1886', 'Rec.1886', ''),
('Rec.2020', 'Rec.2020', ''),
('sRGB', 'sRGB', '')
],
default='sRGB'
)
# Image texture settings.
interpolation: bpy.props.EnumProperty(
name="Interpolation",
description="Set the texture interpolation method",
items=[
('Linear', "Linear", "Use linear interpolation"),
('Closest', "Closest", "Use nearest neighbor interpolation"),
('Cubic', "Cubic", "Use cubic interpolation"),
('Smart', "Smart", "Use smart interpolation"),
],
default='Linear'
)
projection: bpy.props.EnumProperty(
name="Projection",
description="Set the texture projection method",
items=[
('Flat', "Flat", "Flat projection"),
('Box', "Box", "Box projection"),
('Square', "Square", "Square projection"),
('Tube', "Tube", "Tube projection"),
],
default='Flat'
)
extension: bpy.props.EnumProperty(
name="Extension",
description="Set the texture extension method",
items=[
('Repeat', "Repeat", "Repeat the texture"),
('Extend', "Extend", "Extend the texture edges"),
('Clip', "Clip", "Clip the texture to the image bounds"),
('Mirror', "Mirror", "Mirror the texture"),
],
default='Repeat'
)
colorspace: bpy.props.EnumProperty(
name="Color Space",
description="Set the color space of the texture",
items=[
('ACES2065-1', 'ACES2065-1', ''),
('ACEScg', 'ACEScg', ''),
('AgX Base_Display_P3', 'AgX Base Display P3', ''),
('AgX Base_Rec_1886', 'AgX Base Rec.1886', ''),
('AgX Base_Rec_2020', 'AgX Base Rec.2020', ''),
('AgX Base_sRGB', 'AgX Base sRGB', ''),
('AgX Log', 'AgX Log', ''),
('Display P3', 'Display P3', ''),
('Filmic Log', 'Filmic Log', ''),
('Filmic sRGB', 'Filmic sRGB', ''),
('Khronos PBR Neutral sRGB', 'Khronos PBR Neutral sRGB', ''),
('Linear CIE XYZ D65', 'Linear CIE-XYZ D65', ''),
('Linear CIE XYZ E', 'Linear CIE-XYZ E', ''),
('Linear DCI P3 D65', 'Linear DCI-P3 D65', ''),
('Linear FilmLight E Gamut', 'Linear FilmLight E-Gamut', ''),
('Linear Rec.2020', 'Linear Rec.2020', ''),
('Linear Rec.709', 'Linear Rec.709', ''),
('Non-Color', 'Non-Color', ''),
('Rec.1886', 'Rec.1886', ''),
('Rec.2020', 'Rec.2020', ''),
('sRGB', 'sRGB', '')
],
default='sRGB'
)
class SequencedBakeSocket(NodeSocket):
bl_idname = "SequencedBakeSocket"
bl_label = "Sequenced Bake Socket"
# Optional: define socket data type
def draw(self, context, layout, node, text):
layout.label(text=text)
def draw_color(self, context, node):
return (0.8, 0.8, 0.2, 1.0) # Yellow color
class SequencedBakePanel(Panel):
bl_label = "Sequenced Bake"
bl_idname = "VIEW3D_PT_sequenced_bake"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_description = "Bake a material sequence based on the defined settings and keyframed node settings."
bl_category = 'Sequenced Bake'
def draw(self, context):
layout = self.layout
scene = context.scene
sequenced_bake_props = scene.sequenced_bake_props
option_padding = 2.0
# Output Path
box = layout.box()
col = box.column(align=True)
col.label(text="Material Output Path:")
col.prop(sequenced_bake_props, "sequenced_bake_output_path")
col.separator(factor=3.0, type='LINE')
# Generated Image Size
col.label(text="Generated Image Size:")
row = col.row(align=True)
row.prop(sequenced_bake_props, "sequenced_bake_width")
row.prop(sequenced_bake_props, "sequenced_bake_height")
col.separator()
col.label(text="Baked Image Format:")
col.prop(sequenced_bake_props, "sequenced_bake_image_format")
col.separator()
col.prop(sequenced_bake_props, "sequence_is_alpha")
col.prop(sequenced_bake_props, "sequence_use_float")
col.prop(sequenced_bake_props, "sequence_clear_baked_maps")
col.separator(factor=3.0, type='LINE')
col.label(text="Image Texture Settings:")
col.prop(sequenced_bake_props, "interpolation")
col.prop(sequenced_bake_props, "projection")
col.prop(sequenced_bake_props, "extension")
col.prop(sequenced_bake_props, "colorspace")
col.separator(factor=3.0, type='LINE')
# Bake Type Options
col.label(text="Bake Type Options:")
col.prop(sequenced_bake_props, "sequenced_bake_normal")
# Expand additional options if normal is selected
if sequenced_bake_props.sequenced_bake_normal:
col.label(text="Normal Map Options:")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "normal_map_space")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "normal_map_red_channel")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "normal_map_green_channel")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "normal_map_blue_channel")
col.prop(sequenced_bake_props, "sequenced_bake_roughness")
col.prop(sequenced_bake_props, "sequenced_bake_glossy")
# Expand additional options if glossy is selected
if sequenced_bake_props.sequenced_bake_glossy:
col.label(text="Lighting Contributions:")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "glossy_lighting_direct", text="Direct")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "glossy_lighting_indirect", text="Indirect")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "glossy_lighting_color", text="Color")
col.prop(sequenced_bake_props, "sequenced_bake_emission")
col.prop(sequenced_bake_props, "sequenced_bake_ambient_occlusion")
col.prop(sequenced_bake_props, "sequenced_bake_shadow")
col.prop(sequenced_bake_props, "sequenced_bake_position")
col.prop(sequenced_bake_props, "sequenced_bake_uv")
col.prop(sequenced_bake_props, "sequenced_bake_environment")
col.prop(sequenced_bake_props, "sequenced_bake_diffuse")
# Expand additional options if diffuse is selected
if sequenced_bake_props.sequenced_bake_diffuse:
col.label(text="Lighting Contributions:")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "diffuse_lighting_direct", text="Direct")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "diffuse_lighting_indirect", text="Indirect")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "diffuse_lighting_color", text="Color")
col.prop(sequenced_bake_props, "sequenced_bake_transmission")
# Expand additional options if transmission is selected
if sequenced_bake_props.sequenced_bake_transmission:
col.label(text="Lighting Contributions:")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "transmission_lighting_direct", text="Direct")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "transmission_lighting_indirect", text="Indirect")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "transmission_lighting_color", text="Color")
col.prop(sequenced_bake_props, "sequenced_bake_combined")
# Expand additional options if combined is selected
if sequenced_bake_props.sequenced_bake_combined:
col.label(text="Lighting Contributions:")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "combined_lighting_direct", text="Direct")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "combined_lighting_indirect", text="Indirect")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "combined_contribution_deffuse", text="Diffuse")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "combined_contribution_glossy", text="Glossy")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "combined_contribution_transmission", text="Transmission")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "combined_contribution_emit", text="Emit")
col.prop(sequenced_bake_props, "sequenced_bake_metallic")
col.separator(factor=3.0, type='LINE')
col.label(text="Color Management:")
col.prop(sequenced_bake_props, "display_device")
col.prop(sequenced_bake_props, "view_transform")
col.prop(sequenced_bake_props, "look")
col.prop(sequenced_bake_props, "exposure")
col.prop(sequenced_bake_props, "gamma")
col.prop(sequenced_bake_props, "sequencer")
col.separator(factor=3.0, type='LINE')
# Baking Button
col.operator("sequenced_bake.bake", text="Bake Material Sequence")
class SequencedBakeNode(Node):
bl_idname = "ShaderNodeSequencedBake"
bl_label = "Sequenced Bake"
bl_description = "Bake a material sequence based on the defined settings and keyframed node settings."
bl_icon = "NODE"
def init(self, context):
self.width = 300
# self.inputs.new("NodeSocketShader", "Shader")
# self.outputs.new("NodeSocketShader", "Output")
def draw_buttons(self, context, layout):
scene = context.scene
sequenced_bake_props = scene.sequenced_bake_props
option_padding = 2.0
# Output Path
box = layout.box()
col = box.column(align=True)
col.label(text="Material Output Path:")
col.prop(sequenced_bake_props, "sequenced_bake_output_path")
col.separator(factor=3.0, type='LINE')
# Generated Image Size
col.label(text="Generated Image Size:")
row = col.row(align=True)
row.prop(sequenced_bake_props, "sequenced_bake_width")
row.prop(sequenced_bake_props, "sequenced_bake_height")
col.separator()
col.label(text="Baked Image Format:")
col.prop(sequenced_bake_props, "sequenced_bake_image_format")
col.separator()
col.prop(sequenced_bake_props, "sequence_is_alpha")
col.prop(sequenced_bake_props, "sequence_use_float")
col.prop(sequenced_bake_props, "sequence_clear_baked_maps")
col.separator(factor=3.0, type='LINE')
col.label(text="Image Texture Settings:")
col.prop(sequenced_bake_props, "interpolation")
col.prop(sequenced_bake_props, "projection")
col.prop(sequenced_bake_props, "extension")
col.prop(sequenced_bake_props, "colorspace")
col.separator(factor=3.0, type='LINE')
# Bake Type Options
col.label(text="Bake Type Options:")
col.prop(sequenced_bake_props, "sequenced_bake_normal")
# Expand additional options if normal is selected
if sequenced_bake_props.sequenced_bake_normal:
col.label(text="Normal Map Options:")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "normal_map_space")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "normal_map_red_channel")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "normal_map_green_channel")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "normal_map_blue_channel")
col.prop(sequenced_bake_props, "sequenced_bake_roughness")
col.prop(sequenced_bake_props, "sequenced_bake_glossy")
# Expand additional options if glossy is selected
if sequenced_bake_props.sequenced_bake_glossy:
col.label(text="Lighting Contributions:")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "glossy_lighting_direct", text="Direct")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "glossy_lighting_indirect", text="Indirect")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "glossy_lighting_color", text="Color")
col.prop(sequenced_bake_props, "sequenced_bake_emission")
col.prop(sequenced_bake_props, "sequenced_bake_ambient_occlusion")
col.prop(sequenced_bake_props, "sequenced_bake_shadow")
col.prop(sequenced_bake_props, "sequenced_bake_position")
col.prop(sequenced_bake_props, "sequenced_bake_uv")
col.prop(sequenced_bake_props, "sequenced_bake_environment")
col.prop(sequenced_bake_props, "sequenced_bake_diffuse")
# Expand additional options if diffuse is selected
if sequenced_bake_props.sequenced_bake_diffuse:
col.label(text="Lighting Contributions:")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "diffuse_lighting_direct", text="Direct")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "diffuse_lighting_indirect", text="Indirect")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "diffuse_lighting_color", text="Color")
col.prop(sequenced_bake_props, "sequenced_bake_transmission")
# Expand additional options if transmission is selected
if sequenced_bake_props.sequenced_bake_transmission:
col.label(text="Lighting Contributions:")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "transmission_lighting_direct", text="Direct")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "transmission_lighting_indirect", text="Indirect")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "transmission_lighting_color", text="Color")
col.prop(sequenced_bake_props, "sequenced_bake_combined")
# Expand additional options if combined is selected
if sequenced_bake_props.sequenced_bake_combined:
col.label(text="Lighting Contributions:")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "combined_lighting_direct", text="Direct")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "combined_lighting_indirect", text="Indirect")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "combined_contribution_deffuse", text="Diffuse")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "combined_contribution_glossy", text="Glossy")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "combined_contribution_transmission", text="Transmission")
row = col.row()
row.separator(factor=option_padding)
row.prop(sequenced_bake_props, "combined_contribution_emit", text="Emit")
col.prop(sequenced_bake_props, "sequenced_bake_metallic")
col.separator(factor=3.0, type='LINE')
col.label(text="Color Management:")
col.prop(sequenced_bake_props, "display_device")
col.prop(sequenced_bake_props, "view_transform")
col.prop(sequenced_bake_props, "look")
col.prop(sequenced_bake_props, "exposure")
col.prop(sequenced_bake_props, "gamma")
col.prop(sequenced_bake_props, "sequencer")
col.separator(factor=3.0, type='LINE')
# Baking Button
col.operator("sequenced_bake.bake", text="Bake Material Sequence")
def draw_buttons_ext(self, context, layout):
layout.label(text="Extended Settings")
def draw_label(self):
return "Sequenced Bake"
class SequencedBakeOperator(Operator):
bl_idname = "sequenced_bake.bake"
bl_label = "Sequenced Bake"
bl_description = "Bakes the material sequence for the selected bake types"
_props = None
object_name = ""
material_name = ""
def execute(self, context):
if bpy.context.scene.render.engine == 'CYCLES':
self._props = bpy.context.scene.sequenced_bake_props
# Define the root directory.
root_directory = self._props.sequenced_bake_output_path
if not root_directory:
self.report({'ERROR'}, "No directory provided")
return {'CANCELLED'}
# Define the bake types to bake out
bake_types = []
if self._props.sequenced_bake_normal:
bake_types.append('NORMAL')
if self._props.sequenced_bake_roughness:
bake_types.append('ROUGHNESS')
if self._props.sequenced_bake_glossy:
bake_types.append('GLOSSY')
if self._props.sequenced_bake_emission:
bake_types.append('EMIT')
if self._props.sequenced_bake_ambient_occlusion:
bake_types.append('AO')
if self._props.sequenced_bake_shadow:
bake_types.append('SHADOW')
if self._props.sequenced_bake_position:
bake_types.append('POSITION')
if self._props.sequenced_bake_uv:
bake_types.append('UV')
if self._props.sequenced_bake_environment:
bake_types.append('ENVIRONMENT')
if self._props.sequenced_bake_diffuse:
bake_types.append('DIFFUSE')
if self._props.sequenced_bake_transmission:
bake_types.append('TRANSMISSION')
if self._props.sequenced_bake_combined:
bake_types.append('COMBINED')
if self._props.sequenced_bake_metallic:
bake_types.append('METALLIC')
# Get the current frame range
start_frame = bpy.context.scene.frame_start
end_frame = bpy.context.scene.frame_end
frame_range = range(start_frame, end_frame + 1)
# Get the active object
obj = bpy.context.active_object
self.object_name = obj.name
if not obj:
self.report({'ERROR'}, "No active object selected. Please select an object and try again")
return {'CANCELLED'}
# Get the active material
mat = obj.active_material
self.material_name = mat.name
if not mat:
self.report({'ERROR'}, "No active object selected. Please select an object and try again")
return {'CANCELLED'}
# Clear any existing image textures
def clear_generated_textures():
if (self._props.sequence_clear_baked_maps):
try:
for image in bpy.data.images:
if image.users == 0:
bpy.data.images.remove(image)
except Exception as err:
self.report({'ERROR'}, f"Problem with clearing baked maps: {err}")
#
def bake_maps(bake_type):
if bake_type == "METALLIC":
# Disconnect the node connected to the metallic input of the Pricipaled BSDF and connect it directly to the material output node.
connect_metallic_node(mat)
for frame in frame_range:
# Set the frame and update the scene
bpy.context.scene.frame_set(frame)
bpy.context.view_layer.update()
# Format the name of the generated texture.
bake_type_name = self.object_name + "_" + self.material_name + "_" + bake_type
# Create a new texture for the Image Texture node
texture = bpy.data.images.new(
name=bake_type_name,
width=self._props.sequenced_bake_width,
height=self._props.sequenced_bake_height,
alpha=self._props.sequence_is_alpha,
float_buffer=self._props.sequence_use_float # "float_buffer controls the internal precision of the image during creation and baking."
)
# Texture color space.
texture.colorspace_settings.name = self._props.colorspace
# Create a new Image Texture node
image_node = mat.node_tree.nodes.new('ShaderNodeTexImage')
# Image Texture Settings.
image_node.interpolation = self._props.interpolation.capitalize()
image_node.projection = self._props.projection.upper()
image_node.extension = self._props.extension.upper()
# Get the position of the material output node.
material_output_node = None
for node in mat.node_tree.nodes:
if node.type == 'OUTPUT_MATERIAL':
material_output_node = node
break
if material_output_node:
material_output_position = material_output_node.location
else:
# Default if Material Output node isn't found
self.report({'ERROR'},
"Material Output node was not found, Please add your material output node and try again.")
return {'CANCELLED'}
# Set node position to the right of the material output node.
image_node.location = (material_output_position.x + 250, material_output_position.y)
image_node.image = texture
# Select the new Image Texture node making it the active selection.
mat.node_tree.nodes.active = image_node
# Update the UI.
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
# Set bake type and lighting contributions
if bake_type == "DIFFUSE":
bpy.context.scene.render.bake.use_pass_direct = self._props.diffuse_lighting_direct
bpy.context.scene.render.bake.use_pass_indirect = self._props.diffuse_lighting_indirect
bpy.context.scene.render.bake.use_pass_color = self._props.diffuse_lighting_color
elif bake_type == "GLOSSY":
bpy.context.scene.render.bake.use_pass_direct = self._props.glossy_lighting_direct
bpy.context.scene.render.bake.use_pass_indirect = self._props.glossy_lighting_indirect
bpy.context.scene.render.bake.use_pass_color = self._props.glossy_lighting_color
elif bake_type == "TRANSMISSION":
bpy.context.scene.render.bake.use_pass_direct = self._props.transmission_lighting_direct
bpy.context.scene.render.bake.use_pass_indirect = self._props.transmission_lighting_indirect
bpy.context.scene.render.bake.use_pass_color = self._props.transmission_lighting_color
elif bake_type == "NORMAL":
# Set the normal space (Object or Tangent)
if self._props.normal_map_space == 'OBJECT':
bpy.context.scene.render.bake.normal_space = 'OBJECT'
else:
bpy.context.scene.render.bake.normal_space = 'TANGENT'
# Set the swizzle settings for normal channel baking
bpy.context.scene.render.bake.normal_r = self._props.normal_map_red_channel
bpy.context.scene.render.bake.normal_g = self._props.normal_map_green_channel
bpy.context.scene.render.bake.normal_b = self._props.normal_map_blue_channel
# elif bake_type == "ROUGHNESS":
# elif bake_type == "EMIT":
# elif bake_type == "Ambient Occlusion":
# elif bake_type == "SHADOW":
# elif bake_type == "POSITION":
# elif bake_type == "UV":
# elif bake_type == "ENVIRONMENT":
elif bake_type == "COMBINED":
bpy.context.scene.render.bake.use_pass_direct = self._props.combined_lighting_direct
bpy.context.scene.render.bake.use_pass_indirect = self._props.combined_lighting_indirect
bpy.context.scene.render.bake.use_pass_color = self._props.combined_lighting_color
bpy.context.scene.render.bake.use_pass_diffuse = self._props.combined_contribution_deffuse
bpy.context.scene.render.bake.use_pass_glossy = self._props.combined_contribution_glossy
bpy.context.scene.render.bake.use_pass_transmission = self._props.combined_contribution_transmission
bpy.context.scene.render.bake.use_pass_emit = self._props.combined_contribution_emit
# elif bake_type == "METALLIC":
# Color Management options.
# Apply Display Device
bpy.context.scene.display_settings.display_device = self._props.display_device
# Apply View Transform and Look
bpy.context.scene.view_settings.view_transform = self._props.view_transform
agx_prefix = ''
if self._props.view_transform == 'AgX' and self._props.look != 'None':
agx_prefix = 'AgX - '
bpy.context.scene.view_settings.look = agx_prefix + self._props.look
# Apply Exposure and Gamma
bpy.context.scene.view_settings.exposure = self._props.exposure
bpy.context.scene.view_settings.gamma = self._props.gamma
# Apply Sequencer Color Space
try:
bpy.context.scene.sequencer_colorspace_settings.name = self._props.sequencer
except Exception as e:
self.report({'WARNING'}, f"Sequencer color space '{props.sequencer}' not applied: {str(e)}")
# Bake the texture
try:
if bake_type == "METALLIC":
bpy.ops.object.bake(type="EMIT")
else:
bpy.ops.object.bake(type=bake_type)
except Exception as error:
self.report({'ERROR'},
f"There was an error while attempting to bake the {bake_type} map. Error: {error.args}")
return {"CANCELLED"}
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
# Define the output path
image_path = os.path.join(root_directory, bake_type_name, str(frame) + f".{self._props.sequenced_bake_image_format}")
# NOTE: Setting this doesn't seem to make a difference for the saved image.
# "color_depth controls the precision of the saved file during export."
if not self._props.sequence_use_float:
bpy.context.scene.render.image_settings.color_depth = '8'
# Save the rendered image
texture.save_render(image_path)
# Update the Blender interface
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
# Clear ONLY the generated texture nodes after the rendered image is saved.
active_node = mat.node_tree.nodes.active
mat.node_tree.nodes.remove(active_node)
# Reconnect the pricipled BSDF node to the material output node.
if bake_type == "METALLIC":
reconnect_node(mat)
def connect_metallic_node(material):
# Connect the node currenty connected to the metallic channel of the Pricipaled BSDF and connect it directly to the material output.
# Check if the material has a node tree and use nodes
if material.use_nodes:
nodes = material.node_tree.nodes
links = material.node_tree.links
# Find the Principled BSDF and Material Output nodes
principled_bsdf = None
material_output = None
for node in nodes:
if node.type == 'BSDF_PRINCIPLED':
principled_bsdf = node
elif node.type == 'OUTPUT_MATERIAL':
material_output = node
if principled_bsdf and material_output:
# Get the input connected to the 'Metallic' socket of the Principled BSDF node
metallic_input = principled_bsdf.inputs['Metallic']
if metallic_input.is_linked:
# Get the node connected to the Metallic input
metallic_node_link = metallic_input.links[0]
connected_node = metallic_node_link.from_node
connected_output_socket = metallic_node_link.from_socket
# Connect it to the Surface input of the Material Output node
surface_input = material_output.inputs['Surface']
links.new(connected_output_socket, surface_input)
# TODO: Add GLTF Method here.
else:
self.report({'WARNING'}, "The Metallic input is not connected to any node")
else:
self.report({'WARNING'}, "Principled BSDF or Material Output node not found")
else:
self.report({'WARNING'}, "The material does not use nodes")
def reconnect_node(material):
# Reconnects the Pricipled BSDF node to the material output node.
# Check if the material has a node tree
if material.use_nodes:
nodes = material.node_tree.nodes
links = material.node_tree.links
# Find the Principled BSDF and Material Output nodes
principled_bsdf = None
material_output = None
for node in nodes:
if node.type == 'BSDF_PRINCIPLED':
principled_bsdf = node
elif node.type == 'OUTPUT_MATERIAL':
material_output = node
if principled_bsdf and material_output:
# Find the Surface input of the Material Output node
surface_input = material_output.inputs['Surface']
# Disconnect any existing connections to the Surface input
for link in surface_input.links:
links.remove(link)
# Connect the Principled BSDF node's output to the Surface input
bsdf_output_socket = principled_bsdf.outputs['BSDF']
links.new(bsdf_output_socket, surface_input)
self.report({'INFO'}, "Reconnected Principled BSDF to Material Output")
else:
self.report({'WARNING'}, "Principled BSDF or Material Output node not found")
else:
self.report({'WARNING'}, "The material does not use nodes")
if bake_types:
# Start baking the different material map sequences
for bake_type in bake_types:
# Begin baking
bake_maps(bake_type)
# Clear any existing image textures
clear_generated_textures()
else:
self.report({'WARNING'}, "No bake types have been selected, Please choose a bake type and try again.")
return {"CANCELLED"}
self.report({'INFO'}, "Finished.")
return {'FINISHED'}
else:
self.report({'WARNING'},
"Render engine is not set to Cycles.\nPlease switch to Cycles under the rendering tab and try again.")
return {'FINISHED'}