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

Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - TurboLento

Pages: [1]
1
Feature requests / Custom Blending Modes and other requests
« on: September 13, 2010, 12:37:46 am »
Glad it's helpful! I'll add it to the wiki.

It's at:
http://www.sfml-dev.org/wiki/en/sources/masking_using_alpha_and_blending
linked at the "Sources" area from the english wiki.
Hope it's all good :)

2
Feature requests / Custom Blending Modes and other requests
« on: September 12, 2010, 10:35:36 pm »
Ok, I understand that you pay a lot of attention and thought to the structure and cleanliness of SFML, which shows.

I went ahead and implemented a way to use masks as/on Sprites, Shapes and Strings. May not be perfect but it's a start and works for me:). Hope it may help others. It does involve changing the original SFML 1.6 code, adding the "virtual" keyword to the
Code: [Select]
sf::Drawable::Draw function.
I'll add this to the wiki if it's ok.
NOTE: it hasn't been extensively tested so there may be the occasional bug.

Below i'll post the code for several new classes i've added, and then a few example programs on how to use each class.


Ok, to start, you'll have to change a line in the original SFML 1.6 code, and recompile sfml or the "sfml-graphics" lib.

Go to the SFML source root folder, and open "include/SFML/Graphics/Drawable.hpp".
Then find the function definition
Code: [Select]
void Draw(RenderTarget& Target) const; (around line 335)
and replace it with
Code: [Select]
virtual void Draw(RenderTarget& Target) const; (add "virtual" at the start).
And recompile sfml-graphics.

New files:
MaskTypes.h - has the several masking types that you can use. Define a MaskType by using "MaskTypes::Type" and set them using "MaskTypes::BlendWithPreviousMask". The mask types are the following:
    MaskType::None - use the normal sf::BlendMode.
    MaskType::Mask - use this Drawable's Alpha mask as a Mask (only drawing the Drawable's Alpha info, not any color). Will blend the mask with previous existing masks.
    MaskType::MaskOverWritePreviousMask - similar to the previous, but will overwrite the previous mask instead of blending into it (allows to "cut" into an existing mask, and is used by example with "StringMask" - check the example)
    MaskType::BlendWithPreviousMask - set this mask to have a Drawable blend with the mask previously drawn on its area (only draw where alpha > 0)
    MaskType::BlendWithPreviousMaskReversed - set this mask to have a Drawable blend with the NEGATIVE of the mask previously drawn on its area (transparent areas become opaque and vice-versa).[/list]

    MaskUtils.h - Internal class, no need to call this from your code.
    Basically where all the magic happens, OpenGL calls are made, states are saved and restored, etc. Some code that was in "sf::Drawable" related to blend modes was pasted here.
    RenderWindowMask.h - must be used instead of a regular sf::RenderWindow, inherits from it. Adds the "ClearMask()" function, which clears just the color buffer's alpha channel.
    SpriteMasked.h - use this to draw a Sprite using masking, or to use a Sprite AS a mask. Inherits from sf::Sprite. Adds the "maskSetType/maskGetType" functions necessary to define the masking type.
    ShapeMasked.h - use this to use a Shape as a mask or to mask a Shape. Inherits from sf::Shape. Adds the "maskSetType/maskGetType" functions necessary to define the masking type.
    StringMasked.h - use this to use a String as a mask or to mask a String. Inherits from sf::String. Adds the "maskSetType/maskGetType" functions necessary to define the masking type.

    P.S. no need to preserve states (use "App.PreserveOpenGLStates(true)"), it's all done internally.

    MaskTypes.h
    Code: [Select]
    #ifndef MASKTYPES_H_INCLUDED
    #define MASKTYPES_H_INCLUDED

    class MaskTypes
    {
        public:
        enum Type
        {
            None = 0,
            Mask,
            MaskOverWritePreviousMask,
            BlendWithPreviousMask,
            BlendWithPreviousMaskReversed
        };
    };

    #endif // MASKTYPES_H_INCLUDED


    MaskUtils.h
    Code: [Select]

    #ifndef MASKUTILS_H_INCLUDED
    #define MASKUTILS_H_INCLUDED

    #include <SFML/Graphics/Drawable.hpp>

    using namespace sf;

    class MaskUtils
    {
        public:
            MaskUtils()
            {
                reset();
            }

            void reset()
            {
                restoreColorBuffer = false;
                restoreBlendMode = false;
            }

            void setBlendModesBasedOnMaskType(MaskTypes::Type maskType, Blend::Mode blendMode)
            {
                switch(maskType)
                {
                    case MaskTypes::None:
                    {
                        restoreBlendMode = true;
                        blendModePreviouslyEnabled = glIsEnabled(GL_BLEND);

                        // Setup alpha-blending
                        Blend::Mode myBlendMode = blendMode;
                        if (myBlendMode == Blend::None)
                        {
                            glDisable(GL_BLEND);
                        }
                        else
                        {
                            glEnable(GL_BLEND);

                            switch (myBlendMode)
                            {
                                case Blend::Alpha :    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); break;
                                case Blend::Add :      glBlendFunc(GL_SRC_ALPHA, GL_ONE);                 break;
                                case Blend::Multiply : glBlendFunc(GL_DST_COLOR, GL_ZERO);                break;
                                default :                                                                 break;
                            }
                        }
                    }
                    break;

                    case MaskTypes::Mask:
                    {
                        blendModePreviouslyEnabled = glIsEnabled(GL_BLEND);
                        glEnable(GL_BLEND);
                        glColorMask(false, false, false, true);// we can just write the alpha info
                        glBlendFunc(GL_DST_ALPHA, GL_SRC_ALPHA); // include the source (picture we are drawing) alpha, ignore the destination (info already in color buffer) alpha
                        restoreColorBuffer = true;
                        restoreBlendMode = true;
                    }
                    break;
                    case MaskTypes::MaskOverWritePreviousMask:
                    {
                        blendModePreviouslyEnabled = glIsEnabled(GL_BLEND);
                        glEnable(GL_BLEND);
                        glColorMask(false, false, false, true);// we can just write the alpha info
                        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // include the source (picture we are drawing) alpha, ignore the destination (info already in color buffer) alpha
                        restoreColorBuffer = true;
                        restoreBlendMode = true;
                    }
                    break;
                    case MaskTypes::BlendWithPreviousMask:
                    {
                        blendModePreviouslyEnabled = glIsEnabled(GL_BLEND);

                        // draw stuff inside window
                        glEnable(GL_BLEND);
                        glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA); // blend with alpha info in color buffer, which was written by previous image
                        restoreBlendMode = true;
                    }
                    break;
                    case MaskTypes::BlendWithPreviousMaskReversed:
                    {
                        blendModePreviouslyEnabled = glIsEnabled(GL_BLEND);

                        // draw stuff inside window
                        glEnable(GL_BLEND);
                        glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA); // blend with alpha info in color buffer, which was written by previous image
                        restoreBlendMode = true;
                    }
                    break;
                }
            }

            void restoreStates()
            {
                if(restoreColorBuffer)
                    glColorMask(true, true, true, true);  // make sure that for further images that we are writing all colors

                if(restoreBlendMode)
                {
                    if(blendModePreviouslyEnabled)
                        glEnable(GL_BLEND);
                    else
                        glDisable(GL_BLEND);
                }
            }

        private:
            bool restoreColorBuffer;
            bool restoreBlendMode;
            bool blendModePreviouslyEnabled;
    };

    #endif // MASKUTILS_H_INCLUDED


    RenderWindowMask.h
    Code: [Select]

    #ifndef RENDERWINDOWMASK_H_INCLUDED
    #define RENDERWINDOWMASK_H_INCLUDED

    #include <SFML/Graphics.hpp>
    #include "MaskTypes.h"

    using namespace sf;

    class RenderWindowMask : public RenderWindow
    {
        public:
            RenderWindowMask(
                                VideoMode Mode,
                                const std::string& Title,
                                unsigned long WindowStyle = Style::Resize | Style::Close,
                                const WindowSettings& Params = WindowSettings())
            {
                Create(Mode, Title, WindowStyle, Params);
            }

            RenderWindowMask(   WindowHandle Handle,
                                const WindowSettings& Params = WindowSettings())
            {
                Create(Handle, Params);
            }

            // clears the alpha channel
            void ClearMask()
            {
                glColorMask(false, false, false, true); // disable messing with the rgb channels, just change the alpha
                glClear(GL_COLOR_BUFFER_BIT);    // clear the color buffer (just the alpha channel)
                //glClearColor(FillColor.r / 255.f, FillColor.g / 255.f, FillColor.b / 255.f, FillColor.a / 255.f));
                glColorMask(true, true, true, true); // enable messing with the rgb channels, just change the alpha
            }
    };

    #endif // RENDERWINDOWMASK_H_INCLUDED


    SpriteMasked.h
    Code: [Select]

    #ifndef SPRITEMASKED_H_INCLUDED
    #define SPRITEMASKED_H_INCLUDED

    #include <SFML/Graphics/Sprite.hpp>
    #include "MaskTypes.h"
    #include "MaskUtils.h"

    using namespace sf;

    class SpriteMasked : public Sprite
    {
        public:
            SpriteMasked() :
            Sprite(),
            maskType(MaskTypes::None)
            {
            }

            void Draw(RenderTarget& Target) const
            {
                // Save the current modelview matrix and set the new one
                glMatrixMode(GL_MODELVIEW);
                glPushMatrix();
                glMultMatrixf(GetMatrix().Get4x4Elements());

                MaskUtils maskUtils;
                maskUtils.setBlendModesBasedOnMaskType(this->maskType, GetBlendMode());

                // Set color
                Color myColor = GetColor();
                glColor4f(myColor.r / 255.f, myColor.g / 255.f, myColor.b / 255.f, myColor.a / 255.f);

                // Let the derived class render the object geometry
                Render(Target);

                maskUtils.restoreStates();

                // Restore the previous modelview matrix
                glMatrixMode(GL_MODELVIEW);
                glPopMatrix();
            }

            void maskSetType(MaskTypes::Type maskType)
            {
                this->maskType = maskType;
            }

            MaskTypes::Type maskGetType()
            {
                return this->maskType;
            }

        private:
            MaskTypes::Type maskType;

    };

    #endif // SPRITEMASKED_H_INCLUDED


    ShapeMasked.h
    Code: [Select]

    #ifndef SHAPEMASKED_H_INCLUDED
    #define SHAPEMASKED_H_INCLUDED

    #include <SFML/Graphics/Shape.hpp>
    #include "MaskTypes.h"
    #include "MaskUtils.h"

    using namespace sf;

    class ShapeMasked : public Shape
    {
        public:
            ShapeMasked() :
            Shape(),
            maskType(MaskTypes::None)
            {}

            ShapeMasked(sf::Shape& shape) :
            Shape(),
            maskType(MaskTypes::None)
            {
                // copy points
                for(unsigned int idxPoint = 0; idxPoint < shape.GetNbPoints(); ++idxPoint)
                {
                    this->AddPoint(shape.GetPointPosition(idxPoint), shape.GetPointColor(idxPoint), shape.GetPointOutlineColor(idxPoint));
                }

                this->SetOutlineWidth(shape.GetOutlineWidth());
                this->SetColor(shape.GetColor());
            }

            void Draw(RenderTarget& Target) const
            {
                // Save the current modelview matrix and set the new one
                glMatrixMode(GL_MODELVIEW);
                glPushMatrix();
                glMultMatrixf(GetMatrix().Get4x4Elements());

                MaskUtils maskUtils;
                maskUtils.setBlendModesBasedOnMaskType(this->maskType, GetBlendMode());

                // Set color
                Color myColor = GetColor();
                glColor4f(myColor.r / 255.f, myColor.g / 255.f, myColor.b / 255.f, myColor.a / 255.f);

                // Let the derived class render the object geometry
                Render(Target);

                maskUtils.restoreStates();

                // Restore the previous modelview matrix
                glMatrixMode(GL_MODELVIEW);
                glPopMatrix();
            }

            void maskSetType(MaskTypes::Type maskType)
            {
                this->maskType = maskType;
            }

        private:
            MaskTypes::Type maskType;
    };

    #endif // SHAPEMASKED_H_INCLUDED


    StringMasked.h
    Code: [Select]

    #ifndef TEXTMASKED_H_INCLUDED
    #define TEXTMASKED_H_INCLUDED

    #include <SFML/Graphics.hpp>
    #include "MaskTypes.h"
    #include "MaskUtils.h"

    using namespace sf;

    class StringMasked : public sf::String
    {
        public:
            StringMasked() :
            String(),
            maskType(MaskTypes::None)
            {}

            StringMasked(sf::Shape& shape) :
            String(),
            maskType(MaskTypes::None)
            {
            }

            void Draw(RenderTarget& Target) const
            {
                // Save the current modelview matrix and set the new one
                glMatrixMode(GL_MODELVIEW);
                glPushMatrix();
                glMultMatrixf(GetMatrix().Get4x4Elements());

                bool restoreColorBuffer = false;
                bool restoreBlendMode = false;
                bool blendModePreviouslyEnabled;

                MaskUtils maskUtils;
                maskUtils.setBlendModesBasedOnMaskType(this->maskType, GetBlendMode());

                // Set color
                Color myColor = GetColor();
                glColor4f(myColor.r / 255.f, myColor.g / 255.f, myColor.b / 255.f, myColor.a / 255.f);

                // Let the derived class render the object geometry
                Render(Target);

                maskUtils.restoreStates();

                // Restore the previous modelview matrix
                glMatrixMode(GL_MODELVIEW);
                glPopMatrix();
            }

            void maskSetType(MaskTypes::Type maskType)
            {
                this->maskType = maskType;
            }

            MaskTypes::Type maskGetType()
            {
                return this->maskType;
            }

        private:
            MaskTypes::Type maskType;
    };

    #endif // TEXTMASKED_H_INCLUDED



    Example programs:

    SpriteMask example:

    (actually, shows two examples: on the left, we can see a simple masking example. On the right, a way to do sub-windowing, by combining overlapping masked elements).
    Code: [Select]
    #include <SFML/System.hpp>
    #include <SFML/Graphics.hpp>
    #include <iostream>
    #include <fstream>

    #include "SpriteMasked.h"
    #include "RenderWindowMask.h"

    int main()
    {
        //sf::RenderWindow App(sf::VideoMode(800, 600, 32), "SFML Graphics");
        RenderWindowMask App(sf::VideoMode(800, 600, 32), "SFML Graphics");
        //App.PreserveOpenGLStates(true);

        sf::Clock Clock;

        sf::Image Image;
        Image.LoadFromFile("northeast.jpg");


        sf::Image ImageMask;
        ImageMask.LoadFromFile("images/window.tga");

        SpriteMasked spriteMask;
        spriteMask.SetImage(ImageMask);
        spriteMask.SetPosition(200, 200);
        spriteMask.maskSetType(MaskTypes::Mask);


        sf::Image ImageInsideWindow;
        ImageInsideWindow.LoadFromFile("images/image.tga");
        SpriteMasked spriteInsideWindow;
        spriteInsideWindow.SetImage(ImageInsideWindow);
        spriteInsideWindow.SetPosition(200, 200);


        const sf::Input& Input = App.GetInput();
        int             mouseXPrev = Input.GetMouseX(),
                        mouseYPrev = Input.GetMouseY(),
                        mouseX, mouseY;

        float timeSinceLastUpdate;

        int a = 0;


        // Start game loop
        while (App.IsOpened())
        {
            // Process events
            sf::Event Event;
            while (App.GetEvent(Event))
            {
                // Close window : exit
                if (Event.Type == sf::Event::Closed)
                    App.Close();
                if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Escape))
                    App.Close();
            }

            timeSinceLastUpdate = Clock.GetElapsedTime();
            Clock.Reset();

            mouseX = Input.GetMouseX();
            mouseY = Input.GetMouseY();
            float mouseMoveX = (float)(mouseXPrev - mouseX) * timeSinceLastUpdate * 6.0f;
            float mouseMoveY = (float)(mouseYPrev - mouseY) * timeSinceLastUpdate * 6.0f;
            mouseXPrev = mouseX;
            mouseYPrev = mouseY;

            App.Clear(sf::Color(200,0,0,255));


            /////
            ///// Example 1: simple mask
            /////
            // draw sprite to use as mask
            spriteMask.SetScale(1, 1);
            spriteMask.SetPosition(100, 250);
            spriteMask.maskSetType(MaskTypes::Mask);
            App.Draw(spriteMask);

            // draw normal sprite
            spriteInsideWindow.maskSetType(MaskTypes::BlendWithPreviousMask);
            spriteInsideWindow.SetScale(1, 1);
            spriteInsideWindow.SetPosition(100, 250);
            App.Draw(spriteInsideWindow);


            /////
            ///// Example 2: use masking to do sub-windowing
            /////

            // how to draw sub-windows:
            // 1) draw window contents first
            // 2) draw stuff outside afterwards
            // details:
            // 1) draw window contents - background to foreground
            //   a) draw window bg (don't care about mask)
            //   b) draw mask
            //   c) draw "negative" image (BlendWithPreviousMaskReversed)
            //   d) draw other stuff on top of it
            //   e) draw parent mask on top of it
            // 2) draw stuff outside
            //   a) draw "positive" other stuff over it (BlendWithPreviousMask)
            //   ( b) draw mask again if necessary for every new image to draw? dunno)


            // 1) draw window contents - background to foreground
            //   a) draw window bg (don't care about mask)
            //   b) draw mask for next element
            //   c) draw "negative" image (BlendWithPreviousMaskReversed)
            //   d) draw other stuff on top of it
            //   e) draw parent mask on top of it

            //   a) draw window bg (don't care about mask)
            spriteInsideWindow.SetScale(1,1);
            spriteInsideWindow.maskSetType(MaskTypes::None);
            spriteInsideWindow.SetPosition(500, 250);
            App.Draw(spriteInsideWindow);


            //   b) draw mask for next element (1)
            spriteMask.SetScale(0.4, 0.4);
            spriteMask.SetPosition(550, 250);
            spriteMask.maskSetType(MaskTypes::Mask);
            App.Draw(spriteMask);


            //   c) draw "negative" image (BlendWithPreviousMaskReversed) (1)
            spriteInsideWindow.maskSetType(MaskTypes::BlendWithPreviousMask);
            spriteInsideWindow.SetScale(0.4, 0.4);
            spriteInsideWindow.SetPosition(550, 250);
            App.Draw(spriteInsideWindow);


            //   b) draw mask for next element (2)
            spriteMask.SetScale(0.4, 0.4);
            spriteMask.SetPosition(590, 250);
            spriteMask.maskSetType(MaskTypes::Mask);
            App.Draw(spriteMask);

            //   c) draw "negative" image (BlendWithPreviousMaskReversed) (2)
            spriteInsideWindow.maskSetType(MaskTypes::BlendWithPreviousMask);
            spriteInsideWindow.SetScale(0.4, 0.4);
            spriteInsideWindow.SetPosition(590, 250);
            App.Draw(spriteInsideWindow);



            //   e) draw parent mask on top of it
            spriteMask.SetScale(1.0, 1.0);
            spriteMask.SetPosition(500, 200);
            spriteMask.maskSetType(MaskTypes::Mask);
            App.Draw(spriteMask);


            // 2) draw stuff outside
            //   a) draw "positive" other stuff over it (BlendWithPreviousMask)
            spriteInsideWindow.maskSetType(MaskTypes::BlendWithPreviousMaskReversed);
            spriteInsideWindow.SetPosition(350,150);
            spriteInsideWindow.SetScale(2,2);
            App.Draw(spriteInsideWindow);

            // Display window contents on screen
            App.Display();
        }

        return 0;
    }


    ShapeMask example:
    (masking using a sf::Circle)

    Code: [Select]

    #include <SFML/System.hpp>
    #include <SFML/Graphics.hpp>
    #include <iostream>
    #include <fstream>

    #include "SpriteMasked.h"
    #include "ShapeMasked.h"
    #include "RenderWindowMask.h"



    int main()
    {
        //sf::RenderWindow App(sf::VideoMode(800, 600, 32), "SFML Graphics");
        RenderWindowMask App(sf::VideoMode(800, 600, 32), "SFML Graphics");

        sf::Clock Clock;


        sf::Image ImageInsideWindow;
        ImageInsideWindow.LoadFromFile("images/image.tga");
        SpriteMasked spriteInsideWindow;
        spriteInsideWindow.SetImage(ImageInsideWindow);
        spriteInsideWindow.SetPosition(200, 200);
        spriteInsideWindow.maskSetType(MaskTypes::BlendWithPreviousMask);


        const sf::Input& Input = App.GetInput();
        int             mouseXPrev = Input.GetMouseX(),
                        mouseYPrev = Input.GetMouseY(),
                        mouseX, mouseY;

        float timeSinceLastUpdate;


        // masking for shapes
        sf::Shape circle = sf::Shape::Circle(0, 0, 100, sf::Color(255,255,255,0)); // note: zero alpha
        ShapeMasked shapeMask(circle);
        shapeMask.maskSetType(MaskTypes::Mask);

        // Start game loop
        while (App.IsOpened())
        {
            // Process events
            sf::Event Event;
            while (App.GetEvent(Event))
            {
                // Close window : exit
                if (Event.Type == sf::Event::Closed)
                    App.Close();
                if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Escape))
                    App.Close();
            }

            timeSinceLastUpdate = Clock.GetElapsedTime();
            Clock.Reset();

            App.Clear(sf::Color(200,0,0,255));


            // draw shape mask
            shapeMask.SetPosition(500,300);
            App.Draw(shapeMask);

            // draw masked image
            spriteInsideWindow.SetPosition(450, 200);
            App.Draw(spriteInsideWindow);


            // Display window contents on screen
            App.Display();
        }

    }


    StringMask example:

    Code: [Select]

    #include <SFML/System.hpp>
    #include <SFML/Graphics.hpp>
    #include <iostream>
    #include <fstream>

    #include "StringMasked.h"
    #include "SpriteMasked.h"
    #include "RenderWindowMask.h"


    int main()
    {
        //sf::RenderWindow App(sf::VideoMode(800, 600, 32), "SFML Graphics");
        RenderWindowMask App(sf::VideoMode(800, 600, 32), "SFML Graphics"); // we'll need to use our RenderWindow to have access to "ClearMask" function
        //App.PreserveOpenGLStates(true);

        //std::ofstream myfile;
        //myfile.open ("pixels.txt");

        sf::Clock Clock;

        sf::Image Image;
        Image.LoadFromFile("northeast.jpg");

        sf::Image ImageInsideText;
        ImageInsideText.LoadFromFile("images/image.tga");

        SpriteMasked spriteInsideText;
        spriteInsideText.SetImage(ImageInsideText);

        float timeSinceLastUpdate;


        // masking for text
        // Create a graphical string
        StringMasked stringMask;
        stringMask.SetText("Hello !\nHow are you ?");
        stringMask.SetFont(sf::Font::GetDefaultFont());
        stringMask.SetPosition(100,50);

        stringMask.maskSetType(MaskTypes::Mask);
        stringMask.SetColor(sf::Color(0,0,0,255));

        // Start game loop
        while (App.IsOpened())
        {
            // Process events
            sf::Event Event;
            while (App.GetEvent(Event))
            {
                // Close window : exit
                if (Event.Type == sf::Event::Closed)
                    App.Close();
                if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Escape))
                    App.Close();
            }

            timeSinceLastUpdate = Clock.GetElapsedTime();
            Clock.Reset();


            App.Clear(sf::Color(200,0,0,255)); // clear the background to red

            //// How to correctly draw a String to Mask/MaskReverse a sprite:
            //// 1. clear the Window Alpha Mask before drawing the text (or the sprite may appear where you drew previous masks)
            //// 2. draw the SPRITE as the mask (masking everywhere where the sprite would be)
            //// 3. draw the STRING using the mask type "MaskOverWritePreviousMask" (thus "cutting" into the mask the previous sprite left)
            //// 4. draw the Sprite again, normally, using the mask type "BlendWithPreviousMask"

            App.ClearMask(); // 1. clears just the Window's alpha channel

            // 2. use sprite to draw as mask
            spriteInsideText.SetPosition(100,10);

            // save the sprite mask and color state (may be necessary if you reuse this sprite elsewhere)
            MaskTypes::Type prevMaskType = spriteInsideText.maskGetType();
            sf::Color prevSpriteColor = spriteInsideText.GetColor();

            spriteInsideText.SetColor(sf::Color(0,0,0,0));    // set the sprite's alpha to 0
            spriteInsideText.maskSetType(MaskTypes::Mask);    // set the sprite to act as a mask
            App.Draw(spriteInsideText);

            // restore sprite mask+color state
            spriteInsideText.maskSetType(prevMaskType);
            spriteInsideText.SetColor(prevSpriteColor);


            // 3. draw text mask
            stringMask.SetPosition(100,50);
            stringMask.maskSetType(MaskTypes::MaskOverWritePreviousMask);
            App.Draw(stringMask);

            // 4. draw the masked sprite
            spriteInsideText.SetPosition(100,10);
            // using this blending mode, the sprite will be drawn normally and the text area will be "cut" from the sprite
            spriteInsideText.maskSetType(MaskTypes::BlendWithPreviousMask);

            App.Draw(spriteInsideText);




            // Example 2:
            App.ClearMask(); // 1. clears just the Window's alpha channel

            // 2. use sprite to draw as mask
            spriteInsideText.SetPosition(500,10);

            // save the sprite mask and color state (may be necessary if you reuse this sprite elsewhere)
            prevMaskType = spriteInsideText.maskGetType();
            prevSpriteColor = spriteInsideText.GetColor();

            spriteInsideText.SetColor(sf::Color(0,0,0,0));    // set the sprite's alpha to 0
            spriteInsideText.maskSetType(MaskTypes::Mask);    // set the sprite to act as a mask
            App.Draw(spriteInsideText);

            // restore sprite mask+color state
            spriteInsideText.maskSetType(prevMaskType);
            spriteInsideText.SetColor(prevSpriteColor);


            // 3. draw text mask
            stringMask.SetPosition(500,50);
            stringMask.maskSetType(MaskTypes::MaskOverWritePreviousMask);
            App.Draw(stringMask);

            // 4. draw the masked sprite
            spriteInsideText.SetPosition(500,10);

            // using this blending mode, the sprite will be drawn "inside" the text's characters
            spriteInsideText.maskSetType(MaskTypes::BlendWithPreviousMaskReversed);

            App.Draw(spriteInsideText);


            App.Display(); // Display window contents on screen
        }

        //myfile.close();
        return 0;
    }


    This method has a few inconveniences, such as having the overhead of redrawing polygons, and Strings have some boring extra work needed and work the other way around than sprites and shapes, but I think it's altogether a good and flexible solution, and all-encompassing, technology-wise (supporting older hardware).


    Plus, here's a little function for debugging, that saves the current alpha buffer to a file named "imageAlpha.png" (upside down, OpenGL stuff). May help you debug if you run into problems.
    Code: [Select]

    void GetGLPixels()
    {
        unsigned int startX = 0, startY = 0;
        unsigned int width = 800, height = 600;

        GLubyte* pixels = new GLubyte[width * height * 4];

        glReadPixels(startX, startY, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);


        for(unsigned int idxPixelRow = 0; idxPixelRow < height; idxPixelRow++)
        {
            for(unsigned int idxPixelColumn = 0; idxPixelColumn < width; idxPixelColumn++)
            {
                unsigned int index = ((idxPixelRow * width) + idxPixelColumn) * 4;
                unsigned int indexAlpha = ((idxPixelRow * width) + idxPixelColumn);

                float valueRed = pixels[index + 0];
                float valueGreen = pixels[index + 1];
                float valueBlue = pixels[index + 2];
                float valueAlpha = pixels[index + 3];

                pixels[index] = pixels[index + 1] = pixels[index + 2] = 0;
            }
        }


        // create and save image
        sf::Image imageAlpha;
        imageAlpha.LoadFromPixels(width, height, pixels);
        imageAlpha.SaveToFile("imageAlpha.png");


        delete pixels;
    }

    3
    Feature requests / Custom Blending Modes and other requests
    « on: August 30, 2010, 02:03:06 am »
    Hi, Laurent. I'd like to present a few ideas and to humbly request for a few features to allow such ideas to be made.

    Recently I've been looking into how to go about creating Masking/Sub Windows using SFML/OpenGL, using masks including their alpha information (so that we can have windows with areas with partial transparency, etc)

    An example of what I mean:
    Using the following 3 images, the first would be the window contents, the second would serve as the window mask (the grey rectangles area has alpha = 0, and some areas near the inner borders have slightly transparent areas), and the last image would be the background.


    This would result in the image:

    Note the intended fading detail around the mask borders.

    After a week of research and experimenting, I found the following solutions as to how to go about doing this, using OpenGL:

    - use the stencil buffer (works ok, except that I couldn't get the stencil buffer to store alpha information; not supported in older hardware)
    - multi-texturing (complicated)
    - render to texture, using multiple images with the intended alphas and blending modes (not supported in older hardware and some mobile devices (i.e. IPhone, from what I gather))
    - using blending, writing to the color buffer and defining appropriate blending modes, and overlaying images (works well and is the most supported action, including very old hardware).

    So I've decided to go with the last option. I've managed to implement this using plain OpenGL, and by doing the following actions:

    - draw background image(s)
    - enable blending
    - disable writing to r,g or b color channels, allow only writing to alpha channel in the color buffer (glColorMask(false, false, false, true))
    - set blend mode to glBlendFunc(GL_SRC_ALPHA, GL_ZERO) (include the source (picture we are drawing) alpha, ignore the destination (info already in color buffer) alpha)
    - draw mask (image containing stuff to mask by having pixels with alpha < 1)
    - enable writing to all channels in the color buffer with glColorMask(true, true, true, true)
    - set blend mode to write pixels taking in consideration the alpha in the DESTINATION (what's already written to the color buffer's alpha channel) glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA); (blend with alpha info in color buffer, which was written by previous image)
    - draw images that are inside the window (thus masking their pixels using info in the color buffer)
    - repeat previous steps for each supplemental image to draw inside the "window"/mask

    I see requests for these features in several other threads (both masking and custom blend modes), so I think this is of interest to current and future SFML users.

    So here comes the request. I'd like if one of these were made officially in future SFML releases:

    OPTION 1 (simpler in my opinion):
    - make Drawable::Draw() virtual (so as to allow to inherit from Sprite, redefine Sprite::Draw, and create custom blending modes, or adjust the code called in Drawable::Draw to our needs).

    OPTION 2:
    - create a custom BlendMode option and allow passing direct OpenGL glBlend parameters, or a more high-level approach, as you have with BlendMode::Add, Multiply etc.

    I'd be willing to post code/do a wiki on how to use masking to do a window effect.

    Thanks!

    4
    Graphics / Problem with deforming
    « on: July 23, 2010, 09:05:18 pm »
    Ok, I figured it out. It had to do with the texture mapping, it could not be done using glTexCoords2* but had to include the "q" coordinate (thus, the use of glTexCoords4* was necessary). It had to do with mapping the texture to a trapezoidal quad, which causes the undesired effect.

    I've implemented a Sprite which allows the definition of a few parameters to emulate Mode-7 (simulated perspective), by shrinking the top or bottom edge of the sprite and mapping the texture accordingly.



    Commented code is below, along with the pages which helped me figure out what the problem was, the solution, and how to calculate that damned q coord.

    Code: [Select]

    #ifndef SPRITEMODE7_H_INCLUDED
    #define SPRITEMODE7_H_INCLUDED

    #include <SFML/Graphics/Sprite.hpp>

    using namespace sf;

    class SpriteMode7 : public Sprite
    {
        public:
            SpriteMode7():
            Sprite()
            {
                //this->displacementTop = 200.0f;
                this->displacementTop = 0.0f;
                this->displacementBottom = 0.0f;
                this->depthTop = 3.0f;
                this->depthBottom = 1.0f;
                this->scaleVerticalRatio = 1.0f;
            }

            void displacementTopSet(float newDisplacementTop)
            {
                this->displacementTop = newDisplacementTop;
            }

            float displacementTopGet()
            {
                return this->displacementTop;
            }

            void displacementBottomSet(float newDisplacementBottom)
            {
                this->displacementBottom = newDisplacementBottom;
            }

            float displacementBottomGet()
            {
                return this->displacementBottom;
            }

            void depthTopSet(float newDepthTop)
            {
                this->depthTop = newDepthTop;
            }

            float depthTopGet()
            {
                return this->depthTop;
            }

            void depthBottomSet(float newDepthBottom)
            {
                this->depthBottom = newDepthBottom;
            }

            float depthBottomGet()
            {
                return this->depthBottom;
            }

            void Render(RenderTarget& rt) const
            {
                //std::cout << "Sprite::Render()\n";
                IntRect _mySubRect = GetSubRect();

                // Get the sprite size
                float Width  = static_cast<float>(_mySubRect.GetWidth());
                float Height = static_cast<float>(_mySubRect.GetHeight());

                // Check if the image is valid
                const Image* _myImage = GetImage();
                if (_myImage && (_myImage->GetWidth() > 0) && (_myImage->GetHeight() > 0))
                {
                    // Use the "offset trick" to get pixel-perfect rendering
                    // see http://www.opengl.org/resources/faq/technical/transformations.htm#tran0030
                    glTranslatef(0.375f, 0.375f, 0.f);

                    // Bind the texture
                    _myImage->Bind();

                    // Calculate the texture coordinates
                    FloatRect TexCoords = _myImage->GetTexCoords(_mySubRect);
                    /*
                    FloatRect Rect(myIsFlippedX ? TexCoords.Right  : TexCoords.Left,
                                   myIsFlippedY ? TexCoords.Bottom : TexCoords.Top,
                                   myIsFlippedX ? TexCoords.Left   : TexCoords.Right,
                                   myIsFlippedY ? TexCoords.Top    : TexCoords.Bottom);
                    */
                    FloatRect Rect(TexCoords.Left,
                                   TexCoords.Top,
                                   TexCoords.Right,
                                   TexCoords.Bottom);

                    // Draw the sprite's triangles
                    //glBegin(GL_QUADS);
                    glBegin(GL_TRIANGLES);

                        // For the sprite to be displayed correctly (with correct texture mapping, no visible seam in the middle where
                        // the two triangles meet) and appearing with perspective (texture along the furthest edge is "smaller"),
                        // must emulate perspective projection of the texture, using the q coordinate and glTexCoord4f.
                        //
                        // http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Board=2&Number=229833
                        // http://glprogramming.com/red/chapter09.html
                        // http://www.xyzw.us/~cass/qcoord/
                        // http://www.idevgames.com/forums/thread-7430.html
                        // http://www.gamedev.net/community/forums/topic.asp?topic_id=419296
                        //
                        // Details:
                        // - it is best to define the vertices in an order where the "furthest" texture coordinates are in the same line
                        // - using GL_TRIANGLES, could not make it work with GL_QUADS
                        // - it is important that the invW calculation aligns precisely with the ratio we are shrinking an edge, or the texture
                        //      won't line up in the middle; e.g. if an edge is to have 0.5 it's original length, it's z depth should be 2, so that invW = 0.5

                        float invWTop = 1.0f / depthTop;
                        float invWBottom = 1.0f / depthBottom;
                        float edgeMiddle = Width * 0.5f;
                        float edgeScaleTop = edgeMiddle / depthTop; //(Width / (depthTop * 2.0f);
                        float edgeScaleBottom = edgeMiddle / depthBottom; //(Width / (depthBottom * 2.0f);

                        glTexCoord4f(invWTop, 0, 0, invWTop);
                        glVertex2f(edgeMiddle + edgeScaleTop + displacementTop, 0);

                        glTexCoord4f(0, 0, 0, invWTop);
                        glVertex2f(edgeMiddle - edgeScaleTop + displacementTop, 0);

                        glTexCoord4f(0,  invWBottom, 0, invWBottom);
                        glVertex2f(edgeMiddle - edgeScaleBottom + displacementBottom, Height * scaleVerticalRatio);

            //////
            ////// TRI 2
            //////
                        glTexCoord4f(invWTop, 0, 0, invWTop);
                        glVertex2f(edgeMiddle + edgeScaleTop + displacementTop, 0);

                        glTexCoord4f(0, invWBottom, 0, invWBottom);
                        glVertex2f(edgeMiddle - edgeScaleBottom + displacementBottom, Height * scaleVerticalRatio);

                        glTexCoord4f(invWBottom, invWBottom, 0, invWBottom);
                        glVertex2f(edgeMiddle + edgeScaleBottom + displacementBottom, Height * scaleVerticalRatio);

                    glEnd();
                }
                else
                {
                    // Disable texturing
                    glDisable(GL_TEXTURE_2D);

                    // Draw the sprite's triangles
                    glBegin(GL_QUADS);
                        glVertex2f(0,     0);
                        glVertex2f(0,     Height);
                        glVertex2f(Width, Height);
                        glVertex2f(Width, 0);
                    glEnd();
                }
            }

        private:
            float   depthTop, // z depth of sprite's top edge
                    depthBottom,    // z depth of sprite's bottom edge
                    displacementTop,    // number of pixels to horizontally displace this sprite's top edge
                    displacementBottom, // number of pixels to horizontally displace this sprite's bottom edge
                    scaleVerticalRatio;  // scale to vertically apply to sprite, to achieve a better perspective effect
    };

    #endif // SPRITEMODE7_H_INCLUDED

    5
    Graphics / Problem with deforming
    « on: July 22, 2010, 04:27:57 am »
    First of all, this is mostly an OpenGL question, but since my OpenGL knowledge isn't very extensive, my searches aren't being fruitful in solving the problem, and that my issue may be related to some specific code in SFML, I'm posing it here.

    I'm trying to extend a Sprite to allow for Mode-7 (http://en.wikipedia.org/wiki/Mode_7) rendering.

    I have decided to attempt to inherit from Sprite and overload Render().

    Initially, I just want to deform the sprite in a way that the upper edge is smaller than the lower ("scaling" the sprite's upper edge). To do this, I'm simply changing the coordinates for the sprite's top vertices, in the followin manner:

    (in Sprite::Render, replacing the OpenGL rendering part with:)
    Code: [Select]

    glBegin(GL_QUADS);
                glTexCoord2f(Rect.Left,  Rect.Top);    glVertex2f(Width * 0.25f,     0);
                glTexCoord2f(Rect.Left,  Rect.Bottom); glVertex2f(0,     Height);
                glTexCoord2f(Rect.Right, Rect.Bottom); glVertex2f(Width, Height);
                glTexCoord2f(Rect.Right, Rect.Top);    glVertex2f(Width - (Width * 0.25f), 0) ;


    The resulting image has a weird deformation along the line where the polygons meet, not mapping the texture smoothly along the deformed quad.

    Original image:


    Result image:


    What I intended image (mockup):


    Can anyone point me towards some info? Sorry if it's offensive to be this off-topic

    6
    SFML wiki / /!\ List of GUI based on SFML /!\
    « on: May 03, 2010, 11:59:17 pm »
    GUIChan is not here:
    http://guichan.sourceforge.net/wiki/index.php/Main_Page
    and
    http://code.google.com/p/guichan/

    Screenshots:
    http://guichan.sourceforge.net/oldsite/screenshots.shtml

    Integrate guichan into SFML (french tutorial and code):
    http://www.sfml-dev.org/wiki/fr/tutoriels/utiliserguichan

    (Thanks to Lokk for creating the binder to SFML and writing the tutorial)

    GUIChan once had a forum which was closed, but the author (Olof Naessen) is contactable through several means and is active in several forums related to 2D libraries, and replies quickly.

    7
    Window / MouseLeft/MouseEntered and Input.GetMouseX() issue
    « on: April 11, 2010, 09:12:14 pm »
    Wasn't easy, it seems to happen at very particular window configurations. By example, I got it to occur in windowed mode in 650x480, but not at 640x480 or 649x490... odd. And it only happens if RenderWindow is initialized with the flag "sf::Style::Close". I was testing it in my desktop's resolution at 1280x720 and it also occurred.

    By the way, I didn't mention that I'm running SFML in Windows XP.

    The code below creates a window, draws a rectangle at 50,50 to 150,150 and moves the camera to the left if the user places the mouse close to the left screen edge (stopping when the view's left side == 0) or to the right if the user places the mouse close to the right edge.

    The bug is detected when the user places the mouse very near the left screen edge, where the x mouse coordinate turns from 0 to the large value, while the MouseLeft event is not launched, and the camera pans incorrectly (to the right instead of left or stopping).

    Code: [Select]

    #include <SFML/System.hpp>
    #include <SFML/Graphics.hpp>
    #include <iostream>

    sf::Vector2f getCameraPosition(sf::RenderWindow& rw)
    {
        sf::Vector2f pos(rw.GetDefaultView().GetCenter());
        sf::Vector2f halfDims(rw.GetDefaultView().GetHalfSize());
        return sf::Vector2f(pos.x - halfDims.x, pos.y - halfDims.y);
    }

    void setCameraPosition(sf::RenderWindow& rw, const sf::Vector2f& newPosition)
    {
        sf::Vector2f halfDims(rw.GetDefaultView().GetHalfSize());
        rw.GetDefaultView().SetCenter(newPosition.x + halfDims.x, newPosition.y + halfDims.y);
    }

    int main()
    {
        sf::Clock Clock;

        // Create the main rendering window
        sf::RenderWindow App(sf::VideoMode(650, 480, 32), "SFML Graphics", sf::Style::Close);

        bool mouseIsInside = true;

        sf::Shape rectangle = sf::Shape::Rectangle(50, 50, 150, 150, sf::Color(255,0,0,255));

        // Start game loop
        while (App.IsOpened())
        {
            float timeSinceLastUpdate = Clock.GetElapsedTime();
            Clock.Reset();

            // Process events
            sf::Event Event;
            while (App.GetEvent(Event))
            {
                // Close window : exit
                if (Event.Type == sf::Event::Closed)
                    App.Close();
                if(Event.Type == sf::Event::MouseLeft)
                    mouseIsInside = false;
                if(Event.Type == sf::Event::MouseEntered)
                    mouseIsInside = true;
            }

            sf::Vector2f mousePosition(App.GetInput().GetMouseX(), App.GetInput().GetMouseY());
            sf::Vector2f cameraPosition = getCameraPosition(App);
            float scrollSpeed = 100;
            float coordLeftWindowBorder = cameraPosition.x;

            if(mouseIsInside)
            {
                std::cout << "mouse pos:" << mousePosition.x << ", "<< mousePosition.y << "\n";

                if(mousePosition.x > 590)
                    setCameraPosition(App, cameraPosition + sf::Vector2f(scrollSpeed * timeSinceLastUpdate,0));
                else
                if((mousePosition.x < 0 + 50) && (coordLeftWindowBorder > 0))
                    setCameraPosition(App, cameraPosition - sf::Vector2f(scrollSpeed * timeSinceLastUpdate,0));
            }
            //else
            //    std::cout << "out ";

            App.Draw(rectangle);

            // Display window contents on screen
            App.Display();

            // Clear the screen (fill it with black color)
            App.Clear();
        }

        return EXIT_SUCCESS;
    }

    8
    Window / MouseLeft/MouseEntered and Input.GetMouseX() issue
    « on: April 11, 2010, 06:11:17 pm »
    Hi all,

    I've noticed a problem with my program and was wondering if I've detected a bug or if I'm doing anything wrong.

    I'm detecting the sf::Event::MouseEntered/MouseLeft events for my window, and only obtaining the mouse coordinates and doing logic if the mouse is inside. However, if I move the mouse close to the left edge (where mouseX = 0), sometimes either the MouseEntered event will fire (if I've moved the mouse out of the window) or no MouseLeft event will fire, but GetMouseX() will return "65535".

    (I've hacked a function "isMouseInsideWindow()" returning "(this->mouseIsInsideWindow && (this->getMouseX() < this->renderWindow->GetWidth()));", but it's ugly).

    Any ideas? I'm using SFML 1.5, but I don't see any mention to this in the 1.6 change log.

    9
    General / Help setting sfml up
    « on: April 01, 2010, 03:26:24 pm »
    Hi,
    I think you're not linking to "sfml-window-d.lib". In your "Additional Dependencies" section, add libraries in this order:
    sfml-graphics-d.lib
    sfml-window-d.lib
    sfml-system-d.lib

    Oddly, I think the tutorial page for compiling with SFML in VC++ doesn't mention this, but the one for Code::Blocks does.

    Edit: edited for library order correction.

    Pages: [1]