0
\$\begingroup\$

Assume there is a viewport that "looks" at some Sprite2D. This viewport's view is providing the texture for a Sprite3D by:

Sprite3D.texture = spriteViewport.get_texture()

It works fine, and the Sprite3D looks exactly like Sprite2D.

This effectively makes the Sprite3D's texture a "dynamic" one: if Sprite2D changes (or anything in the view of the viewport changes) these changes will be immediately reflected on Sprite3D.

While this is an intended behaviour of ViewportTexture, I would like to know if there is a way to make a "one and done" snap of that viewport's view. To render the dynamic ViewportTexture to a static Texture2D so that any subsequent changes in the viewport do not affect the Sprite3D anymore. Ideally without the use of resources or .png workarounds and in the form of a function akin to:

func updateTexture(viewport: Subviewport,sprite3D: Sprite3D)
        <code that permanently redraws the texture of the sprite3D to whatever was in viewport's view>

The obvious solution is saving the "viewport view" as a .png and then assigning the .png to the Sprite3D's texture. However, it is computationally slow and takes extra space, creates extra files, i.e. a mess and I'd rather avoid it.

I've also tried creating a new Texture2D and then assigning the viewport texture to it like so:

newTex = Texture2D.new()
newTex = someViewport.get_texture()

It did not work. Any tips?

\$\endgroup\$
0

1 Answer 1

1
\$\begingroup\$

I'm assuming this question is related to your previous one and, therefore, that you are using Godot 4.

By default, the behaviour of a SubViewport is to update its render target when visible to the current camera. You can change this by editing its Update Mode in the Inspector:

enter image description here

Now, the SubViewport will update only once; all subsequent changes to child nodes will not render (thus "pausing" the render target).

When the game is running, you can update the SubViewport's render texture programmatically; every time you set SubViewport.render_target_update_mode to SubViewport.UpdateMode.UPDATE_ONCE, the engine will update the render target for the current frame and then pause its rendering again. This will achieve your "one and done" desired behaviour.

Sample usage:

extends Sprite3D


var sprite_viewport: SubViewport


func _ready() -> void:
    # Reference the SubViewport
    sprite_viewport = $SpriteViewport


func update_texture() ->  void:
    # Request a one-shot rendering update.
    # Other Nodes may also call this function.
    sprite_viewport.set_update_mode(SubViewport.UpdateMode.UPDATE_ONCE)
\$\endgroup\$
2
  • \$\begingroup\$ Thank you. You are right, I use godot 4.2.1. I DEFINITELY tried the "Update mode" before but it didn't work. Trying again revealed, that setting the "UPDATE MODE" to "Once" somehow gets ignored, and the Remote monitor shows it as being set to "always". I'm going to attempt to set it programmatically now. UPDATE: It worked programmatically. Did not work when setting through the Godot Editor. \$\endgroup\$
    – Noideas
    Commented May 3 at 6:06
  • \$\begingroup\$ The description for the "Update once" option says that it will render the target, then set itself to "Never update" right after. It actually works, but nothing interesting happens when the scene is first loaded 😁 \$\endgroup\$
    – liggiorgio
    Commented May 3 at 6:17

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .