How to capture viewport image in tool mode?

35 Views Asked by At

I'm trying to capture images of MeshInstance3D via SubViewport and assigning them as textures to sprites as such:

enter image description here

@tool
extends Node2D

const PIXEL_PER_METER = 100.0
@export var btn := false : set=set_btn

func set_btn(new_val):
    if(new_val):
        assign_images()

func assign_images():
    var sub_viewport=$SubViewport
    var camera=$SubViewport/Camera3D
    var mesh_instances=[ $SubViewport/A, $SubViewport/B, $SubViewport/C ]
    var sprites=[ $A, $B, $C ]
    
    sub_viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS
    camera.projection = Camera3D.PROJECTION_ORTHOGONAL
    
    for i in len(mesh_instances):
        var sprite=sprites[i]
        var mesh=mesh_instances[i]
        var mesh_aabb=mesh.get_aabb()
        camera.size = max(abs(mesh_aabb.size.x), abs(mesh_aabb.size.y))
        camera.global_position = Vector3(mesh.global_position.x, mesh.global_position.y, camera.global_position.z)
        sprite.position = Vector2(mesh.position.x * PIXEL_PER_METER, mesh.position.y * -PIXEL_PER_METER)
        #await get_tree().process_frame  # this doesn't work either
        sprite.texture=ImageTexture.create_from_image(sub_viewport.get_texture().get_image())

How ever this doesn't work, it doesn't capture the right image, nor does it place the sprite at right position relative to it's mesh counterpart:

enter image description here

For the image capture part, I suspect SubViewport & Camera3D isn't being updated quickly enough hence it captures the wrong image.

As for the position part, I believe 1 m in 3D translates to 100 pixels in 2D, maybe due to the unit differences the size & position of image is wrong.

So how do I implement this properly?

Note: I'm open to an alternate approach as long as it gives the same result, and no MeshInstance2D does not work

Minimal reproduction project (MRP)

0

There are 0 best solutions below