0 votes

So I've been working on my project which has been going quite well, yet I stumbled on another Block which I don't seem to be able to get around on my own.

Here is what I am trying to Achieve:

I am trying to use the Colors of an Image and a Heightmap to draw Lines so it can fake a 3D terrain (like VoxelSpace: https://github.com/s-macke/VoxelSpace).

By the way it worked in GDScript, but the performance was not... realistic (I'm talking about ~1.5 minutes for one Frame).
That's why I decided to write the whole thing again just in C++ and Implement it as a GDNative Module.

So my questions are:

How can I load an Image?

How can I obtain data from individual Pixels?

Can I get the Pixel data directly?
(directly meaning not storing the data in an array and using it in the draw function that way, but directly as in when it is called in the draw Function)

Thank you in advance!

asked Mar 28 in Projects by Lord_Kaltenberg (14 points)

1 Answer

+2 votes

You load the image just the same way as you did in GDScript, the syntax is just a bit different.

Ref<Image> image; // do this once and store it in a member variable
image.instance();
image->load("res://image.png");

image->lock(); // this where you need access to pixels
Color c = image->get_pixel(42, 42);
image->unlock();

You can also read the pixel data directly by using get_data() https://docs.godotengine.org/en/stable/classes/class_image.html#class-image-method-get-data
Then use a Read struct to access by pointer/ But you will have to deal with the internal layout of the pixel format, because that gives you a raw array of bytes.

tbh, I would actually implement only the CPU-intensive part in GDNative, not the whole thing. So for example, you'd load the image or handle the GUI in GDScript and only do rendering in C++, because GDNative can be rough on the edges, while GDScript allows you to make mistakes without crashing too much. But that's my opinion.
Also depending on how much draw calls you do, GDNative won't shave the overhead of Godot's draw functions.

Even better, for a rendering problem like you have, shaders sound like a much better candidate (although, it may require a much different approach than the ones shown in VoxelSpace I suppose).

answered Mar 28 by Zylann (26,787 points)
edited Mar 28 by Zylann

So I am rewriting this since I just accidentally canceled my Comment earlier (Oops).
Also this time I'll keep it shorter:

First:
Thank You for the quick and to the Point response!
Second:
Since I still didn't have success I'm assuming I am doing something wrong, and currently I'm trying to pinpoint possible errors. Meanwhile I would like to ask you how you can store the Ref image in a member variable? Images presumably have to have their own type, correct? Like var in GDScript? Is there an equivalent to that in C++/GDNative?
Third:
I am actually considering to truly switch lanes and go through the Shader approach it really seems to be much more appropriate to my needs. That being said I know less about Shaders than about GDNative... Nonetheless I'll give it a go. Thank you for that Tip aswell!

Lastly:
I wish you a great Day!

how you can store the Ref image in a member variable? Images presumably have to have their own type, correct? Like var in GDScript? Is there an equivalent to that in C++/GDNative?

It looks like you are not experienced in C++? If so, it is a bit risky to go on this kind of project without learning the language first. Also you should have followed this to see how it works with GDNative: https://docs.godotengine.org/en/stable/tutorials/plugins/gdnative/index.html

In C++ there is no var for members, everything is typed. Godot class instances are all heap-allocated, so they must be stored by pointer (T*). Image is a reference-counted type (it inherits Reference), but if you keep it a pointer it won't automatically manage its reference count. So it is common to wrap it inside a Ref<T> template container, to automate this (like GDScript does behind the scenes).
An Image member variable then looks like this:

class MyClass : public BaseType {
    GODOT_CLASS(MyClass, BaseType)
private:
    Ref<Image> _image;

public:
    void set_image(Ref<Image> image) {
        _image = image;
    }

    //...
};

Okay now I figured it out and it actually works!
But the performance is still abysmal... So I'll just move on to Shaders.

It looks like you are not experienced in C++? If so, it is a bit risky to go on this kind of project without learning the language first.

Well I am new to C++. The only things I've done so far with it was like a pong game once, and that was years ago.

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.