ab4df77e963568b19acd324d6de767c580f3ce65
[blender-addons-contrib.git] / camera_overscan.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 bl_info = {
20     "name": "Camera Overscan",
21     "author": "John Roper, Barnstorm VFX, Luca Scheller",
22     "version": (1, 2, 1),
23     "blender": (2, 76, 0),
24     "location": "Render Settings > Camera Overscan",
25     "description": "Render Overscan",
26     "warning": "",
27     "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Render/Camera_Overscan",
28     "tracker_url": "",
29     "category": "Render"}
30
31 import bpy
32 from bpy.types import (
33         Operator,
34         PropertyGroup,
35         )
36 from bpy.props import (
37         BoolProperty,
38         IntProperty,
39         FloatProperty,
40         StringProperty,
41         PointerProperty,
42         )
43
44
45 class CODuplicateCamera(Operator):
46     bl_idname = "scene.co_duplicate_camera"
47     bl_label = "Bake to New Camera"
48     bl_description = ("Make a new overscan camera with all the settings builtin\n"
49                       "Needs an active Camera type in the Scene")
50
51     @classmethod
52     def poll(cls, context):
53         active_cam = getattr(context.scene, "camera", None)
54         return active_cam is not None
55
56     def execute(self, context):
57         active_cam = getattr(context.scene, "camera", None)
58         try:
59             if active_cam and active_cam.type == 'CAMERA':
60                 cam_obj = active_cam.copy()
61                 cam_obj.data = active_cam.data.copy()
62                 cam_obj.name = "Camera_Overscan"
63                 context.scene.objects.link(cam_obj)
64         except:
65             self.report({'WARNING'}, "Setting up a new Overscan Camera has failed")
66             return {'CANCELLED'}
67
68         return {'FINISHED'}
69
70
71 def RO_Update(self, context):
72     scene = context.scene
73     overscan = scene.camera_overscan
74     render_settings = scene.render
75     active_camera = getattr(scene, "camera", None)
76     active_cam = getattr(active_camera, "data", None)
77
78     # Check if there is a camera type in the scene (Object as camera doesn't work)
79     if not active_cam or active_camera.type not in {'CAMERA'}:
80         return None
81
82     if overscan.RO_Activate:
83         if overscan.RO_Safe_SensorSize == -1:
84             # Safe Property Values
85             overscan.RO_Safe_Res_X = render_settings.resolution_x
86             overscan.RO_Safe_Res_Y = render_settings.resolution_y
87             overscan.RO_Safe_SensorSize = active_cam.sensor_width
88             overscan.RO_Safe_SensorFit = active_cam.sensor_fit
89
90         if overscan.RO_Custom_Res_X == 0 or overscan.RO_Custom_Res_Y == 0:
91             # avoid infinite recursion on props update
92             if overscan.RO_Custom_Res_X != render_settings.resolution_x:
93                 overscan.RO_Custom_Res_X = render_settings.resolution_x
94             if overscan.RO_Custom_Res_Y != render_settings.resolution_y:
95                 overscan.RO_Custom_Res_Y = render_settings.resolution_y
96
97         # Reset Property Values
98         active_cam.sensor_width = scene.camera_overscan.RO_Safe_SensorSize
99
100         # Calc Sensor Size
101         active_cam.sensor_fit = 'HORIZONTAL'
102         sensor_size_factor = overscan.RO_Custom_Res_X / overscan.RO_Safe_Res_X
103         Old_SensorSize = active_cam.sensor_width
104         New_SensorSize = Old_SensorSize * sensor_size_factor
105
106         # Set New Property Values
107         active_cam.sensor_width = New_SensorSize
108         render_settings.resolution_x = overscan.RO_Custom_Res_X
109         render_settings.resolution_y = overscan.RO_Custom_Res_Y
110
111     else:
112         if overscan.RO_Safe_SensorSize != -1:
113             # Set Property Values
114             render_settings.resolution_x = overscan.RO_Safe_Res_X
115             render_settings.resolution_y = overscan.RO_Safe_Res_Y
116             active_cam.sensor_width = overscan.RO_Safe_SensorSize
117             active_cam.sensor_fit = overscan.RO_Safe_SensorFit
118             overscan.RO_Safe_SensorSize = -1
119
120
121 def RO_Menu(self, context):
122     scene = context.scene
123     overscan = scene.camera_overscan
124     layout = self.layout
125     row = layout.row()
126     active_cam = getattr(scene, "camera", None)
127
128     if active_cam and active_cam.type == 'CAMERA':
129         row.prop(overscan, 'RO_Activate', text="Use Overscan")
130         row_enable = row.row(align=True)
131         if not overscan.RO_Activate:
132             row_enable.enabled = False
133         row_enable.prop(overscan, 'RO_Custom_Res_X', text="X")
134         row_enable.prop(overscan, 'RO_Custom_Res_Y', text="Y")
135         row = layout.row()
136         if not overscan.RO_Activate:
137             row.enabled = False
138         row.operator("scene.co_duplicate_camera", icon="RENDER_STILL")
139     else:
140         row.label(text="No active Camera type in the Scene", icon='INFO')
141
142
143 class camera_overscan_props(PropertyGroup):
144     RO_Activate: BoolProperty(
145                         default=False,
146                         description="Enable/Disable Camera Overscan\n"
147                                     "Affects the active Scene Camera only\n"
148                                     "(Objects as cameras are not supported)",
149                         update=RO_Update
150                         )
151     RO_Custom_Res_X: IntProperty(
152                         default=0,
153                         min=0,
154                         max=65536,
155                         update=RO_Update
156                         )
157     RO_Custom_Res_Y: IntProperty(
158                         default=0,
159                         min=0,
160                         max=65536,
161                         update=RO_Update
162                         )
163     RO_Safe_Res_X: FloatProperty()
164     RO_Safe_Res_Y: FloatProperty()
165
166     # the hard limit is sys.max which is too much, used 65536 instead
167     RO_Safe_SensorSize: FloatProperty(
168                         default=-1,
169                         min=-1,
170                         max=65536
171                         )
172     RO_Safe_SensorFit: StringProperty()
173
174
175 def register():
176     bpy.utils.register_module(__name__)
177     bpy.types.RENDER_PT_dimensions.append(RO_Menu)
178     bpy.types.Scene.camera_overscan = PointerProperty(
179                                         type=camera_overscan_props
180                                         )
181
182
183 def unregister():
184     bpy.utils.unregister_module(__name__)
185     bpy.types.RENDER_PT_dimensions.remove(RO_Menu)
186     del bpy.types.Scene.camera_overscan
187
188
189 if __name__ == "__main__":
190     register()