Amaranth Addon
[blender-addons-contrib.git] / amaranth / render / meshlight_add.py
1 import bpy
2 from mathutils import Vector
3 from amaranth.utils import cycles_exists
4
5
6 # FEATURE: Add Meshlight
7 class AMTH_OBJECT_OT_meshlight_add(bpy.types.Operator):
8
9     """Add a light emitting mesh"""
10     bl_idname = "object.meshlight_add"
11     bl_label = "Add Meshlight"
12     bl_options = {'REGISTER', 'UNDO'}
13
14     single_sided = bpy.props.BoolProperty(
15         name="Single Sided",
16         default=True,
17         description="Only emit light on one side",
18         )
19
20     is_constant = bpy.props.BoolProperty(
21         name="Constant Falloff",
22         default=False,
23         description="Energy is constant (i.e. the Sun), "
24                     "independent of how close to the source you are",
25         )
26
27     visible = bpy.props.BoolProperty(
28         name="Visible on Camera",
29         default=False,
30         description="Whether to show the meshlight source on Camera",
31         )
32
33     size = bpy.props.FloatProperty(
34         name="Size",
35         description="Meshlight size. Lower is sharper shadows, higher is softer",
36         min=0.01, max=100.0,
37         default=1.0,
38         )
39
40     strength = bpy.props.FloatProperty(
41         name="Strength",
42         min=0.01, max=100000.0,
43         default=1.5,
44         step=0.25,
45         )
46
47     temperature = bpy.props.FloatProperty(
48         name="Temperature",
49         min=800, max=12000.0,
50         default=5500.0,
51         step=800.0,
52         description="Temperature in Kelvin. Lower is warmer, higher is colder",
53         )
54
55     rotation = bpy.props.FloatVectorProperty(
56         name="Rotation",
57         subtype='EULER',
58         )
59
60     def execute(self, context):
61         scene = context.scene
62         # exists = False
63         number = 1
64
65         for obs in bpy.data.objects:
66             if obs.name.startswith("light_meshlight"):
67                 number += 1
68
69         meshlight_name = 'light_meshlight_%.2d' % number
70
71         bpy.ops.mesh.primitive_grid_add(
72             x_subdivisions=4, y_subdivisions=4,
73             rotation=self.rotation, radius=self.size)
74
75         bpy.context.object.name = meshlight_name
76         meshlight = scene.objects[meshlight_name]
77         meshlight.show_wire = True
78         meshlight.show_all_edges = True
79
80         material = bpy.data.materials.get(meshlight_name)
81
82         if not material:
83             material = bpy.data.materials.new(meshlight_name)
84
85         bpy.ops.object.material_slot_add()
86         meshlight.active_material = material
87
88         material.use_nodes = True
89         material.diffuse_color = (1, 0.5, 0)
90         nodes = material.node_tree.nodes
91         links = material.node_tree.links
92
93         # clear default nodes to start nice fresh
94         for no in nodes:
95             nodes.remove(no)
96
97         if self.single_sided:
98             geometry = nodes.new(type="ShaderNodeNewGeometry")
99
100             transparency = nodes.new(type="ShaderNodeBsdfTransparent")
101             transparency.inputs[0].default_value = (1, 1, 1, 1)
102             transparency.location = geometry.location
103             transparency.location += Vector((0.0, -55.0))
104
105             emission = nodes.new(type="ShaderNodeEmission")
106             emission.inputs['Strength'].default_value = self.strength
107             emission.location = transparency.location
108             emission.location += Vector((0.0, -80.0))
109
110             blackbody = nodes.new(type="ShaderNodeBlackbody")
111             blackbody.inputs['Temperature'].default_value = self.temperature
112             blackbody.location = emission.location
113             blackbody.location += Vector((-180.0, 0.0))
114             blackbody.label = 'Temperature'
115
116             mix = nodes.new(type="ShaderNodeMixShader")
117             mix.location = geometry.location
118             mix.location += Vector((180.0, 0.0))
119             mix.inputs[2].show_expanded = True
120
121             output = nodes.new(type="ShaderNodeOutputMaterial")
122             output.inputs[1].hide = True
123             output.inputs[2].hide = True
124             output.location = mix.location
125             output.location += Vector((180.0, 0.0))
126
127             # Make links
128             links.new(geometry.outputs['Backfacing'], mix.inputs[0])
129             links.new(transparency.outputs['BSDF'], mix.inputs[1])
130             links.new(emission.outputs['Emission'], mix.inputs[2])
131             links.new(blackbody.outputs['Color'], emission.inputs['Color'])
132             links.new(mix.outputs['Shader'], output.inputs['Surface'])
133
134             for sockets in geometry.outputs:
135                 sockets.hide = True
136         else:
137             emission = nodes.new(type="ShaderNodeEmission")
138             emission.inputs['Strength'].default_value = self.strength
139
140             blackbody = nodes.new(type="ShaderNodeBlackbody")
141             blackbody.inputs['Temperature'].default_value = self.temperature
142             blackbody.location = emission.location
143             blackbody.location += Vector((-180.0, 0.0))
144             blackbody.label = 'Temperature'
145
146             output = nodes.new(type="ShaderNodeOutputMaterial")
147             output.inputs[1].hide = True
148             output.inputs[2].hide = True
149             output.location = emission.location
150             output.location += Vector((180.0, 0.0))
151
152             links.new(blackbody.outputs['Color'], emission.inputs['Color'])
153             links.new(emission.outputs['Emission'], output.inputs['Surface'])
154
155         if self.is_constant:
156             falloff = nodes.new(type="ShaderNodeLightFalloff")
157             falloff.inputs['Strength'].default_value = self.strength
158             falloff.location = emission.location
159             falloff.location += Vector((-180.0, -80.0))
160
161             links.new(falloff.outputs['Constant'], emission.inputs['Strength'])
162
163             for sockets in falloff.outputs:
164                 sockets.hide = True
165
166         # so it shows slider on properties editor
167         for sockets in emission.inputs:
168             sockets.show_expanded = True
169
170         material.cycles.sample_as_light = True
171         meshlight.cycles_visibility.shadow = False
172         meshlight.cycles_visibility.camera = self.visible
173
174         return {'FINISHED'}
175
176
177 def ui_menu_lamps_add(self, context):
178     if cycles_exists() and context.scene.render.engine == 'CYCLES':
179         self.layout.separator()
180         self.layout.operator(
181             AMTH_OBJECT_OT_meshlight_add.bl_idname,
182             icon="LAMP_AREA", text="Meshlight")
183
184 # //FEATURE: Add Meshlight: Single Sided
185
186
187 def register():
188     bpy.utils.register_class(AMTH_OBJECT_OT_meshlight_add)
189     bpy.types.INFO_MT_mesh_add.append(ui_menu_lamps_add)
190
191
192 def unregister():
193     bpy.utils.unregister_class(AMTH_OBJECT_OT_meshlight_add)
194     bpy.types.INFO_MT_mesh_add.remove(ui_menu_lamps_add)