0 votes


Is there a way to render a scene to a texture, free the scene and then use the texture as image.

I have some custom GraphNodes. I want to generate preview images for each node automatically when the game starts. I want to use these textures as items in an ItemList. I tried the following:

The scene I am trying to render to a texture has the following hierarchy.

enter image description here

I have a script attached to an ItemList node in another scene (my main scene). In the ready method of the ItemList node I use gettexture to get the texture from the Viewport of the instantiated scene I wish to render to a texture and then I try to add an item to the ItemList using the texture I just created. This is the script I am using in the ItemList node:

extends ItemList

func _ready():
    var scene = load("res://Scenes/Previews/Vector3NodePreview.tscn").instance()
    var imageTexture = ImageTexture.new()
    var image = scene.get_node("Viewport").get_texture().get_data()
    add_item(scene.get_node("Viewport/Node").title, imageTexture)

The only way I got it to render to a texture was to have the ViewportContainer be a child of my main scene. Then the code above works. However, the ViewportContainer renders ontop of my main scene as well. If I set the Visible property to false of the ViewportContainer, then the texture becomes empty. If I make the ViewportContainer render behind it's parent, then touches stop working.

What am I doing wrong and how can I achieve the effect I desire?

To put it into more simple words: I want to render a single frame of a scene to a texture and then use that texture as am ItemList item.

in Engine by (63 points)
retagged by

1 Answer

0 votes

You can query the image data from the viewport texture.

var img = viewport.get_texture().get_data()

and assign it to another texture (here assigned to a TextureRect control):

var tex=ImageTexture.new()

I use it for transferring a viewport over the network (remote controlling an app). It should work. :-)

by (3,280 points)

Greeting wombatstampede,

I tried what you suggest, but it is still not working. I updated my question with more details.


I managed to solve my problems, though I do not fully understand why my code works.

I chaned the script attached to the ItemList to this:

extends ItemList

var preview_generated = false

func _ready():

func _process(delta):
    if (!preview_generated):
        preview_generated = true

func generate_previews(nodes):
    for node in nodes:
        var scene : Node = load(node).instance()
        yield(get_tree(), "idle_frame")
        var imageTexture = ImageTexture.new()
        var image = Image.new()
        add_item(scene.get_node("Viewport/Node").title, imageTexture)

Without the addchiild / removechild lines the script does not work. Without the yield(gettree(), "idleframe") the code does not work ether.

Does anyone know why?

First of all:
Good idea to use image.copy_from. That eliminates the possibility that the data from get_data() is shared. (I'm unsure about that though())

You're loading a scene and then instanciating it. Probably it won't render (to the viewport) unless it is part of the scenetree. Therefore adding it as a child helps.

Also it won't render unless at least one (or two) "frame" has passed.
This is described here under "Capture":

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.