1160 lines
46 KiB
Python
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'}
|