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

Author Topic: Find right vector element to draw with indexed culling?  (Read 1895 times)

0 Members and 1 Guest are viewing this topic.

Noob

  • Newbie
  • *
  • Posts: 15
    • View Profile
Find right vector element to draw with indexed culling?
« on: March 28, 2014, 11:18:12 pm »
OK, here goes.

In my game I have a vector of sf::Sprites that contains all the sprites used to draw a map, with their positions on the map already set. My current method for drawing the map just loops through all of the elements in the vector and calls draw() for each one.

This draws everything, even stuff outside of the current view, which drags the FPS down to single digits for some of my large maps.

What I'm trying to do is something like what this person has done with indexed culling.
http://en.sfml-dev.org/forums/index.php?topic=7933.0

I understand how this works but the way my map is stored means I have to figure out how to find which sprite should be drawn based on where the current view is.

CreateMap.h
#pragma once
#include "SFML\Graphics.hpp"
#include <vector>

class CreateMap : public sf::Drawable, public sf::Transformable
{
public:
        CreateMap(const std::string fileName, float spriteSize, int gameScale, sf::Image& spriteSheet)
        {
                mapScale = gameScale;
                mapSpriteSize = spriteSize;
                mapImage.loadFromFile(fileName);
                textures.loadFromImage(spriteSheet, sf::IntRect(0, 144, 256, 48));

                TILE_WIDTH = 16 * gameScale;
                TILE_HEIGHT = 16 * gameScale;

                MAP_WIDTH = 64 ;
                MAP_HEIGHT = 64 ;

                loadMap();
        };

        // Loads the map
        void loadMap();

        // Used to update the position of the view, which is used by the draw function to find out which spirtes to draw.
        void indexedCulling(sf::View& view);

private:
        // The map comes from an image file.
        sf::Image mapImage;

        // CreateMap.cpp assigns each colour found in the above image file to a particular sprite found on a spritesheet image file that has been loaded elsewhere in the program.
        std::vector <sf::Color> mapPixels;

        // Each sprite by now has been assigned a texture and a position on the map.
        std::vector <sf::Sprite> mapSprites;

        // The size of each sprite. Currently 16x16 pixels.
        float mapSpriteSize;

        // How big the map
        int mapScale;

        // This holds the image that each sprite is getting its texture from.
        sf::Texture textures;

        int TILE_WIDTH, TILE_HEIGHT;
        int MAP_WIDTH, MAP_HEIGHT;
        int startX, endX;
        int startY, endY;

        // This is what I want to use to draw the map
        void draw(sf::RenderTarget& target, sf::RenderStates states) const
        {
                states.transform *= getTransform();

                for(int y = startY; y < endY; y++)
                {
                        for(int x = startX; x < endX; x++)
                        {
                                target.draw(mapSprites[y + x]);
                        }
                }
        };
};

CreateMap.cpp
#include "CreateMap.h"

void CreateMap::loadMap()
{
        int mapSize = mapImage.getSize().x * mapImage.getSize().y;
        mapPixels.resize(mapSize);

        // Sets the size of the vector to the size of the map image
        mapSprites.resize(mapSize);

        // Add new tiles here
        sf::Color Grass                 ( 34, 177,  76, 255);
        sf::Color Water                 (  0, 162, 232, 255);
        sf::Color Sand                  (255, 242,   0, 255);
        sf::Color Tree                  (185, 122,  87, 255);
        sf::Color Waves                 ( 63,  72, 204, 255);
        sf::Color Bush                  (146, 195,  98, 255);
        sf::Color Brown_Brick           ( 53,  51,  45, 255);
        sf::Color Snow                  (206, 214, 205, 255);
        sf::Color Marble                        (159, 172, 149, 255);
        sf::Color BLACK                 (  0,   0,   0, 255);


        sf::Color VOID                  (255,   0, 255, 255);

        int i = 0;

        for(int y = 0; y < mapImage.getSize().y; y++)
        {
                for(int x = 0; x < mapImage.getSize().x; x++)
                {
                        mapPixels[i] = mapImage.getPixel(x, y);

                        mapSprites[i].setTexture(textures, false);
                        // Check to see what texture the pixel on the map should be
                        if              (mapPixels[i] == Grass) mapSprites[i].setTextureRect(sf::IntRect( 64,  16,  16,  16));
                        else if (mapPixels[i] == Water) mapSprites[i].setTextureRect(sf::IntRect( 80,  32,  16,  16));
                        else if (mapPixels[i] == Waves) mapSprites[i].setTextureRect(sf::IntRect( 96,  32,  16,  16));
                        else if (mapPixels[i] == Sand)  mapSprites[i].setTextureRect(sf::IntRect( 16,   0,  16,  16));
                        else if (mapPixels[i] == Tree)  mapSprites[i].setTextureRect(sf::IntRect( 48,   0,  16,  16));
                        else if (mapPixels[i] == Bush)  mapSprites[i].setTextureRect(sf::IntRect( 48,   16,  16,  16));
                        else if (mapPixels[i] == Brown_Brick)   mapSprites[i].setTextureRect(sf::IntRect( 0,   32,  16,  16));
                        else if (mapPixels[i] == Snow)  mapSprites[i].setTextureRect(sf::IntRect( 112,  32,  16,  16));
                        else if (mapPixels[i] == Marble)        mapSprites[i].setTextureRect(sf::IntRect( 48,   32,  16,  16));
                        else if (mapPixels[i] == BLACK) mapSprites[i].setTextureRect(sf::IntRect(  64,  0,  16,  16));

                        // Displays pink if it can't find a texture to use
                        else mapSprites[i].setTextureRect(sf::IntRect(96, 0, 16, 16));
                       
                        mapSprites[i].setScale(mapScale,mapScale);
                        mapSprites[i].setPosition(x * (mapSpriteSize * mapScale), y * (mapSpriteSize * mapScale));

                        i++;
                }
        }
}

// Should only render what is inside the view. Gets called in the game update function.
void CreateMap::indexedCulling(sf::View& view)
{
        startX = (view.getCenter().x - ( 6 * TILE_WIDTH))/TILE_WIDTH;
        endX = (view.getCenter().x + ( 6 * TILE_WIDTH))/TILE_WIDTH;
        startY = (view.getCenter().y - ( 6 * TILE_HEIGHT))/TILE_HEIGHT;
        endY = (view.getCenter().y + ( 6 * TILE_HEIGHT))/TILE_HEIGHT;

        if(startX > MAP_WIDTH)
                startX = MAP_WIDTH;
        if(startX < 0)
                startX = 0;

        if(startY > MAP_HEIGHT)
                startY = MAP_HEIGHT;
        if(startY < 0)
                startY = 0;

        if(endY > MAP_HEIGHT)
                endY = MAP_HEIGHT;
        if(endY < 0)
                endY = 0;

        if(endX > MAP_WIDTH)
                endX = MAP_WIDTH;
        if(endX < 0)
                endX = 0;
}

I'm stumped on how to get this to only render what is inside the view without having to change a lot of my existing code and would appreciate if anyone would point me in the right direction.

I know this is something I should figure out for myself, but I have been stuck on this for a few weeks and I want to move onto other things.

Sorry if my code looks messy. I'm going to clean it up when this is working.  :(
Let me know if I need to explain anything more.
« Last Edit: March 28, 2014, 11:23:47 pm by Noob »
Killing goblins at Lumbridge. Windows 7 - 64 bit. AMD Radeon HD 6700. Using VS2012 Express, SFML 2.1 - Static.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
AW: Find right vector element to draw with indexed culling?
« Reply #1 on: March 29, 2014, 01:14:57 am »
Your first optimization should be using a vertex array instead of sprites, because if you profile the application you'd notice that most of the time is wasted on the draw calls.

Next, you 2D->1D index conversion is broken. y+x won't represent a map, given that for example x=1, y=3 (index = x+y = 4) the same is as x=3, y=1 (index = x+y = 4), while they should represent 2 different fields on the map. ;)
Instead it should be more something like: y*NumberOfColumns+x
« Last Edit: March 29, 2014, 01:16:55 am by eXpl0it3r »
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Noob

  • Newbie
  • *
  • Posts: 15
    • View Profile
Re: Find right vector element to draw with indexed culling?
« Reply #2 on: March 30, 2014, 12:26:29 am »


Instead it should be more something like: y*NumberOfColumns+x

Yay, that is exactly what I was looking for. :D

I knew the problem was something to do with going from 2D to 1D. I face-palmed at how simple the solution is.
I was over-complicating things for myself, trying to formulate a way of turning my 1D index of sprites into 2D, thinking that it might work that way. :-\
Killing goblins at Lumbridge. Windows 7 - 64 bit. AMD Radeon HD 6700. Using VS2012 Express, SFML 2.1 - Static.