Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Updating texture of sprite.  (Read 15500 times)

0 Members and 1 Guest are viewing this topic.

karposhark

  • Newbie
  • *
  • Posts: 7
    • View Profile
Updating texture of sprite.
« on: September 10, 2017, 09:05:11 pm »
Hi

I am trying to update the texture of a sprite by updating the current texture with a new image instead of assigning another texture to the sprite. See code below if it seems a bit vague. To bad for me, it does not seem to work. :( (Though maybe it is just because I am doing something wrong...)

In the official tutorials I have found the following quote (https://www.sfml-dev.org/tutorials/2.4/graphics-sprite.php) :
Quote
When you set the texture of a sprite, all it does internally is store a pointer to the texture instance. Therefore, if the texture is destroyed or moves elsewhere in memory, the sprite ends up with an invalid texture pointer.
   
As the quote states, a sprite holds a pointer to the texture it uses, so my idea would be that when the texture changes, the appearance of the sprite also changes. Of course this quote is targeting the C++-version, but i sort of expected that it would be the same for the .NET binding.

I do know that there a several ways around this problem, like creating an image that holds both the first and the second image so i just need to adjust the TextureRect, but I would like to know if there is any way to reach my goal as I am currently trying.

Thanks
     
class MyClass {
        private Image mImage;
        private Texture mTexture;

        public TextureHandlerClass() {
                mImage = new Image( /* image path */ );
                mTexture = new Texture(mImage);
                Sprite a = new Sprite(mTexture);
                Sprite b = new Sprite(mTexture);
                Sprite c = new Sprite(mTexture);
                Sprite d = new Sprite(mTexture);
        }

        private void ColorUpdate(object sender, EventArgs e) {
                mImage = new Image( /* other image path */ );
                mTexture.Update(mImage);
        }
}
 
« Last Edit: September 10, 2017, 10:06:37 pm by karposhark »

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 10998
    • View Profile
    • development blog
    • Email
Re: Updating texture of sprite.
« Reply #1 on: September 10, 2017, 09:10:39 pm »
"Doesn't seem to work" isn't really a problem description. ;)

How did you determine that it doesn't work?
What's the result you're expecting and what result are you getting?
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

karposhark

  • Newbie
  • *
  • Posts: 7
    • View Profile
Re: Updating texture of sprite.
« Reply #2 on: September 10, 2017, 10:05:03 pm »
I'll try to make it more clear ;).

Suppose I have an image of a cat and an image of a dog. First I load the image of the cat and assign it to the Texture 'my_texture'. Next, I create 10 instances of Sprite with this 'my_texture'.

At a certain moment in time, all the cats are at the exact same moment replaced by dogs. I load the image of the dog and use 'my_texture.Update(dog_image)' to update 'my_texture'. Because all of the 10 instances of Sprite have been created with the same Texture, and 'my_texture' has been updated with the dog image, I expected that all the Sprites would now show a dog, instead of a cat. However, the Sprites are still displaying the image of the cat.

So to wrap it up: I guessed that, because each Sprite keeps a pointer to its Texture, changing the Texture would change the image displayed by the Sprite. This does not seem to be the case. Should I invoke a method from Sprite to let it know the texture has changed?

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 10998
    • View Profile
    • development blog
    • Email
Re: Updating texture of sprite.
« Reply #3 on: September 10, 2017, 10:23:16 pm »
However, the Sprites are still displaying the image of the cat.
That's all that was missing from your initial post. ;)

This shouldn't happen. Keep in mind the comment in the documentation for the update function:

Quote
Update the texture from an image.

Although the source image can be smaller than the texture, this function is usually used for updating the whole texture. The other overload, which has (x, y) additional arguments, is more convenient for updating a sub-area of the texture.

No additional check is performed on the size of the image, passing an image bigger than the texture will lead to an undefined behavior.

This function does nothing if the texture was not previously created.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

karposhark

  • Newbie
  • *
  • Posts: 7
    • View Profile
Re: Updating texture of sprite.
« Reply #4 on: September 10, 2017, 10:54:06 pm »
I double checked it and I can confirm the images are both the same size. ;)

This is exactly what I do. While debugging I can clearly see that the if-test succeeds.
If I uncomment the last line, I works for a single Sprite, but I am looking for updating multiple Sprites at once (and this approach would be quite some overhead).

public MyClass : Sprite {
        private Image mImg;

        public MyClass() {
                mImg = new Image("ArrowRed.png");
               
                this.Texture = new Texture(mImg);
                this.TextureRect = new IntRect(0, 0, 32, 32);
        }

        public void OnAction() {
                Image newImg = new Image("ArrowGreen.png");

                if (mImg.Size == newImg.Size) {
                        this.Texture.Update(newImg);
                        //this.Texture = new Texture(newImg); (This one works obviously)
                }
        }
}
 

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Updating texture of sprite.
« Reply #5 on: September 11, 2017, 08:08:51 am »
This could be a bug in the .Net binding. It would be great if you could write a complete and minimal code that tests just this feature, outside your current projet (ie. load a texture, assign it to a sprite, update it and draw the sprite).
Laurent Gomila - SFML developer

karposhark

  • Newbie
  • *
  • Posts: 7
    • View Profile
Re: Updating texture of sprite.
« Reply #6 on: September 11, 2017, 09:49:45 am »
I just wrote the code to test this (see attachments). After 2 seconds, each blue 'A' image should change to the yellow 'B' image.

Note the commented line (r33). You can uncomment it to see the expected result.

I am using SFML.Net 2.2.0.0 64-bit.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Updating texture of sprite.
« Reply #7 on: September 11, 2017, 12:54:13 pm »
Thanks. Could you try the same code, but with the other update functions (from a pixel array and/or from a window)?

A useful test would be the same thing with CSFML directly, but that's probably too much to ask ;D
Laurent Gomila - SFML developer

karposhark

  • Newbie
  • *
  • Posts: 7
    • View Profile
Re: Updating texture of sprite.
« Reply #8 on: September 14, 2017, 12:38:56 pm »
Hi

I just tried the other update methods, but the result stayed the same.
However I did notice that when using the MouseButtonPressed-event instead of the Timer-event, it all seemed to work.

So now I am wondering: is it possible that the update-method from Texture fails when it is called from a thread that is not the main-thread? I can't recall having read about it in the documentation...

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 10998
    • View Profile
    • development blog
    • Email
Re: Updating texture of sprite.
« Reply #9 on: September 14, 2017, 12:44:15 pm »
Can you show your modified code?

I just created an example with CSFML and either I did something wrong, or it does really seem like there's no update happening.
Edit: I messed up, see below.

#include <SFML/Graphics.h>

int main()
{
    sfVideoMode mode = {800, 600, 32};
    sfRenderWindow* window;
    sfTexture* texture;
    sfSprite* sprite;
    sfImage* image;
    sfEvent event;

    window = sfRenderWindow_create(mode, "SFML window", sfResize | sfClose, NULL);
    if (!window)
        return -1;

    texture = sfTexture_createFromFile("A.png", NULL);
    if (!texture)
        return -1;
    sprite = sfSprite_create();
    sfSprite_setTexture(sprite, texture, sfTrue);

    image = sfImage_createFromFile("B.png");

    while (sfRenderWindow_isOpen(window))
    {
        while (sfRenderWindow_pollEvent(window, &event))
        {
            if (event.type == sfEvtClosed)
                sfRenderWindow_close(window);
            else if (event.type == sfEvtMouseButtonPressed)
            {
                printf("MouseButton Pressed - updateFromImage\n");
                sfTexture_updateFromImage(texture, image, 64, 64);
            }
            else if (event.type == sfEvtKeyPressed)
            {
                printf("Key Pressed - updateFromPixels\n");
                sfTexture_updateFromPixels(texture, sfImage_getPixelsPtr(image), 64, 64, 64, 64);
            }
        }

        sfRenderWindow_clear(window, sfBlack);
        sfRenderWindow_drawSprite(window, sprite, NULL);
        sfRenderWindow_display(window);
    }

    sfImage_destroy(image);
    sfSprite_destroy(sprite);
    sfTexture_destroy(texture);
    sfRenderWindow_destroy(window);
    return 0;
}
 
« Last Edit: September 14, 2017, 01:46:17 pm by eXpl0it3r »
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 10998
    • View Profile
    • development blog
    • Email
Re: Updating texture of sprite.
« Reply #10 on: September 14, 2017, 01:44:15 pm »
Nevermind, it was a misunderstanding of the API. The x and y values on the update functions describe the offsets and not the size or similar. As such they need to be 0.
Changing my code to the follow makes it run without issues.

#include <SFML/Graphics.h>

int main()
{
    sfVideoMode mode = {800, 600, 32};
    sfRenderWindow* window;
    sfTexture* texture;
    sfSprite* sprite;
    sfImage* image;
    sfEvent event;

    window = sfRenderWindow_create(mode, "SFML window", sfResize | sfClose, NULL);
    if (!window)
        return -1;

    texture = sfTexture_createFromFile("A.png", NULL);
    if (!texture)
        return -1;
    sprite = sfSprite_create();
    sfSprite_setTexture(sprite, texture, sfTrue);

    image = sfImage_createFromFile("B.png");
    if (!image)
        return -1;

    while (sfRenderWindow_isOpen(window))
    {
        while (sfRenderWindow_pollEvent(window, &event))
        {
            if (event.type == sfEvtClosed)
                sfRenderWindow_close(window);
            else if (event.type == sfEvtMouseButtonPressed)
            {
                printf("MouseButton Pressed - updateFromImage\n");
                sfTexture_updateFromImage(texture, image, 0, 0);
            }
            else if (event.type == sfEvtKeyPressed)
            {
                printf("Key Pressed - updateFromPixels\n");
                sfTexture_updateFromPixels(texture, sfImage_getPixelsPtr(image), 64, 64, 0, 0);
            }
        }

        sfRenderWindow_clear(window, sfBlack);
        sfRenderWindow_drawSprite(window, sprite, NULL);
        sfRenderWindow_display(window);
    }

    sfImage_destroy(image);
    sfSprite_destroy(sprite);
    sfTexture_destroy(texture);
    sfRenderWindow_destroy(window);
    return 0;
}
 

So it's not an issue with CSFML as far as I can tell.

So now I am wondering: is it possible that the update-method from Texture fails when it is called from a thread that is not the main-thread? I can't recall having read about it in the documentation...
The update will call OpenGL functions, with them no being run in the same thread, it's very well possible that issues can arise. You'd have to activate the thread's context before using anything in that thread and since OpenGL isn't multi-threaded, the OpenGL commands will be put into the queue and executed maybe slightly later.

For animation timing, you may want to go the non timer event route and use some clock to track the passed time etc.
It will most likely also be more precise as you're not relying on OS callbacks/interrupts which need to go through the event pump and precise timers (but I might also be wrong on this one).
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

karposhark

  • Newbie
  • *
  • Posts: 7
    • View Profile
Re: Updating texture of sprite.
« Reply #11 on: September 14, 2017, 02:51:38 pm »
I can confirm that the problem was related to calling the update-method in a separate thread. I adjusted my code as follows (full code in attachment):
Thread thread = new Thread(delegate () {
        Monitor.Enter(w);
        w.SetActive(true);

        tex.Update(imgB);

        w.SetActive(false);
        Monitor.Exit(w);
});
thread.Start();

while (w.IsOpen) {
        w.DispatchEvents();

        Monitor.Enter(w);
        w.SetActive(true);

        w.Clear(Color.White);
        w.Draw(spr1);
        w.Draw(spr2);
        w.Draw(spr3);
        w.Draw(spr4);

        w.SetActive(false);
        Monitor.Exit(w);

        w.Display();
}
 

Without the Monitor I got the 'Failed to activate the window's context'-message. I don't know whether this is the most elegant solution, but it seems to work without errors.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Updating texture of sprite.
« Reply #12 on: September 14, 2017, 02:59:21 pm »
You should never have to explicitely call w.SetActive, SFML is supposed to handle context switches in threads automatically (in this use case, at least).
Laurent Gomila - SFML developer

karposhark

  • Newbie
  • *
  • Posts: 7
    • View Profile
Re: Updating texture of sprite.
« Reply #13 on: September 14, 2017, 03:05:24 pm »
Do you mean that this should work?

Thread thread = new Thread(delegate () {
        Monitor.Enter(w);
        //w.SetActive(true);

        tex.Update(imgB);

        //w.SetActive(false);
        Monitor.Exit(w);
});
thread.Start();

while (w.IsOpen) {
        w.DispatchEvents();

        Monitor.Enter(w);
        //w.SetActive(true);

        w.Clear(Color.White);
        w.Draw(spr1);
        w.Draw(spr2);
        w.Draw(spr3);
        w.Draw(spr4);

        //w.SetActive(false);
        Monitor.Exit(w);

        w.Display();
}    

If I run the program with the code like this, the textures won't update.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Updating texture of sprite.
« Reply #14 on: September 14, 2017, 03:23:23 pm »
Yes, it should. In C++, I'm pretty sure it would work. Maybe .Net threads are not standard system threads?
Laurent Gomila - SFML developer