Skip to content
Snippets Groups Projects
camera_overscan.py 7.01 KiB
Newer Older
  • Learn to ignore specific revisions
  • # ##### BEGIN GPL LICENSE BLOCK #####
    #
    #  This program is free software; you can redistribute it and/or
    #  modify it under the terms of the GNU General Public License
    #  as published by the Free Software Foundation; either version 2
    #  of the License, or (at your option) any later version.
    #
    #  This program is distributed in the hope that it will be useful,
    #  but WITHOUT ANY WARRANTY; without even the implied warranty of
    #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    #  GNU General Public License for more details.
    #
    #  You should have received a copy of the GNU General Public License
    #  along with this program; if not, write to the Free Software Foundation,
    #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    #
    # ##### END GPL LICENSE BLOCK #####
    
    bl_info = {
        "name": "Camera Overscan",
        "author": "John Roper, Barnstorm VFX, Luca Scheller",
    
        "version": (1, 2, 2),
    
        "blender": (2, 80, 0),
    
        "location": "Render Settings > Camera Overscan",
        "description": "Render Overscan",
        "warning": "",
    
        "doc_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Render/Camera_Overscan",
    
        "tracker_url": "",
        "category": "Render"}
    
    import bpy
    from bpy.types import (
            Operator,
            PropertyGroup,
            )
    from bpy.props import (
            BoolProperty,
            IntProperty,
            FloatProperty,
            StringProperty,
            PointerProperty,
            )
    
    
    class CODuplicateCamera(Operator):
        bl_idname = "scene.co_duplicate_camera"
        bl_label = "Bake to New Camera"
        bl_description = ("Make a new overscan camera with all the settings builtin\n"
                          "Needs an active Camera type in the Scene")
    
        @classmethod
        def poll(cls, context):
            active_cam = getattr(context.scene, "camera", None)
            return active_cam is not None
    
        def execute(self, context):
            active_cam = getattr(context.scene, "camera", None)
            try:
                if active_cam and active_cam.type == 'CAMERA':
                    cam_obj = active_cam.copy()
                    cam_obj.data = active_cam.data.copy()
                    cam_obj.name = "Camera_Overscan"
    
                    context.collection.objects.link(cam_obj)
    
            except:
                self.report({'WARNING'}, "Setting up a new Overscan Camera has failed")
                return {'CANCELLED'}
    
            return {'FINISHED'}
    
    
    def RO_Update(self, context):
        scene = context.scene
        overscan = scene.camera_overscan
        render_settings = scene.render
        active_camera = getattr(scene, "camera", None)
        active_cam = getattr(active_camera, "data", None)
    
        # Check if there is a camera type in the scene (Object as camera doesn't work)
        if not active_cam or active_camera.type not in {'CAMERA'}:
            return None
    
        if overscan.RO_Activate:
            if overscan.RO_Safe_SensorSize == -1:
                # Safe Property Values
                overscan.RO_Safe_Res_X = render_settings.resolution_x
                overscan.RO_Safe_Res_Y = render_settings.resolution_y
                overscan.RO_Safe_SensorSize = active_cam.sensor_width
                overscan.RO_Safe_SensorFit = active_cam.sensor_fit
    
            if overscan.RO_Custom_Res_X == 0 or overscan.RO_Custom_Res_Y == 0:
                # avoid infinite recursion on props update
                if overscan.RO_Custom_Res_X != render_settings.resolution_x:
                    overscan.RO_Custom_Res_X = render_settings.resolution_x
                if overscan.RO_Custom_Res_Y != render_settings.resolution_y:
                    overscan.RO_Custom_Res_Y = render_settings.resolution_y
    
            # Reset Property Values
            active_cam.sensor_width = scene.camera_overscan.RO_Safe_SensorSize
    
            # Calc Sensor Size
            active_cam.sensor_fit = 'HORIZONTAL'
            sensor_size_factor = overscan.RO_Custom_Res_X / overscan.RO_Safe_Res_X
            Old_SensorSize = active_cam.sensor_width
            New_SensorSize = Old_SensorSize * sensor_size_factor
    
            # Set New Property Values
            active_cam.sensor_width = New_SensorSize
            render_settings.resolution_x = overscan.RO_Custom_Res_X
            render_settings.resolution_y = overscan.RO_Custom_Res_Y
    
        else:
            if overscan.RO_Safe_SensorSize != -1:
                # Set Property Values
                render_settings.resolution_x = overscan.RO_Safe_Res_X
                render_settings.resolution_y = overscan.RO_Safe_Res_Y
                active_cam.sensor_width = overscan.RO_Safe_SensorSize
                active_cam.sensor_fit = overscan.RO_Safe_SensorFit
                overscan.RO_Safe_SensorSize = -1
    
    
    def RO_Menu(self, context):
        scene = context.scene
        overscan = scene.camera_overscan
        active_cam = getattr(scene, "camera", None)
    
        layout = self.layout
    
    
        if active_cam and active_cam.type == 'CAMERA':
    
            col = layout.column(align=True)
            col.prop(overscan, "RO_Activate", text="Use Overscan")
    
            sub = col.column(align=True)
            sub.active = overscan.RO_Activate
            sub.prop(overscan, "RO_Custom_Res_X", text="Overscan X")
            sub.prop(overscan, "RO_Custom_Res_Y", text="Y")
    
            col = layout.column(align=True)
            col.active = overscan.RO_Activate
            col.operator("scene.co_duplicate_camera", icon="RENDER_STILL")
    
            col = layout.column()
            col.label(text="No active Camera type in the Scene", icon='INFO')
    
    
    
    class camera_overscan_props(PropertyGroup):
    
        RO_Activate: BoolProperty(
    
                            default=False,
                            description="Enable/Disable Camera Overscan\n"
                                        "Affects the active Scene Camera only\n"
                                        "(Objects as cameras are not supported)",
                            update=RO_Update
                            )
    
        RO_Custom_Res_X: IntProperty(
    
                            min=4,
    
                            subtype='PIXEL',
    
        RO_Custom_Res_Y: IntProperty(
    
                            min=4,
    
                            subtype='PIXEL',
    
        RO_Safe_Res_X: FloatProperty()
        RO_Safe_Res_Y: FloatProperty()
    
    
        # the hard limit is sys.max which is too much, used 65536 instead
    
        RO_Safe_SensorSize: FloatProperty(
    
                            default=-1,
                            min=-1,
                            max=65536
                            )
    
        RO_Safe_SensorFit: StringProperty()
    
        bpy.utils.register_class(CODuplicateCamera)
        bpy.utils.register_class(camera_overscan_props)
    
        bpy.types.RENDER_PT_format.append(RO_Menu)
    
        bpy.types.Scene.camera_overscan = PointerProperty(
                                            type=camera_overscan_props
                                            )
    
    
    def unregister():
    
        bpy.utils.unregister_class(CODuplicateCamera)
        bpy.utils.unregister_class(camera_overscan_props)
    
        bpy.types.RENDER_PT_format.remove(RO_Menu)
    
        del bpy.types.Scene.camera_overscan
    
    
    if __name__ == "__main__":
        register()