1 # SPDX-License-Identifier: GPL-2.0-or-later
3 # author Daniel Schalla, maintained by meta-androcto
6 "name": "Tri-lighting",
7 "author": "Daniel Schalla",
10 "location": "View3D > Add > Lights",
11 "description": "Add 3 Point Lighting to Selected / Active Object",
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",
19 from bpy
.types
import Operator
20 from bpy
.props
import (
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(
44 distance
: FloatProperty(
55 contrast
: IntProperty(
61 leftangle
: IntProperty(
67 rightangle
: IntProperty(
73 backangle
: IntProperty(
80 ('POINT', "Point", "Point Light"),
81 ('SUN', "Sun", "Sun Light"),
82 ('SPOT', "Spot", "Spot Light"),
83 ('AREA', "Area", "Area Light")
85 primarytype
: EnumProperty(
88 description
="Choose the types of Key Lights you would like",
89 items
=Light_Type_List
,
92 secondarytype
: EnumProperty(
94 name
="Fill + Back Type",
95 description
="Choose the types of secondary Lights you would like",
96 items
=Light_Type_List
,
101 def poll(cls
, context
):
102 return context
.active_object
is not None
104 def draw(self
, context
):
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
):
132 collection
= context
.collection
133 scene
= context
.scene
134 view
= context
.space_data
135 if view
.type == 'VIEW_3D':
138 camera
= scene
.camera
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()
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
)
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")
181 single_vector
= (1 / vector_length
) * delta_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"
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
))
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
265 bpy
.utils
.register_class(OBJECT_OT_TriLighting
)
266 bpy
.types
.VIEW3D_MT_light_add
.append(menu_func
)
269 bpy
.utils
.unregister_class(OBJECT_OT_TriLighting
)
270 bpy
.types
.VIEW3D_MT_light_add
.remove(menu_func
)
272 if __name__
== "__main__":