Skip to content
Snippets Groups Projects
lighting_tri_lights.py 9.54 KiB
Newer Older
  • Learn to ignore specific revisions
  • # SPDX-License-Identifier: GPL-2.0-or-later
    
    # author Daniel Schalla, maintained by meta-androcto
    
    
    bl_info = {
        "name": "Tri-lighting",
        "author": "Daniel Schalla",
        "version": (0, 1, 4),
        "blender": (2, 80, 0),
        "location": "View3D > Add > Lights",
        "description": "Add 3 Point Lighting to Selected / Active Object",
        "warning": "",
        "tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/",
    
        "doc_url": "{BLENDER_MANUAL_URL}/addons/lighting/trilighting.html",
    
        "category": "Lighting",
    }
    
    import bpy
    from bpy.types import Operator
    from bpy.props import (
            EnumProperty,
            FloatProperty,
            IntProperty,
            )
    from math import (
            sin, cos,
            radians,
            sqrt,
            )
    
    
    class OBJECT_OT_TriLighting(Operator):
        bl_idname = "object.trilighting"
        bl_label = "Tri-Lighting Creator"
        bl_description = ("Add 3 Point Lighting to Selected / Active Object\n"
                          "Needs an existing Active Object")
        bl_options = {'REGISTER', 'UNDO'}
        COMPAT_ENGINES = {'CYCLES', 'EEVEE'}
    
        height: FloatProperty(
                name="Height",
                default=5
                )
        distance: FloatProperty(
                name="Distance",
                default=5,
                min=0.1,
                subtype="DISTANCE"
                )
        energy: IntProperty(
                name="Base Energy",
                default=3,
                min=1
                )
        contrast: IntProperty(
                name="Contrast",
                default=50,
                min=-100, max=100,
                subtype="PERCENTAGE"
                )
        leftangle: IntProperty(
                name="Left Angle",
                default=26,
                min=1, max=90,
                subtype="ANGLE"
                )
        rightangle: IntProperty(
                name="Right Angle",
                default=45,
                min=1, max=90,
                subtype="ANGLE"
                )
        backangle: IntProperty(
                name="Back Angle",
                default=235,
                min=90, max=270,
                subtype="ANGLE"
                )
        Light_Type_List = [
                ('POINT', "Point", "Point Light"),
                ('SUN', "Sun", "Sun Light"),
                ('SPOT', "Spot", "Spot Light"),
                ('AREA', "Area", "Area Light")
                ]
        primarytype: EnumProperty(
                attr='tl_type',
                name="Key Type",
                description="Choose the types of Key Lights you would like",
                items=Light_Type_List,
                default='AREA'
                )
        secondarytype: EnumProperty(
                attr='tl_type',
                name="Fill + Back Type",
                description="Choose the types of secondary Lights you would like",
                items=Light_Type_List,
                default="AREA"
                )
    
        @classmethod
        def poll(cls, context):
            return context.active_object is not None
    
        def draw(self, context):
            layout = self.layout
    
            layout.label(text="Position:")
            col = layout.column(align=True)
            col.prop(self, "height")
            col.prop(self, "distance")
    
            layout.label(text="Light:")
            col = layout.column(align=True)
            col.prop(self, "energy")
            col.prop(self, "contrast")
    
            layout.label(text="Orientation:")
            col = layout.column(align=True)
            col.prop(self, "leftangle")
            col.prop(self, "rightangle")
            col.prop(self, "backangle")
    
            col = layout.column()
            col.label(text="Key Light Type:")
            col.prop(self, "primarytype", text="")
            col.label(text="Fill + Back Type:")
            col.prop(self, "secondarytype", text="")
    
    
        def execute(self, context):
            try:
                collection = context.collection
                scene = context.scene
                view = context.space_data
                if view.type == 'VIEW_3D':
                    camera = view.camera
                else:
                    camera = scene.camera
    
                if (camera is None):
                    cam_data = bpy.data.cameras.new(name='Camera')
                    cam_obj = bpy.data.objects.new(name='Camera', object_data=cam_data)
                    collection.objects.link(cam_obj)
                    scene.camera = cam_obj
                    bpy.ops.view3d.camera_to_view()
                    camera = cam_obj
    
                    # Leave camera view again, otherwise redo does not work correctly.
                    bpy.ops.view3d.view_camera()
    
    
                obj = bpy.context.view_layer.objects.active
    
                # Calculate Energy for each Lamp
                if(self.contrast > 0):
                    keyEnergy = self.energy
                    backEnergy = (self.energy / 100) * abs(self.contrast)
                    fillEnergy = (self.energy / 100) * abs(self.contrast)
                else:
                    keyEnergy = (self.energy / 100) * abs(self.contrast)
                    backEnergy = self.energy
                    fillEnergy = self.energy
    
                # Calculate Direction for each Lamp
    
                # Calculate current Distance and get Delta
                obj_position = obj.location
                cam_position = camera.location
    
                delta_position = cam_position - obj_position
                vector_length = sqrt(
                                (pow(delta_position.x, 2) +
                                 pow(delta_position.y, 2) +
                                 pow(delta_position.z, 2))
                                )
                if not vector_length:
                    # division by zero most likely
                    self.report({'WARNING'},
                                "Operation Cancelled. No viable object in the scene")
    
                    return {'CANCELLED'}
    
                single_vector = (1 / vector_length) * delta_position
    
                # Calc back position
                singleback_vector = single_vector.copy()
                singleback_vector.x = cos(radians(self.backangle)) * single_vector.x + \
                                      (-sin(radians(self.backangle)) * single_vector.y)
    
                singleback_vector.y = sin(radians(self.backangle)) * single_vector.x + \
                                     (cos(radians(self.backangle)) * single_vector.y)
    
                backx = obj_position.x + self.distance * singleback_vector.x
                backy = obj_position.y + self.distance * singleback_vector.y
    
                backData = bpy.data.lights.new(name="TriLamp-Back", type=self.secondarytype)
                backData.energy = backEnergy
    
                backLamp = bpy.data.objects.new(name="TriLamp-Back", object_data=backData)
                collection.objects.link(backLamp)
                backLamp.location = (backx, backy, self.height)
    
                trackToBack = backLamp.constraints.new(type="TRACK_TO")
                trackToBack.target = obj
                trackToBack.track_axis = "TRACK_NEGATIVE_Z"
                trackToBack.up_axis = "UP_Y"
    
                # Calc right position
                singleright_vector = single_vector.copy()
                singleright_vector.x = cos(radians(self.rightangle)) * single_vector.x + \
                                      (-sin(radians(self.rightangle)) * single_vector.y)
    
                singleright_vector.y = sin(radians(self.rightangle)) * single_vector.x + \
                                      (cos(radians(self.rightangle)) * single_vector.y)
    
                rightx = obj_position.x + self.distance * singleright_vector.x
                righty = obj_position.y + self.distance * singleright_vector.y
    
                rightData = bpy.data.lights.new(name="TriLamp-Fill", type=self.secondarytype)
                rightData.energy = fillEnergy
                rightLamp = bpy.data.objects.new(name="TriLamp-Fill", object_data=rightData)
                collection.objects.link(rightLamp)
                rightLamp.location = (rightx, righty, self.height)
                trackToRight = rightLamp.constraints.new(type="TRACK_TO")
                trackToRight.target = obj
                trackToRight.track_axis = "TRACK_NEGATIVE_Z"
                trackToRight.up_axis = "UP_Y"
    
                # Calc left position
                singleleft_vector = single_vector.copy()
                singleleft_vector.x = cos(radians(-self.leftangle)) * single_vector.x + \
                                    (-sin(radians(-self.leftangle)) * single_vector.y)
                singleleft_vector.y = sin(radians(-self.leftangle)) * single_vector.x + \
                                    (cos(radians(-self.leftangle)) * single_vector.y)
                leftx = obj_position.x + self.distance * singleleft_vector.x
                lefty = obj_position.y + self.distance * singleleft_vector.y
    
                leftData = bpy.data.lights.new(name="TriLamp-Key", type=self.primarytype)
                leftData.energy = keyEnergy
    
                leftLamp = bpy.data.objects.new(name="TriLamp-Key", object_data=leftData)
                collection.objects.link(leftLamp)
                leftLamp.location = (leftx, lefty, self.height)
                trackToLeft = leftLamp.constraints.new(type="TRACK_TO")
                trackToLeft.target = obj
                trackToLeft.track_axis = "TRACK_NEGATIVE_Z"
                trackToLeft.up_axis = "UP_Y"
    
            except Exception as e:
                self.report({'WARNING'},
                            "Some operations could not be performed (See Console for more info)")
    
                print("\n[Add Advanced  Objects]\nOperator: "
                      "object.trilighting\nError: {}".format(e))
    
                return {'CANCELLED'}
    
            return {'FINISHED'}
    
    def menu_func(self, context):
        self.layout.operator(OBJECT_OT_TriLighting.bl_idname, text="3 Point Lights", icon='LIGHT')
    
    
    
    # Register all operators and menu
    def register():
        bpy.utils.register_class(OBJECT_OT_TriLighting)
        bpy.types.VIEW3D_MT_light_add.append(menu_func)
    
    def unregister():
        bpy.utils.unregister_class(OBJECT_OT_TriLighting)
        bpy.types.VIEW3D_MT_light_add.remove(menu_func)
    
    if __name__ == "__main__":
        register()