1 # Any copyright is dedicated to the Public Domain. (blender_graphics_cropper.py)
2 # https://creativecommons.org/publicdomain/zero/1.0/
4 import bpy
, math
, gpu
, blf
5 from mathutils
import *
6 from gpu_extras
.batch
import batch_for_shader
9 "name":"Graphics Cropper",
10 "author": "Harry Godden (hgn)",
21 # ------------------------------------------------------------------------------
23 cropper_view_draw_handler
= None
24 cropper_ui_draw_handler
= None
25 cropper_view_shader
= gpu
.shader
.from_builtin('3D_SMOOTH_COLOR')
27 def cropper_draw_ui():
28 mtx
= bpy
.context
.region_data
.perspective_matrix
29 w
= bpy
.context
.region
.width
30 h
= bpy
.context
.region
.height
32 for obj
in bpy
.context
.scene
.objects
:
33 if obj
.cropper_data
.enabled
and obj
.visible_get():
34 x
= obj
.cropper_data
.resolution
[0]
35 y
= obj
.cropper_data
.resolution
[1]
36 c
= Vector((1,1,0,1)) if obj
.select_get() else Vector((0.6,0.4,0,1))
37 p0
= obj
.matrix_world
@ Vector((0,0,0))
38 p1
= obj
.matrix_world
@ Vector((x
,0,0))
39 p2
= obj
.matrix_world
@ Vector((x
,y
,0))
40 p3
= obj
.matrix_world
@ Vector((0,y
,0))
42 co
= mtx
@ Vector((p3
[0],p3
[1],p3
[2],1.0))
53 blf
.position(0,co
[0],co
[1]+8,0)
55 blf
.color(0,c
[0],c
[1],c
[2],c
[3])
56 blf
.draw(0,obj
.cropper_data
.filename
)
59 global cropper_view_shader
64 for obj
in bpy
.context
.scene
.objects
:
65 if obj
.cropper_data
.enabled
and obj
.visible_get():
66 x
= obj
.cropper_data
.resolution
[0]
67 y
= obj
.cropper_data
.resolution
[1]
68 c
= Vector((1,1,0,1)) if obj
.select_get() else Vector((0.6,0.4,0,1))
69 p0
= obj
.matrix_world
@ Vector((0,0,0))
70 p1
= obj
.matrix_world
@ Vector((x
,0,0))
71 p2
= obj
.matrix_world
@ Vector((x
,y
,0))
72 p3
= obj
.matrix_world
@ Vector((0,y
,0))
74 verts
+= [p0
,p1
,p1
,p2
,p2
,p3
,p3
,p0
]
75 colours
+= [c
,c
,c
,c
,c
,c
,c
,c
]
77 cropper_view_shader
.bind()
78 gpu
.state
.depth_mask_set(False)
79 gpu
.state
.line_width_set(2.0)
80 gpu
.state
.face_culling_set('BACK')
81 gpu
.state
.depth_test_set('NONE')
82 gpu
.state
.blend_set('NONE')
83 lines
= batch_for_shader( cropper_view_shader
, 'LINES', \
84 { "pos":verts
, "color":colours
})
85 lines
.draw( cropper_view_shader
)
88 # ------------------------------------------------------------------------------
90 def cropper_render_item(obj
,context
):
91 cam
= context
.scene
.camera
92 original_pos_x
= cam
.location
[0]
93 original_pos_y
= cam
.location
[1]
94 original_res_x
= context
.scene
.render
.resolution_x
95 original_res_y
= context
.scene
.render
.resolution_y
96 original_file
= context
.scene
.render
.filepath
97 original_scale
= cam
.data
.ortho_scale
99 x
= obj
.cropper_data
.resolution
[0]
100 y
= obj
.cropper_data
.resolution
[1]
101 fmt
= context
.scene
.render
.image_settings
.file_format
.lower()
102 fn
= F
"{obj.cropper_data.filename}.{fmt}"
104 p0
= obj
.matrix_world
@ Vector((0,0,0))
105 p1
= obj
.matrix_world
@ Vector((x
,0,0))
106 p2
= obj
.matrix_world
@ Vector((x
,y
,0))
107 p3
= obj
.matrix_world
@ Vector((0,y
,0))
108 c
= obj
.matrix_world
@ Vector((x
*0.5,y
*0.5,0))
110 if x
> y
: s
= p2
[0]-p0
[0]
111 else: s
= p2
[1]-p0
[1]
113 cam
.location
[0] = c
[0]
114 cam
.location
[1] = c
[1]
116 cam
.data
.ortho_scale
= s
117 context
.scene
.render
.resolution_x
= x
118 context
.scene
.render
.resolution_y
= y
119 context
.scene
.render
.filepath
= original_file
+ fn
120 print( F
"render to: {context.scene.render.filepath}" )
121 bpy
.ops
.render
.render(write_still
=True)
124 context
.scene
.render
.resolution_x
= original_res_x
125 context
.scene
.render
.resolution_y
= original_res_y
126 context
.scene
.render
.filepath
= original_file
128 cam
.data
.ortho_scale
= original_scale
129 cam
.location
[0] = original_pos_x
130 cam
.location
[1] = original_pos_y
132 class CROPPER_RENDER_ALL(bpy
.types
.Operator
):
133 bl_idname
="cropper.render_all"
134 bl_label
="Render all"
135 def execute(_
,context
):
136 for obj
in context
.scene
.objects
:
137 if obj
.cropper_data
.enabled
and obj
.visible_get():
138 cropper_render_item(obj
,context
)
142 class CROPPER_RENDER(bpy
.types
.Operator
):
143 bl_idname
="cropper.render"
145 def execute(_
,context
):
146 obj
= context
.active_object
147 if obj
!= None and obj
.cropper_data
.enabled
:
148 cropper_render_item(obj
,context
)
152 class CROPPER_OBJ_SETTINGS(bpy
.types
.PropertyGroup
):
153 enabled
: bpy
.props
.BoolProperty( name
="enabled" )
154 filename
: bpy
.props
.StringProperty( name
="filename" )
155 resolution
: bpy
.props
.IntVectorProperty( name
="resolution",size
=2 )
157 class CROPPER_OBJ_PANEL(bpy
.types
.Panel
):
158 bl_label
="Cropper Settings"
159 bl_idname
="SCENE_PT_cropper"
160 bl_space_type
='PROPERTIES'
161 bl_region_type
='WINDOW'
165 active_object
= context
.active_object
166 if active_object
== None: return
167 _
.layout
.prop( active_object
.cropper_data
, "enabled" )
170 if not active_object
.cropper_data
.enabled
:
173 box
.prop( active_object
.cropper_data
, "filename" )
174 box
.prop( active_object
.cropper_data
, "resolution" )
175 box
.operator( "cropper.render" )
176 _
.layout
.operator( "cropper.render_all" )
178 classes
= [ CROPPER_OBJ_SETTINGS
, CROPPER_OBJ_PANEL
, \
179 CROPPER_RENDER
, CROPPER_RENDER_ALL
]
182 global cropper_view_draw_handler
, cropper_ui_draw_handler
185 bpy
.utils
.register_class(c
)
187 bpy
.types
.Object
.cropper_data
= \
188 bpy
.props
.PointerProperty(type=CROPPER_OBJ_SETTINGS
)
190 cropper_view_draw_handler
= bpy
.types
.SpaceView3D
.draw_handler_add(\
191 cropper_draw
,(),'WINDOW','POST_VIEW')
192 cropper_ui_draw_handler
= bpy
.types
.SpaceView3D
.draw_handler_add(\
193 cropper_draw_ui
,(),'WINDOW','POST_PIXEL')
196 global cropper_view_draw_handler
, cropper_ui_draw_handler
199 bpy
.utils
.unregister_class(c
)
201 bpy
.types
.SpaceView3D
.draw_handler_remove(cropper_view_draw_handler
,'WINDOW')
202 bpy
.types
.SpaceView3D
.draw_handler_remove(cropper_ui_draw_handler
,'WINDOW')