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 - StriderPulse599

Pages: [1] 2
1
Graphics / How sf::VertexArray works OpenGL-wise?
« on: March 01, 2024, 11:29:59 am »
I tried digging through source code but can't pinpoint what does what. I'm almost done with learning OpenGL, but I want to learn how SFML handled sf::VertexArray since I've been using it a lot.

2
General / Is drawing to sf::Texture a good idea?
« on: December 20, 2023, 03:09:21 am »
Currently I'm using a single sf::VertexArray to draw to one window, then modify some vertices before drawing to another window.

Would drawing shared elements to sf::Texture and then drawing over it be a good idea? I know that draw calls are generally expensive, so I wanted to ask first

3
General / Re: What image formats are supported?
« on: December 11, 2023, 04:36:35 pm »
To be clear, this:
https://www.sfml-dev.org/documentation/2.6.1/
is the API documentation (that you mentioned in the original post).
There, you can click Classes and look up all the available classes, including sf::Image.

Hope this helps :)

I've actually swept through docs, but I didn't expect it to be hidden inside "Member Function". This is really important stuff that should be in "Detailed Description", and tutorial should say where exactly it is

4
General / Re: What image formats are supported?
« on: December 10, 2023, 02:09:21 pm »
https://www.sfml-dev.org/documentation/2.6.1/classsf_1_1Image.php#a9e4f2aa8e36d0cabde5ed5a4ef80290b

Quote
The supported image formats are bmp, png, tga, jpg, gif, psd, hdr, pic and pnm. Some format options are not supported, like jpeg with arithmetic coding or ASCII pnm. If this function fails, the image is left unchanged.

Thanks, this information should've been included in detailed description for sake of convenience

5
General / What image formats are supported?
« on: December 09, 2023, 10:36:02 pm »
Tutorial only says "SFML supports most common image file formats. The full list is available in the API documentation. " which is nowhere to be seen

6
General / Re: Is this DDA raycast algorithm good enough?
« on: November 18, 2023, 07:56:05 pm »
I'm currently busy with other things

Okay. Then my only question is: what do "wonky" and "clipping" mean in the following context?:
The only problem is how wonky the TriangleFan can be, as more than 0.1 difference between angles cause clipping

At higher difference between angles and bigger distance, the line between two TriangleFan points will clip through the object (or be too far off) and often create wiggle effect. It's barely noticeable up to 0.3 (depends on distance mostly), and the only alternative is instead casting one ray per tile, check if it hit something, and then cast two rays at edges of objects.

For now I need to actually focus on gameplay, then I'll see if I need fully dynamic light or static lightmap/floodfill

7
General / Re: Is this DDA raycast algorithm good enough?
« on: November 15, 2023, 06:41:44 pm »
    for (float i = 0; i < 6.27; i += 0.01)
    {
        lightBaker(i);
    }
I'm guessing this creates 627 vectors around the circle (not perfectly even at the end, though, due to the obvious rounding of pi), right? So it's a sort of LUT?
I don't think that's what the term light baking refers to but it's up to you what you call things, of course ;D

That aside, when you're casting rays, do you need this many? I would presume that once you hit something, you can track it in both directions until you reach the line's end points and all of the directions between could be discarded unless there is something in front. Maybe its simpler to calculate all edges that are facing the player and flatten them (in a similar way in concept to the way 3D perspective is flattened to 2D) and then check each line (and cull ones fully "behind" other lines, again like perspective culling).
Your way (although it may be the best way; I've not intensely tested it) seems to be more the "bitmap/brute force" to the other (my?) way's "vector/scalable" graphics. Just a metaphor of how I'm visualising it.

I only looked at those parts because, as eXpl0it3r mentioned, nobody is going to look through your entire code unless you have a specific issue; those parts jumped out at me. :)

This is for 2D lightning, not a raycast game. The "baking" is just calculating all sin,cos,tan and couple of other values which are used by all light entities (saves some CPU). As for the name itself, I just was bit bored and now all variables in my light pipeline now resemble cake ingredients.

The reason why there is so many is because all points are then used to create a sf::TriangleFan entities that are then drawn onto light map. Drawing along hit object might be a good idea, but I'm currently busy with other things

8
General / Is this DDA raycast algorithm good enough?
« on: November 12, 2023, 12:34:47 pm »
I'm trying to implement a DDA raycast algorithm and managed to dug up some working code, then reworked it to draw a sf::TriangleFan for light map. The only problem is how wonky the TriangleFan can be, as more than 0.1 difference between angles cause clipping

Reworked code (original: https://codereview.stackexchange.com/questions/190662/2d-raycasting-implementation):
#include <SFML\Graphics.hpp>
#include <iostream>

const int MAP_W = 10;
const int MAP_H = 10;

//map data, 1 represents wall, 0 - no wall
int map[MAP_W][MAP_H] =
{
    { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    { 1, 0, 0, 0, 1, 1, 0, 0, 0, 1 },
    { 1, 0, 0, 0, 1, 0, 0, 1, 0, 1 },
    { 1, 0, 0, 0, 1, 1, 1, 1, 0, 1 },
    { 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 1, 0, 0, 0, 0, 1, 0, 0, 0, 1 },
    { 1, 0, 0, 0, 0, 0, 1, 0, 0, 1 },
    { 1, 0, 1, 0, 0, 1, 0, 1, 0, 1 },
    { 1, 0, 1, 0, 0, 1, 0, 0, 0, 1 },
    { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
};

sf::Vector2i playerMapPos = { 5, 5 };
sf::Vector2f playerWorldPos;

sf::Vector2f tileSize;

std::vector<std::vector<float>> bakedCakes;

//get raycast closest hit point
sf::Vector2f getDistToClosestHitPoint(sf::Vector2i rayMapPos, sf::Vector2f rayWorldPos, std::vector<float> cake)
{
    sf::Vector2f rayDir = { cake[1], cake[2]};

    float dyh = 0; //dist y to next horizontal tile
    float dxh = 0; //dist x to next horizontal tile

    if (rayWorldPos.y == rayMapPos.y * tileSize.y) dyh = tileSize.y;
   
    else
    {
        if (rayDir.y < 0) dyh = rayWorldPos.y - (rayMapPos.y * tileSize.y);
        else dyh = (rayMapPos.y + 1) * tileSize.y - rayWorldPos.y;
    }

    dxh = dyh / cake[3];
    if (rayDir.y < 0) //invert distances values when pointing upwards
    {
        dxh = -dxh;
        dyh = -dyh;
    }

    float dyv = 0; //dist y to next vertical tile
    float dxv = 0; //dist x to next vertical tile

    if (rayWorldPos.x == rayMapPos.x * tileSize.x) dxv = tileSize.x;
   
    else
    {
        if (rayDir.x < 0) dxv = rayWorldPos.x - (rayMapPos.x * tileSize.x);
        else dxv = (rayMapPos.x + 1) * tileSize.x - rayWorldPos.x;
    }

    dyv = dxv * cake[3];
    if (rayDir.x < 0) //invert distances values when pointing upwards
    {
        dxv = -dxv;
        dyv = -dyv;
    }

    //calc squares and compare them
    float sqrLenHor = dxh * dxh + dyh * dyh;
    float sqrLenVer = dxv * dxv + dyv * dyv;

    //select distances which squares are lower
    float dx = sqrLenHor < sqrLenVer ? dxh : dxv;
    float dy = sqrLenHor < sqrLenVer ? dyh : dyv;
   
    return { dx, dy };
}

sf::VertexArray hitLines(sf::TriangleFan);

void visualizePlayerRaycast(sf::RenderWindow& gameWindow, std::vector<float> cake)
{
    sf::Vector2f totalDist = { 0, 0 };

    float angle = cake[0];
   
    sf::Vector2f dir = { cake[1], cake[2] };

    //get distance to first hit point
    sf::Vector2f dist = getDistToClosestHitPoint(playerMapPos, playerWorldPos, cake);

    //first ray hit position coordinates
    sf::Vector2f rayWorldPos = { playerWorldPos.x + dist.x, playerWorldPos.y + dist.y };
    sf::Vector2i rayPosMap = { int(rayWorldPos.x / tileSize.x), int(rayWorldPos.y / tileSize.y) }; //just divide world coordinates by tile size

    bool hit = false;
    while (!hit)
    {
        //out of array range exceptions handling
        if (rayPosMap.x < 0 || rayPosMap.x >= MAP_W || rayPosMap.y < 0 || rayPosMap.y >= MAP_H) break;

        //checking that actually hit side is wall side
        int hitTileX = rayPosMap.x;
        int hitTileY = rayPosMap.y;

        //fix checking walls when hit them on their right or bottom side, check walls earlier them
        if (rayWorldPos.x == rayPosMap.x * tileSize.x && dir.x < 0) hitTileX--; //hit wall left side
        if (rayWorldPos.y == rayPosMap.y * tileSize.y && dir.y < 0) hitTileY--; //hit wall up side

        if (map[hitTileY][hitTileX] == 1)
        {
            hitLines.append({ { rayWorldPos.x, rayWorldPos.y }, sf::Color::Red });
            hit = true; //end raycasting loop
        }
        else
        {
            //move ray to next closest horizontal or vertical side
            sf::Vector2f dist = getDistToClosestHitPoint({ rayPosMap.x, rayPosMap.y }, { rayWorldPos.x, rayWorldPos.y }, cake);

            //apply new move
            rayWorldPos.x += dist.x;
            rayWorldPos.y += dist.y;

            totalDist += dist;

            //update map positions
            rayPosMap.x = rayWorldPos.x / tileSize.x;
            rayPosMap.y = rayWorldPos.y / tileSize.y;
        }
    }
   
}

void lightBaker(float angle)
{
    sf::Vector2f rayDir = { cos(angle), sin(angle) };

    std::vector<float> cake = { angle, cos(angle), sin(angle), tan(angle) };
    bakedCakes.push_back(cake);
}

int main()
{
    sf::RenderWindow gameWindow(sf::VideoMode(1000, 800), "Raycast Test");
    gameWindow.setFramerateLimit(60);
   
    for (float i = 0; i < 6.27; i += 0.01)
    {
        lightBaker(i);
    }
    lightBaker(0);
   
    tileSize = { (float)gameWindow.getView().getSize().x / MAP_W, (float)gameWindow.getView().getSize().y / MAP_H };
    playerWorldPos = { playerMapPos.x * tileSize.x, playerMapPos.y * tileSize.y, };

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

        float speed = 5.0f;

        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::W)) playerWorldPos -= sf::Vector2f{ 0.0f, 1.0f } *speed;
        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::S)) playerWorldPos += sf::Vector2f{ 0.0f, 1.0f } *speed;
        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::A)) playerWorldPos -= sf::Vector2f{ 1.0f, 0.0f } *speed;
        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::D)) playerWorldPos += sf::Vector2f{ 1.0f, 0.0f } *speed;

        playerMapPos = { (int)(playerWorldPos.x / tileSize.x), (int)(playerWorldPos.y / tileSize.y) };

        gameWindow.clear(sf::Color::White);

        for (int y = 0; y < MAP_H; y++)
        {
            for (int x = 0; x < MAP_W; x++)
            {
                sf::RectangleShape tile(tileSize);
                tile.setPosition(x * tileSize.x, y * tileSize.y);
                tile.setOutlineThickness(1.0f);
                tile.setOutlineColor(sf::Color::Black);

                //we need to check by [y][x] to draw correctly because of array structure
                if (map[y][x] == 1)
                {
                    //if map[y][x] is blockade, make it black
                    tile.setFillColor(sf::Color::Black);
                    tile.setOutlineColor(sf::Color::White);
                }

                gameWindow.draw(tile);
            }
        }
        //draw player
        sf::CircleShape player(25);
        player.setOrigin({ 25, 25 });
        player.setPosition(playerWorldPos);
        player.setFillColor(sf::Color::Black);
       

        hitLines.append({ playerWorldPos, sf::Color::Red });
        for (std::vector<float> cake : bakedCakes)
        {
            visualizePlayerRaycast(gameWindow, cake);
        }
        gameWindow.draw(hitLines);
        gameWindow.draw(player);
        gameWindow.display();
        hitLines.clear();
    }
}

9
General / Re: How to implement lightning?
« on: November 09, 2023, 06:45:01 pm »
Building light map from primitives worked great on small scale, but the only problem is drawing darkness to fill everything around.

You could use a "post-processing"-style setup and draw to an intermediate step and then draw all the light stuff together at once.
This would be something like:
  • create a render texture that covers the entire part you want to draw (likely just the entire window but this can be modified e.g. if you're using double-pixels, you could use a half-size rendertexture)
  • fill it with black (that's all the dark parts sorted right away!)
  • draw your lights to the render texture (can be coloured but white is normal light; can also have transparency and likely should to allow overlapping lights), adding them together (blendmode: add)
  • draw the render texture to the window with some sort of filter (blendmode: multiply is the simplest but a shader may do a better job if you use coloured lights)

Worked like a charm, code is bellow so others can use it. Colors seems to be fine too, but I'll need to create something like campfire to test it for sure. Do you have snippet/manual for that shader so I can compare the results?

int main()
{
    sf::Vector2f resolution = { 500, 500 };

    sf::RenderWindow window(sf::VideoMode(resolution.x, resolution.y), "LightExample", sf::Style::Close);

    sf::RenderTexture texture;
    texture.create(resolution.x, resolution.y);

    sf::CircleShape light(80.f, 8);
    light.setFillColor(sf::Color(250, 250, 250, 250));
    light.setPosition(250, 250);


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

        texture.clear(sf::Color( 0, 0, 0, 250));
        texture.draw(triangle, sf::BlendAdd);
        sf::Sprite renderTextureSprite(texture.getTexture());
       
        window.clear();

        window.draw(renderTextureSprite, sf::RenderStates(sf::BlendMultiply));

        window.display();
    }
}
 

10
General / Re: How to implement lightning?
« on: November 08, 2023, 02:06:25 pm »
Just because something a few years old doesn't necessarily mean that it's outdated. SFML 2.0 came out 10 years ago and what worked back then, will also work today. ;)

What have you tried already?

Have you taken a look at Candle? https://github.com/MiguelMJ/Candle

Building light map from primitives worked great on small scale, but the only problem is drawing darkness to fill everything around. I tried to dig up any manuals/snippets on how to use sf::BlendMode with VertexArray primitives, or other methods that would allow me to simply add transparency to certain points of texture, but can't find anything.

I know about Candle, but I want to pull this off myself since I'm only one problem away from solution

11
General / How to implement lightning?
« on: November 07, 2023, 08:55:28 pm »
All topics seems to be heavily outdated and I can't wrap my head around shaders.

My rendering pipeline uses single VertexArray (triangle primitives). Is there a way to blend specific elements of the array, or another method to create a lightning map? I know sf::BlendMode exists but no idea how to use it

12
Graphics / Re: setFrameLimit causes absurd CPU usage
« on: November 03, 2023, 11:37:12 am »
Do you enforce VSync in your global GPU driver settings?

Yup, I did and turning it off solved the problem. Is there any way to detect that and still limit the game to 60 FPS, or would this work too:


int main()
{

    sf::Vector2f resolution = { 1920, 1080 };

    sf::RenderWindow window(sf::VideoMode(resolution.x, resolution.y), "ChronoTest", sf::Style::Close);

    using clock = std::chrono::steady_clock;
    using frameLimit = std::chrono::duration<int, std::ratio<1, 60>>;

    while (window.isOpen())
    {
        auto frameStart = clock::now();

        // Game Logic
       
        if (clock::now() - frameStart < frameLimit(1)) std::this_thread::sleep_until(frameStart + frameLimit(1));
       
    }
   
    return 0;
}

13
Graphics / Re: setFrameLimit causes absurd CPU usage
« on: November 03, 2023, 12:33:11 am »
How do you "measure" CPU usage?
From Task Manager, Resource Monitor also shows a huge difference in CPU usage.

14
Graphics / setFrameLimit causes absurd CPU usage
« on: November 02, 2023, 11:18:15 pm »
Any window capped at 60 FPS suddenly gets 5-10% extra CPU usage. Everything works fine uncapped/capped at 120+ no matter if it's simple rectangle or a medium-sized game prototype.

Example:
#include <SFML/Graphics.hpp>

int main()
{
    sf::RenderWindow window(sf::VideoMode(800, 800), "Encore");
    window.setFramerateLimit(60);

    sf::RectangleShape rectangle;
    rectangle.setSize(sf::Vector2f(100, 100));
    rectangle.setPosition(0, 0);
    rectangle.setFillColor(sf::Color(100, 250, 50));

    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
            {
                window.close();
                return 0;
            }
        }
       
        window.clear();
        window.draw(rectangle);
        window.display();
    }

    return 0;
}

15
Each sf::Sprite that is drawn uses a "draw call". The draw call is most often the most significant time wasted when drawing multiple objects. If there are very many sprites, the number of draw calls is high and can cause performance issues.

However, using a vertex array (or similar) allows you to draw multiple shapes at once with just one draw call, which can severely improve performance. Those shapes can be textured and rectangular so multiple sprites can be mimicked.

As for primitives, if you want to draw rectangles, you simply use the triangles primitives and create two triangles to create each rectangle. It requires a slight duplication of vertices and would therefore be 6 vertices per quad/rectangle:
sf::VertexArray rectangles(sf::PrimitiveType::Triangles, 12u); // 2 rectangles = 12 vertices

std::size_t firstVertexOfRectangle;
sf::Vector2f topLeft;
sf::Vector2f bottomRight;

// first rectangle: 100,100 - 300,200 (200x100)
firstVertexOfRectangle = 0u;
topLeft = { 100.f, 100.f };
bottomRight = { 300.f, 200.f };

rectangles[firstVertexOfRectangle + 0u].position = topLeft;
rectangles[firstVertexOfRectangle + 1u].position = { topLeft.x, bottomRight.y };
rectangles[firstVertexOfRectangle + 2u].position = bottomRight;
rectangles[firstVertexOfRectangle + 5u].position = { topLeft.y, bottomRight.x };
rectangles[firstVertexOfRectangle + 3u] = rectangles[firstVertexOfRectangle + 0u];
rectangles[firstVertexOfRectangle + 4u] = rectangles[firstVertexOfRectangle + 2u];

// second rectangle: 500,200 - 600,500 (100x300)
firstVertexOfRectangle = 6u;
topLeft = { 500.f, 200.f };
bottomRight = { 600.f, 500.f };

rectangles[firstVertexOfRectangle + 0u].position = topLeft;
rectangles[firstVertexOfRectangle + 1u].position = { topLeft.x, bottomRight.y };
rectangles[firstVertexOfRectangle + 2u].position = bottomRight;
rectangles[firstVertexOfRectangle + 5u].position = { topLeft.y, bottomRight.x };
rectangles[firstVertexOfRectangle + 3u] = rectangles[firstVertexOfRectangle + 0u];
rectangles[firstVertexOfRectangle + 4u] = rectangles[firstVertexOfRectangle + 2u];

// later: window.draw(rectangles); // draws both rectangles with just one draw code

Note that the actual assigning of the vertices is the same both times.

You can assign colours and the texture co-ordinates in the same way. You can do that before the assigning of the "+ 3u" and "+ 4u" (at the time when the positions are assigned) as we're duplicating the entire vertex including colour and texture! 8)

I know that triangles can be used to make rectangles and it only takes two more points, but I want to know if there is performance difference between triangle and rectangle primitive.

Btw, where I can post for feedback on my rendering pipeline?

Pages: [1] 2
anything