Skip to content

Instantly share code, notes, and snippets.

@emailandxu
Created November 20, 2023 09:50
Show Gist options
  • Save emailandxu/f30849c40d5fe0a70a73e16ee27ada17 to your computer and use it in GitHub Desktop.
Save emailandxu/f30849c40d5fe0a70a73e16ee27ada17 to your computer and use it in GitHub Desktop.

Revisions

  1. emailandxu created this gist Nov 20, 2023.
    111 changes: 111 additions & 0 deletions blender_viewport_to_image_per_view.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,111 @@
    #from this: https://blender.stackexchange.com/a/231881

    import bpy
    import gpu

    # Draw function which copies data from the 3D View
    def draw(self, context):

    if self.modal_redraw == True:

    # get currently bound framebuffer
    self.framebuffer = gpu.state.active_framebuffer_get()

    # get information on current viewport
    self.viewport_info = gpu.state.viewport_get()
    self.width = self.viewport_info[2]
    self.height = self.viewport_info[3]

    # Write copied data to image
    ######################################################
    # resize image obect to fit the current 3D View size
    self.framebuffer_image.scale(self.width, self.height)

    # obtain pixels from the framebuffer
    self.pixelBuffer = self.framebuffer.read_color(0, 0, self.width, self.height, 4, 0, 'FLOAT')

    # write all pixels into the blender image
    self.pixelBuffer.dimensions = self.width * self.height * 4
    self.framebuffer_image.pixels.foreach_set(self.pixelBuffer)

    # reset draw variable:
    # This is here to prevent excessive redrawing
    self.modal_redraw = False


    # Modal operator for controlled redrawing of the image object
    # NOTE: This code is only for a more conveniant testing of the draw function
    # If you want to stop the test, press 'ESC'

    class ModalFramebufferCopy(bpy.types.Operator):
    bl_idname = "view3d.modal_framebuffer_copy"
    bl_label = "Draw 3D View Framebuffer"

    def __init__(self):
    print("Start example code")

    # init variables
    self.width = 32
    self.height = 32
    self.modal_redraw = False
    self.image_name = "color_buffer_copy"
    self.framebuffer = None
    self.viewport_info = None
    self.pixelBuffer = None

    # create or update image object to which the framebuffer
    # data will be copied
    if not self.image_name in bpy.data.images:
    self.framebuffer_image = bpy.data.images.new(self.image_name , 32, 32, float_buffer=True)
    else:
    self.framebuffer_image = bpy.data.images[self.image_name ]


    #
    def __del__(self):
    print("End example code")


    # modal operator for controlled redraw of the image
    def modal(self, context, event):
    # stop the execution of this example code if 'ESC' is pressed
    if event.type in {'ESC'}:
    bpy.types.SpaceView3D.draw_handler_remove(self._handle_3d, 'WINDOW')
    print("Removing draw handler")
    return {'CANCELLED'}

    else:

    # set draw variable to update:
    # This is here to prevent excessive redrawing
    self.modal_redraw = True

    return {'PASS_THROUGH'}
    #return {'RUNNING_MODAL'}


    def invoke(self, context, event):
    print("Invoking modal operator")

    # Add the region OpenGL drawing callback
    # draw in view space with 'POST_VIEW' and 'PRE_VIEW'
    self._handle_3d = bpy.types.SpaceView3D.draw_handler_add(draw, (self, context), 'WINDOW', 'PRE_VIEW') # this draws the viewport objects alone (without grid)
    # self._handle_3d = bpy.types.SpaceView3D.draw_handler_add(draw, (self, context), 'WINDOW', 'POST_VIEW') # this draws the grid alone (without objects)

    context.window_manager.modal_handler_add(self)
    return {'RUNNING_MODAL'}


    def register():
    bpy.utils.register_class(ModalFramebufferCopy)


    def unregister():
    bpy.utils.unregister_class(ModalFramebufferCopy)


    if __name__ == "__main__":
    register()

    # Invoke modal operator for the example code
    bpy.ops.view3d.modal_framebuffer_copy('INVOKE_DEFAULT')