1 # SPDX-FileCopyrightText: 2019-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 # author Daniel Schalla, maintained by meta-androcto
8 "name": "Tri-lighting",
9 "author": "Daniel Schalla",
11 "blender": (2, 80, 0),
12 "location": "View3D > Add > Lights",
13 "description": "Add 3 Point Lighting to Selected / Active Object",
15 "tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/",
16 "doc_url": "{BLENDER_MANUAL_URL}/addons/lighting/trilighting.html",
17 "category": "Lighting",
21 from bpy
.types
import Operator
22 from bpy
.props
import (
34 class OBJECT_OT_TriLighting(Operator
):
35 bl_idname
= "object.trilighting"
36 bl_label
= "Tri-Lighting Creator"
37 bl_description
= ("Add 3 Point Lighting to Selected / Active Object\n"
38 "Needs an existing Active Object")
39 bl_options
= {'REGISTER', 'UNDO'}
40 COMPAT_ENGINES
= {'CYCLES', 'EEVEE'}
42 height
: FloatProperty(
46 distance
: FloatProperty(
57 contrast
: IntProperty(
63 leftangle
: IntProperty(
69 rightangle
: IntProperty(
75 backangle
: IntProperty(
82 ('POINT', "Point", "Point Light"),
83 ('SUN', "Sun", "Sun Light"),
84 ('SPOT', "Spot", "Spot Light"),
85 ('AREA', "Area", "Area Light")
87 primarytype
: EnumProperty(
90 description
="Choose the types of Key Lights you would like",
91 items
=Light_Type_List
,
94 secondarytype
: EnumProperty(
96 name
="Fill + Back Type",
97 description
="Choose the types of secondary Lights you would like",
98 items
=Light_Type_List
,
103 def poll(cls
, context
):
104 return context
.active_object
is not None
106 def draw(self
, context
):
109 layout
.label(text
="Position:")
110 col
= layout
.column(align
=True)
111 col
.prop(self
, "height")
112 col
.prop(self
, "distance")
114 layout
.label(text
="Light:")
115 col
= layout
.column(align
=True)
116 col
.prop(self
, "energy")
117 col
.prop(self
, "contrast")
119 layout
.label(text
="Orientation:")
120 col
= layout
.column(align
=True)
121 col
.prop(self
, "leftangle")
122 col
.prop(self
, "rightangle")
123 col
.prop(self
, "backangle")
125 col
= layout
.column()
126 col
.label(text
="Key Light Type:")
127 col
.prop(self
, "primarytype", text
="")
128 col
.label(text
="Fill + Back Type:")
129 col
.prop(self
, "secondarytype", text
="")
132 def execute(self
, context
):
134 collection
= context
.collection
135 scene
= context
.scene
136 view
= context
.space_data
137 if view
.type == 'VIEW_3D':
140 camera
= scene
.camera
143 cam_data
= bpy
.data
.cameras
.new(name
='Camera')
144 cam_obj
= bpy
.data
.objects
.new(name
='Camera', object_data
=cam_data
)
145 collection
.objects
.link(cam_obj
)
146 scene
.camera
= cam_obj
147 bpy
.ops
.view3d
.camera_to_view()
149 # Leave camera view again, otherwise redo does not work correctly.
150 bpy
.ops
.view3d
.view_camera()
152 obj
= bpy
.context
.view_layer
.objects
.active
154 # Calculate Energy for each Lamp
155 if(self
.contrast
> 0):
156 keyEnergy
= self
.energy
157 backEnergy
= (self
.energy
/ 100) * abs(self
.contrast
)
158 fillEnergy
= (self
.energy
/ 100) * abs(self
.contrast
)
160 keyEnergy
= (self
.energy
/ 100) * abs(self
.contrast
)
161 backEnergy
= self
.energy
162 fillEnergy
= self
.energy
164 # Calculate Direction for each Lamp
166 # Calculate current Distance and get Delta
167 obj_position
= obj
.location
168 cam_position
= camera
.location
170 delta_position
= cam_position
- obj_position
171 vector_length
= sqrt(
172 (pow(delta_position
.x
, 2) +
173 pow(delta_position
.y
, 2) +
174 pow(delta_position
.z
, 2))
176 if not vector_length
:
177 # division by zero most likely
178 self
.report({'WARNING'},
179 "Operation Cancelled. No viable object in the scene")
183 single_vector
= (1 / vector_length
) * delta_position
186 singleback_vector
= single_vector
.copy()
187 singleback_vector
.x
= cos(radians(self
.backangle
)) * single_vector
.x
+ \
188 (-sin(radians(self
.backangle
)) * single_vector
.y
)
190 singleback_vector
.y
= sin(radians(self
.backangle
)) * single_vector
.x
+ \
191 (cos(radians(self
.backangle
)) * single_vector
.y
)
193 backx
= obj_position
.x
+ self
.distance
* singleback_vector
.x
194 backy
= obj_position
.y
+ self
.distance
* singleback_vector
.y
196 backData
= bpy
.data
.lights
.new(name
="TriLamp-Back", type=self
.secondarytype
)
197 backData
.energy
= backEnergy
199 backLamp
= bpy
.data
.objects
.new(name
="TriLamp-Back", object_data
=backData
)
200 collection
.objects
.link(backLamp
)
201 backLamp
.location
= (backx
, backy
, self
.height
)
203 trackToBack
= backLamp
.constraints
.new(type="TRACK_TO")
204 trackToBack
.target
= obj
205 trackToBack
.track_axis
= "TRACK_NEGATIVE_Z"
206 trackToBack
.up_axis
= "UP_Y"
208 # Calc right position
209 singleright_vector
= single_vector
.copy()
210 singleright_vector
.x
= cos(radians(self
.rightangle
)) * single_vector
.x
+ \
211 (-sin(radians(self
.rightangle
)) * single_vector
.y
)
213 singleright_vector
.y
= sin(radians(self
.rightangle
)) * single_vector
.x
+ \
214 (cos(radians(self
.rightangle
)) * single_vector
.y
)
216 rightx
= obj_position
.x
+ self
.distance
* singleright_vector
.x
217 righty
= obj_position
.y
+ self
.distance
* singleright_vector
.y
219 rightData
= bpy
.data
.lights
.new(name
="TriLamp-Fill", type=self
.secondarytype
)
220 rightData
.energy
= fillEnergy
221 rightLamp
= bpy
.data
.objects
.new(name
="TriLamp-Fill", object_data
=rightData
)
222 collection
.objects
.link(rightLamp
)
223 rightLamp
.location
= (rightx
, righty
, self
.height
)
224 trackToRight
= rightLamp
.constraints
.new(type="TRACK_TO")
225 trackToRight
.target
= obj
226 trackToRight
.track_axis
= "TRACK_NEGATIVE_Z"
227 trackToRight
.up_axis
= "UP_Y"
230 singleleft_vector
= single_vector
.copy()
231 singleleft_vector
.x
= cos(radians(-self
.leftangle
)) * single_vector
.x
+ \
232 (-sin(radians(-self
.leftangle
)) * single_vector
.y
)
233 singleleft_vector
.y
= sin(radians(-self
.leftangle
)) * single_vector
.x
+ \
234 (cos(radians(-self
.leftangle
)) * single_vector
.y
)
235 leftx
= obj_position
.x
+ self
.distance
* singleleft_vector
.x
236 lefty
= obj_position
.y
+ self
.distance
* singleleft_vector
.y
238 leftData
= bpy
.data
.lights
.new(name
="TriLamp-Key", type=self
.primarytype
)
239 leftData
.energy
= keyEnergy
241 leftLamp
= bpy
.data
.objects
.new(name
="TriLamp-Key", object_data
=leftData
)
242 collection
.objects
.link(leftLamp
)
243 leftLamp
.location
= (leftx
, lefty
, self
.height
)
244 trackToLeft
= leftLamp
.constraints
.new(type="TRACK_TO")
245 trackToLeft
.target
= obj
246 trackToLeft
.track_axis
= "TRACK_NEGATIVE_Z"
247 trackToLeft
.up_axis
= "UP_Y"
249 except Exception as e
:
250 self
.report({'WARNING'},
251 "Some operations could not be performed (See Console for more info)")
253 print("\n[Add Advanced Objects]\nOperator: "
254 "object.trilighting\nError: {}".format(e
))
260 def menu_func(self
, context
):
261 self
.layout
.operator(OBJECT_OT_TriLighting
.bl_idname
, text
="3 Point Lights", icon
='LIGHT')
265 # Register all operators and menu
267 bpy
.utils
.register_class(OBJECT_OT_TriLighting
)
268 bpy
.types
.VIEW3D_MT_light_add
.append(menu_func
)
271 bpy
.utils
.unregister_class(OBJECT_OT_TriLighting
)
272 bpy
.types
.VIEW3D_MT_light_add
.remove(menu_func
)
274 if __name__
== "__main__":