expre-gen/blender/3.4/scripts/addons/amaranth/render/meshlight_add.py
2025-10-17 09:47:23 +08:00

197 lines
6.5 KiB
Python

# SPDX-License-Identifier: GPL-2.0-or-later
import bpy
from mathutils import Vector
from amaranth.utils import cycles_exists
# FEATURE: Add Meshlight
class AMTH_OBJECT_OT_meshlight_add(bpy.types.Operator):
"""Add a light emitting mesh"""
bl_idname = "object.meshlight_add"
bl_label = "Add Meshlight"
bl_options = {'REGISTER', 'UNDO'}
single_sided: bpy.props.BoolProperty(
name="Single Sided",
default=True,
description="Only emit light on one side",
)
is_constant: bpy.props.BoolProperty(
name="Constant Falloff",
default=False,
description="Energy is constant (i.e. the Sun), "
"independent of how close to the source you are",
)
visible: bpy.props.BoolProperty(
name="Visible on Camera",
default=False,
description="Whether to show the meshlight source on Camera",
)
size: bpy.props.FloatProperty(
name="Size",
description="Meshlight size. Lower is sharper shadows, higher is softer",
min=0.01, max=100.0,
default=1.0,
)
strength: bpy.props.FloatProperty(
name="Strength",
min=0.01, max=100000.0,
default=1.5,
step=0.25,
)
temperature: bpy.props.FloatProperty(
name="Temperature",
min=800, max=12000.0,
default=5500.0,
step=100.0,
description="Temperature in Kelvin. Lower is warmer, higher is colder",
)
rotation: bpy.props.FloatVectorProperty(
name="Rotation",
subtype='EULER',
)
def execute(self, context):
scene = context.scene
# exists = False
number = 1
for obs in bpy.data.objects:
if obs.name.startswith("light_meshlight"):
number += 1
meshlight_name = 'light_meshlight_%.2d' % number
bpy.ops.mesh.primitive_grid_add(
x_subdivisions=4, y_subdivisions=4,
rotation=self.rotation, size=self.size)
bpy.context.object.name = meshlight_name
meshlight = scene.objects[meshlight_name]
meshlight.show_wire = True
meshlight.show_all_edges = True
material = bpy.data.materials.get(meshlight_name)
if not material:
material = bpy.data.materials.new(meshlight_name)
bpy.ops.object.material_slot_add()
meshlight.active_material = material
material.use_nodes = True
material.diffuse_color = (1, 0.5, 0, 1)
nodes = material.node_tree.nodes
links = material.node_tree.links
# clear default nodes to start nice fresh
for no in nodes:
nodes.remove(no)
if self.single_sided:
geometry = nodes.new(type="ShaderNodeNewGeometry")
transparency = nodes.new(type="ShaderNodeBsdfTransparent")
transparency.inputs[0].default_value = (1, 1, 1, 1)
transparency.location = geometry.location
transparency.location += Vector((0.0, -55.0))
emission = nodes.new(type="ShaderNodeEmission")
emission.inputs['Strength'].default_value = self.strength
emission.location = transparency.location
emission.location += Vector((0.0, -80.0))
blackbody = nodes.new(type="ShaderNodeBlackbody")
blackbody.inputs['Temperature'].default_value = self.temperature
blackbody.location = emission.location
blackbody.location += Vector((-180.0, 0.0))
blackbody.label = 'Temperature'
mix = nodes.new(type="ShaderNodeMixShader")
mix.location = geometry.location
mix.location += Vector((180.0, 0.0))
mix.inputs[2].show_expanded = True
output = nodes.new(type="ShaderNodeOutputMaterial")
output.inputs[1].hide = True
output.inputs[2].hide = True
output.location = mix.location
output.location += Vector((180.0, 0.0))
# Make links
links.new(geometry.outputs['Backfacing'], mix.inputs[0])
links.new(transparency.outputs['BSDF'], mix.inputs[1])
links.new(emission.outputs['Emission'], mix.inputs[2])
links.new(blackbody.outputs['Color'], emission.inputs['Color'])
links.new(mix.outputs['Shader'], output.inputs['Surface'])
for sockets in geometry.outputs:
sockets.hide = True
else:
emission = nodes.new(type="ShaderNodeEmission")
emission.inputs['Strength'].default_value = self.strength
blackbody = nodes.new(type="ShaderNodeBlackbody")
blackbody.inputs['Temperature'].default_value = self.temperature
blackbody.location = emission.location
blackbody.location += Vector((-180.0, 0.0))
blackbody.label = 'Temperature'
output = nodes.new(type="ShaderNodeOutputMaterial")
output.inputs[1].hide = True
output.inputs[2].hide = True
output.location = emission.location
output.location += Vector((180.0, 0.0))
links.new(blackbody.outputs['Color'], emission.inputs['Color'])
links.new(emission.outputs['Emission'], output.inputs['Surface'])
if self.is_constant:
falloff = nodes.new(type="ShaderNodeLightFalloff")
falloff.inputs['Strength'].default_value = self.strength
falloff.location = emission.location
falloff.location += Vector((-180.0, -80.0))
links.new(falloff.outputs['Constant'], emission.inputs['Strength'])
for sockets in falloff.outputs:
sockets.hide = True
# so it shows slider on properties editor
for sockets in emission.inputs:
sockets.show_expanded = True
material.cycles.sample_as_light = True
meshlight.visible_shadow = False
meshlight.visible_camera = self.visible
return {'FINISHED'}
def ui_menu_lamps_add(self, context):
if cycles_exists() and context.scene.render.engine == 'CYCLES':
self.layout.separator()
self.layout.operator(
AMTH_OBJECT_OT_meshlight_add.bl_idname,
icon="LIGHT_AREA", text="Meshlight")
# //FEATURE: Add Meshlight: Single Sided
def register():
bpy.utils.register_class(AMTH_OBJECT_OT_meshlight_add)
bpy.types.VIEW3D_MT_light_add.append(ui_menu_lamps_add)
def unregister():
bpy.utils.unregister_class(AMTH_OBJECT_OT_meshlight_add)
bpy.types.VIEW3D_MT_light_add.remove(ui_menu_lamps_add)