1 import bpy
, math
, gpu
, blf
2 from mathutils
import *
3 from gpu_extras
.batch
import batch_for_shader
6 "name":"Graphics Cropper",
7 "author": "Harry Godden (hgn)",
18 # ------------------------------------------------------------------------------
20 cropper_view_draw_handler
= None
21 cropper_ui_draw_handler
= None
22 cropper_view_shader
= gpu
.shader
.from_builtin('3D_SMOOTH_COLOR')
24 def cropper_draw_ui():
25 mtx
= bpy
.context
.region_data
.perspective_matrix
26 w
= bpy
.context
.region
.width
27 h
= bpy
.context
.region
.height
29 for obj
in bpy
.context
.scene
.objects
:
30 if obj
.cropper_data
.enabled
and obj
.visible_get():
31 x
= obj
.cropper_data
.resolution
[0]
32 y
= obj
.cropper_data
.resolution
[1]
33 c
= Vector((1,1,0,1)) if obj
.select_get() else Vector((0.6,0.4,0,1))
34 p0
= obj
.matrix_world
@ Vector((0,0,0))
35 p1
= obj
.matrix_world
@ Vector((x
,0,0))
36 p2
= obj
.matrix_world
@ Vector((x
,y
,0))
37 p3
= obj
.matrix_world
@ Vector((0,y
,0))
39 co
= mtx
@ Vector((p3
[0],p3
[1],p3
[2],1.0))
50 blf
.position(0,co
[0],co
[1]+8,0)
52 blf
.color(0,c
[0],c
[1],c
[2],c
[3])
53 blf
.draw(0,obj
.cropper_data
.filename
)
56 global cropper_view_shader
61 for obj
in bpy
.context
.scene
.objects
:
62 if obj
.cropper_data
.enabled
and obj
.visible_get():
63 x
= obj
.cropper_data
.resolution
[0]
64 y
= obj
.cropper_data
.resolution
[1]
65 c
= Vector((1,1,0,1)) if obj
.select_get() else Vector((0.6,0.4,0,1))
66 p0
= obj
.matrix_world
@ Vector((0,0,0))
67 p1
= obj
.matrix_world
@ Vector((x
,0,0))
68 p2
= obj
.matrix_world
@ Vector((x
,y
,0))
69 p3
= obj
.matrix_world
@ Vector((0,y
,0))
71 verts
+= [p0
,p1
,p1
,p2
,p2
,p3
,p3
,p0
]
72 colours
+= [c
,c
,c
,c
,c
,c
,c
,c
]
74 cropper_view_shader
.bind()
75 gpu
.state
.depth_mask_set(False)
76 gpu
.state
.line_width_set(2.0)
77 gpu
.state
.face_culling_set('BACK')
78 gpu
.state
.depth_test_set('NONE')
79 gpu
.state
.blend_set('NONE')
80 lines
= batch_for_shader( cropper_view_shader
, 'LINES', \
81 { "pos":verts
, "color":colours
})
82 lines
.draw( cropper_view_shader
)
85 # ------------------------------------------------------------------------------
87 def cropper_render_item(obj
,context
):
88 cam
= context
.scene
.camera
89 original_pos_x
= cam
.location
[0]
90 original_pos_y
= cam
.location
[1]
91 original_res_x
= context
.scene
.render
.resolution_x
92 original_res_y
= context
.scene
.render
.resolution_y
93 original_file
= context
.scene
.render
.filepath
94 original_scale
= cam
.data
.ortho_scale
96 x
= obj
.cropper_data
.resolution
[0]
97 y
= obj
.cropper_data
.resolution
[1]
98 fmt
= context
.scene
.render
.image_settings
.file_format
.lower()
99 fn
= F
"{obj.cropper_data.filename}.{fmt}"
101 p0
= obj
.matrix_world
@ Vector((0,0,0))
102 p1
= obj
.matrix_world
@ Vector((x
,0,0))
103 p2
= obj
.matrix_world
@ Vector((x
,y
,0))
104 p3
= obj
.matrix_world
@ Vector((0,y
,0))
105 c
= obj
.matrix_world
@ Vector((x
*0.5,y
*0.5,0))
107 if x
> y
: s
= p2
[0]-p0
[0]
108 else: s
= p2
[1]-p0
[1]
110 cam
.location
[0] = c
[0]
111 cam
.location
[1] = c
[1]
113 cam
.data
.ortho_scale
= s
114 context
.scene
.render
.resolution_x
= x
115 context
.scene
.render
.resolution_y
= y
116 context
.scene
.render
.filepath
= original_file
+ fn
117 print( F
"render to: {context.scene.render.filepath}" )
118 bpy
.ops
.render
.render(write_still
=True)
121 context
.scene
.render
.resolution_x
= original_res_x
122 context
.scene
.render
.resolution_y
= original_res_y
123 context
.scene
.render
.filepath
= original_file
125 cam
.data
.ortho_scale
= original_scale
126 cam
.location
[0] = original_pos_x
127 cam
.location
[1] = original_pos_y
129 class CROPPER_RENDER_ALL(bpy
.types
.Operator
):
130 bl_idname
="cropper.render_all"
131 bl_label
="Render all"
132 def execute(_
,context
):
133 for obj
in context
.scene
.objects
:
134 if obj
.cropper_data
.enabled
and obj
.visible_get():
135 cropper_render_item(obj
,context
)
139 class CROPPER_RENDER(bpy
.types
.Operator
):
140 bl_idname
="cropper.render"
142 def execute(_
,context
):
143 obj
= context
.active_object
144 if obj
!= None and obj
.cropper_data
.enabled
:
145 cropper_render_item(obj
,context
)
149 class CROPPER_OBJ_SETTINGS(bpy
.types
.PropertyGroup
):
150 enabled
: bpy
.props
.BoolProperty( name
="enabled" )
151 filename
: bpy
.props
.StringProperty( name
="filename" )
152 resolution
: bpy
.props
.IntVectorProperty( name
="resolution",size
=2 )
154 class CROPPER_OBJ_PANEL(bpy
.types
.Panel
):
155 bl_label
="Cropper Settings"
156 bl_idname
="SCENE_PT_cropper"
157 bl_space_type
='PROPERTIES'
158 bl_region_type
='WINDOW'
162 active_object
= context
.active_object
163 if active_object
== None: return
164 _
.layout
.prop( active_object
.cropper_data
, "enabled" )
167 if not active_object
.cropper_data
.enabled
:
170 box
.prop( active_object
.cropper_data
, "filename" )
171 box
.prop( active_object
.cropper_data
, "resolution" )
172 box
.operator( "cropper.render" )
173 _
.layout
.operator( "cropper.render_all" )
175 classes
= [ CROPPER_OBJ_SETTINGS
, CROPPER_OBJ_PANEL
, \
176 CROPPER_RENDER
, CROPPER_RENDER_ALL
]
179 global cropper_view_draw_handler
, cropper_ui_draw_handler
182 bpy
.utils
.register_class(c
)
184 bpy
.types
.Object
.cropper_data
= \
185 bpy
.props
.PointerProperty(type=CROPPER_OBJ_SETTINGS
)
187 cropper_view_draw_handler
= bpy
.types
.SpaceView3D
.draw_handler_add(\
188 cropper_draw
,(),'WINDOW','POST_VIEW')
189 cropper_ui_draw_handler
= bpy
.types
.SpaceView3D
.draw_handler_add(\
190 cropper_draw_ui
,(),'WINDOW','POST_PIXEL')
193 global cropper_view_draw_handler
, cropper_ui_draw_handler
196 bpy
.utils
.unregister_class(c
)
198 bpy
.types
.SpaceView3D
.draw_handler_remove(cropper_view_draw_handler
,'WINDOW')
199 bpy
.types
.SpaceView3D
.draw_handler_remove(cropper_ui_draw_handler
,'WINDOW')