595 lines
30 KiB
Python
595 lines
30 KiB
Python
"""
|
|
Generation operators for text texture creation.
|
|
Contains operators for generating textures and complete shaders.
|
|
"""
|
|
|
|
import bpy
|
|
from bpy.types import Operator
|
|
import os
|
|
import json
|
|
|
|
# Import required functions from other modules
|
|
from ..core.generation_engine import generate_texture_image, generate_preview
|
|
|
|
|
|
class TEXT_TEXTURE_OT_generate(Operator):
|
|
"""Generate final texture and pack it"""
|
|
bl_idname = "text_texture.generate"
|
|
bl_label = "Generate Text Texture"
|
|
bl_description = "Generate final high-quality texture and pack it into the blend file"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
try:
|
|
props = context.scene.text_texture_props
|
|
|
|
# IMPORTANT: Generate at EXACT user-specified dimensions
|
|
final_width = props.texture_width
|
|
final_height = props.texture_height
|
|
|
|
print(f"Generating texture at {final_width}x{final_height}px")
|
|
|
|
# Generate at FULL resolution specified by user
|
|
result = generate_texture_image(props, final_width, final_height)
|
|
if not result:
|
|
self.report({'ERROR'}, "Failed to generate texture image")
|
|
return {'CANCELLED'}
|
|
|
|
# Extract diffuse and normal map images
|
|
if isinstance(result, tuple):
|
|
img, normal_map_img = result
|
|
else:
|
|
# Backward compatibility
|
|
img = result
|
|
normal_map_img = None
|
|
|
|
if not img:
|
|
self.report({'ERROR'}, "Failed to generate diffuse image")
|
|
return {'CANCELLED'}
|
|
|
|
# Create diffuse texture
|
|
img_name = f"TextTexture_{props.text[:20].replace(' ', '_')}"
|
|
|
|
# Remove existing diffuse texture
|
|
if img_name in bpy.data.images:
|
|
bpy.data.images.remove(bpy.data.images[img_name])
|
|
|
|
# Create new diffuse image with USER SPECIFIED dimensions
|
|
blender_img = bpy.data.images.new(img_name, final_width, final_height, alpha=True)
|
|
|
|
# Convert PIL to Blender for diffuse texture
|
|
pixels = []
|
|
for y in range(final_height):
|
|
for x in range(final_width):
|
|
r, g, b, a = img.getpixel((x, final_height - 1 - y))
|
|
pixels.extend([r/255.0, g/255.0, b/255.0, a/255.0])
|
|
|
|
blender_img.pixels = pixels
|
|
blender_img.pack() # PACK FINAL IMAGE
|
|
|
|
# Create normal map texture if enabled and generated
|
|
normal_map_blender_img = None
|
|
if normal_map_img and props.generate_normal_map:
|
|
normal_map_name = f"TextTexture_Normal_{props.text[:20].replace(' ', '_')}"
|
|
|
|
# Remove existing normal map texture
|
|
if normal_map_name in bpy.data.images:
|
|
bpy.data.images.remove(bpy.data.images[normal_map_name])
|
|
|
|
# Create new normal map image
|
|
normal_map_blender_img = bpy.data.images.new(normal_map_name, final_width, final_height, alpha=False)
|
|
normal_map_blender_img.colorspace_settings.name = 'Non-Color' # Important for normal maps
|
|
|
|
# Convert PIL to Blender for normal map
|
|
normal_pixels = []
|
|
for y in range(final_height):
|
|
for x in range(final_width):
|
|
r, g, b = normal_map_img.getpixel((x, final_height - 1 - y))
|
|
normal_pixels.extend([r/255.0, g/255.0, b/255.0, 1.0]) # Alpha always 1.0 for normal maps
|
|
|
|
normal_map_blender_img.pixels = normal_pixels
|
|
normal_map_blender_img.pack() # PACK NORMAL MAP
|
|
print(f"[Normal Map] Created and packed normal map texture: {normal_map_name}")
|
|
|
|
# Add to shader
|
|
if context.space_data and context.space_data.type == 'NODE_EDITOR':
|
|
if context.space_data.tree_type == 'ShaderNodeTree':
|
|
material = context.space_data.id
|
|
if material and material.use_nodes:
|
|
nodes = material.node_tree.nodes
|
|
|
|
# Add diffuse texture node
|
|
img_node = nodes.new(type='ShaderNodeTexImage')
|
|
img_node.image = blender_img
|
|
img_node.location = (0, 0)
|
|
img_node.select = True
|
|
nodes.active = img_node
|
|
|
|
# Add normal map node if normal map was created
|
|
if normal_map_blender_img:
|
|
normal_node = nodes.new(type='ShaderNodeTexImage')
|
|
normal_node.image = normal_map_blender_img
|
|
normal_node.location = (0, -300)
|
|
normal_node.label = "Normal Map"
|
|
print(f"[Normal Map] Added normal map node to shader editor")
|
|
|
|
# Provide comprehensive feedback
|
|
if normal_map_blender_img:
|
|
self.report({'INFO'}, f"Generated and packed: {img_name} + {normal_map_blender_img.name} ({final_width}x{final_height}px)")
|
|
else:
|
|
self.report({'INFO'}, f"Generated and packed: {img_name} ({final_width}x{final_height}px)")
|
|
return {'FINISHED'}
|
|
|
|
except Exception as e:
|
|
self.report({'ERROR'}, f"Failed: {str(e)}")
|
|
return {'CANCELLED'}
|
|
|
|
|
|
class TEXT_TEXTURE_OT_generate_shader(Operator):
|
|
"""Generate complete shader with text texture"""
|
|
bl_idname = "text_texture.generate_shader"
|
|
bl_label = "Generate Shader"
|
|
bl_description = "Generate a complete material shader with the text texture"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
try:
|
|
props = context.scene.text_texture_props
|
|
|
|
# Generate texture first
|
|
final_width = props.texture_width
|
|
final_height = props.texture_height
|
|
|
|
print(f"Generating shader with texture at {final_width}x{final_height}px")
|
|
|
|
result = generate_texture_image(props, final_width, final_height)
|
|
if not result:
|
|
self.report({'ERROR'}, "Failed to generate texture image")
|
|
return {'CANCELLED'}
|
|
|
|
# Extract diffuse and normal map images
|
|
if isinstance(result, tuple):
|
|
img, normal_map_img = result
|
|
else:
|
|
# Backward compatibility
|
|
img = result
|
|
normal_map_img = None
|
|
|
|
if not img:
|
|
self.report({'ERROR'}, "Failed to generate diffuse image")
|
|
return {'CANCELLED'}
|
|
|
|
# Create diffuse texture name
|
|
img_name = f"TextTexture_{props.text[:20].replace(' ', '_')}"
|
|
|
|
# Remove existing diffuse texture if it exists
|
|
if img_name in bpy.data.images:
|
|
bpy.data.images.remove(bpy.data.images[img_name])
|
|
|
|
# Create new diffuse texture
|
|
blender_img = bpy.data.images.new(img_name, final_width, final_height, alpha=True)
|
|
|
|
# Convert PIL to Blender for diffuse texture
|
|
pixels = []
|
|
for y in range(final_height):
|
|
for x in range(final_width):
|
|
r, g, b, a = img.getpixel((x, final_height - 1 - y))
|
|
pixels.extend([r/255.0, g/255.0, b/255.0, a/255.0])
|
|
|
|
blender_img.pixels = pixels
|
|
blender_img.pack()
|
|
|
|
# Create normal map texture if enabled and generated
|
|
normal_map_blender_img = None
|
|
if normal_map_img and props.generate_normal_map:
|
|
normal_map_name = f"TextTexture_Normal_{props.text[:20].replace(' ', '_')}"
|
|
|
|
# Remove existing normal map texture
|
|
if normal_map_name in bpy.data.images:
|
|
bpy.data.images.remove(bpy.data.images[normal_map_name])
|
|
|
|
# Create new normal map image
|
|
normal_map_blender_img = bpy.data.images.new(normal_map_name, final_width, final_height, alpha=False)
|
|
normal_map_blender_img.colorspace_settings.name = 'Non-Color' # Important for normal maps
|
|
|
|
# Convert PIL to Blender for normal map
|
|
normal_pixels = []
|
|
for y in range(final_height):
|
|
for x in range(final_width):
|
|
r, g, b = normal_map_img.getpixel((x, final_height - 1 - y))
|
|
normal_pixels.extend([r/255.0, g/255.0, b/255.0, 1.0]) # Alpha always 1.0 for normal maps
|
|
|
|
normal_map_blender_img.pixels = normal_pixels
|
|
normal_map_blender_img.pack()
|
|
print(f"[Normal Map] Created normal map texture for shader: {normal_map_name}")
|
|
|
|
# Create or get material using custom name or fallback
|
|
if props.custom_material_name.strip():
|
|
material_name = props.custom_material_name.strip()
|
|
else:
|
|
# Fallback to text-based naming
|
|
clean_text = props.text[:15].replace(' ', '_').replace('\n', '_')
|
|
material_name = f"TextTexture_Mat_{clean_text}"
|
|
|
|
if material_name in bpy.data.materials:
|
|
mat = bpy.data.materials[material_name]
|
|
# Clear existing nodes
|
|
mat.node_tree.nodes.clear()
|
|
else:
|
|
mat = bpy.data.materials.new(name=material_name)
|
|
mat.use_nodes = True
|
|
mat.node_tree.nodes.clear()
|
|
|
|
# Set up shader nodes
|
|
nodes = mat.node_tree.nodes
|
|
links = mat.node_tree.links
|
|
|
|
# Create nodes
|
|
output_node = nodes.new(type='ShaderNodeOutputMaterial')
|
|
output_node.location = (400, 0)
|
|
|
|
# Shader type based on settings
|
|
if props.shader_type == 'PRINCIPLED':
|
|
shader_node = nodes.new(type='ShaderNodeBsdfPrincipled')
|
|
shader_node.location = (200, 0)
|
|
|
|
# Create diffuse texture node
|
|
tex_node = nodes.new(type='ShaderNodeTexImage')
|
|
tex_node.image = blender_img
|
|
tex_node.location = (-200, 0)
|
|
|
|
# Connect texture to shader
|
|
if props.shader_connection == 'BASE_COLOR':
|
|
links.new(tex_node.outputs['Color'], shader_node.inputs['Base Color'])
|
|
elif props.shader_connection == 'EMISSION':
|
|
links.new(tex_node.outputs['Color'], shader_node.inputs['Emission Color'])
|
|
shader_node.inputs['Emission Strength'].default_value = props.emission_strength
|
|
elif props.shader_connection == 'ALPHA':
|
|
links.new(tex_node.outputs['Alpha'], shader_node.inputs['Alpha'])
|
|
# Also connect color for visibility
|
|
links.new(tex_node.outputs['Color'], shader_node.inputs['Base Color'])
|
|
mat.blend_method = 'BLEND'
|
|
|
|
# Connect alpha if enabled
|
|
if props.use_alpha:
|
|
links.new(tex_node.outputs['Alpha'], shader_node.inputs['Alpha'])
|
|
mat.blend_method = 'BLEND'
|
|
|
|
# Add normal map nodes if normal map was generated
|
|
if normal_map_blender_img:
|
|
# Create normal map texture node
|
|
normal_tex_node = nodes.new(type='ShaderNodeTexImage')
|
|
normal_tex_node.image = normal_map_blender_img
|
|
normal_tex_node.location = (-200, -300)
|
|
normal_tex_node.label = "Normal Map"
|
|
|
|
# Create normal map node
|
|
normal_map_node = nodes.new(type='ShaderNodeNormalMap')
|
|
normal_map_node.location = (0, -300)
|
|
|
|
# Connect normal map texture to normal map node
|
|
links.new(normal_tex_node.outputs['Color'], normal_map_node.inputs['Color'])
|
|
|
|
# Connect normal map node to shader
|
|
links.new(normal_map_node.outputs['Normal'], shader_node.inputs['Normal'])
|
|
|
|
print(f"[Normal Map] Added normal map nodes to Principled BSDF shader")
|
|
|
|
# Handle shadow/reflection disabling
|
|
current_shader = shader_node
|
|
final_x_location = 400
|
|
|
|
# Disable shadows if enabled
|
|
if props.disable_shadows:
|
|
# Create light path node
|
|
light_path_node = nodes.new(type='ShaderNodeLightPath')
|
|
light_path_node.location = (50, 150)
|
|
|
|
# Create transparent BSDF
|
|
transparent_node = nodes.new(type='ShaderNodeBsdfTransparent')
|
|
transparent_node.location = (200, 150)
|
|
|
|
# Create mix shader for shadows
|
|
shadow_mix_node = nodes.new(type='ShaderNodeMixShader')
|
|
shadow_mix_node.location = (350, 50)
|
|
|
|
# Connect light path "Is Shadow Ray" to mix factor
|
|
links.new(light_path_node.outputs['Is Shadow Ray'], shadow_mix_node.inputs['Fac'])
|
|
|
|
# Mix principled BSDF with transparent BSDF
|
|
links.new(current_shader.outputs['BSDF'], shadow_mix_node.inputs[1]) # Shader 1 (not shadow)
|
|
links.new(transparent_node.outputs['BSDF'], shadow_mix_node.inputs[2]) # Shader 2 (shadow)
|
|
|
|
current_shader = shadow_mix_node
|
|
final_x_location = 500
|
|
|
|
# Disable reflections if enabled
|
|
if props.disable_reflections:
|
|
# Create or reuse light path node
|
|
if not props.disable_shadows:
|
|
light_path_node = nodes.new(type='ShaderNodeLightPath')
|
|
light_path_node.location = (50, 200)
|
|
|
|
# Create transparent BSDF for reflections
|
|
reflection_transparent_node = nodes.new(type='ShaderNodeBsdfTransparent')
|
|
reflection_transparent_node.location = (200, 200)
|
|
|
|
# Create mix shader for reflections
|
|
reflection_mix_node = nodes.new(type='ShaderNodeMixShader')
|
|
reflection_mix_node.location = (final_x_location - 50, 100)
|
|
|
|
# Connect light path "Is Reflection Ray" to mix factor
|
|
links.new(light_path_node.outputs['Is Reflection Ray'], reflection_mix_node.inputs['Fac'])
|
|
|
|
# Mix current shader with transparent BSDF
|
|
links.new(current_shader.outputs['Shader' if props.disable_shadows else 'BSDF'], reflection_mix_node.inputs[1]) # Shader 1 (not reflection)
|
|
links.new(reflection_transparent_node.outputs['BSDF'], reflection_mix_node.inputs[2]) # Shader 2 (reflection)
|
|
|
|
current_shader = reflection_mix_node
|
|
final_x_location = final_x_location + 50
|
|
|
|
# Disable backfacing if enabled
|
|
if props.disable_backfacing:
|
|
# Create or reuse light path node
|
|
if not props.disable_shadows and not props.disable_reflections:
|
|
light_path_node = nodes.new(type='ShaderNodeLightPath')
|
|
light_path_node.location = (50, 250)
|
|
|
|
# Create geometry node for backfacing detection
|
|
geometry_node = nodes.new(type='ShaderNodeNewGeometry')
|
|
geometry_node.location = (50, 300)
|
|
|
|
# Create transparent BSDF for backfaces
|
|
backface_transparent_node = nodes.new(type='ShaderNodeBsdfTransparent')
|
|
backface_transparent_node.location = (200, 250)
|
|
|
|
# Create mix shader for backfaces
|
|
backface_mix_node = nodes.new(type='ShaderNodeMixShader')
|
|
backface_mix_node.location = (final_x_location - 50, 150)
|
|
|
|
# Connect geometry "Backfacing" to mix factor
|
|
links.new(geometry_node.outputs['Backfacing'], backface_mix_node.inputs['Fac'])
|
|
|
|
# Mix current shader with transparent BSDF
|
|
current_output = 'Shader' if (props.disable_shadows or props.disable_reflections) else 'BSDF'
|
|
links.new(current_shader.outputs[current_output], backface_mix_node.inputs[1]) # Shader 1 (front face)
|
|
links.new(backface_transparent_node.outputs['BSDF'], backface_mix_node.inputs[2]) # Shader 2 (backface)
|
|
|
|
current_shader = backface_mix_node
|
|
final_x_location = final_x_location + 50
|
|
|
|
# Update output node position
|
|
output_node.location = (final_x_location, 0)
|
|
|
|
# Connect final shader to output
|
|
output_socket = 'Shader' if (props.disable_shadows or props.disable_reflections) else 'BSDF'
|
|
links.new(current_shader.outputs[output_socket], output_node.inputs['Surface'])
|
|
|
|
elif props.shader_type == 'EMISSION':
|
|
shader_node = nodes.new(type='ShaderNodeEmission')
|
|
shader_node.location = (200, 0)
|
|
|
|
# Create texture node
|
|
tex_node = nodes.new(type='ShaderNodeTexImage')
|
|
tex_node.image = blender_img
|
|
tex_node.location = (-200, 0)
|
|
|
|
# Connect texture to emission
|
|
links.new(tex_node.outputs['Color'], shader_node.inputs['Color'])
|
|
shader_node.inputs['Strength'].default_value = props.emission_strength
|
|
|
|
# Handle shadow/reflection disabling
|
|
current_shader = shader_node
|
|
final_x_location = 400
|
|
|
|
# Disable shadows if enabled
|
|
if props.disable_shadows:
|
|
# Create light path node
|
|
light_path_node = nodes.new(type='ShaderNodeLightPath')
|
|
light_path_node.location = (50, 150)
|
|
|
|
# Create transparent BSDF
|
|
transparent_node = nodes.new(type='ShaderNodeBsdfTransparent')
|
|
transparent_node.location = (200, 150)
|
|
|
|
# Create mix shader for shadows
|
|
shadow_mix_node = nodes.new(type='ShaderNodeMixShader')
|
|
shadow_mix_node.location = (350, 50)
|
|
|
|
# Connect light path "Is Shadow Ray" to mix factor
|
|
links.new(light_path_node.outputs['Is Shadow Ray'], shadow_mix_node.inputs['Fac'])
|
|
|
|
# Mix emission with transparent BSDF
|
|
links.new(current_shader.outputs['Emission'], shadow_mix_node.inputs[1]) # Shader 1 (not shadow)
|
|
links.new(transparent_node.outputs['BSDF'], shadow_mix_node.inputs[2]) # Shader 2 (shadow)
|
|
|
|
current_shader = shadow_mix_node
|
|
final_x_location = 500
|
|
|
|
# Disable reflections if enabled
|
|
if props.disable_reflections:
|
|
# Create or reuse light path node
|
|
if not props.disable_shadows:
|
|
light_path_node = nodes.new(type='ShaderNodeLightPath')
|
|
light_path_node.location = (50, 200)
|
|
|
|
# Create transparent BSDF for reflections
|
|
reflection_transparent_node = nodes.new(type='ShaderNodeBsdfTransparent')
|
|
reflection_transparent_node.location = (200, 200)
|
|
|
|
# Create mix shader for reflections
|
|
reflection_mix_node = nodes.new(type='ShaderNodeMixShader')
|
|
reflection_mix_node.location = (final_x_location - 50, 100)
|
|
|
|
# Connect light path "Is Reflection Ray" to mix factor
|
|
links.new(light_path_node.outputs['Is Reflection Ray'], reflection_mix_node.inputs['Fac'])
|
|
|
|
# Mix current shader with transparent BSDF
|
|
links.new(current_shader.outputs['Shader' if props.disable_shadows else 'Emission'], reflection_mix_node.inputs[1]) # Shader 1 (not reflection)
|
|
links.new(reflection_transparent_node.outputs['BSDF'], reflection_mix_node.inputs[2]) # Shader 2 (reflection)
|
|
|
|
current_shader = reflection_mix_node
|
|
final_x_location = final_x_location + 50
|
|
|
|
# Update output node position
|
|
output_node.location = (final_x_location, 0)
|
|
|
|
# Connect final shader to output
|
|
output_socket = 'Shader' if (props.disable_shadows or props.disable_reflections) else 'Emission'
|
|
links.new(current_shader.outputs[output_socket], output_node.inputs['Surface'])
|
|
|
|
elif props.shader_type == 'TRANSPARENT':
|
|
shader_node = nodes.new(type='ShaderNodeBsdfTransparent')
|
|
mix_node = nodes.new(type='ShaderNodeMixShader')
|
|
principled_node = nodes.new(type='ShaderNodeBsdfPrincipled')
|
|
|
|
shader_node.location = (200, 100)
|
|
mix_node.location = (350, 0)
|
|
principled_node.location = (200, -100)
|
|
|
|
# Create texture node
|
|
tex_node = nodes.new(type='ShaderNodeTexImage')
|
|
tex_node.image = blender_img
|
|
tex_node.location = (-200, 0)
|
|
|
|
# Connect texture
|
|
links.new(tex_node.outputs['Color'], principled_node.inputs['Base Color'])
|
|
links.new(tex_node.outputs['Alpha'], mix_node.inputs['Fac'])
|
|
|
|
# Connect shaders to mix
|
|
links.new(shader_node.outputs['BSDF'], mix_node.inputs[1])
|
|
links.new(principled_node.outputs['BSDF'], mix_node.inputs[2])
|
|
|
|
# Handle shadow/reflection disabling
|
|
current_shader = mix_node
|
|
final_x_location = 500
|
|
|
|
# Disable shadows if enabled
|
|
if props.disable_shadows:
|
|
# Create light path node
|
|
light_path_node = nodes.new(type='ShaderNodeLightPath')
|
|
light_path_node.location = (50, 200)
|
|
|
|
# Create transparent BSDF for shadows (reuse existing transparent node)
|
|
shadow_transparent_node = nodes.new(type='ShaderNodeBsdfTransparent')
|
|
shadow_transparent_node.location = (350, 150)
|
|
|
|
# Create mix shader for shadows
|
|
shadow_mix_node = nodes.new(type='ShaderNodeMixShader')
|
|
shadow_mix_node.location = (500, 50)
|
|
|
|
# Connect light path "Is Shadow Ray" to mix factor
|
|
links.new(light_path_node.outputs['Is Shadow Ray'], shadow_mix_node.inputs['Fac'])
|
|
|
|
# Mix current shader with transparent BSDF
|
|
links.new(current_shader.outputs['Shader'], shadow_mix_node.inputs[1]) # Shader 1 (not shadow)
|
|
links.new(shadow_transparent_node.outputs['BSDF'], shadow_mix_node.inputs[2]) # Shader 2 (shadow)
|
|
|
|
current_shader = shadow_mix_node
|
|
final_x_location = 650
|
|
|
|
# Disable reflections if enabled
|
|
if props.disable_reflections:
|
|
# Create or reuse light path node
|
|
if not props.disable_shadows:
|
|
light_path_node = nodes.new(type='ShaderNodeLightPath')
|
|
light_path_node.location = (50, 250)
|
|
|
|
# Create transparent BSDF for reflections
|
|
reflection_transparent_node = nodes.new(type='ShaderNodeBsdfTransparent')
|
|
reflection_transparent_node.location = (350, 200)
|
|
|
|
# Create mix shader for reflections
|
|
reflection_mix_node = nodes.new(type='ShaderNodeMixShader')
|
|
reflection_mix_node.location = (final_x_location - 50, 100)
|
|
|
|
# Connect light path "Is Reflection Ray" to mix factor
|
|
links.new(light_path_node.outputs['Is Reflection Ray'], reflection_mix_node.inputs['Fac'])
|
|
|
|
# Mix current shader with transparent BSDF
|
|
links.new(current_shader.outputs['Shader'], reflection_mix_node.inputs[1]) # Shader 1 (not reflection)
|
|
links.new(reflection_transparent_node.outputs['BSDF'], reflection_mix_node.inputs[2]) # Shader 2 (reflection)
|
|
|
|
current_shader = reflection_mix_node
|
|
final_x_location = final_x_location + 50
|
|
|
|
# Disable backfacing if enabled
|
|
if props.disable_backfacing:
|
|
# Create or reuse light path node
|
|
if not props.disable_shadows and not props.disable_reflections:
|
|
light_path_node = nodes.new(type='ShaderNodeLightPath')
|
|
light_path_node.location = (50, 300)
|
|
|
|
# Create geometry node for backfacing detection
|
|
geometry_node = nodes.new(type='ShaderNodeNewGeometry')
|
|
geometry_node.location = (50, 350)
|
|
|
|
# Create transparent BSDF for backfaces
|
|
backface_transparent_node = nodes.new(type='ShaderNodeBsdfTransparent')
|
|
backface_transparent_node.location = (350, 300)
|
|
|
|
# Create mix shader for backfaces
|
|
backface_mix_node = nodes.new(type='ShaderNodeMixShader')
|
|
backface_mix_node.location = (final_x_location + 50, 150)
|
|
|
|
# Connect geometry "Backfacing" to mix factor
|
|
links.new(geometry_node.outputs['Backfacing'], backface_mix_node.inputs['Fac'])
|
|
|
|
# Mix current shader with transparent BSDF
|
|
links.new(current_shader.outputs['Shader'], backface_mix_node.inputs[1]) # Shader 1 (front face)
|
|
links.new(backface_transparent_node.outputs['BSDF'], backface_mix_node.inputs[2]) # Shader 2 (backface)
|
|
|
|
current_shader = backface_mix_node
|
|
final_x_location = final_x_location + 150
|
|
|
|
# Update output node position
|
|
output_node.location = (final_x_location, 0)
|
|
|
|
# Connect final shader to output
|
|
links.new(current_shader.outputs['Shader'], output_node.inputs['Surface'])
|
|
|
|
mat.blend_method = 'BLEND'
|
|
|
|
# Assign material to active object if enabled and possible
|
|
if props.auto_assign_material and context.active_object and context.active_object.type in ['MESH', 'CURVE']:
|
|
obj = context.active_object
|
|
# Check if material slot exists, reuse if found, otherwise create new one
|
|
slot_found = False
|
|
for i, slot in enumerate(obj.material_slots):
|
|
if slot.material and slot.material.name == material_name:
|
|
# Material already exists in a slot, update it
|
|
obj.material_slots[i].material = mat
|
|
slot_found = True
|
|
break
|
|
|
|
if not slot_found:
|
|
# Create new material slot if not found
|
|
obj.data.materials.append(mat)
|
|
|
|
self.report({'INFO'}, f"Generated shader '{material_name}' and assigned to {obj.name}")
|
|
else:
|
|
self.report({'INFO'}, f"Generated shader '{material_name}' (assign manually to object)")
|
|
|
|
# Switch to Shader Editor if available and set material
|
|
for area in context.screen.areas:
|
|
if area.type == 'NODE_EDITOR':
|
|
for space in area.spaces:
|
|
if space.type == 'NODE_EDITOR':
|
|
space.shader_type = 'OBJECT'
|
|
space.id = mat
|
|
area.tag_redraw()
|
|
break
|
|
|
|
return {'FINISHED'}
|
|
|
|
except Exception as e:
|
|
self.report({'ERROR'}, f"Shader generation failed: {str(e)}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
return {'CANCELLED'}
|
|
|
|
# Export all operator classes for wildcard imports
|
|
__all__ = [
|
|
'TEXT_TEXTURE_OT_generate',
|
|
'TEXT_TEXTURE_OT_generate_shader',
|
|
] |