mirror of
				https://code.rhodecode.com/u/OOOOOOOOOOOOOOOO/OOOOOOOOOOOOOOOO/0080-0-8-088-00-080-00-880-8-0-0800
				synced 2025-10-31 17:55:31 +00:00 
			
		
		
		
	
		
			
	
	
		
			308 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
		
		
			
		
	
	
			308 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
|   | # ##### 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": "Render Border", | ||
|  |     "description": "Render Border", | ||
|  |     "author": "Christian Brinkmann, David Boho", | ||
|  |     "version": (0, 0, 5), | ||
|  |     "blender": (2, 80, 0), | ||
|  |     "tracker_url": "https://github.com/p2or/blender-renderborder", | ||
|  |     "location": "Camera > Properties > Data > Render Border", | ||
|  |     "category": "Render" | ||
|  | } | ||
|  | 
 | ||
|  | import bpy | ||
|  | from bpy.app.handlers import persistent | ||
|  | 
 | ||
|  | 
 | ||
|  | def round_pixels(pixel_float): | ||
|  |     return round(pixel_float, 2) | ||
|  | 
 | ||
|  | def calc_normalized(pixels_int, pixel_max): | ||
|  |     return pixels_int / pixel_max if pixel_max else 0.0 | ||
|  | 
 | ||
|  | def calc_pixels(normalized_float, pixel_max): | ||
|  |     return normalized_float * pixel_max | ||
|  | 
 | ||
|  | def calc_width(res_x, min_x, max_x): | ||
|  |     return res_x * max_x - res_x * min_x | ||
|  | 
 | ||
|  | def calc_height(res_y, min_y, max_y): | ||
|  |     return res_y * max_y - res_y * min_y   | ||
|  | 
 | ||
|  | def calc_centerX(res_x, min_x, width): | ||
|  |     return res_x * min_x + width / 2 | ||
|  | 
 | ||
|  | def calc_centerY(res_y, min_y, height): | ||
|  |     return res_y * min_y + height / 2 | ||
|  | 
 | ||
|  | 
 | ||
|  | # ------------------------------------------------------------------------ | ||
|  | #   Properties | ||
|  | # ------------------------------------------------------------------------ | ||
|  | 
 | ||
|  | class RenderBorder(bpy.types.PropertyGroup): | ||
|  |      | ||
|  |     # static member | ||
|  |     _rd = None | ||
|  |     _resX = _resY = _minX = _maxX = _minY = _maxY = 0  | ||
|  |     _width = _height = _centerX = _centerY = 0 | ||
|  |                         | ||
|  |     def set_centerX(self, value): | ||
|  |         diffX = calc_normalized((value - self._centerX), self._resX) | ||
|  |         self._rd.border_min_x += diffX | ||
|  |         self._rd.border_max_x += diffX | ||
|  |         RenderBorder._minX = calc_pixels(self._rd.border_min_x, self._resX) | ||
|  |         RenderBorder._maxX = calc_pixels(self._rd.border_max_x, self._resX) | ||
|  |         RenderBorder._width = calc_width(self._resX, self._rd.border_min_x, self._rd.border_max_x) | ||
|  |         RenderBorder._centerX = value | ||
|  |      | ||
|  |     def set_centerY(self, value): | ||
|  |         diffY = calc_normalized((value - self._centerY), self._resY) | ||
|  |         self._rd.border_min_y += diffY | ||
|  |         self._rd.border_max_y += diffY | ||
|  |         RenderBorder._minY = calc_pixels(self._rd.border_min_y, self._resY) | ||
|  |         RenderBorder._maxY = calc_pixels(self._rd.border_max_y, self._resY) | ||
|  |         RenderBorder._height = calc_height(self._resY, self._rd.border_min_y, self._rd.border_max_y) | ||
|  |         RenderBorder._centerY = value | ||
|  |      | ||
|  |     def set_minX(self, value): | ||
|  |         self._rd.border_min_x = calc_normalized(value, self._resX) | ||
|  |         RenderBorder._minX = round_pixels(calc_pixels(self._rd.border_min_x, self._resX)) | ||
|  |         RenderBorder._width = calc_width(self._resX, self._rd.border_min_x, self._rd.border_max_x) | ||
|  |         RenderBorder._centerX = calc_centerX(self._resX, self._rd.border_min_x, self._width) | ||
|  |      | ||
|  |     def set_maxX(self, value): | ||
|  |         self._rd.border_max_x = calc_normalized(value, self._resX) | ||
|  |         RenderBorder._maxX = round_pixels(calc_pixels(self._rd.border_max_x, self._resX)) | ||
|  |         RenderBorder._width = calc_width(self._resX, self._rd.border_min_x, self._rd.border_max_x) | ||
|  |         RenderBorder._centerX = calc_centerX(self._resX, self._rd.border_min_x, self._width) | ||
|  | 
 | ||
|  |     def set_minY(self, value): | ||
|  |         self._rd.border_min_y = calc_normalized(value, self._resY) | ||
|  |         RenderBorder._minY = round_pixels(calc_pixels(self._rd.border_min_y, self._resY)) | ||
|  |         RenderBorder._height = calc_height(self._resY, self._rd.border_min_y, self._rd.border_max_y) | ||
|  |         RenderBorder._centerY = calc_centerY(self._resY, self._rd.border_min_y, self._height) | ||
|  |          | ||
|  |     def set_maxY(self, value): | ||
|  |         self._rd.border_max_y = calc_normalized(value, self._resY) | ||
|  |         RenderBorder._maxY = round_pixels(calc_pixels(self._rd.border_max_y, self._resY)) | ||
|  |         RenderBorder._height = calc_height(self._resY, self._rd.border_min_y, self._rd.border_max_y) | ||
|  |         RenderBorder._centerY = calc_centerY(self._resY, self._rd.border_min_y, self._height) | ||
|  |      | ||
|  |     def set_useBorder(self, value): | ||
|  |         self._rd.use_border = value | ||
|  |             | ||
|  |     def get_centerX(self): | ||
|  |         return RenderBorder._centerX | ||
|  |      | ||
|  |     def get_centerY(self): | ||
|  |         return RenderBorder._centerY | ||
|  |          | ||
|  |     def get_minX(self): | ||
|  |         return RenderBorder._minX | ||
|  |      | ||
|  |     def get_maxX(self): | ||
|  |         return RenderBorder._maxX | ||
|  |      | ||
|  |     def get_minY(self): | ||
|  |         return RenderBorder._minY | ||
|  |      | ||
|  |     def get_maxY(self): | ||
|  |         return RenderBorder._maxY | ||
|  | 
 | ||
|  |     def get_width(self): | ||
|  |         return abs(round_pixels(RenderBorder._width)) | ||
|  |      | ||
|  |     def get_height(self): | ||
|  |         return abs(round_pixels(RenderBorder._height)) | ||
|  |          | ||
|  |     def get_useBorder(self): | ||
|  |         bpy.ops.rborder.init_border() | ||
|  |         return self._rd.use_border | ||
|  | 
 | ||
|  |     center_x : bpy.props.IntProperty( | ||
|  |         name = "Center X", | ||
|  |         description =   ("Horizontal center of the render border box"), | ||
|  |         min = 0, default = 0, get=get_centerX, set=set_centerX ) | ||
|  |      | ||
|  |     center_y : bpy.props.IntProperty( | ||
|  |         name = "Center Y", | ||
|  |         description =   ("Vertical center of the render border box"), | ||
|  |         min = 0, default = 0, get=get_centerY, set=set_centerY ) | ||
|  | 
 | ||
|  |     width : bpy.props.IntProperty( | ||
|  |         name = "Width", | ||
|  |         description =   ("Width of render border box"), | ||
|  |         min = 0, default = 0, get=get_width) | ||
|  | 
 | ||
|  |     height : bpy.props.IntProperty( | ||
|  |         name = "Height", | ||
|  |         description =   ("Height of render border box"), | ||
|  |         min = 0, default = 0, get=get_height) | ||
|  |                     | ||
|  |     min_x : bpy.props.IntProperty( | ||
|  |         description =   ("Pixel distance between the left edge " | ||
|  |                         "of the camera border and the left " | ||
|  |                         "side of the render border box"), | ||
|  |         name = "Min X", min = 0, default = 0, get=get_minX, set=set_minX ) | ||
|  |      | ||
|  |     max_x : bpy.props.IntProperty( | ||
|  |         description =   ("Pixel distance between the right edge " | ||
|  |                         "of the camera border and the right " | ||
|  |                         "side of the render border box"), | ||
|  |         name = "Max X",min = 0, default = 0, get=get_maxX, set=set_maxX ) | ||
|  |          | ||
|  |     min_y : bpy.props.IntProperty( | ||
|  |         description =   ("Pixel distance between the bottom edge " | ||
|  |                         "of the camera border and the bottom " | ||
|  |                         "edge of the render border box"), | ||
|  |         name = "Min Y", min = 0, default = 0, get=get_minY, set=set_minY ) | ||
|  |     | ||
|  |     max_y : bpy.props.IntProperty( | ||
|  |         description =   ("Pixel distance between the top edge " | ||
|  |                         "of the camera border and the top " | ||
|  |                         "edge of the render border box"), | ||
|  |         name = "Max Y", min = 0, default = 0, get=get_maxY, set=set_maxY ) | ||
|  |      | ||
|  |     use_rborder : bpy.props.BoolProperty( | ||
|  |         name = "Use render border", description = "Use render border",  | ||
|  |         get=get_useBorder, set=set_useBorder) | ||
|  | 
 | ||
|  | 
 | ||
|  | # ------------------------------------------------------------------------ | ||
|  | # Operators | ||
|  | # ------------------------------------------------------------------------ | ||
|  | 
 | ||
|  | class RBORDER_OT_init_border(bpy.types.Operator): | ||
|  |     bl_idname = "rborder.init_border" | ||
|  |     bl_label = "Init Render Border" | ||
|  |     bl_options = {'INTERNAL'} | ||
|  |    | ||
|  |     def execute(self, context): | ||
|  |         scn = context.scene | ||
|  |         RenderBorder._rd = scn.render | ||
|  |         RenderBorder._resX = scn.render.resolution_x | ||
|  |         RenderBorder._resY = scn.render.resolution_y | ||
|  |          | ||
|  |         rbx = scn.renderborder | ||
|  |         rbx.min_x = round_pixels(calc_pixels(scn.render.border_min_x, scn.render.resolution_x)) | ||
|  |         rbx.min_y = round_pixels(calc_pixels(scn.render.border_min_y, scn.render.resolution_y)) | ||
|  |         rbx.max_x = round_pixels(calc_pixels(scn.render.border_max_x, scn.render.resolution_x)) | ||
|  |         rbx.max_y = round_pixels(calc_pixels(scn.render.border_max_y, scn.render.resolution_y)) | ||
|  |         return {'FINISHED'} | ||
|  | 
 | ||
|  | 
 | ||
|  | class RBORDER_OT_reset_border(bpy.types.Operator): | ||
|  |     bl_idname = "rborder.reset_border" | ||
|  |     bl_label = "Reset Render Border" | ||
|  |     bl_description = "Fit render border to the current camera resolution" | ||
|  |     bl_options = {'REGISTER', 'UNDO'} | ||
|  |      | ||
|  |     def execute(self, context): | ||
|  |         scn = context.scene | ||
|  |         rbx = scn.renderborder | ||
|  |         rbx.min_x = 0 | ||
|  |         rbx.min_y = 0 | ||
|  |         rbx.max_x = scn.render.resolution_x | ||
|  |         rbx.max_y = scn.render.resolution_y | ||
|  |         self.report({'INFO'}, "Render Border adapted") | ||
|  |         return {'FINISHED'} | ||
|  | 
 | ||
|  | 
 | ||
|  | # ------------------------------------------------------------------------ | ||
|  | #   Panel | ||
|  | # ------------------------------------------------------------------------ | ||
|  | 
 | ||
|  | class RBORDER_PT_camera(bpy.types.Panel): | ||
|  |     bl_label = "Render Border" | ||
|  |     bl_space_type = 'PROPERTIES' | ||
|  |     bl_region_type = 'WINDOW' | ||
|  |     bl_context = "data" | ||
|  |      | ||
|  |     @classmethod | ||
|  |     def poll(cls, context): | ||
|  |         return context.active_object.type == "CAMERA" | ||
|  |      | ||
|  |     def draw_header(self, context): | ||
|  |         scn = context.scene | ||
|  |         rbx = scn.renderborder | ||
|  |         self.layout.prop(rbx, "use_rborder", text="") | ||
|  |      | ||
|  |     def draw(self, context): | ||
|  |         scn = context.scene | ||
|  |         rbx = scn.renderborder | ||
|  |         layout = self.layout | ||
|  |        | ||
|  |         row = layout.row() | ||
|  |         col = row.column(align=True) | ||
|  |         rowsub = col.row(align=True) | ||
|  |         rowsub.prop(rbx, "min_x", text="X") | ||
|  |         rowsub.prop(rbx, "max_x", text="R") | ||
|  |         rowsub = col.row(align=True) | ||
|  |         rowsub.prop(rbx, "min_y", text="Y") | ||
|  |         rowsub.prop(rbx, "max_y", text="T") | ||
|  |         col.prop(rbx, "center_x") | ||
|  |         col.prop(rbx, "center_y") | ||
|  |         col.operator("rborder.reset_border", text="Reset Render Border", icon='FILE_REFRESH') | ||
|  |         row = layout.row() | ||
|  |         col = layout.column(align=True) | ||
|  |         rowsub = col.row(align=True) | ||
|  |         rowsub = row.split(factor=0.3, align=True) | ||
|  |         rowsub.prop(scn.render, "use_crop_to_border", text="Crop Image") | ||
|  |         rowsub.alignment = 'RIGHT' | ||
|  |         rowsub.label(text="Width: {}px Height: {}px".format(rbx.width, rbx.height)) | ||
|  | 
 | ||
|  |          | ||
|  | # ------------------------------------------------------------------------ | ||
|  | #   Registration | ||
|  | # ------------------------------------------------------------------------ | ||
|  | 
 | ||
|  | @persistent | ||
|  | def init_renderborder_member(dummy): | ||
|  |     bpy.ops.rborder.init_border() | ||
|  | 
 | ||
|  | 
 | ||
|  | classes = ( | ||
|  |     RenderBorder, | ||
|  |     RBORDER_OT_init_border, | ||
|  |     RBORDER_OT_reset_border, | ||
|  |     RBORDER_PT_camera | ||
|  | ) | ||
|  | 
 | ||
|  | def register(): | ||
|  |     from bpy.utils import register_class | ||
|  |     for cls in classes: | ||
|  |         register_class(cls) | ||
|  |      | ||
|  |     bpy.types.Scene.renderborder = bpy.props.PointerProperty(type=RenderBorder) | ||
|  |     bpy.app.handlers.load_post.append(init_renderborder_member) | ||
|  | 
 | ||
|  | def unregister(): | ||
|  |     from bpy.utils import unregister_class | ||
|  |     for cls in reversed(classes): | ||
|  |         unregister_class(cls) | ||
|  |      | ||
|  |     bpy.app.handlers.load_post.remove(init_renderborder_member) | ||
|  |     del bpy.types.Scene.renderborder | ||
|  | 
 | ||
|  | 
 | ||
|  | if __name__ == "__main__": | ||
|  |     register() |