How to capture viewport image in tool mode?

Godot Version

Godot Version 4.2


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

extends Node2D

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

func set_btn(new_val):

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

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:

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?

Minimal reproduction project (MRP)

This is what the result ends up looking like: