Import_3ds: Improved distance cue node setup
[blender-addons.git] / lighting_tri_lights.py
blobf7a03c678b7362c8c411a9cfbf177627fb8e119a
1 # SPDX-FileCopyrightText: 2019-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 # author Daniel Schalla, maintained by meta-androcto
7 bl_info = {
8 "name": "Tri-lighting",
9 "author": "Daniel Schalla",
10 "version": (0, 1, 4),
11 "blender": (2, 80, 0),
12 "location": "View3D > Add > Lights",
13 "description": "Add 3 Point Lighting to Selected / Active Object",
14 "warning": "",
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",
20 import bpy
21 from bpy.types import Operator
22 from bpy.props import (
23 EnumProperty,
24 FloatProperty,
25 IntProperty,
27 from math import (
28 sin, cos,
29 radians,
30 sqrt,
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(
43 name="Height",
44 default=5
46 distance: FloatProperty(
47 name="Distance",
48 default=5,
49 min=0.1,
50 subtype="DISTANCE"
52 energy: IntProperty(
53 name="Base Energy",
54 default=3,
55 min=1
57 contrast: IntProperty(
58 name="Contrast",
59 default=50,
60 min=-100, max=100,
61 subtype="PERCENTAGE"
63 leftangle: IntProperty(
64 name="Left Angle",
65 default=26,
66 min=1, max=90,
67 subtype="ANGLE"
69 rightangle: IntProperty(
70 name="Right Angle",
71 default=45,
72 min=1, max=90,
73 subtype="ANGLE"
75 backangle: IntProperty(
76 name="Back Angle",
77 default=235,
78 min=90, max=270,
79 subtype="ANGLE"
81 Light_Type_List = [
82 ('POINT', "Point", "Point Light"),
83 ('SUN', "Sun", "Sun Light"),
84 ('SPOT', "Spot", "Spot Light"),
85 ('AREA', "Area", "Area Light")
87 primarytype: EnumProperty(
88 attr='tl_type',
89 name="Key Type",
90 description="Choose the types of Key Lights you would like",
91 items=Light_Type_List,
92 default='AREA'
94 secondarytype: EnumProperty(
95 attr='tl_type',
96 name="Fill + Back Type",
97 description="Choose the types of secondary Lights you would like",
98 items=Light_Type_List,
99 default="AREA"
102 @classmethod
103 def poll(cls, context):
104 return context.active_object is not None
106 def draw(self, context):
107 layout = self.layout
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):
133 try:
134 collection = context.collection
135 scene = context.scene
136 view = context.space_data
137 if view.type == 'VIEW_3D':
138 camera = view.camera
139 else:
140 camera = scene.camera
142 if (camera is None):
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()
148 camera = cam_obj
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)
159 else:
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")
181 return {'CANCELLED'}
183 single_vector = (1 / vector_length) * delta_position
185 # Calc back 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"
229 # Calc left position
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))
256 return {'CANCELLED'}
258 return {'FINISHED'}
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
266 def register():
267 bpy.utils.register_class(OBJECT_OT_TriLighting)
268 bpy.types.VIEW3D_MT_light_add.append(menu_func)
270 def unregister():
271 bpy.utils.unregister_class(OBJECT_OT_TriLighting)
272 bpy.types.VIEW3D_MT_light_add.remove(menu_func)
274 if __name__ == "__main__":
275 register()