2025-12-01
This commit is contained in:
@@ -144,6 +144,15 @@ class POLIIGON_OT_hdri(Operator):
|
||||
"""Runs once per operator call before drawing occurs."""
|
||||
super().__init__(*args, **kwargs)
|
||||
self.exec_count = 0
|
||||
self.node_output_world = None
|
||||
self.node_tex_coord = None
|
||||
self.node_mapping = None
|
||||
self.node_tex_env_light = None
|
||||
self.node_background_light = None
|
||||
self.node_tex_env_bg = None
|
||||
self.node_background_bg = None
|
||||
self.node_mix_shader = None
|
||||
self.node_light_path = None
|
||||
|
||||
@staticmethod
|
||||
def init_context(addon_version: str) -> None:
|
||||
@@ -194,9 +203,6 @@ class POLIIGON_OT_hdri(Operator):
|
||||
f"({self.size_bg}), expected '4K_JPG' or '1K_EXR'")
|
||||
raise ValueError(msg)
|
||||
|
||||
if self.size == size_bg_eff:
|
||||
name_bg = name_light
|
||||
|
||||
cTB.logger.debug("POLIIGON_OT_hdri "
|
||||
f"{asset_name}, {name_light}, {name_bg}")
|
||||
|
||||
@@ -267,7 +273,7 @@ class POLIIGON_OT_hdri(Operator):
|
||||
return {"CANCELLED"}
|
||||
file_tex_light = files_tex_exr[0]
|
||||
|
||||
if cTB.settings["hdri_use_jpg_bg"] and filetype_bg == "JPG":
|
||||
if filetype_bg == "JPG":
|
||||
size_bg = asset_type_data.get_size(
|
||||
size_bg_eff,
|
||||
local_only=True,
|
||||
@@ -314,170 +320,23 @@ class POLIIGON_OT_hdri(Operator):
|
||||
# Reset apply for Redo Last menu to work properly
|
||||
self.do_apply = False
|
||||
|
||||
# Check if same texture is used for both lighting and background
|
||||
use_simple_layout = file_tex_bg == file_tex_light
|
||||
# Use same name for both light and background only when using same file (simple layout)
|
||||
if use_simple_layout:
|
||||
name_bg = name_light
|
||||
# ...............................................................................................
|
||||
|
||||
node_tex_coord = None
|
||||
node_mapping = None
|
||||
|
||||
node_tex_env_light = None
|
||||
node_background_light = None
|
||||
|
||||
node_tex_env_bg = None
|
||||
node_background_bg = None
|
||||
|
||||
node_mix_shader = None
|
||||
node_light_path = None
|
||||
|
||||
node_output_world = None
|
||||
|
||||
if not bpy.context.scene.world:
|
||||
bpy.ops.world.new()
|
||||
bpy.context.scene.world = bpy.data.worlds[-1]
|
||||
|
||||
context.scene.world.use_nodes = True
|
||||
|
||||
nodes_world = context.scene.world.node_tree.nodes
|
||||
links_world = context.scene.world.node_tree.links
|
||||
for _node in nodes_world:
|
||||
if _node.type == "TEX_COORD":
|
||||
if _node.label == "Mapping":
|
||||
node_tex_coord = _node
|
||||
|
||||
elif _node.type == "MAPPING":
|
||||
if _node.label == "Mapping":
|
||||
node_mapping = _node
|
||||
|
||||
elif _node.type == "TEX_ENVIRONMENT":
|
||||
if _node.label == "Lighting":
|
||||
node_tex_env_light = _node
|
||||
elif _node.label == "Background":
|
||||
node_tex_env_bg = _node
|
||||
|
||||
elif _node.type == "BACKGROUND":
|
||||
if _node.label == "Lighting":
|
||||
node_background_light = _node
|
||||
elif _node.label == "Background":
|
||||
node_background_bg = _node
|
||||
elif len(nodes_world) == 2:
|
||||
node_background_light = _node
|
||||
node_background_light.label = "Lighting"
|
||||
node_background_light.location = mathutils.Vector(
|
||||
(-110, 200))
|
||||
|
||||
elif _node.type == "MIX_SHADER":
|
||||
node_mix_shader = _node
|
||||
|
||||
elif _node.type == "LIGHT_PATH":
|
||||
node_light_path = _node
|
||||
|
||||
elif _node.type == "OUTPUT_WORLD":
|
||||
node_output_world = _node
|
||||
|
||||
if node_tex_coord is None:
|
||||
node_tex_coord = nodes_world.new("ShaderNodeTexCoord")
|
||||
node_tex_coord.label = "Mapping"
|
||||
node_tex_coord.location = mathutils.Vector((-1080, 420))
|
||||
|
||||
if node_mapping is None:
|
||||
node_mapping = nodes_world.new("ShaderNodeMapping")
|
||||
node_mapping.label = "Mapping"
|
||||
node_mapping.location = mathutils.Vector((-870, 420))
|
||||
|
||||
if node_tex_env_light is None:
|
||||
node_tex_env_light = nodes_world.new("ShaderNodeTexEnvironment")
|
||||
node_tex_env_light.label = "Lighting"
|
||||
node_tex_env_light.location = mathutils.Vector((-470, 420))
|
||||
|
||||
if node_tex_env_bg is None:
|
||||
node_tex_env_bg = nodes_world.new("ShaderNodeTexEnvironment")
|
||||
node_tex_env_bg.label = "Background"
|
||||
node_tex_env_bg.location = mathutils.Vector((-470, 100))
|
||||
|
||||
if node_background_light is None:
|
||||
node_background_light = nodes_world.new("ShaderNodeBackground")
|
||||
node_background_light.label = "Lighting"
|
||||
node_background_light.location = mathutils.Vector((-110, 200))
|
||||
|
||||
if node_background_bg is None:
|
||||
node_background_bg = nodes_world.new("ShaderNodeBackground")
|
||||
node_background_bg.label = "Background"
|
||||
node_background_bg.location = mathutils.Vector((-110, 70))
|
||||
|
||||
if node_mix_shader is None:
|
||||
node_mix_shader = nodes_world.new("ShaderNodeMixShader")
|
||||
node_mix_shader.location = mathutils.Vector((110, 300))
|
||||
|
||||
if node_light_path is None:
|
||||
node_light_path = nodes_world.new("ShaderNodeLightPath")
|
||||
node_light_path.location = mathutils.Vector((-110, 550))
|
||||
|
||||
if node_output_world is None:
|
||||
node_output_world = nodes_world.new("ShaderNodeOutputWorld")
|
||||
node_output_world.location = mathutils.Vector((370, 300))
|
||||
|
||||
links_world.new(
|
||||
node_tex_coord.outputs["Generated"],
|
||||
node_mapping.inputs["Vector"])
|
||||
links_world.new(
|
||||
node_mapping.outputs["Vector"],
|
||||
node_tex_env_light.inputs["Vector"])
|
||||
links_world.new(
|
||||
node_tex_env_light.outputs["Color"],
|
||||
node_background_light.inputs["Color"])
|
||||
links_world.new(
|
||||
node_background_light.outputs[0],
|
||||
node_mix_shader.inputs[1])
|
||||
|
||||
links_world.new(
|
||||
node_tex_coord.outputs["Generated"],
|
||||
node_mapping.inputs["Vector"])
|
||||
links_world.new(
|
||||
node_mapping.outputs["Vector"],
|
||||
node_tex_env_bg.inputs["Vector"])
|
||||
links_world.new(
|
||||
node_tex_env_bg.outputs["Color"],
|
||||
node_background_bg.inputs["Color"])
|
||||
links_world.new(
|
||||
node_background_bg.outputs[0],
|
||||
node_mix_shader.inputs[2])
|
||||
|
||||
links_world.new(
|
||||
node_light_path.outputs[0],
|
||||
node_mix_shader.inputs[0])
|
||||
|
||||
links_world.new(
|
||||
node_mix_shader.outputs[0],
|
||||
node_output_world.inputs[0])
|
||||
|
||||
if name_light in bpy.data.images.keys():
|
||||
img_light = bpy.data.images[name_light]
|
||||
|
||||
if use_simple_layout:
|
||||
self._simple_layout(name_light,
|
||||
file_tex_light,
|
||||
asset_data)
|
||||
else:
|
||||
file_tex_light_norm = os.path.normpath(file_tex_light)
|
||||
img_light = bpy.data.images.load(file_tex_light_norm)
|
||||
img_light.name = name_light
|
||||
img_light.poliigon = "HDRIs;" + asset_name
|
||||
self.set_poliigon_props_image(img_light, asset_data)
|
||||
|
||||
if name_bg in bpy.data.images.keys():
|
||||
img_bg = bpy.data.images[name_bg]
|
||||
|
||||
else:
|
||||
file_tex_bg_norm = os.path.normpath(file_tex_bg)
|
||||
img_bg = bpy.data.images.load(file_tex_bg_norm)
|
||||
img_bg.name = name_bg
|
||||
self.set_poliigon_props_image(img_bg, asset_data)
|
||||
|
||||
if "Rotation" in node_mapping.inputs:
|
||||
node_mapping.inputs["Rotation"].default_value[2] = self.rotation
|
||||
else:
|
||||
node_mapping.rotation[2] = self.rotation
|
||||
|
||||
node_tex_env_light.image = img_light
|
||||
node_background_light.inputs["Strength"].default_value = self.hdr_strength
|
||||
|
||||
node_tex_env_bg.image = img_bg
|
||||
node_background_bg.inputs["Strength"].default_value = self.hdr_strength
|
||||
self._complex_layout(name_light,
|
||||
file_tex_light,
|
||||
name_bg,
|
||||
file_tex_bg,
|
||||
asset_data)
|
||||
|
||||
self.set_poliigon_props_world(context, asset_data)
|
||||
|
||||
@@ -485,10 +344,391 @@ class POLIIGON_OT_hdri(Operator):
|
||||
|
||||
if self.exec_count == 0:
|
||||
cTB.signal_import_asset(asset_id=self.asset_id)
|
||||
cTB.set_first_local_asset()
|
||||
self.exec_count += 1
|
||||
self.report({"INFO"}, _t("HDRI Imported : {0}").format(asset_name))
|
||||
return {"FINISHED"}
|
||||
|
||||
def _get_poliigon_hdri_nodes(self, nodes_world) -> dict:
|
||||
"""Identify existing Poliigon HDRI nodes by their labels and types.
|
||||
|
||||
Returns a dictionary mapping node roles to existing nodes, or None if not found.
|
||||
This allows us to preserve user-authored nodes while only replacing Poliigon ones.
|
||||
"""
|
||||
poliigon_nodes = {
|
||||
'tex_coord': None,
|
||||
'mapping': None,
|
||||
'tex_env_light': None,
|
||||
'tex_env_bg': None,
|
||||
'background_light': None,
|
||||
'background_bg': None,
|
||||
'mix_shader': None,
|
||||
'light_path': None,
|
||||
'output_world': None
|
||||
}
|
||||
|
||||
for node in nodes_world:
|
||||
try:
|
||||
# Skip invalid nodes
|
||||
if not hasattr(node, 'type') or not hasattr(node, 'label'):
|
||||
continue
|
||||
|
||||
if node.type == "TEX_COORD" and node.label == "Mapping":
|
||||
poliigon_nodes['tex_coord'] = node
|
||||
elif node.type == "MAPPING" and node.label == "Mapping":
|
||||
poliigon_nodes['mapping'] = node
|
||||
elif node.type == "TEX_ENVIRONMENT":
|
||||
if node.label == "Lighting":
|
||||
poliigon_nodes['tex_env_light'] = node
|
||||
elif node.label == "Background":
|
||||
poliigon_nodes['tex_env_bg'] = node
|
||||
elif node.type == "BACKGROUND":
|
||||
if node.label == "Lighting":
|
||||
poliigon_nodes['background_light'] = node
|
||||
elif node.label == "Background":
|
||||
poliigon_nodes['background_bg'] = node
|
||||
elif poliigon_nodes['background_light'] is None:
|
||||
# If background_light isn't assigned yet, assign the
|
||||
# first one we identify. This avoids clutter in the
|
||||
# default blender world layout by reusing both nodes.
|
||||
poliigon_nodes['background_light'] = node
|
||||
elif node.type == "MIX_SHADER":
|
||||
# Mix shader nodes created by Poliigon typically don't have custom labels
|
||||
# but we only want to replace if it's connected to our setup
|
||||
if self._is_poliigon_mix_shader(node):
|
||||
poliigon_nodes['mix_shader'] = node
|
||||
elif node.type == "LIGHT_PATH":
|
||||
# Similar to mix shader - identify by connection pattern
|
||||
if self._is_poliigon_light_path(node):
|
||||
poliigon_nodes['light_path'] = node
|
||||
elif node.type == "OUTPUT_WORLD":
|
||||
poliigon_nodes['output_world'] = node
|
||||
except Exception as e:
|
||||
# Skip problematic nodes instead of crashing
|
||||
cTB.logger.warning(f"Skipping problematic node during identification: {str(e)}")
|
||||
continue
|
||||
|
||||
return poliigon_nodes
|
||||
|
||||
def _is_poliigon_mix_shader(self, node) -> bool:
|
||||
"""Check if a MixShader node is part of the Poliigon HDRI setup."""
|
||||
try:
|
||||
# Validate node has required attributes
|
||||
if not hasattr(node, 'inputs') or len(node.inputs) < 3:
|
||||
return False
|
||||
|
||||
# Check if it's connected to background nodes with Poliigon labels
|
||||
input_1 = node.inputs[1]
|
||||
input_2 = node.inputs[2]
|
||||
|
||||
if hasattr(input_1, 'is_linked') and input_1.is_linked:
|
||||
if hasattr(input_1, 'links') and input_1.links:
|
||||
linked_node = input_1.links[0].from_node
|
||||
if (hasattr(linked_node, 'type') and linked_node.type == "BACKGROUND" and
|
||||
hasattr(linked_node, 'label') and linked_node.label == "Lighting"):
|
||||
return True
|
||||
|
||||
if hasattr(input_2, 'is_linked') and input_2.is_linked:
|
||||
if hasattr(input_2, 'links') and input_2.links:
|
||||
linked_node = input_2.links[0].from_node
|
||||
if (hasattr(linked_node, 'type') and linked_node.type == "BACKGROUND" and
|
||||
hasattr(linked_node, 'label') and linked_node.label == "Background"):
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
def _is_poliigon_light_path(self, node) -> bool:
|
||||
"""Check if a LightPath node is part of the Poliigon HDRI setup."""
|
||||
try:
|
||||
# Validate node has required attributes
|
||||
if not hasattr(node, 'outputs'):
|
||||
return False
|
||||
|
||||
# Check if it's connected to a mix shader that's part of Poliigon setup
|
||||
for output in node.outputs:
|
||||
if hasattr(output, 'is_linked') and output.is_linked:
|
||||
if hasattr(output, 'links'):
|
||||
for link in output.links:
|
||||
if hasattr(link, 'to_node'):
|
||||
linked_node = link.to_node
|
||||
if (hasattr(linked_node, 'type') and linked_node.type == "MIX_SHADER" and
|
||||
self._is_poliigon_mix_shader(linked_node)):
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
def _remove_poliigon_nodes(self, nodes_world, poliigon_nodes: dict) -> None:
|
||||
"""Safely remove only the identified Poliigon HDRI nodes."""
|
||||
nodes_to_remove = []
|
||||
|
||||
for node_role, node in poliigon_nodes.items():
|
||||
if node is not None and node_role != 'output_world': # Never remove output_world
|
||||
nodes_to_remove.append(node)
|
||||
|
||||
# Remove nodes in a separate loop to avoid modifying collection during iteration
|
||||
for node in nodes_to_remove:
|
||||
try:
|
||||
# Additional validation before removal
|
||||
if hasattr(node, 'name') and node.name in nodes_world:
|
||||
nodes_world.remove(node)
|
||||
except Exception as e:
|
||||
cTB.logger.warning(f"Failed to remove node {getattr(node, 'name', 'unnamed')}: {str(e)}")
|
||||
continue
|
||||
|
||||
def _create_or_reuse_output_world(self, nodes_world, poliigon_nodes: dict) -> object:
|
||||
"""Create or reuse the output world node."""
|
||||
output_world = poliigon_nodes.get('output_world')
|
||||
|
||||
# Validate that the existing output world is still valid
|
||||
if output_world is not None:
|
||||
try:
|
||||
# Test if the node is still accessible and valid
|
||||
_ = output_world.type
|
||||
_ = output_world.location
|
||||
return output_world
|
||||
except Exception:
|
||||
# Node is corrupted or no longer accessible
|
||||
cTB.logger.warning("Existing output world node is corrupted, creating new one")
|
||||
output_world = None
|
||||
|
||||
if output_world is None:
|
||||
# Create new output world node
|
||||
output_world = nodes_world.new("ShaderNodeOutputWorld")
|
||||
output_world.location = mathutils.Vector((250, 225.0))
|
||||
|
||||
return output_world
|
||||
|
||||
def _validate_node_setup(self) -> bool:
|
||||
"""Validate that all required nodes were created successfully."""
|
||||
required_nodes = ['node_output_world', 'node_tex_coord', 'node_mapping', 'node_tex_env_light', 'node_background_light']
|
||||
|
||||
for node_attr in required_nodes:
|
||||
if not hasattr(self, node_attr) or getattr(self, node_attr) is None:
|
||||
cTB.logger.error(f"Required node {node_attr} was not created")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _simple_layout(self,
|
||||
name_light: str,
|
||||
file_tex_light: str,
|
||||
asset_data: AssetData
|
||||
) -> None:
|
||||
if not bpy.context.scene.world:
|
||||
bpy.ops.world.new()
|
||||
bpy.context.scene.world = bpy.data.worlds[-1]
|
||||
bpy.context.scene.world.use_nodes = True
|
||||
nodes_world = bpy.context.scene.world.node_tree.nodes
|
||||
links_world = bpy.context.scene.world.node_tree.links
|
||||
|
||||
# Use improved selective node management
|
||||
try:
|
||||
poliigon_nodes = self._get_poliigon_hdri_nodes(nodes_world)
|
||||
self._remove_poliigon_nodes(nodes_world, poliigon_nodes)
|
||||
self.node_output_world = self._create_or_reuse_output_world(nodes_world, poliigon_nodes)
|
||||
except Exception as e:
|
||||
cTB.logger.error(f"Failed during node identification/removal: {str(e)}")
|
||||
# Fallback to more aggressive approach if needed
|
||||
self.node_output_world = None
|
||||
for node in nodes_world:
|
||||
if node.type == "OUTPUT_WORLD":
|
||||
self.node_output_world = node
|
||||
break
|
||||
if self.node_output_world is None:
|
||||
self.node_output_world = nodes_world.new("ShaderNodeOutputWorld")
|
||||
self.node_output_world.location = mathutils.Vector((320, 300))
|
||||
|
||||
# Create new Poliigon nodes
|
||||
try:
|
||||
self.node_tex_coord = nodes_world.new("ShaderNodeTexCoord")
|
||||
self.node_tex_coord.label = "Mapping"
|
||||
self.node_tex_coord.location = mathutils.Vector((-720, 300))
|
||||
|
||||
self.node_mapping = nodes_world.new("ShaderNodeMapping")
|
||||
self.node_mapping.label = "Mapping"
|
||||
self.node_mapping.location = mathutils.Vector((-470, 300))
|
||||
|
||||
self.node_tex_env_light = nodes_world.new("ShaderNodeTexEnvironment")
|
||||
self.node_tex_env_light.label = "Lighting"
|
||||
self.node_tex_env_light.location = mathutils.Vector((-220, 300))
|
||||
|
||||
self.node_background_light = nodes_world.new("ShaderNodeBackground")
|
||||
self.node_background_light.label = "Lighting"
|
||||
self.node_background_light.location = mathutils.Vector((120, 300))
|
||||
|
||||
# Validate node creation
|
||||
if not self._validate_node_setup():
|
||||
raise Exception("Failed to create required nodes")
|
||||
|
||||
except Exception as e:
|
||||
cTB.logger.error(f"Failed to create nodes in simple layout: {str(e)}")
|
||||
raise
|
||||
|
||||
# Create connections with error handling
|
||||
try:
|
||||
links_world.new(self.node_tex_coord.outputs["Generated"], self.node_mapping.inputs["Vector"])
|
||||
links_world.new(self.node_mapping.outputs["Vector"], self.node_tex_env_light.inputs["Vector"])
|
||||
links_world.new(self.node_tex_env_light.outputs["Color"], self.node_background_light.inputs["Color"])
|
||||
links_world.new(self.node_background_light.outputs[0], self.node_output_world.inputs["Surface"])
|
||||
except Exception as e:
|
||||
cTB.logger.error(f"Failed to create node links in simple layout: {str(e)}")
|
||||
raise
|
||||
|
||||
# Load images
|
||||
try:
|
||||
if name_light in bpy.data.images.keys():
|
||||
img_light = bpy.data.images[name_light]
|
||||
else:
|
||||
file_tex_light_norm = os.path.normpath(file_tex_light)
|
||||
img_light = bpy.data.images.load(file_tex_light_norm)
|
||||
img_light.name = name_light
|
||||
img_light.poliigon = "HDRIs;" + asset_data.asset_name
|
||||
self.set_poliigon_props_image(img_light, asset_data)
|
||||
except Exception as e:
|
||||
cTB.logger.error(f"Failed to load images in simple layout: {str(e)}")
|
||||
raise
|
||||
|
||||
# Set node properties
|
||||
try:
|
||||
if "Rotation" in self.node_mapping.inputs:
|
||||
self.node_mapping.inputs["Rotation"].default_value[2] = self.rotation
|
||||
else:
|
||||
self.node_mapping.rotation[2] = self.rotation
|
||||
self.node_tex_env_light.image = img_light
|
||||
self.node_background_light.inputs["Strength"].default_value = self.hdr_strength
|
||||
except Exception as e:
|
||||
cTB.logger.error(f"Failed to set node properties in simple layout: {str(e)}")
|
||||
raise
|
||||
|
||||
def _complex_layout(self,
|
||||
name_light: str,
|
||||
file_tex_light: str,
|
||||
name_bg: str,
|
||||
file_tex_bg: str,
|
||||
asset_data: AssetData
|
||||
) -> None:
|
||||
if not bpy.context.scene.world:
|
||||
bpy.ops.world.new()
|
||||
bpy.context.scene.world = bpy.data.worlds[-1]
|
||||
bpy.context.scene.world.use_nodes = True
|
||||
nodes_world = bpy.context.scene.world.node_tree.nodes
|
||||
links_world = bpy.context.scene.world.node_tree.links
|
||||
|
||||
# Use improved selective node management
|
||||
try:
|
||||
poliigon_nodes = self._get_poliigon_hdri_nodes(nodes_world)
|
||||
self._remove_poliigon_nodes(nodes_world, poliigon_nodes)
|
||||
self.node_output_world = self._create_or_reuse_output_world(nodes_world, poliigon_nodes)
|
||||
except Exception as e:
|
||||
cTB.logger.error(f"Failed during node identification/removal: {str(e)}")
|
||||
# Fallback to more aggressive approach if needed
|
||||
self.node_output_world = None
|
||||
for node in nodes_world:
|
||||
if node.type == "OUTPUT_WORLD":
|
||||
self.node_output_world = node
|
||||
break
|
||||
if self.node_output_world is None:
|
||||
self.node_output_world = nodes_world.new("ShaderNodeOutputWorld")
|
||||
self.node_output_world.location = mathutils.Vector((250, 225.0))
|
||||
|
||||
# Create all required nodes
|
||||
try:
|
||||
self.node_tex_coord = nodes_world.new("ShaderNodeTexCoord")
|
||||
self.node_tex_coord.label = "Mapping"
|
||||
self.node_tex_coord.location = mathutils.Vector((-950, 225))
|
||||
|
||||
self.node_mapping = nodes_world.new("ShaderNodeMapping")
|
||||
self.node_mapping.label = "Mapping"
|
||||
self.node_mapping.location = mathutils.Vector((-750, 225))
|
||||
|
||||
self.node_tex_env_light = nodes_world.new("ShaderNodeTexEnvironment")
|
||||
self.node_tex_env_light.label = "Lighting"
|
||||
self.node_tex_env_light.location = mathutils.Vector((-550, 350))
|
||||
|
||||
self.node_tex_env_bg = nodes_world.new("ShaderNodeTexEnvironment")
|
||||
self.node_tex_env_bg.label = "Background"
|
||||
self.node_tex_env_bg.location = mathutils.Vector((-550, 100))
|
||||
|
||||
self.node_background_light = nodes_world.new("ShaderNodeBackground")
|
||||
self.node_background_light.label = "Lighting"
|
||||
self.node_background_light.location = mathutils.Vector((-250, 100))
|
||||
|
||||
self.node_background_bg = nodes_world.new("ShaderNodeBackground")
|
||||
self.node_background_bg.label = "Background"
|
||||
self.node_background_bg.location = mathutils.Vector((-250, -50))
|
||||
|
||||
self.node_mix_shader = nodes_world.new("ShaderNodeMixShader")
|
||||
self.node_mix_shader.location = mathutils.Vector((0, 225))
|
||||
|
||||
self.node_light_path = nodes_world.new("ShaderNodeLightPath")
|
||||
self.node_light_path.location = mathutils.Vector((-250, 440))
|
||||
|
||||
# Validate critical nodes for complex layout
|
||||
critical_nodes = ['node_output_world', 'node_tex_coord', 'node_mapping',
|
||||
'node_tex_env_light', 'node_background_light', 'node_tex_env_bg',
|
||||
'node_background_bg', 'node_mix_shader', 'node_light_path']
|
||||
for node_attr in critical_nodes:
|
||||
if not hasattr(self, node_attr) or getattr(self, node_attr) is None:
|
||||
raise Exception(f"Required node {node_attr} was not created")
|
||||
|
||||
except Exception as e:
|
||||
cTB.logger.error(f"Failed to create nodes in complex layout: {str(e)}")
|
||||
raise
|
||||
|
||||
# Create all links after ensuring nodes exist
|
||||
try:
|
||||
links_world.new(self.node_tex_coord.outputs["Generated"], self.node_mapping.inputs["Vector"])
|
||||
links_world.new(self.node_mapping.outputs["Vector"], self.node_tex_env_light.inputs["Vector"])
|
||||
links_world.new(self.node_tex_env_light.outputs["Color"], self.node_background_light.inputs["Color"])
|
||||
links_world.new(self.node_background_light.outputs[0], self.node_mix_shader.inputs[1])
|
||||
links_world.new(self.node_mapping.outputs["Vector"], self.node_tex_env_bg.inputs["Vector"])
|
||||
links_world.new(self.node_tex_env_bg.outputs["Color"], self.node_background_bg.inputs["Color"])
|
||||
links_world.new(self.node_background_bg.outputs[0], self.node_mix_shader.inputs[2])
|
||||
links_world.new(self.node_light_path.outputs[0], self.node_mix_shader.inputs[0])
|
||||
links_world.new(self.node_mix_shader.outputs[0], self.node_output_world.inputs[0])
|
||||
except Exception as e:
|
||||
cTB.logger.error(f"Failed to create node links: {str(e)}")
|
||||
raise
|
||||
|
||||
# Load images safely
|
||||
try:
|
||||
# Load light image
|
||||
if name_light in bpy.data.images.keys():
|
||||
img_light = bpy.data.images[name_light]
|
||||
else:
|
||||
file_tex_light_norm = os.path.normpath(file_tex_light)
|
||||
img_light = bpy.data.images.load(file_tex_light_norm)
|
||||
img_light.name = name_light
|
||||
img_light.poliigon = "HDRIs;" + asset_data.asset_name
|
||||
self.set_poliigon_props_image(img_light, asset_data)
|
||||
|
||||
# Load background image
|
||||
if name_bg in bpy.data.images.keys():
|
||||
img_bg = bpy.data.images[name_bg]
|
||||
else:
|
||||
file_tex_bg_norm = os.path.normpath(file_tex_bg)
|
||||
img_bg = bpy.data.images.load(file_tex_bg_norm)
|
||||
img_bg.name = name_bg
|
||||
self.set_poliigon_props_image(img_bg, asset_data)
|
||||
|
||||
# Set node properties safely
|
||||
if "Rotation" in self.node_mapping.inputs:
|
||||
self.node_mapping.inputs["Rotation"].default_value[2] = self.rotation
|
||||
else:
|
||||
self.node_mapping.rotation[2] = self.rotation
|
||||
|
||||
self.node_tex_env_light.image = img_light
|
||||
self.node_background_light.inputs["Strength"].default_value = self.hdr_strength
|
||||
|
||||
self.node_tex_env_bg.image = img_bg
|
||||
self.node_background_bg.inputs["Strength"].default_value = self.hdr_strength
|
||||
|
||||
except Exception as e:
|
||||
cTB.logger.error(f"Failed to load or setup images: {str(e)}")
|
||||
raise
|
||||
|
||||
def set_poliigon_props_image(
|
||||
self, img: bpy.types.Image, asset_data: AssetData) -> None:
|
||||
"""Sets Poliigon property of an imported image."""
|
||||
|
||||
Reference in New Issue
Block a user