193 lines
6.9 KiB
Python
193 lines
6.9 KiB
Python
# ##### 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, 1),
|
|
"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
|
|
layout = self.layout
|
|
row = layout.row()
|
|
active_cam = getattr(scene, "camera", None)
|
|
|
|
if active_cam and active_cam.type == 'CAMERA':
|
|
row.prop(overscan, 'RO_Activate', text="Use Overscan")
|
|
row_enable = row.row(align=True)
|
|
if not overscan.RO_Activate:
|
|
row_enable.enabled = False
|
|
row_enable.prop(overscan, 'RO_Custom_Res_X', text="X")
|
|
row_enable.prop(overscan, 'RO_Custom_Res_Y', text="Y")
|
|
row = layout.row()
|
|
if not overscan.RO_Activate:
|
|
row.enabled = False
|
|
row.operator("scene.co_duplicate_camera", icon="RENDER_STILL")
|
|
else:
|
|
row.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(
|
|
default=0,
|
|
min=0,
|
|
max=65536,
|
|
update=RO_Update
|
|
)
|
|
RO_Custom_Res_Y: IntProperty(
|
|
default=0,
|
|
min=0,
|
|
max=65536,
|
|
update=RO_Update
|
|
)
|
|
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()
|
|
|
|
|
|
def register():
|
|
bpy.utils.register_class(CODuplicateCamera)
|
|
bpy.utils.register_class(camera_overscan_props)
|
|
bpy.types.RENDER_PT_dimensions.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_dimensions.remove(RO_Menu)
|
|
del bpy.types.Scene.camera_overscan
|
|
|
|
|
|
if __name__ == "__main__":
|
|
register()
|