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

Author Topic: Layering Two or More Textures on top of eachother (if its possible)  (Read 15458 times)

0 Members and 1 Guest are viewing this topic.

SickReferenceBro

  • Newbie
  • *
  • Posts: 8
    • View Profile
    • Email
Hello,

To start, I'm a game developer who has recently come over to sfml and it's a breath of fresh air and I've enjoyed learning it, but my problem is one of the features I'll need to code for my upcomming game requires me to have multi layered textures.

What I need is for an inventory system, items are randomly created and can look different so for example:
"bloodied oak baseball bat" - "scratched steel baseball bat" would both look different, both would use a baseball bat blank sprite, then the first would use the oak texture, the problem is bloodied is an extra condition, I would need to layer it on top of the oak texture. Perhaps using a sprite sheet with some transparency and then having a bloodied texture on the sheet that is blood splatters among transparent space, but how do I layer that on top of the other texture? do I create a second sprite and place it directly above the existing one?

The same would apply for the steel bat, it would use a steel texture then place transparent scratches on top of it, I'm fairly new to graphical programming in general but I'm getting the hang of it, I may be completely off but if anyone has a clue what I should do, I'm all ears! :)

Thanks a bunch for reading

GraphicsWhale

  • Full Member
  • ***
  • Posts: 131
    • View Profile
Re: Layering Two or More Textures on top of eachother (if its possible)
« Reply #1 on: December 17, 2015, 05:32:51 am »
To start, I'm a game developer

You don't say.  :P

What I need is for an inventory system, items are randomly created and can look different so for example:
"bloodied oak baseball bat" - "scratched steel baseball bat" would both look different, both would use a baseball bat blank sprite, then the first would use the oak texture, the problem is bloodied is an extra condition, I would need to layer it on top of the oak texture. Perhaps using a sprite sheet with some transparency and then having a bloodied texture on the sheet that is blood splatters among transparent space, but how do I layer that on top of the other texture? do I create a second sprite and place it directly above the existing one?
Thanks a bunch for reading

I'm not sure if SFML provides anything that will directly solve the problem, but there is transparency (as you've mentioned), so you should be able to draw another texture over it (via a Sprite or RectangleShape placed right above what you want to apply the overlay to). You can make the overlaying image transparent by setting the sprite or shape's color to a color with the appropriate alpha value.

Example:
sf::Texture weapon_tex;
base_image.loadFromFile("weapon.png");

sf::Texture blood_tex;
blood_overlay.loadFromFile("blood.png");

sf::RectangleShape weapon_slot;
weapon_slot.setPosition(sf::Vector2f(0, 0));
weapon_slot.setSize(sf::Vector2f(80, 80));

sf::RectangleShape weapon_slot_overlay;
weapon_slot_overlay.setPosition(sf::Vector2f(0, 0));
weapon_slot_overlay.setSize(sf::Vector2f(80, 80));
weapon_slot_overlay.setFillColor(sf::Color(255, 255, 255, 128)); //128 is half transparency

weapon_slot.setTexture(&weapon_tex);
weapon_slot_overlay.setTexture(&blood_tex);

window.draw(weapon_slot);
window.draw(weapon_slot_overlay);
 
« Last Edit: December 17, 2015, 05:38:49 am by GraphicsWhale »

SickReferenceBro

  • Newbie
  • *
  • Posts: 8
    • View Profile
    • Email
Re: Layering Two or More Textures on top of eachother (if its possible)
« Reply #2 on: December 17, 2015, 11:53:59 am »
Thank you, I thought as much and have managed to get it working with two different overlaying textures.



Though another issue I'm having trouble understanding is, how can instead of using a rectangle shape, have the textures placed over a baseball bat shape?

noct

  • Newbie
  • *
  • Posts: 24
    • View Profile
    • nctdev.pl
Re: Layering Two or More Textures on top of eachother (if its possible)
« Reply #3 on: December 17, 2015, 04:00:18 pm »
If you'd not like to display your entire texture, just a piece, you'd have to use
setTextureRect(sf::IntRect() )
to cut desired part and then display sprite as usual.

Else if you thought about something like two pics combined into one, I think SFML won't help you there. Maybe creating all single "bloodied bat", "scratched bat" etc textures will do?

I've got similar inventory system btw ;)
« Last Edit: December 17, 2015, 04:08:26 pm by noct »

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Layering Two or More Textures on top of eachother (if its possible)
« Reply #4 on: December 17, 2015, 06:23:12 pm »
Clipping masks could also be useful for that, unfortunately SFML doesn't support them yet.

Depending on your project size and required flexibility, creating all the textures in a image editor before hand could give you the most artistic possibilities.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

SickReferenceBro

  • Newbie
  • *
  • Posts: 8
    • View Profile
    • Email
Re: Layering Two or More Textures on top of eachother (if its possible)
« Reply #5 on: December 18, 2015, 04:21:19 pm »
@Noct, it's not a bad idea but I am dead set on procedurally generated graphics

@eXpl0it3r, I recall clipping masks in photoshop, I am not to sure how I'll go about it but I'm sure I'll figure it out.

My current issue is down to something I don't quite understand, it's not an SFML issue as far as I can tell, it's just because I'm quite new to graphical programming.

but I'm putting the textures over a rectangle, and that's fine, but is it possible to instead of drawing a rectangle, drawing a shape that looks like a baseball bat or a sword, and then putting the textures over that?

I've seen how 3D models work, where you have the model and then the texture, but of course that's probably different in 2D graphical development.

Thanks for the help so far by the way guys, I'm gutted I never came to SFML sooner, it's well made and easy to work with and the few little projects I've done in my first week have been mindblowing to me!

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: Layering Two or More Textures on top of eachother (if its possible)
« Reply #6 on: December 18, 2015, 04:47:24 pm »


but I'm putting the textures over a rectangle, and that's fine, but is it possible to instead of drawing a rectangle, drawing a shape that looks like a baseball bat or a sword, and then putting the textures over that?
Clipping masks :D
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Arcade

  • Full Member
  • ***
  • Posts: 230
    • View Profile
Re: Layering Two or More Textures on top of eachother (if its possible)
« Reply #7 on: December 18, 2015, 04:58:40 pm »
And to be clear, the idea of a clipping mask is to cut out a portion of your texture in the shape of the baseball bat, and then draw that cut out section where you want it be used. It won't "wrap" and scale the texture around the baseball bat shape like what you could get with a 3d model. If you have a big blood texture, for example, you would need to decide the proper place within the texture to cut out.

And, as others have mentioned, SFML doesn't directly support clipping masks right now. Other people have attempted to come up with an implementation, though. If you search these forums you should be able to find more information on that.

Mortal

  • Sr. Member
  • ****
  • Posts: 284
    • View Profile
Re: Layering Two or More Textures on top of eachother (if its possible)
« Reply #8 on: December 18, 2015, 05:21:18 pm »
if i understand it, i believe it can be done by sf::RenderTexture like this

#include <SFML/Graphics.hpp>

int main()
{
        sf::RenderWindow window(sf::VideoMode(800, 600), "SFML art");
        window.setVerticalSyncEnabled(true);

        sf::RenderTexture target;
        if (!target.create(window.getSize().x, window.getSize().y))
                return -1;

        sf::Texture baseballTexture;

        if (!baseballTexture.loadFromFile("baseball.png")) // any baseball texture https://www.fotolia.com/p/202657402
                return -1;

        sf::Sprite baseball(baseballTexture);

        target.clear(sf::Color::White);
        target.draw(baseball, sf::BlendAlpha);
        target.display();

        while (window.isOpen())
        {
                sf::Event event;
                while (window.pollEvent(event))
                {
                        if (event.type == sf::Event::Closed)
                                window.close();
                }

                if (sf::Mouse::isButtonPressed(sf::Mouse::Left))
                {
                        // draw blood drops over background as many as you like
                        sf::CircleShape circle(5.f, 32u);
                        circle.setPosition(sf::Vector2f(sf::Mouse::getPosition(window)));
                        circle.setFillColor(sf::Color(255, 0, 0, 50));
                        target.draw(circle, sf::BlendAlpha);
                        target.display();
                }

                window.clear();
                window.draw(sf::Sprite(target.getTexture()));
                window.display();
        }
}

result will be something like this:



« Last Edit: December 18, 2015, 08:37:45 pm by MORTAL »

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: Layering Two or More Textures on top of eachother (if its possible)
« Reply #9 on: December 19, 2015, 12:01:33 am »
it can be done by sf::RenderTexture like this
I think the blood effect would be an image/texture that is overlaid on top of the original image that does not exceed the edge of that original (they mentioned clipping already above). Unfortunately, I don't think your solution achieves that.

You can achieve this using a render texture (as Mortal mentioned) though.
You can use the alpha channel of the base image directly.
Draw the image to be overlaid on an empty (transparent) render texture.
Draw the base image also onto the render texture using a blend mode that takes into account the alpha of the base image as well (ignore the base image's colour).
Use the render texture instead of the image to be overlaid.

Here's an example:
#include <SFML/Graphics.hpp>
#include <iostream>
int main()
{
        sf::Texture clipTexture, overlayTexture;
        if (!clipTexture.loadFromFile("sfml-logo-small.png") ||
                !overlayTexture.loadFromFile("blood.png"))
        {
                std::cerr << "Unable to load textures." << std::endl;
                return EXIT_FAILURE;
        }
        sf::Sprite clipSprite(clipTexture);
        sf::Sprite overlaySprite(overlayTexture);
        sf::RenderWindow window(sf::VideoMode(clipTexture.getSize().x, clipTexture.getSize().y), "Clip Sprite", sf::Style::Default);
        window.setFramerateLimit(60);

        sf::BlendMode blendMode(sf::BlendMode::Zero, sf::BlendMode::One, sf::BlendMode::Add, sf::BlendMode::DstAlpha, sf::BlendMode::OneMinusSrcAlpha, sf::BlendMode::Subtract);
        sf::RenderTexture renderTexture;
        renderTexture.create(clipTexture.getSize().x, clipTexture.getSize().y);
        renderTexture.clear(sf::Color::Transparent);
        renderTexture.draw(overlaySprite);
        renderTexture.draw(clipSprite, blendMode);
        renderTexture.display();
        sf::Sprite renderSprite(renderTexture.getTexture());

        bool showOverlay{ true };
        bool showBase{ true };
        while (window.isOpen())
        {
                sf::Event event;
                while (window.pollEvent(event))
                {
                        if (event.type == sf::Event::Closed || event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape)
                                window.close();
                        if (event.type == sf::Event::KeyPressed)
                        {
                                if (event.key.code == sf::Keyboard::Num1) // press 1 to toggle base/clip image
                                        showBase = !showBase;
                                else if (event.key.code == sf::Keyboard::Num2) // press 2 to toggle overlay image
                                        showOverlay = !showOverlay;
                        }
                }
                window.clear(sf::Color::Blue);
                if (showBase)
                        window.draw(clipSprite);
                if (showOverlay)
                        window.draw(renderSprite);
                window.display();
        }
        return EXIT_SUCCESS;
}
Both base sprite and overlaid sprite (clipped):

Overlaid sprite only (still clipped):


I have attached the two images (SFML logo and blood) so that you can try out this code as is.

EDIT: slightly modified provided code so that the window size is based on the size of the first texture, as is shown in the screenshots.
« Last Edit: December 19, 2015, 04:07:10 am by Hapax »
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

SickReferenceBro

  • Newbie
  • *
  • Posts: 8
    • View Profile
    • Email
Re: Layering Two or More Textures on top of eachother (if its possible)
« Reply #10 on: December 19, 2015, 01:22:34 am »
Thanks for all the responses, this is a very smart and active community!

I went and looked up clipping masks so I understand what they, wasn't sure how exactly they work but I guess mortal got the ball rolling and Hapax has just answered my last question!

Thanks a bunch everyone!