1 # gpl: author Daniel Schalla, maintained by meta-androcto
4 "name": "Tri-lighting",
5 "author": "Daniel Schalla",
8 "location": "View3D > Add > Lights",
9 "description": "Add 3 Point Lighting to Selected / Active Object",
11 "tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/",
12 "doc_url": "{BLENDER_MANUAL_URL}/addons/lighting/trilighting.html",
13 "category": "Lighting",
17 from bpy
.types
import Operator
18 from bpy
.props
import (
30 class OBJECT_OT_TriLighting(Operator
):
31 bl_idname
= "object.trilighting"
32 bl_label
= "Tri-Lighting Creator"
33 bl_description
= ("Add 3 Point Lighting to Selected / Active Object\n"
34 "Needs an existing Active Object")
35 bl_options
= {'REGISTER', 'UNDO'}
36 COMPAT_ENGINES
= {'CYCLES', 'EEVEE'}
38 height
: FloatProperty(
42 distance
: FloatProperty(
53 contrast
: IntProperty(
59 leftangle
: IntProperty(
65 rightangle
: IntProperty(
71 backangle
: IntProperty(
78 ('POINT', "Point", "Point Light"),
79 ('SUN', "Sun", "Sun Light"),
80 ('SPOT', "Spot", "Spot Light"),
81 ('AREA', "Area", "Area Light")
83 primarytype
: EnumProperty(
86 description
="Choose the types of Key Lights you would like",
87 items
=Light_Type_List
,
90 secondarytype
: EnumProperty(
92 name
="Fill + Back Type",
93 description
="Choose the types of secondary Lights you would like",
94 items
=Light_Type_List
,
99 def poll(cls
, context
):
100 return context
.active_object
is not None
102 def draw(self
, context
):
105 layout
.label(text
="Position:")
106 col
= layout
.column(align
=True)
107 col
.prop(self
, "height")
108 col
.prop(self
, "distance")
110 layout
.label(text
="Light:")
111 col
= layout
.column(align
=True)
112 col
.prop(self
, "energy")
113 col
.prop(self
, "contrast")
115 layout
.label(text
="Orientation:")
116 col
= layout
.column(align
=True)
117 col
.prop(self
, "leftangle")
118 col
.prop(self
, "rightangle")
119 col
.prop(self
, "backangle")
121 col
= layout
.column()
122 col
.label(text
="Key Light Type:")
123 col
.prop(self
, "primarytype", text
="")
124 col
.label(text
="Fill + Back Type:")
125 col
.prop(self
, "secondarytype", text
="")
128 def execute(self
, context
):
130 collection
= context
.collection
131 scene
= context
.scene
132 view
= context
.space_data
133 if view
.type == 'VIEW_3D':
136 camera
= scene
.camera
139 cam_data
= bpy
.data
.cameras
.new(name
='Camera')
140 cam_obj
= bpy
.data
.objects
.new(name
='Camera', object_data
=cam_data
)
141 collection
.objects
.link(cam_obj
)
142 scene
.camera
= cam_obj
143 bpy
.ops
.view3d
.camera_to_view()
145 # Leave camera view again, otherwise redo does not work correctly.
146 bpy
.ops
.view3d
.view_camera()
148 obj
= bpy
.context
.view_layer
.objects
.active
150 # Calculate Energy for each Lamp
151 if(self
.contrast
> 0):
152 keyEnergy
= self
.energy
153 backEnergy
= (self
.energy
/ 100) * abs(self
.contrast
)
154 fillEnergy
= (self
.energy
/ 100) * abs(self
.contrast
)
156 keyEnergy
= (self
.energy
/ 100) * abs(self
.contrast
)
157 backEnergy
= self
.energy
158 fillEnergy
= self
.energy
160 # Calculate Direction for each Lamp
162 # Calculate current Distance and get Delta
163 obj_position
= obj
.location
164 cam_position
= camera
.location
166 delta_position
= cam_position
- obj_position
167 vector_length
= sqrt(
168 (pow(delta_position
.x
, 2) +
169 pow(delta_position
.y
, 2) +
170 pow(delta_position
.z
, 2))
172 if not vector_length
:
173 # division by zero most likely
174 self
.report({'WARNING'},
175 "Operation Cancelled. No viable object in the scene")
179 single_vector
= (1 / vector_length
) * delta_position
182 singleback_vector
= single_vector
.copy()
183 singleback_vector
.x
= cos(radians(self
.backangle
)) * single_vector
.x
+ \
184 (-sin(radians(self
.backangle
)) * single_vector
.y
)
186 singleback_vector
.y
= sin(radians(self
.backangle
)) * single_vector
.x
+ \
187 (cos(radians(self
.backangle
)) * single_vector
.y
)
189 backx
= obj_position
.x
+ self
.distance
* singleback_vector
.x
190 backy
= obj_position
.y
+ self
.distance
* singleback_vector
.y
192 backData
= bpy
.data
.lights
.new(name
="TriLamp-Back", type=self
.secondarytype
)
193 backData
.energy
= backEnergy
195 backLamp
= bpy
.data
.objects
.new(name
="TriLamp-Back", object_data
=backData
)
196 collection
.objects
.link(backLamp
)
197 backLamp
.location
= (backx
, backy
, self
.height
)
199 trackToBack
= backLamp
.constraints
.new(type="TRACK_TO")
200 trackToBack
.target
= obj
201 trackToBack
.track_axis
= "TRACK_NEGATIVE_Z"
202 trackToBack
.up_axis
= "UP_Y"
204 # Calc right position
205 singleright_vector
= single_vector
.copy()
206 singleright_vector
.x
= cos(radians(self
.rightangle
)) * single_vector
.x
+ \
207 (-sin(radians(self
.rightangle
)) * single_vector
.y
)
209 singleright_vector
.y
= sin(radians(self
.rightangle
)) * single_vector
.x
+ \
210 (cos(radians(self
.rightangle
)) * single_vector
.y
)
212 rightx
= obj_position
.x
+ self
.distance
* singleright_vector
.x
213 righty
= obj_position
.y
+ self
.distance
* singleright_vector
.y
215 rightData
= bpy
.data
.lights
.new(name
="TriLamp-Fill", type=self
.secondarytype
)
216 rightData
.energy
= fillEnergy
217 rightLamp
= bpy
.data
.objects
.new(name
="TriLamp-Fill", object_data
=rightData
)
218 collection
.objects
.link(rightLamp
)
219 rightLamp
.location
= (rightx
, righty
, self
.height
)
220 trackToRight
= rightLamp
.constraints
.new(type="TRACK_TO")
221 trackToRight
.target
= obj
222 trackToRight
.track_axis
= "TRACK_NEGATIVE_Z"
223 trackToRight
.up_axis
= "UP_Y"
226 singleleft_vector
= single_vector
.copy()
227 singleleft_vector
.x
= cos(radians(-self
.leftangle
)) * single_vector
.x
+ \
228 (-sin(radians(-self
.leftangle
)) * single_vector
.y
)
229 singleleft_vector
.y
= sin(radians(-self
.leftangle
)) * single_vector
.x
+ \
230 (cos(radians(-self
.leftangle
)) * single_vector
.y
)
231 leftx
= obj_position
.x
+ self
.distance
* singleleft_vector
.x
232 lefty
= obj_position
.y
+ self
.distance
* singleleft_vector
.y
234 leftData
= bpy
.data
.lights
.new(name
="TriLamp-Key", type=self
.primarytype
)
235 leftData
.energy
= keyEnergy
237 leftLamp
= bpy
.data
.objects
.new(name
="TriLamp-Key", object_data
=leftData
)
238 collection
.objects
.link(leftLamp
)
239 leftLamp
.location
= (leftx
, lefty
, self
.height
)
240 trackToLeft
= leftLamp
.constraints
.new(type="TRACK_TO")
241 trackToLeft
.target
= obj
242 trackToLeft
.track_axis
= "TRACK_NEGATIVE_Z"
243 trackToLeft
.up_axis
= "UP_Y"
245 except Exception as e
:
246 self
.report({'WARNING'},
247 "Some operations could not be performed (See Console for more info)")
249 print("\n[Add Advanced Objects]\nOperator: "
250 "object.trilighting\nError: {}".format(e
))
256 def menu_func(self
, context
):
257 self
.layout
.operator(OBJECT_OT_TriLighting
.bl_idname
, text
="3 Point Lights", icon
='LIGHT')
261 # Register all operators and menu
263 bpy
.utils
.register_class(OBJECT_OT_TriLighting
)
264 bpy
.types
.VIEW3D_MT_light_add
.append(menu_func
)
267 bpy
.utils
.unregister_class(OBJECT_OT_TriLighting
)
268 bpy
.types
.VIEW3D_MT_light_add
.remove(menu_func
)
270 if __name__
== "__main__":