[Godot 3.1 Stable] Render packed scene to a texture to be used with ItemList

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Juxxec

Greetings,

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 get_texture 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()
    imageTexture.create_from_image(image)
    add_item(scene.get_node("Viewport/Node").title, imageTexture)
    pass

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.

:bust_in_silhouette: Reply From: wombatstampede

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()
tex.create_from_image(img)
aTextureRectNode.texture=tex

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

Greeting wombatstampede,

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

Juxxec | 2019-04-04 19:22

Greetings!

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():
    pass
    
func _process(delta):
    if (!preview_generated):
        generate_previews(["res://Scenes/Previews/Vector3NodePreview.tscn"])
        preview_generated = true
        
func generate_previews(nodes):
    for node in nodes:
        var scene : Node = load(node).instance()
        add_child(scene)
        yield(get_tree(), "idle_frame")
        var imageTexture = ImageTexture.new()
        var image = Image.new()
        image.copy_from(scene.get_node("Viewport").get_texture().get_data())
        imageTexture.create_from_image(image)
        add_item(scene.get_node("Viewport/Node").title, imageTexture)
        remove_child(scene)
        scene.queue_free()

Without the add_chiild / remove_child lines the script does not work. Without the yield(get_tree(), “idle_frame”) the code does not work ether.

Does anyone know why?

Juxxec | 2019-04-04 19:52

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”:
Viewports — Godot Engine (3.1) documentation in English

wombatstampede | 2019-04-05 07:20