Sun Position: replace DMS label by the Enter Coordinates field
[blender-addons.git] / lighting_tri_lights.py
blob5b31ad27db036a2829f8e82e3f1232122aed057a
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 # author Daniel Schalla, maintained by meta-androcto
5 bl_info = {
6 "name": "Tri-lighting",
7 "author": "Daniel Schalla",
8 "version": (0, 1, 4),
9 "blender": (2, 80, 0),
10 "location": "View3D > Add > Lights",
11 "description": "Add 3 Point Lighting to Selected / Active Object",
12 "warning": "",
13 "tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/",
14 "doc_url": "{BLENDER_MANUAL_URL}/addons/lighting/trilighting.html",
15 "category": "Lighting",
18 import bpy
19 from bpy.types import Operator
20 from bpy.props import (
21 EnumProperty,
22 FloatProperty,
23 IntProperty,
25 from math import (
26 sin, cos,
27 radians,
28 sqrt,
32 class OBJECT_OT_TriLighting(Operator):
33 bl_idname = "object.trilighting"
34 bl_label = "Tri-Lighting Creator"
35 bl_description = ("Add 3 Point Lighting to Selected / Active Object\n"
36 "Needs an existing Active Object")
37 bl_options = {'REGISTER', 'UNDO'}
38 COMPAT_ENGINES = {'CYCLES', 'EEVEE'}
40 height: FloatProperty(
41 name="Height",
42 default=5
44 distance: FloatProperty(
45 name="Distance",
46 default=5,
47 min=0.1,
48 subtype="DISTANCE"
50 energy: IntProperty(
51 name="Base Energy",
52 default=3,
53 min=1
55 contrast: IntProperty(
56 name="Contrast",
57 default=50,
58 min=-100, max=100,
59 subtype="PERCENTAGE"
61 leftangle: IntProperty(
62 name="Left Angle",
63 default=26,
64 min=1, max=90,
65 subtype="ANGLE"
67 rightangle: IntProperty(
68 name="Right Angle",
69 default=45,
70 min=1, max=90,
71 subtype="ANGLE"
73 backangle: IntProperty(
74 name="Back Angle",
75 default=235,
76 min=90, max=270,
77 subtype="ANGLE"
79 Light_Type_List = [
80 ('POINT', "Point", "Point Light"),
81 ('SUN', "Sun", "Sun Light"),
82 ('SPOT', "Spot", "Spot Light"),
83 ('AREA', "Area", "Area Light")
85 primarytype: EnumProperty(
86 attr='tl_type',
87 name="Key Type",
88 description="Choose the types of Key Lights you would like",
89 items=Light_Type_List,
90 default='AREA'
92 secondarytype: EnumProperty(
93 attr='tl_type',
94 name="Fill + Back Type",
95 description="Choose the types of secondary Lights you would like",
96 items=Light_Type_List,
97 default="AREA"
100 @classmethod
101 def poll(cls, context):
102 return context.active_object is not None
104 def draw(self, context):
105 layout = self.layout
107 layout.label(text="Position:")
108 col = layout.column(align=True)
109 col.prop(self, "height")
110 col.prop(self, "distance")
112 layout.label(text="Light:")
113 col = layout.column(align=True)
114 col.prop(self, "energy")
115 col.prop(self, "contrast")
117 layout.label(text="Orientation:")
118 col = layout.column(align=True)
119 col.prop(self, "leftangle")
120 col.prop(self, "rightangle")
121 col.prop(self, "backangle")
123 col = layout.column()
124 col.label(text="Key Light Type:")
125 col.prop(self, "primarytype", text="")
126 col.label(text="Fill + Back Type:")
127 col.prop(self, "secondarytype", text="")
130 def execute(self, context):
131 try:
132 collection = context.collection
133 scene = context.scene
134 view = context.space_data
135 if view.type == 'VIEW_3D':
136 camera = view.camera
137 else:
138 camera = scene.camera
140 if (camera is None):
141 cam_data = bpy.data.cameras.new(name='Camera')
142 cam_obj = bpy.data.objects.new(name='Camera', object_data=cam_data)
143 collection.objects.link(cam_obj)
144 scene.camera = cam_obj
145 bpy.ops.view3d.camera_to_view()
146 camera = cam_obj
147 # Leave camera view again, otherwise redo does not work correctly.
148 bpy.ops.view3d.view_camera()
150 obj = bpy.context.view_layer.objects.active
152 # Calculate Energy for each Lamp
153 if(self.contrast > 0):
154 keyEnergy = self.energy
155 backEnergy = (self.energy / 100) * abs(self.contrast)
156 fillEnergy = (self.energy / 100) * abs(self.contrast)
157 else:
158 keyEnergy = (self.energy / 100) * abs(self.contrast)
159 backEnergy = self.energy
160 fillEnergy = self.energy
162 # Calculate Direction for each Lamp
164 # Calculate current Distance and get Delta
165 obj_position = obj.location
166 cam_position = camera.location
168 delta_position = cam_position - obj_position
169 vector_length = sqrt(
170 (pow(delta_position.x, 2) +
171 pow(delta_position.y, 2) +
172 pow(delta_position.z, 2))
174 if not vector_length:
175 # division by zero most likely
176 self.report({'WARNING'},
177 "Operation Cancelled. No viable object in the scene")
179 return {'CANCELLED'}
181 single_vector = (1 / vector_length) * delta_position
183 # Calc back position
184 singleback_vector = single_vector.copy()
185 singleback_vector.x = cos(radians(self.backangle)) * single_vector.x + \
186 (-sin(radians(self.backangle)) * single_vector.y)
188 singleback_vector.y = sin(radians(self.backangle)) * single_vector.x + \
189 (cos(radians(self.backangle)) * single_vector.y)
191 backx = obj_position.x + self.distance * singleback_vector.x
192 backy = obj_position.y + self.distance * singleback_vector.y
194 backData = bpy.data.lights.new(name="TriLamp-Back", type=self.secondarytype)
195 backData.energy = backEnergy
197 backLamp = bpy.data.objects.new(name="TriLamp-Back", object_data=backData)
198 collection.objects.link(backLamp)
199 backLamp.location = (backx, backy, self.height)
201 trackToBack = backLamp.constraints.new(type="TRACK_TO")
202 trackToBack.target = obj
203 trackToBack.track_axis = "TRACK_NEGATIVE_Z"
204 trackToBack.up_axis = "UP_Y"
206 # Calc right position
207 singleright_vector = single_vector.copy()
208 singleright_vector.x = cos(radians(self.rightangle)) * single_vector.x + \
209 (-sin(radians(self.rightangle)) * single_vector.y)
211 singleright_vector.y = sin(radians(self.rightangle)) * single_vector.x + \
212 (cos(radians(self.rightangle)) * single_vector.y)
214 rightx = obj_position.x + self.distance * singleright_vector.x
215 righty = obj_position.y + self.distance * singleright_vector.y
217 rightData = bpy.data.lights.new(name="TriLamp-Fill", type=self.secondarytype)
218 rightData.energy = fillEnergy
219 rightLamp = bpy.data.objects.new(name="TriLamp-Fill", object_data=rightData)
220 collection.objects.link(rightLamp)
221 rightLamp.location = (rightx, righty, self.height)
222 trackToRight = rightLamp.constraints.new(type="TRACK_TO")
223 trackToRight.target = obj
224 trackToRight.track_axis = "TRACK_NEGATIVE_Z"
225 trackToRight.up_axis = "UP_Y"
227 # Calc left position
228 singleleft_vector = single_vector.copy()
229 singleleft_vector.x = cos(radians(-self.leftangle)) * single_vector.x + \
230 (-sin(radians(-self.leftangle)) * single_vector.y)
231 singleleft_vector.y = sin(radians(-self.leftangle)) * single_vector.x + \
232 (cos(radians(-self.leftangle)) * single_vector.y)
233 leftx = obj_position.x + self.distance * singleleft_vector.x
234 lefty = obj_position.y + self.distance * singleleft_vector.y
236 leftData = bpy.data.lights.new(name="TriLamp-Key", type=self.primarytype)
237 leftData.energy = keyEnergy
239 leftLamp = bpy.data.objects.new(name="TriLamp-Key", object_data=leftData)
240 collection.objects.link(leftLamp)
241 leftLamp.location = (leftx, lefty, self.height)
242 trackToLeft = leftLamp.constraints.new(type="TRACK_TO")
243 trackToLeft.target = obj
244 trackToLeft.track_axis = "TRACK_NEGATIVE_Z"
245 trackToLeft.up_axis = "UP_Y"
247 except Exception as e:
248 self.report({'WARNING'},
249 "Some operations could not be performed (See Console for more info)")
251 print("\n[Add Advanced Objects]\nOperator: "
252 "object.trilighting\nError: {}".format(e))
254 return {'CANCELLED'}
256 return {'FINISHED'}
258 def menu_func(self, context):
259 self.layout.operator(OBJECT_OT_TriLighting.bl_idname, text="3 Point Lights", icon='LIGHT')
263 # Register all operators and menu
264 def register():
265 bpy.utils.register_class(OBJECT_OT_TriLighting)
266 bpy.types.VIEW3D_MT_light_add.append(menu_func)
268 def unregister():
269 bpy.utils.unregister_class(OBJECT_OT_TriLighting)
270 bpy.types.VIEW3D_MT_light_add.remove(menu_func)
272 if __name__ == "__main__":
273 register()