init
authorhgn <hgodden00@gmail.com>
Wed, 3 Aug 2022 11:07:03 +0000 (12:07 +0100)
committerhgn <hgodden00@gmail.com>
Wed, 3 Aug 2022 11:07:03 +0000 (12:07 +0100)
blender_graphics_cropper.py [new file with mode: 0644]

diff --git a/blender_graphics_cropper.py b/blender_graphics_cropper.py
new file mode 100644 (file)
index 0000000..ad12dfc
--- /dev/null
@@ -0,0 +1,199 @@
+import bpy, math, gpu, blf
+from mathutils import *
+from gpu_extras.batch import batch_for_shader
+
+bl_info = {
+   "name":"Graphics Cropper",
+   "author": "Harry Godden (hgn)",
+   "version": (0,1),
+   "blender":(3,1,0),
+   "location":"",
+   "descriptin":"",
+   "warning":"",
+   "wiki_url":"",
+   "category":"",
+}
+
+# Clicky clicky GUI
+# ------------------------------------------------------------------------------
+
+cropper_view_draw_handler = None
+cropper_ui_draw_handler = None
+cropper_view_shader = gpu.shader.from_builtin('3D_SMOOTH_COLOR')
+
+def cropper_draw_ui():
+   mtx = bpy.context.region_data.perspective_matrix
+   w = bpy.context.region.width
+   h = bpy.context.region.height
+
+   for obj in bpy.context.scene.objects:
+      if obj.cropper_data.enabled:
+         x = obj.cropper_data.resolution[0]
+         y = obj.cropper_data.resolution[1]
+         c = Vector((1,1,0,1)) if obj.select_get() else Vector((0.6,0.4,0,1))
+         p0 = obj.matrix_world @ Vector((0,0,0))
+         p1 = obj.matrix_world @ Vector((x,0,0))
+         p2 = obj.matrix_world @ Vector((x,y,0))
+         p3 = obj.matrix_world @ Vector((0,y,0))
+
+         co = mtx @ Vector((p3[0],p3[1],p3[2],1.0))
+
+         co[0] /= co[3]
+         co[1] /= co[3]
+         co[0] *= 0.5
+         co[1] *= 0.5
+         co[0] += 0.5
+         co[1] += 0.5
+         co[0] *= w
+         co[1] *= h
+
+         blf.position(0,co[0],co[1]+8,0)
+         blf.size(0,20,48)
+         blf.color(0,c[0],c[1],c[2],c[3])
+         blf.draw(0,obj.cropper_data.filename)
+
+def cropper_draw():
+   global cropper_view_shader
+
+   verts = []
+   colours = []
+
+   for obj in bpy.context.scene.objects:
+      if obj.cropper_data.enabled:
+         x = obj.cropper_data.resolution[0]
+         y = obj.cropper_data.resolution[1]
+         c = Vector((1,1,0,1)) if obj.select_get() else Vector((0.6,0.4,0,1))
+         p0 = obj.matrix_world @ Vector((0,0,0))
+         p1 = obj.matrix_world @ Vector((x,0,0))
+         p2 = obj.matrix_world @ Vector((x,y,0))
+         p3 = obj.matrix_world @ Vector((0,y,0))
+
+         verts += [p0,p1,p1,p2,p2,p3,p3,p0]
+         colours += [c,c,c,c,c,c,c,c]
+
+   cropper_view_shader.bind()
+   gpu.state.depth_mask_set(False)
+   gpu.state.line_width_set(2.0)
+   gpu.state.face_culling_set('BACK')
+   gpu.state.depth_test_set('NONE')
+   gpu.state.blend_set('NONE')
+   lines = batch_for_shader( cropper_view_shader, 'LINES', \
+                             { "pos":verts, "color":colours })
+   lines.draw( cropper_view_shader )
+
+# Blender
+# ------------------------------------------------------------------------------
+
+def cropper_render_item(obj,context):
+   cam = context.scene.camera
+   original_pos_x = cam.location[0]
+   original_pos_y = cam.location[1]
+   original_res_x = context.scene.render.resolution_x
+   original_res_y = context.scene.render.resolution_y
+   original_file = context.scene.render.filepath
+   original_scale = cam.data.ortho_scale
+   
+   x = obj.cropper_data.resolution[0]
+   y = obj.cropper_data.resolution[1]
+   fmt = context.scene.render.image_settings.file_format.lower()
+   fn = F"{obj.cropper_data.filename}.{fmt}"
+
+   p0 = obj.matrix_world @ Vector((0,0,0))
+   p1 = obj.matrix_world @ Vector((x,0,0))
+   p2 = obj.matrix_world @ Vector((x,y,0))
+   p3 = obj.matrix_world @ Vector((0,y,0))
+   c = obj.matrix_world @ Vector((x*0.5,y*0.5,0))
+
+   if x > y: s = p2[0]-p0[0]
+   else: s = p2[1]-p0[1]
+   
+   cam.location[0] = c[0]
+   cam.location[1] = c[1]
+
+   cam.data.ortho_scale = s
+   context.scene.render.resolution_x = x
+   context.scene.render.resolution_y = y
+   context.scene.render.filepath = original_file + fn
+   print( F"render to: {context.scene.render.filepath}" )
+   bpy.ops.render.render(write_still=True)
+
+   # Reset
+   context.scene.render.resolution_x = original_res_x
+   context.scene.render.resolution_y = original_res_y
+   context.scene.render.filepath = original_file
+
+   cam.data.ortho_scale = original_scale
+   cam.location[0] = original_pos_x
+   cam.location[1] = original_pos_y
+
+class CROPPER_RENDER_ALL(bpy.types.Operator):
+   bl_idname="cropper.render_all"
+   bl_label="Render all"
+   def execute(_,context):
+      for obj in context.scene.objects:
+         if obj.cropper_data.enabled:
+            cropper_render_item(obj,context)
+
+      return {'FINISHED'}
+
+class CROPPER_RENDER(bpy.types.Operator):
+   bl_idname="cropper.render"
+   bl_label="Render"
+   def execute(_,context):
+      obj = context.active_object
+      if obj != None and obj.cropper_data.enabled: 
+         cropper_render_item(obj,context)
+
+      return {'FINISHED'}
+
+class CROPPER_OBJ_SETTINGS(bpy.types.PropertyGroup):
+   enabled: bpy.props.BoolProperty( name="enabled" )
+   filename: bpy.props.StringProperty( name="filename" )
+   resolution: bpy.props.IntVectorProperty( name="resolution",size=2 )
+
+class CROPPER_OBJ_PANEL(bpy.types.Panel):
+   bl_label="Cropper Settings"
+   bl_idname="SCENE_PT_cropper"
+   bl_space_type='PROPERTIES'
+   bl_region_type='WINDOW'
+   bl_context="object"
+
+   def draw(_,context):
+      active_object = context.active_object
+      if active_object == None: return
+      _.layout.prop( active_object.cropper_data, "enabled" )
+
+      box = _.layout.box()
+      if not active_object.cropper_data.enabled:
+         box.enabled = False
+
+      box.prop( active_object.cropper_data, "filename" )
+      box.prop( active_object.cropper_data, "resolution" )
+      box.operator( "cropper.render" )
+      _.layout.operator( "cropper.render_all" )
+
+classes = [ CROPPER_OBJ_SETTINGS, CROPPER_OBJ_PANEL, \
+            CROPPER_RENDER, CROPPER_RENDER_ALL ]
+
+def register():
+   global cropper_view_draw_handler, cropper_ui_draw_handler
+
+   for c in classes:
+      bpy.utils.register_class(c)
+
+   bpy.types.Object.cropper_data = \
+         bpy.props.PointerProperty(type=CROPPER_OBJ_SETTINGS)
+
+   cropper_view_draw_handler = bpy.types.SpaceView3D.draw_handler_add(\
+      cropper_draw,(),'WINDOW','POST_VIEW')
+   cropper_ui_draw_handler = bpy.types.SpaceView3D.draw_handler_add(\
+      cropper_draw_ui,(),'WINDOW','POST_PIXEL')
+
+def unregister():
+   global cropper_view_draw_handler, cropper_ui_draw_handler
+
+   for c in classes:
+      bpy.utils.unregister_class(c)
+
+   bpy.types.SpaceView3D.draw_handler_remove(cropper_view_draw_handler,'WINDOW')
+   bpy.types.SpaceView3D.draw_handler_remove(cropper_ui_draw_handler,'WINDOW')