update helpers/location to 'frosted' ui
[carveJwlIkooP6JGAAIwe30JlM.git] / blender_graphics_cropper.py
1 # Any copyright is dedicated to the Public Domain. (blender_graphics_cropper.py)
2 # https://creativecommons.org/publicdomain/zero/1.0/
3
4 import bpy, math, gpu, blf
5 from mathutils import *
6 from gpu_extras.batch import batch_for_shader
7
8 bl_info = {
9 "name":"Graphics Cropper",
10 "author": "Harry Godden (hgn)",
11 "version": (0,1),
12 "blender":(3,1,0),
13 "location":"",
14 "descriptin":"",
15 "warning":"",
16 "wiki_url":"",
17 "category":"",
18 }
19
20 # Clicky clicky GUI
21 # ------------------------------------------------------------------------------
22
23 cropper_view_draw_handler = None
24 cropper_ui_draw_handler = None
25 cropper_view_shader = gpu.shader.from_builtin('3D_SMOOTH_COLOR')
26
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
31
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))
41
42 co = mtx @ Vector((p3[0],p3[1],p3[2],1.0))
43
44 co[0] /= co[3]
45 co[1] /= co[3]
46 co[0] *= 0.5
47 co[1] *= 0.5
48 co[0] += 0.5
49 co[1] += 0.5
50 co[0] *= w
51 co[1] *= h
52
53 blf.position(0,co[0],co[1]+8,0)
54 blf.size(0,20,48)
55 blf.color(0,c[0],c[1],c[2],c[3])
56 blf.draw(0,obj.cropper_data.filename)
57
58 def cropper_draw():
59 global cropper_view_shader
60
61 verts = []
62 colours = []
63
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))
73
74 verts += [p0,p1,p1,p2,p2,p3,p3,p0]
75 colours += [c,c,c,c,c,c,c,c]
76
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 )
86
87 # Blender
88 # ------------------------------------------------------------------------------
89
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
98
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}"
103
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))
109
110 if x > y: s = p2[0]-p0[0]
111 else: s = p2[1]-p0[1]
112
113 cam.location[0] = c[0]
114 cam.location[1] = c[1]
115
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)
122
123 # Reset
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
127
128 cam.data.ortho_scale = original_scale
129 cam.location[0] = original_pos_x
130 cam.location[1] = original_pos_y
131
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)
139
140 return {'FINISHED'}
141
142 class CROPPER_RENDER(bpy.types.Operator):
143 bl_idname="cropper.render"
144 bl_label="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)
149
150 return {'FINISHED'}
151
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 )
156
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'
162 bl_context="object"
163
164 def draw(_,context):
165 active_object = context.active_object
166 if active_object == None: return
167 _.layout.prop( active_object.cropper_data, "enabled" )
168
169 box = _.layout.box()
170 if not active_object.cropper_data.enabled:
171 box.enabled = False
172
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" )
177
178 classes = [ CROPPER_OBJ_SETTINGS, CROPPER_OBJ_PANEL, \
179 CROPPER_RENDER, CROPPER_RENDER_ALL ]
180
181 def register():
182 global cropper_view_draw_handler, cropper_ui_draw_handler
183
184 for c in classes:
185 bpy.utils.register_class(c)
186
187 bpy.types.Object.cropper_data = \
188 bpy.props.PointerProperty(type=CROPPER_OBJ_SETTINGS)
189
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')
194
195 def unregister():
196 global cropper_view_draw_handler, cropper_ui_draw_handler
197
198 for c in classes:
199 bpy.utils.unregister_class(c)
200
201 bpy.types.SpaceView3D.draw_handler_remove(cropper_view_draw_handler,'WINDOW')
202 bpy.types.SpaceView3D.draw_handler_remove(cropper_ui_draw_handler,'WINDOW')