Algorytm Generowania Dachu Budynku

Algorytm Generowania Dachu Budynku
tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 540
0

Witam. Piszę algorytm generujący dach budynku. Mam problem z kafelkami. Dla następujących wartości: tile_width = 16, tile_height = 8,tile_border = 1 algorytm działa poprawnie lecz gdy zmienię te parametry na np. 32,16,1 to już nie. Podejrzewam, że zrobiłem błąd w obliczaniu dy

my_house.pngmy_house_1.png

Kopiuj
void loadTexture(std::ifstream& file) {
    
    
    
    // check the size
    cout << size.x << " " << size.y << "\n";

    short walls_height = 3;

    // create basic image to render house
    sf::Image house_image;
    house_image.create(size.x * 16, (size.y + walls_height*2 + 2) * 16, sf::Color::Transparent);

    // load the wall texture
    sf::Image wall;
    wall.create(32, 32, sf::Color::Transparent);
    wall = getTexture("walls/wooden_wall")->texture->copyToImage();
  
    
    // DRAWING A WALLS
    for (short y = 1; y <= walls_height; y++) {
        for (short x = 0; x < size.x/2; x++) {
            house_image.copy(wall, x*2*16, house_image.getSize().y - y *2*16);
        }
    }

    cout << "\n\n warning! \n";

    // DRAWING A WALLS UNDER THE ROOF
    short height = size.x/4;
    short width = size.x/2;
    short start_x;
    short end_x;

    for (int y = 0; y <= height; y++) {
        start_x = y;
        end_x = width - y - 1;

        for (int x = 0; x < width; x++) {
            if (x >= start_x && x <= end_x) {
                house_image.copy(wall, x * 2 *16, house_image.getSize().y - (y + walls_height + 1) * 2 * 16);
            }
        }
    }

    // DRAWING A ROOF
    sf::Color color = sf::Color(51, 38, 21);

    sf::Vector2f p1(0, walls_height * 2*16);
    sf::Vector2f p2(size.x/2*16, (walls_height+size.x/4) * 2*16);
    sf::Vector2f p3(size.x*16, walls_height * 2*16);
    sf::Vector2f p4(size.x*16, (walls_height +size.y/4 + 1) * 2*16);
    sf::Vector2f p5(size.x / 2 * 16, (walls_height + size.x / 4 + size.y/4 + 1) * 2 * 16);
    sf::Vector2f p6(0, (walls_height + size.y/4 + 1) * 2 * 16);
    
    sf::VertexArray left_quad(sf::Quads, 4);
    left_quad[0].position = p1;
    left_quad[1].position = p2;
    left_quad[2].position = p5;
    left_quad[3].position = p6;
    left_quad[0].color = color;
    left_quad[1].color = color;
    left_quad[2].color = color;
    left_quad[3].color = color;

    sf::VertexArray right_quad(sf::Quads, 4);
    right_quad[0].position = p2;
    right_quad[1].position = p3;
    right_quad[2].position = p4;
    right_quad[3].position = p5;
    right_quad[0].color = color;
    right_quad[1].color = color;
    right_quad[2].color = color;
    right_quad[3].color = color;

    sf::RenderTexture rtex;
    rtex.create(house_image.getSize().x, house_image.getSize().y);
    rtex.draw(left_quad);
    rtex.draw(right_quad);

    // CREATE A TILES

    float tile_width = 16;
    float tile_height = 8;
    float tile_border = 1;

    sf::Color color_outside = sf::Color(51, 38, 21);
    sf::Color color_inside = sf::Color(185, 0, 0);
    
    for (float y = 0; y < (size.y / 2 +2)* 16; y += tile_height) {
        for (float dx = 0; dx < size.x / 2 * 16; dx += tile_width) {

            float len_of_roof = float(size.x / 4) * 2.0f * 16.0f;
            float dy = len_of_roof / tile_height * float(dx) / tile_width;

            // outside of tile
            sf::VertexArray outside(sf::Quads, 4);
            outside[0].position = sf::Vector2f(dx, y + dy + (walls_height) * 2 * 16);
            outside[1].position = sf::Vector2f(dx + tile_width, y + dy + (walls_height) * 2 * 16 + tile_height);
            outside[2].position = sf::Vector2f(dx + tile_width, y + dy + (walls_height) * 2 * 16 + 2*tile_height);
            outside[3].position = sf::Vector2f(dx, y + dy + (walls_height) * 2 * 16 + tile_height);
            outside[0].color = color_outside;
            outside[1].color = color_outside;
            outside[2].color = color_outside;
            outside[3].color = color_outside;
            rtex.draw(outside);

            // inside of tile
            sf::VertexArray inside(sf::Quads, 4);
            inside[0].position = sf::Vector2f(dx + tile_border, y + dy + (walls_height) * 2 * 16 + tile_border);
            inside[1].position = sf::Vector2f(dx + tile_width - tile_border, y + dy + (walls_height) * 2 * 16 + tile_height + tile_border);
            inside[2].position = sf::Vector2f(dx + tile_width - tile_border, y + dy + (walls_height ) * 2 * 16 + 2*tile_height - tile_border);
            inside[3].position = sf::Vector2f(dx + tile_border, y + dy + (walls_height) * 2 * 16 + tile_height - tile_border);
            inside[0].color = color_inside;
            inside[1].color = color_inside;
            inside[2].color = color_inside;
            inside[3].color = color_inside;
            rtex.draw(inside);
        }
    }


    sf::Image roof_image = rtex.getTexture().copyToImage();

    house_image.copy(roof_image, 0, 0 ,sf::IntRect(0,0,0,0),true);

    // create main tex
    sf::Texture* tex = new sf::Texture();
    tex->loadFromImage(house_image);

    // create the sprite
    sprite = sf::Sprite();
    sprite.setTexture(*tex);
    sprite.setOrigin(tex->getSize().x / 2, tex->getSize().y);
    sprite.setPosition(position);

}
flowCRANE
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Tuchów
  • Postów: 12269
1

Dach na pierwszym obrazku też zdaje się wyglądać niepoprawnie. Dwa problemy widzę. Pierwszy jest taki, że dachówki nie są równoległe do krawędzi dachu — takl ma być, czy to też bug? A drugi jest taki, że dachówek brakuje u góry, a na dole jest za dużo. To powoduje, że u góry widać ciemnobrązowe tło dachu, a dachówki na dole zdają się lewitować.

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 540
0

W takim razie jak to naprawić?

Zrobiłem coś takiego i teraz oblicza mi rozmiary kafelków ale

Kopiuj
float tiles_rows = 8;
float tiles_columns = 16;

float tile_width = float(size.x/4)*2.0f*16.0f / tiles_columns;
float tile_height = (float(size.x / 4) * 2.0f * 16.0f) / tiles_rows;
float tile_border = 1;

my_house_2.png

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 540
0

Teraz mam taki kod:
my_house_3.png

Kopiuj
void loadTexture(std::ifstream& file) {
    
    
    
    // check the size
    cout << size.x << " " << size.y << "\n";

    short walls_height = 3;

    // create basic image to render house
    sf::Image house_image;
    house_image.create(size.x * 16, (size.y + walls_height*2 + 2) * 16, sf::Color::Transparent);

    // load the wall texture
    sf::Image wall;
    wall.create(32, 32, sf::Color::Transparent);
    wall = getTexture("walls/wooden_wall")->texture->copyToImage();
  
    
    // DRAWING A WALLS
    for (short y = 1; y <= walls_height; y++) {
        for (short x = 0; x < size.x/2; x++) {
            house_image.copy(wall, x*2*16, house_image.getSize().y - y *2*16);
        }
    }

    cout << "\n\n warning! \n";

    // DRAWING A WALLS UNDER THE ROOF
    short height = size.x/4;
    short width = size.x/2;
    short start_x;
    short end_x;

    for (int y = 0; y <= height; y++) {
        start_x = y;
        end_x = width - y - 1;

        for (int x = 0; x < width; x++) {
            if (x >= start_x && x <= end_x) {
                house_image.copy(wall, x * 2 *16, house_image.getSize().y - (y + walls_height + 1) * 2 * 16);
            }
        }
    }

    // DRAWING A ROOF
    sf::Color color = sf::Color(51, 38, 21);

    sf::Vector2f p1(0, walls_height * 2*16);
    sf::Vector2f p2(size.x/2*16, (walls_height+size.x/4) * 2*16);
    sf::Vector2f p3(size.x*16, walls_height * 2*16);
    sf::Vector2f p4(size.x*16, (walls_height +size.y/4 + 1) * 2*16);
    sf::Vector2f p5(size.x / 2 * 16, (walls_height + size.x / 4 + size.y/4 + 1) * 2 * 16);
    sf::Vector2f p6(0, (walls_height + size.y/4 + 1) * 2 * 16);
    
    sf::VertexArray left_quad(sf::Quads, 4);
    left_quad[0].position = p1;
    left_quad[1].position = p2;
    left_quad[2].position = p5;
    left_quad[3].position = p6;
    left_quad[0].color = color;
    left_quad[1].color = color;
    left_quad[2].color = color;
    left_quad[3].color = color;

    sf::VertexArray right_quad(sf::Quads, 4);
    right_quad[0].position = p2;
    right_quad[1].position = p3;
    right_quad[2].position = p4;
    right_quad[3].position = p5;
    right_quad[0].color = color;
    right_quad[1].color = color;
    right_quad[2].color = color;
    right_quad[3].color = color;

    sf::RenderTexture rtex;
    rtex.create(house_image.getSize().x, house_image.getSize().y);
    rtex.draw(left_quad);
    rtex.draw(right_quad);

    // CREATE A TILES

    float tiles_rows = 8;
    float tiles_columns = 16;

    float tile_width = float(size.x/4)*2.0f*16.0f / tiles_columns;
    float tile_height = float(size.y/2)*2.0f*16.0f / tiles_rows;
    float tile_border = 1;

    sf::Color color_outside = sf::Color(51, 38, 21);
    sf::Color color_inside = sf::Color(185, 0, 0);
    
    for (float y = 0; y < (size.y)* 16; y += tile_height) {
        for (float dx = 0; dx < size.x / 2 * 16; dx += tile_width) {

            float len_of_roof = float(size.x / 4) * 2.0f * 16.0f;
            float dy = len_of_roof / tile_height * float(dx) / tile_width;

            // outside of tile
            sf::VertexArray outside(sf::Quads, 4);
            outside[0].position = sf::Vector2f(dx, y + dy + (walls_height) * 2 * 16);
            outside[1].position = sf::Vector2f(dx + tile_width, y + dy + (walls_height) * 2 * 16 + tile_height/2.0f);
            outside[2].position = sf::Vector2f(dx + tile_width, y + dy + (walls_height) * 2 * 16 + tile_height);
            outside[3].position = sf::Vector2f(dx, y + dy + (walls_height) * 2 * 16 + tile_height/2.0f);
            outside[0].color = color_outside;
            outside[1].color = color_outside;
            outside[2].color = color_outside;
            outside[3].color = color_outside;
            rtex.draw(outside);

            // inside of tile
            sf::VertexArray inside(sf::Quads, 4);
            inside[0].position = sf::Vector2f(dx + tile_border, y + dy + (walls_height) * 2 * 16 + tile_border);
            inside[1].position = sf::Vector2f(dx + tile_width - tile_border, y + dy + (walls_height) * 2 * 16 + tile_height/2.0f + tile_border);
            inside[2].position = sf::Vector2f(dx + tile_width - tile_border, y + dy + (walls_height ) * 2 * 16 + tile_height - tile_border);
            inside[3].position = sf::Vector2f(dx + tile_border, y + dy + (walls_height) * 2 * 16 + tile_height/2.0f - tile_border);
            inside[0].color = color_inside;
            inside[1].color = color_inside;
            inside[2].color = color_inside;
            inside[3].color = color_inside;
            rtex.draw(inside);
        }
    }


    sf::Image roof_image = rtex.getTexture().copyToImage();

    house_image.copy(roof_image, 0, 0 ,sf::IntRect(0,0,0,0),true);

    // create main tex
    sf::Texture* tex = new sf::Texture();
    tex->loadFromImage(house_image);

    // create the sprite
    sprite = sf::Sprite();
    sprite.setTexture(*tex);
    sprite.setOrigin(tex->getSize().x / 2, tex->getSize().y);
    sprite.setPosition(position);

}
tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 540
0

Już prawie mam
float dy = dx / tile_width * tile_height/2.0f;
my_house_4.png

flowCRANE
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Tuchów
  • Postów: 12269
0

Skup się najpierw na tym, aby dachówka renderowana była pod kątem 45°, zgodnie ze spadem dachu.

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 540
0

flowCRANE > Jeśli tak, to dorzuć 0.5f do którejś współrzędnej w osi Y. Bez względu na to co jest problemem, tak czy siak trzeba go naprawić, aby ten dach ładnie wyglądał, tak czysto piksel artowo. ;)

to błąd SFML gdyż jak ustawiłem tile_border=0 to zniknęły nierówności

screenshot-20241107135328.png

flowCRANE
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Tuchów
  • Postów: 12269
0
tBane napisał(a):

to błąd SFML gdyż jak ustawiłem tile_border=0 to zniknęły nierówności

Na pewno SFML-a? A nie obliczenia związane właśnie z borderem? ;)

FA
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: warszawa
  • Postów: 315
0

1(Napisz algorytm który generuje kratownice, o określonych wymiarach.
2)Napisz fukcje która robi obrót o n stopni.
3)Oraz drugą funkcje która robi translate(przesuwa o odległośc x y).
4)Zostosuj obrót i translate na kratownicy.

5 dopieść racznie dachówki tak by były coool.

W gratisie dostajesz funkcje matematyczne które sie przydadzą na potem oraz kratownice, oraz fukcje kratownicowa do wykorzystanie później np do budowy dróg lub UI.

flowCRANE
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Tuchów
  • Postów: 12269
2

screenshot-20241107160600.png

Zdaje się działać. Kod odpowiedzialny za renderowanie tego wszystkiego wygląda tak:

Kopiuj
procedure TFormMain.DrawTiles(ACanvas: TCanvas; AOrigin: TPoint; ASizeX, ASizeY, AGapX, AGapY, ACountX, ACountY: Integer);
var
  Tile0: TPoint;
  Tile1: TPoint;
  Tile2: TPoint;
  Tile3: TPoint;
  Index: TPoint;
begin
  ACanvas.Pen.Color   := RGBToColor(185, 0, 0);
  ACanvas.Brush.Color := RGBToColor(185, 0, 0);

  Index.X := 0;
  Index.Y := 0;

  while Index.Y < ACountY do
  begin
    while Index.X < ACountX do
    begin
      Tile0.X := AOrigin.X + Index.X * ASizeX + Index.X * AGapX;
      Tile0.Y := AOrigin.Y + Index.Y * ASizeY + Index.Y * AGapY + Index.X * ASizeX + Index.X * AGapX;
      Tile1.X := Tile0.X + ASizeX - 1;
      Tile1.Y := Tile0.Y + ASizeX - 1;
      Tile2.X := Tile0.X + ASizeX - 1;
      Tile2.Y := Tile0.Y + ASizeX - 1 + ASizeY - 1;
      Tile3.X := Tile0.X;
      Tile3.Y := Tile0.Y + ASizeY - 1;

      ACanvas.Polygon([Tile0, Tile1, Tile2, Tile3]);

      Index.X += 1;
    end;

    Index.X := 0;
    Index.Y += 1;
  end;
end;

Jeśli chodzi o parametry tej metody, to AOrigin to współrzędne lewego górnego rogu, od którego rozpoczyna się renderowanie dachówek. ASizeX i ASizeY to szerokość i wysokość jednej dachówki w pikselach. AGapX i AGapY to rozmiar szczeliny pomiędzy sąsiednimi dachówkami, w poziomie i pionie. ACountX i ACountY to liczba dachówek do namalowania (w poziomie i pionie). ACanvas to płótno, na którym demówka maluje (Ty malujesz za pomocą SFML i jego funkcji, więc ten parametr zignoruj).

Jeśli chodzi o zmienne lokalne, to zmienne od Tile0 do Tile3 zawierają współrzędne wierzchołków aktualnie malowanej dachówki. W przypadku malowania wielokątów (poligonów), pamiętaj, że kolejność podawania wierzchołków ma znaczenie. Natomiast zmienna Index zawiera indeks dachówki do namalowania, w poziomie i pionie.

Funkcja ta renderuje dachówki od lewej do prawej, z góry na dół. Każda dachówka jest renderowana pod kątem 45°, tak jak na zrzutach od Ciebie. W załączniku jest skompilowana apka — możesz się pobawić samodzielnie.

W razie czego, renderowanie lewej strony dachu to ten sam algorytm, tyle że dodawanie należy zamienić na odejmowanie (przy obliczaniu współrzędnych). Całkiem możliwe, że wystarczy skorzystać z dodatkowej zmiennej liczbowej z kierunkiem renderowania (1 to dach w prawo, a -1 to dach w lewo) i używać jej jako mnożnika.

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 540
2

Dzięki @flowCRANE za pomoc :-) Poniżej zamieszczam przepisany kod na c++

screenshot-20241107191413.png

Kopiuj
void loadTexture(std::ifstream& file) {
    // check the size (16x16)
    cout << size.x << " " << size.y << "\n";

    short walls_height = 3;

    // create basic image to render house
    sf::Image house_image;
    house_image.create(size.x * 16, (size.y + walls_height*2 + 2) * 16, sf::Color::Transparent);

    // load the wall texture
    sf::Image wall;
    wall.create(32, 32, sf::Color::Transparent);
    wall = getTexture("walls/wooden_wall")->texture->copyToImage();
  
    
    // DRAWING A WALLS
    for (short y = 1; y <= walls_height; y++) {
        for (short x = 0; x < size.x/2; x++) {
            house_image.copy(wall, x*2*16, house_image.getSize().y - y *2*16);
        }
    }

    cout << "\n\n warning! \n";

    // DRAWING A WALLS UNDER THE ROOF
    short height = size.x/4;
    short width = size.x/2;
    short start_x;
    short end_x;

    for (int y = 0; y <= height; y++) {
        start_x = y;
        end_x = width - y - 1;

        for (int x = 0; x < width; x++) {
            if (x >= start_x && x <= end_x) {
                house_image.copy(wall, x * 2 *16, house_image.getSize().y - (y + walls_height + 1) * 2 * 16);
            }
        }
    }

    // DRAWING A ROOF
    sf::Color color = sf::Color(51, 38, 21);

    sf::Vector2f p1(0, walls_height * 2*16);
    sf::Vector2f p2(size.x/2*16, (walls_height+size.x/4) * 2*16);
    sf::Vector2f p3(size.x*16, walls_height * 2*16);
    sf::Vector2f p4(size.x*16, (walls_height +size.y/4 + 1) * 2*16);
    sf::Vector2f p5(size.x / 2 * 16, (walls_height + size.x / 4 + size.y/4 + 1) * 2 * 16);
    sf::Vector2f p6(0, (walls_height + size.y/4 + 1) * 2 * 16);
    
    sf::VertexArray left_quad(sf::Quads, 4);
    left_quad[0].position = p1;
    left_quad[1].position = p2;
    left_quad[2].position = p5;
    left_quad[3].position = p6;
    left_quad[0].color = color;
    left_quad[1].color = color;
    left_quad[2].color = color;
    left_quad[3].color = color;

    sf::VertexArray right_quad(sf::Quads, 4);
    right_quad[0].position = p2;
    right_quad[1].position = p3;
    right_quad[2].position = p4;
    right_quad[3].position = p5;
    right_quad[0].color = color;
    right_quad[1].color = color;
    right_quad[2].color = color;
    right_quad[3].color = color;

    sf::RenderTexture rtex;
    rtex.create(house_image.getSize().x, house_image.getSize().y);
    rtex.draw(left_quad);
    rtex.draw(right_quad);

    // CREATE A TILES

    float tiles_rows = 8;
    float tiles_columns = 8;

    float tile_width = float(size.x)/4.0f*32.0f / tiles_columns;
    float tile_height = float(size.y/4 +1)*32.0f / tiles_rows;
    float tile_border = 1;

    sf::Color color_outside = sf::Color(51, 38, 21);
    sf::Color color_inside = sf::Color(128, 24, 24);
    
    // LEFT SIDE OF ROOF
    for (float y = 0; y < tiles_rows; y += 1) {
        for (float x = 0; x < tiles_columns; x += 1) {
            sf::Vector2f quad_pos(x * tile_width, walls_height * 32 + y * tile_height + x * tile_width);

            // outside of tile
            sf::VertexArray outside(sf::Quads, 4);
            outside[0].position = sf::Vector2f(quad_pos.x, quad_pos.y);
            outside[1].position = sf::Vector2f(quad_pos.x + tile_width, quad_pos.y + tile_width);
            outside[2].position = sf::Vector2f(quad_pos.x + tile_width, quad_pos.y + tile_width + tile_height);
            outside[3].position = sf::Vector2f(quad_pos.x, quad_pos.y + tile_height);
            outside[0].color = color_outside;
            outside[1].color = color_outside;
            outside[2].color = color_outside;
            outside[3].color = color_outside;
            rtex.draw(outside);
            
            // inside of tile
            sf::VertexArray inside(sf::Quads, 4);
            inside[0].position = sf::Vector2f(quad_pos.x + tile_border, quad_pos.y + tile_border);
            inside[1].position = sf::Vector2f(quad_pos.x + tile_width-tile_border, quad_pos.y + tile_width + tile_border);
            inside[2].position = sf::Vector2f(quad_pos.x + tile_width-tile_border, quad_pos.y + tile_width + tile_height-tile_border);
            inside[3].position = sf::Vector2f(quad_pos.x + tile_border, quad_pos.y + tile_height - tile_border);
            inside[0].color = color_inside;
            inside[1].color = color_inside;
            inside[2].color = color_inside;
            inside[3].color = color_inside;
            rtex.draw(inside);
        }
    }

    // RIGHT SIDE OF ROOF
    for (float y = 0; y < tiles_rows; y += 1) {
        for (float x = 0; x < tiles_columns; x += 1) {
            sf::Vector2f quad_pos(size.x*16.0f-tile_width - x * tile_width, walls_height * 32 + y * tile_height + x * tile_width);

            // outside of tile
            sf::VertexArray outside(sf::Quads, 4);
            outside[0].position = sf::Vector2f(quad_pos.x, quad_pos.y + tile_width);
            outside[1].position = sf::Vector2f(quad_pos.x + tile_width, quad_pos.y);
            outside[2].position = sf::Vector2f(quad_pos.x + tile_width, quad_pos.y + tile_height);
            outside[3].position = sf::Vector2f(quad_pos.x, quad_pos.y + tile_width + tile_height);
            outside[0].color = color_outside;
            outside[1].color = color_outside;
            outside[2].color = color_outside;
            outside[3].color = color_outside;
            rtex.draw(outside);

            // inside of tile
            sf::VertexArray inside(sf::Quads, 4);
            inside[0].position = sf::Vector2f(quad_pos.x + tile_border, quad_pos.y + tile_width + tile_border);
            inside[1].position = sf::Vector2f(quad_pos.x + tile_width - tile_border, quad_pos.y + tile_border);
            inside[2].position = sf::Vector2f(quad_pos.x + tile_width - tile_border, quad_pos.y + tile_height - tile_border);
            inside[3].position = sf::Vector2f(quad_pos.x + tile_border, quad_pos.y + tile_width + tile_height - tile_border);
            inside[0].color = color_inside;
            inside[1].color = color_inside;
            inside[2].color = color_inside;
            inside[3].color = color_inside;
            rtex.draw(inside);
        }
    }


    sf::Image roof_image = rtex.getTexture().copyToImage();

    house_image.copy(roof_image, 0, 0 ,sf::IntRect(0,0,0,0),true);

    // create main tex
    sf::Texture* tex = new sf::Texture();
    tex->loadFromImage(house_image);

    // create the sprite
    sprite = sf::Sprite();
    sprite.setTexture(*tex);
    sprite.setOrigin(tex->getSize().x / 2, tex->getSize().y);
    sprite.setPosition(position);

}
}
flowCRANE
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Tuchów
  • Postów: 12269
1

IMO ten kod da się zoptymalizować. Z tego co widać, kolor pomiędzy dachówkami jest taki sam na całym dachu, więc zamiast malować go dla każdej dachówki z osobna (jako wielokąt), wystarczy go namalować raz — dla całej strony dachu. Dzięki temu jedna strona dachu wymagać będzie wyrenderowania jednego wielokąta (ciemnobrązowe tło, przebijające pomiędzy dachówkami) oraz zadana liczba dachówek. W ten sposób obniżysz złożoność z 2n do n+1, czyli niemal dwukrotnie, a efekt na ekranie będzie taki sam.

Przykład, który podałem wcześniej (snippet z mojej demówki) też da się zoptymalizować — w końcu obliczanie współrzędnej każdej dachówki nie musi oznaczać każdorazowego mnożenia jej indeksu przez rozmiary i offsety. Wystarczy raz policzyć współrzędne (dla pierwszej dachówki w danym rzędzie), a następnie, na koniec każdej iteracji, po prostu przesunąć wierzchołki dachówki w bok i na dół (o określoną liczbę pikseli). Dla osi Y można zrobić to samo — raz policzyć współrzędną Y (dla pierwszego rzędu), a potem, po wyrenderowaniu całego rzędu, inkrementować tę współrzędną o wysokość dachówki.

Kilka zmiennych więcej trzeba będzie użyć, ale koniec końców znacznie mniej obliczeń trzeba będzie wykonać, a to podniesie ogólną wydajność renderowania dachów.

PS: ustawianie kolorów wierzchołków powinieneś przenieść przed pętle, dlatego że ich kolor się nie zmienia. Obecnie w każdej iteracji pętli ustawiasz dla nich w kółko ten sam kolor, co nie ma za bardzo sensu. Ja wiem, że optymalizator może coś takiego wyciąć (loop invariant code motion), ale to nie oznacza, że nie możesz zrobić tego sam i mieć porządek w kodzie. 😉

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 540
0

Zrobiłem przerwę między stronami dachu, aby umiejscowić tam jakąś belkę nośną czy jak to się nazywa oraz dodałem tekstury do tilesów. Zrobiłem też naprzemienne rozłożenie dachówek oraz wystawanie ich z lewej i prawej strony, tak aby dach wyglądał bardziej naturalnie. Niestety gdzieś pogubiłem się w obliczeniach (p1-p6) i źle są renderowane quady lewy i prawy dachu (// DRAWING A ROOF) a na pewno źle obliczam p2 i p5 ...

my_house_8.png

Kopiuj
void loadTexture(std::ifstream& file) {

    // check the size
    cout << size.x << " " << size.y << "\n";

    short walls_height = 3;
    
    float tiles_rows = 8;
    float tiles_columns = 8;

    float tile_width = float(size.x) / 4.0f * 32.0f / tiles_columns;
    float tile_height = float(size.y / 4 + 2) * 32.0f / tiles_rows;
    float tile_border = 1;

    sf::Color color_outside = sf::Color::Black; //sf::Color(51, 38, 21);
    sf::Color color_inside = sf::Color(128, 24, 24);

    // create basic image to render house
    sf::Image house_image;
    house_image.create(size.x * 16 + tile_width, (size.y + walls_height*2 + 2) * 16, sf::Color::Transparent);

    // load the wall texture
    sf::Image wall;
    wall.create(32, 32, sf::Color::Transparent);
    wall = getTexture("walls/wooden_wall")->texture->copyToImage();
  
    
    // DRAWING A WALLS
    for (short y = 1; y <= walls_height; y++) {
        for (short x = 0; x < size.x/2; x++) {
            house_image.copy(wall, x*2*16 + tile_width/2.0f, house_image.getSize().y - y *2*16);
        }
    }

    // DRAWING A WALLS UNDER THE ROOF
    short height = size.x/4;
    short width = size.x/2;
    short start_x;
    short end_x;

    for (int y = 0; y <= height; y++) {
        start_x = y;
        end_x = width - y - 1;

        for (int x = 0; x < width; x++) {
            if (x >= start_x && x <= end_x) {
                house_image.copy(wall, x * 2 *16 + tile_width/2.0f, house_image.getSize().y - (y + walls_height + 1) * 2 * 16);
            }
        }
    }

    // DRAWING A ROOF
    sf::Color color = sf::Color::Black;

    sf::Vector2f p1(0, (walls_height-1.0f) * 2.0f *16.0f);
    sf::Vector2f p2(tile_width/2.0f + size.x/2.0f *16.0f, (walls_height-1.0f+size.x/4.0f) * 32.0f + tile_height/2.0f);
    sf::Vector2f p3(tile_width + size.x*16.0f, (walls_height-1.0f) * 32.0f);
    sf::Vector2f p4(tile_width + size.x*16.0f, (walls_height-1.0f) * 32.0f + tiles_rows*tile_height);
    sf::Vector2f p5(tile_width/2.0f + size.x / 2.0f * 16.0f, (walls_height -1.0f + size.x / 4.0f) * 32.0f + tiles_rows*tile_height + tile_height/2.0f);
    sf::Vector2f p6(0, (walls_height-1.0f) * 32.0f + tiles_rows*tile_height);
    
    sf::VertexArray left_quad(sf::Quads, 4);
    left_quad[0].position = p1;
    left_quad[1].position = p2;
    left_quad[2].position = p5;
    left_quad[3].position = p6;

    for (short i = 0; i < 4; i++)
        left_quad[i].color = color;

    sf::VertexArray right_quad(sf::Quads, 4);
    right_quad[0].position = p2;
    right_quad[1].position = p3;
    right_quad[2].position = p4;
    right_quad[3].position = p5;

    for (short i = 0; i < 4; i++)
        right_quad[i].color = color;

    sf::RenderTexture rtex;
    rtex.create(house_image.getSize().x, house_image.getSize().y);
    rtex.draw(left_quad);
    rtex.draw(right_quad);

    // CREATE A TILES
    sf::RenderStates rstate(getTexture("tiles/tile_5_highlands")->texture);

    // LEFT SIDE OF ROOF - EVEN TILES
    for (float x = 0; x < tiles_columns; x += 2) {
        for (float y = 0; y < tiles_rows; y += 1) {

            sf::Vector2f quad_pos(x * tile_width, (walls_height-1.0f) * 32.0f + y * tile_height + x * tile_width);
            
            sf::VertexArray tile(sf::Quads, 4);
            tile[0].position = sf::Vector2f(quad_pos.x + tile_border, quad_pos.y + tile_border);
            tile[1].position = sf::Vector2f(quad_pos.x + tile_width-tile_border, quad_pos.y + tile_width + tile_border);
            tile[2].position = sf::Vector2f(quad_pos.x + tile_width-tile_border, quad_pos.y + tile_width + tile_height-tile_border);
            tile[3].position = sf::Vector2f(quad_pos.x + tile_border, quad_pos.y + tile_height - tile_border);
            
            tile[0].texCoords = sf::Vector2f(0, 0);
            tile[1].texCoords = sf::Vector2f(tile_width, 0);
            tile[2].texCoords = sf::Vector2f(tile_width, tile_height);
            tile[3].texCoords = sf::Vector2f(0, tile_height);

            rtex.draw(tile,rstate);
        }
    }

    // LEFT SIDE OF ROOF - ODD TILES
    for (float x = 1; x < tiles_columns; x += 2) {
        
        // bottom tile
        sf::Vector2f bottom_pos(x * tile_width, (walls_height-1.0f) * 32.0f + x * tile_width + tile_height/2.0f);
        sf::VertexArray bottom_tile(sf::Quads, 4);
        bottom_tile[0].position = sf::Vector2f(bottom_pos.x + tile_border, bottom_pos.y - tile_height/2.0f + tile_border);
        bottom_tile[1].position = sf::Vector2f(bottom_pos.x + tile_width - tile_border, bottom_pos.y + tile_width - tile_height/2.0f+tile_border);
        bottom_tile[2].position = sf::Vector2f(bottom_pos.x + tile_width - tile_border, bottom_pos.y + tile_width - tile_border);
        bottom_tile[3].position = sf::Vector2f(bottom_pos.x + tile_border, bottom_pos.y - tile_border);

        bottom_tile[0].texCoords = sf::Vector2f(0, 0);
        bottom_tile[1].texCoords = sf::Vector2f(tile_width, 0);
        bottom_tile[2].texCoords = sf::Vector2f(tile_width, tile_height);
        bottom_tile[3].texCoords = sf::Vector2f(0, tile_height);

        rtex.draw(bottom_tile, rstate);

        // center tiles
        for (float y = 0; y < tiles_rows-1; y += 1) {
        
            sf::Vector2f quad_pos(x * tile_width, (walls_height-1.0f) * 32.0f + y * tile_height + x * tile_width + tile_height/2.0f);

            sf::VertexArray tile(sf::Quads, 4);
            tile[0].position = sf::Vector2f(quad_pos.x + tile_border, quad_pos.y + tile_border);
            tile[1].position = sf::Vector2f(quad_pos.x + tile_width - tile_border, quad_pos.y + tile_width + tile_border);
            tile[2].position = sf::Vector2f(quad_pos.x + tile_width - tile_border, quad_pos.y + tile_width + tile_height - tile_border);
            tile[3].position = sf::Vector2f(quad_pos.x + tile_border, quad_pos.y + tile_height - tile_border);

            tile[0].texCoords = sf::Vector2f(0, 0);
            tile[1].texCoords = sf::Vector2f(tile_width, 0);
            tile[2].texCoords = sf::Vector2f(tile_width, tile_height);
            tile[3].texCoords = sf::Vector2f(0, tile_height);

            rtex.draw(tile, rstate);
        }

        // top tile
        sf::Vector2f top_pos(x * tile_width, (walls_height-1.0f) * 32.0f + (tiles_rows) * tile_height + x * tile_width - tile_height/2.0f);
        sf::VertexArray top_tile(sf::Quads, 4);
        top_tile[0].position = sf::Vector2f(top_pos.x + tile_border, top_pos.y + tile_border);
        top_tile[1].position = sf::Vector2f(top_pos.x + tile_width-tile_border, top_pos.y + tile_width +tile_border);
        top_tile[2].position = sf::Vector2f(top_pos.x + tile_width-tile_border, top_pos.y + tile_width+tile_height/2.0f-tile_border);
        top_tile[3].position = sf::Vector2f(top_pos.x + tile_border, top_pos.y+tile_height/2.0f-tile_border);

        top_tile[0].texCoords = sf::Vector2f(0, 0);
        top_tile[1].texCoords = sf::Vector2f(tile_width, 0);
        top_tile[2].texCoords = sf::Vector2f(tile_width, tile_height);
        top_tile[3].texCoords = sf::Vector2f(0, tile_height);

        rtex.draw(top_tile, rstate);
    }


    // RIGHT SIDE OF ROOF - EVEN TILES
    for (float x = 0; x < tiles_columns; x += 2) {
        for (float y = 0; y < tiles_rows; y += 1) {
        
            sf::Vector2f quad_pos(size.x*16.0f - x * tile_width, (walls_height-1.0f) * 32.0f + y * tile_height + x * tile_width);
            sf::VertexArray tile(sf::Quads, 4);
            tile[0].position = sf::Vector2f(quad_pos.x + tile_border, quad_pos.y + tile_width + tile_border);
            tile[1].position = sf::Vector2f(quad_pos.x + tile_width - tile_border, quad_pos.y + tile_border);
            tile[2].position = sf::Vector2f(quad_pos.x + tile_width - tile_border, quad_pos.y + tile_height - tile_border);
            tile[3].position = sf::Vector2f(quad_pos.x + tile_border, quad_pos.y + tile_width + tile_height - tile_border);
            
            tile[0].texCoords = sf::Vector2f(0, 0);
            tile[1].texCoords = sf::Vector2f(tile_width, 0);
            tile[2].texCoords = sf::Vector2f(tile_width, tile_height);
            tile[3].texCoords = sf::Vector2f(0, tile_height);

            sf::RenderStates rstate(getTexture("tiles/tile_5_highlands")->texture);
            rtex.draw(tile, rstate);
        }
    }

    // RIGHT SIDE OF ROOF - ODD TILES
    for (float x = 1; x < tiles_columns; x += 2) {

        // bottom tile
        sf::Vector2f bottom_pos(size.x/2.0f*16.0f + x * tile_width, (walls_height - 1.0f + size.x/4.0f) * 32.0f - x * tile_width - tile_height/2.0f);
        sf::VertexArray bottom_tile(sf::Quads, 4);
        bottom_tile[0].position = sf::Vector2f(bottom_pos.x + tile_border, bottom_pos.y + tile_width + tile_border + tile_height/2.0f);
        bottom_tile[1].position = sf::Vector2f(bottom_pos.x + tile_width - tile_border, bottom_pos.y + tile_border + tile_height/2.0f);
        bottom_tile[2].position = sf::Vector2f(bottom_pos.x + tile_width - tile_border, bottom_pos.y + tile_height - tile_border);
        bottom_tile[3].position = sf::Vector2f(bottom_pos.x + tile_border, bottom_pos.y + tile_width + tile_height - tile_border);

        bottom_tile[0].texCoords = sf::Vector2f(0, 0);
        bottom_tile[1].texCoords = sf::Vector2f(tile_width, 0);
        bottom_tile[2].texCoords = sf::Vector2f(tile_width, tile_height);
        bottom_tile[3].texCoords = sf::Vector2f(0, tile_height);

        rtex.draw(bottom_tile, rstate);

        // center tiles
        for (float y = 0; y < tiles_rows - 1; y += 1) {

            sf::Vector2f quad_pos(size.x * 16.0f - x * tile_width, (walls_height-1.0f) * 32.0f + y * tile_height + x * tile_width + tile_height/2.0f);

            sf::VertexArray tile(sf::Quads, 4);
            tile[0].position = sf::Vector2f(quad_pos.x + tile_border, quad_pos.y + tile_width + tile_border);
            tile[1].position = sf::Vector2f(quad_pos.x + tile_width - tile_border, quad_pos.y + tile_border);
            tile[2].position = sf::Vector2f(quad_pos.x + tile_width - tile_border, quad_pos.y + tile_height - tile_border);
            tile[3].position = sf::Vector2f(quad_pos.x + tile_border, quad_pos.y + tile_width + tile_height - tile_border);

            tile[0].texCoords = sf::Vector2f(0, 0);
            tile[1].texCoords = sf::Vector2f(tile_width, 0);
            tile[2].texCoords = sf::Vector2f(tile_width, tile_height);
            tile[3].texCoords = sf::Vector2f(0, tile_height);

            rtex.draw(tile, rstate);
        }

        // top tile
        sf::Vector2f top_pos(size.x / 2.0f * 16.0f + x * tile_width, (walls_height-1.0f+size.x/4.0f)*32.0f + tiles_rows*tile_height - x * tile_width - tile_height/2.0f);
        sf::VertexArray top_tile(sf::Quads, 4);
        top_tile[0].position = sf::Vector2f(top_pos.x + tile_border, top_pos.y + tile_width + tile_border);
        top_tile[1].position = sf::Vector2f(top_pos.x + tile_width - tile_border, top_pos.y + tile_border);
        top_tile[2].position = sf::Vector2f(top_pos.x + tile_width - tile_border, top_pos.y + tile_height/2.0f - tile_border);
        top_tile[3].position = sf::Vector2f(top_pos.x + tile_border, top_pos.y + tile_width + tile_height/2.0f  - tile_border);

        top_tile[0].texCoords = sf::Vector2f(0, 0);
        top_tile[1].texCoords = sf::Vector2f(tile_width, 0);
        top_tile[2].texCoords = sf::Vector2f(tile_width, tile_height);
        top_tile[3].texCoords = sf::Vector2f(0, tile_height);

        rtex.draw(top_tile, rstate);
    }


    sf::Image roof_image = rtex.getTexture().copyToImage();

    house_image.copy(roof_image, 0, 0 ,sf::IntRect(0,0,0,0),true);

    // create main tex
    sf::Texture* tex = new sf::Texture();
    tex->loadFromImage(house_image);

    // create the sprite
    sprite = sf::Sprite();
    sprite.setTexture(*tex);
    sprite.setOrigin(tex->getSize().x / 2, tex->getSize().y);
    sprite.setPosition(position);

}
flowCRANE
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Tuchów
  • Postów: 12269
1

Wiem, że temat dotyczy algorytmu renderującego dach w sposób dynamiczny, ale może warto zapytać inaczej — czemu dach renderujesz za pomocą wielokątów (każdą dachówkę z osobna), zamiast używać do tego prostokątnych tekstur, czyli tak jak robi się to w tego typu dwuwymiarowych grach a pixelartową oprawą?

Obecnie masz masę obliczeń do wykonania, a do tego jeśli zechcesz, aby dach wyglądał inaczej, to całe te obliczenia trzeba modyfikować. Zamiast tego, powinieneś mieć kafle zawierające dachówki namalowane już pod zadanym kątem i renderować je w formie normalnych prostokątów, tak jakbyś renderował szachownicę. Wystarczy kilka typów kafli, aby wszystko wyrenderować. Jeśli chcesz mieć dodatkowe dekoracje np. na głównej belce, to wystarczy dodać kilka nowych kafli.

W ten sposób dach nie tylko łatwiej byłoby renderować (dwie pętle, łącznie kilka linijek kodu), ale też miałbyś możliwość wygodnego dodania nowego zestawu kafli z dachówkami wyglądającymi inaczej (np. gont bitumiczny zamiast klasycznej dachówki ceramicznej), dachy mógłbyś wyklikać w edytorze, tak jak wszystko inne, dach danego rodzaju mógłby mieć dowolny kolor (ot uzyskany za pomocą modulacji koloru), a do tego w wybranych miejscach mógłby posiadać dekoracje (np. pęknięta dachówka albo dziura w dachu jako osobne kafle). Miałbyś wygodę i elastyczność, a do tego kilka linijek kodu renderującego dowolną kompozycję.

Ogólnie rzecz biorąc, wszystko powinno być renderowane za pomocą kafli (ot prostokątnych tekstur), cały teren i wszystkie obiekty na nim się znajdujące (budynki, drzewa, aktorzy itd.), a to czego nie da się kaflami wyrenderować, powinno być zaimplementowane osobno.

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 540
1

Nie wiem. Po prostu tak zrobiłem i już nie chcę tego zmieniać, bo działa. Najważniejsze jest to, że ten dach jest generowany dynamicznie :-)
Jeszcze pozostało dodać belki. Nie wiem jakie wpisać współrzędne tekstury.

screenshot-20241109150513.png

Kopiuj
// WOODEN BEAMS
// main wooden beam
sf::VertexArray beam(sf::Quads, 4);
sf::Vector2f pos;
sf::Color beam_color = sf::Color(100, 55, 30);
sf::RenderStates rstate2;
rstate2.texture = getTexture("tiles/tile_6")->texture;

pos.x = size.x / 2.f * 16.0f + tile_width/2.0f;
pos.y = (walls_height - 1.0f + size.x / 4.0f) * 32.0f;
beam[0].position = sf::Vector2f(pos.x-tile_width/2.0f, pos.y);
beam[1].position = sf::Vector2f(pos.x+tile_width/2.0f, pos.y);
beam[2].position = sf::Vector2f(pos.x+tile_width/2.0f, pos.y+tiles_rows*tile_height);
beam[3].position = sf::Vector2f(pos.x-tile_width/2.0f, pos.y+tiles_rows*tile_height);

//beam[0].texCoords = ??? ;
//beam[1].texCoords = ??? ;
//beam[2].texCoords = ??? ;
//beam[3].texCoords = ??? ;

rtex.draw(beam, rstate2);
tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 540
1

Dobra. Udało mi się zrobić automatyczne generowanie dachów i działa dla różnych rozmiarów. Załączam dodatkowo kod, może kiedyś komuś się przyda :-)
screenshot-20241109181456.png

Kopiuj
void loadTexture(std::ifstream& file) {

    // check the size
    cout << size.x << " " << size.y << "\n";

    short walls_height = 3;

    float tiles_rows = 16; //size.y / 2;
    float tiles_columns = 16; // size.x / 2;

    float tile_width = float(size.x)/4.0f*32.0f/tiles_columns;
    float tile_height = float(size.y)/2.0f*32.0f/tiles_rows;
    float tile_border = 1;

    sf::Color color_outside = sf::Color::Black; //sf::Color(51, 38, 21);
    sf::Color color_inside = sf::Color(128, 24, 24);

    // create basic image to render house
    sf::Image house_image;
    house_image.create(size.x*16+tile_width, (walls_height+size.x/2.0f)*16.0f+tiles_rows*tile_height+tile_height, sf::Color::Transparent);

    // load the wall texture
    sf::Image wall;
    wall.create(32, 32, sf::Color::Transparent);
    wall = getTexture("walls/wooden_wall")->texture->copyToImage();


    // DRAWING A WALLS
    for (short y = 1; y <= walls_height; y++) {
        for (short x = 0; x < size.x / 2; x++) {
            house_image.copy(wall, x * 2 * 16 + tile_width / 2.0f, house_image.getSize().y - y * 2 * 16);
        }
    }

    // DRAWING A WALLS UNDER THE ROOF
    short height = size.x / 4;
    short width = size.x / 2;
    short start_x;
    short end_x;

    for (int y = 0; y <= height; y++) {
        start_x = y;
        end_x = width - y - 1;

        for (int x = 0; x < width; x++) {
            if (x >= start_x && x <= end_x) {
                house_image.copy(wall, x * 2 * 16 + tile_width / 2.0f, house_image.getSize().y - (y + walls_height + 1) * 2 * 16);
            }
        }
    }

    // DRAWING A ROOF
    sf::Color color = sf::Color::Black;

    sf::Vector2f p1(0, (walls_height - 1.0f) * 2.0f * 16.0f);
    sf::Vector2f p2(tile_width / 2.0f + size.x / 2.0f * 16.0f, (walls_height - 1.0f + size.x / 4.0f) * 32.0f + tile_height / 4.0f);
    sf::Vector2f p3(tile_width + size.x * 16.0f, (walls_height - 1.0f) * 32.0f);
    sf::Vector2f p4(tile_width + size.x * 16.0f, (walls_height - 1.0f) * 32.0f + tiles_rows * tile_height);
    sf::Vector2f p5(tile_width / 2.0f + size.x / 2.0f * 16.0f, (walls_height - 1.0f + size.x / 4.0f) * 32.0f + tiles_rows * tile_height + tile_height / 4.0f);
    sf::Vector2f p6(0, (walls_height - 1.0f) * 32.0f + tiles_rows * tile_height);

    sf::VertexArray left_quad(sf::Quads, 4);
    left_quad[0].position = p1;
    left_quad[1].position = p2;
    left_quad[2].position = p5;
    left_quad[3].position = p6;

    for (short i = 0; i < 4; i++)
        left_quad[i].color = color;

    sf::VertexArray right_quad(sf::Quads, 4);
    right_quad[0].position = p2;
    right_quad[1].position = p3;
    right_quad[2].position = p4;
    right_quad[3].position = p5;

    for (short i = 0; i < 4; i++)
        right_quad[i].color = color;

    sf::RenderTexture rtex;
    rtex.create(house_image.getSize().x, house_image.getSize().y);
    rtex.draw(left_quad);
    rtex.draw(right_quad);

    // CREATE A TILES
    sf::RenderStates rstate(getTexture("tiles/tile_5_highlands")->texture);

    // LEFT SIDE OF ROOF - EVEN TILES
    for (float x = 0; x < tiles_columns; x += 2) {
        for (float y = 0; y < tiles_rows; y += 1) {

            sf::Vector2f quad_pos(x * tile_width, (walls_height - 1.0f) * 32.0f + y * tile_height + x * tile_width);

            sf::VertexArray tile(sf::Quads, 4);
            tile[0].position = sf::Vector2f(quad_pos.x + tile_border, quad_pos.y + tile_border);
            tile[1].position = sf::Vector2f(quad_pos.x + tile_width - tile_border, quad_pos.y + tile_width + tile_border);
            tile[2].position = sf::Vector2f(quad_pos.x + tile_width - tile_border, quad_pos.y + tile_width + tile_height - tile_border);
            tile[3].position = sf::Vector2f(quad_pos.x + tile_border, quad_pos.y + tile_height - tile_border);

            tile[0].texCoords = sf::Vector2f(0, 0);
            tile[1].texCoords = sf::Vector2f(tile_width, 0);
            tile[2].texCoords = sf::Vector2f(tile_width, tile_height);
            tile[3].texCoords = sf::Vector2f(0, tile_height);

            rtex.draw(tile, rstate);
        }
    }

    // LEFT SIDE OF ROOF - ODD TILES
    for (float x = 1; x < tiles_columns; x += 2) {

        // bottom tile
        sf::Vector2f bottom_pos(x * tile_width, (walls_height - 1.0f) * 32.0f + x * tile_width + tile_height / 2.0f);
        sf::VertexArray bottom_tile(sf::Quads, 4);
        bottom_tile[0].position = sf::Vector2f(bottom_pos.x + tile_border, bottom_pos.y - tile_height / 2.0f + tile_border);
        bottom_tile[1].position = sf::Vector2f(bottom_pos.x + tile_width - tile_border, bottom_pos.y + tile_width - tile_height / 2.0f + tile_border);
        bottom_tile[2].position = sf::Vector2f(bottom_pos.x + tile_width - tile_border, bottom_pos.y + tile_width - tile_border);
        bottom_tile[3].position = sf::Vector2f(bottom_pos.x + tile_border, bottom_pos.y - tile_border);

        bottom_tile[0].texCoords = sf::Vector2f(0, 0);
        bottom_tile[1].texCoords = sf::Vector2f(tile_width, 0);
        bottom_tile[2].texCoords = sf::Vector2f(tile_width, tile_height);
        bottom_tile[3].texCoords = sf::Vector2f(0, tile_height);

        rtex.draw(bottom_tile, rstate);

        // center tiles
        for (float y = 0; y < tiles_rows - 1; y += 1) {

            sf::Vector2f quad_pos(x * tile_width, (walls_height - 1.0f) * 32.0f + y * tile_height + x * tile_width + tile_height / 2.0f);

            sf::VertexArray tile(sf::Quads, 4);
            tile[0].position = sf::Vector2f(quad_pos.x + tile_border, quad_pos.y + tile_border);
            tile[1].position = sf::Vector2f(quad_pos.x + tile_width - tile_border, quad_pos.y + tile_width + tile_border);
            tile[2].position = sf::Vector2f(quad_pos.x + tile_width - tile_border, quad_pos.y + tile_width + tile_height - tile_border);
            tile[3].position = sf::Vector2f(quad_pos.x + tile_border, quad_pos.y + tile_height - tile_border);

            tile[0].texCoords = sf::Vector2f(0, 0);
            tile[1].texCoords = sf::Vector2f(tile_width, 0);
            tile[2].texCoords = sf::Vector2f(tile_width, tile_height);
            tile[3].texCoords = sf::Vector2f(0, tile_height);

            rtex.draw(tile, rstate);
        }

        // top tile
        sf::Vector2f top_pos(x * tile_width, (walls_height - 1.0f) * 32.0f + tiles_rows * tile_height + x * tile_width - tile_height / 2.0f);
        sf::VertexArray top_tile(sf::Quads, 4);
        top_tile[0].position = sf::Vector2f(top_pos.x + tile_border, top_pos.y + tile_border);
        top_tile[1].position = sf::Vector2f(top_pos.x + tile_width - tile_border, top_pos.y + tile_width + tile_border);
        top_tile[2].position = sf::Vector2f(top_pos.x + tile_width - tile_border, top_pos.y + tile_width + tile_height / 2.0f - tile_border);
        top_tile[3].position = sf::Vector2f(top_pos.x + tile_border, top_pos.y + tile_height / 2.0f - tile_border);

        top_tile[0].texCoords = sf::Vector2f(0, 0);
        top_tile[1].texCoords = sf::Vector2f(tile_width, 0);
        top_tile[2].texCoords = sf::Vector2f(tile_width, tile_height);
        top_tile[3].texCoords = sf::Vector2f(0, tile_height);

        rtex.draw(top_tile, rstate);
    }


    // RIGHT SIDE OF ROOF - EVEN TILES
    for (float x = 0; x < tiles_columns; x += 2) {
        for (float y = 0; y < tiles_rows; y += 1) {

            sf::Vector2f quad_pos(size.x * 16.0f - x * tile_width, (walls_height - 1.0f) * 32.0f + y * tile_height + x * tile_width);
            sf::VertexArray tile(sf::Quads, 4);
            tile[0].position = sf::Vector2f(quad_pos.x + tile_border, quad_pos.y + tile_width + tile_border);
            tile[1].position = sf::Vector2f(quad_pos.x + tile_width - tile_border, quad_pos.y + tile_border);
            tile[2].position = sf::Vector2f(quad_pos.x + tile_width - tile_border, quad_pos.y + tile_height - tile_border);
            tile[3].position = sf::Vector2f(quad_pos.x + tile_border, quad_pos.y + tile_width + tile_height - tile_border);

            tile[0].texCoords = sf::Vector2f(0, 0);
            tile[1].texCoords = sf::Vector2f(tile_width, 0);
            tile[2].texCoords = sf::Vector2f(tile_width, tile_height);
            tile[3].texCoords = sf::Vector2f(0, tile_height);

            sf::RenderStates rstate(getTexture("tiles/tile_5_highlands")->texture);
            rtex.draw(tile, rstate);
        }
    }

    // RIGHT SIDE OF ROOF - ODD TILES
    for (float x = 1; x < tiles_columns; x += 2) {

        // bottom tile
        sf::Vector2f bottom_pos(size.x / 2.0f * 16.0f + x * tile_width, (walls_height - 1.0f + size.x / 4.0f) * 32.0f - x * tile_width - tile_height / 2.0f);
        sf::VertexArray bottom_tile(sf::Quads, 4);
        bottom_tile[0].position = sf::Vector2f(bottom_pos.x + tile_border, bottom_pos.y + tile_width + tile_border + tile_height / 2.0f);
        bottom_tile[1].position = sf::Vector2f(bottom_pos.x + tile_width - tile_border, bottom_pos.y + tile_border + tile_height / 2.0f);
        bottom_tile[2].position = sf::Vector2f(bottom_pos.x + tile_width - tile_border, bottom_pos.y + tile_height - tile_border);
        bottom_tile[3].position = sf::Vector2f(bottom_pos.x + tile_border, bottom_pos.y + tile_width + tile_height - tile_border);

        bottom_tile[0].texCoords = sf::Vector2f(0, 0);
        bottom_tile[1].texCoords = sf::Vector2f(tile_width, 0);
        bottom_tile[2].texCoords = sf::Vector2f(tile_width, tile_height);
        bottom_tile[3].texCoords = sf::Vector2f(0, tile_height);

        rtex.draw(bottom_tile, rstate);

        // center tiles
        for (float y = 0; y < tiles_rows - 1; y += 1) {

            sf::Vector2f quad_pos(size.x * 16.0f - x * tile_width, (walls_height - 1.0f) * 32.0f + y * tile_height + x * tile_width + tile_height / 2.0f);

            sf::VertexArray tile(sf::Quads, 4);
            tile[0].position = sf::Vector2f(quad_pos.x + tile_border, quad_pos.y + tile_width + tile_border);
            tile[1].position = sf::Vector2f(quad_pos.x + tile_width - tile_border, quad_pos.y + tile_border);
            tile[2].position = sf::Vector2f(quad_pos.x + tile_width - tile_border, quad_pos.y + tile_height - tile_border);
            tile[3].position = sf::Vector2f(quad_pos.x + tile_border, quad_pos.y + tile_width + tile_height - tile_border);

            tile[0].texCoords = sf::Vector2f(0, 0);
            tile[1].texCoords = sf::Vector2f(tile_width, 0);
            tile[2].texCoords = sf::Vector2f(tile_width, tile_height);
            tile[3].texCoords = sf::Vector2f(0, tile_height);

            rtex.draw(tile, rstate);
        }

        // top tile
        sf::Vector2f top_pos(size.x / 2.0f * 16.0f + x * tile_width, (walls_height - 1.0f + size.x / 4.0f) * 32.0f + tiles_rows * tile_height - x * tile_width - tile_height / 2.0f);
        sf::VertexArray top_tile(sf::Quads, 4);
        top_tile[0].position = sf::Vector2f(top_pos.x + tile_border, top_pos.y + tile_width + tile_border);
        top_tile[1].position = sf::Vector2f(top_pos.x + tile_width - tile_border, top_pos.y + tile_border);
        top_tile[2].position = sf::Vector2f(top_pos.x + tile_width - tile_border, top_pos.y + tile_height / 2.0f - tile_border);
        top_tile[3].position = sf::Vector2f(top_pos.x + tile_border, top_pos.y + tile_width + tile_height / 2.0f - tile_border);

        top_tile[0].texCoords = sf::Vector2f(0, 0);
        top_tile[1].texCoords = sf::Vector2f(tile_width, 0);
        top_tile[2].texCoords = sf::Vector2f(tile_width, tile_height);
        top_tile[3].texCoords = sf::Vector2f(0, tile_height);

        rtex.draw(top_tile, rstate);
    }

    // WOODEN BEAMS
    // main wooden beam
    sf::VertexArray beam(sf::Quads, 4);
    sf::Vector2f pos;

    pos.x = size.x / 2.f * 16.0f + tile_width/2.0f;
    pos.y = (walls_height - 1.0f + size.x / 4.0f) * 32.0f;
    beam[0].position = sf::Vector2f(pos.x-tile_width/2.0f, pos.y);
    beam[1].position = sf::Vector2f(pos.x+tile_width/2.0f, pos.y);
    beam[2].position = sf::Vector2f(pos.x+tile_width/2.0f, pos.y+tiles_rows*tile_height);
    beam[3].position = sf::Vector2f(pos.x-tile_width/2.0f, pos.y+tiles_rows*tile_height);
    
    for (short i = 0; i < 4; i++)
        beam[i].color = sf::Color::Black;

    rtex.draw(beam);

    for (float y = 0; y < tiles_rows; y+=1) {
        sf::VertexArray tile(sf::Quads, 4);
        tile[0].position = sf::Vector2f(size.x/2.0f*16.0f+tile_border, (walls_height-1.0f+size.x/4.0f)*32.0f+y*tile_height+tile_border);
        tile[1].position = sf::Vector2f(size.x/2.0f*16.0f+tile_width-tile_border, (walls_height-1.0f+size.x/4.0f)*32.0f+y*tile_height+tile_border);
        tile[2].position = sf::Vector2f(size.x/2.0f*16.0f+tile_width-tile_border, (walls_height-1.0f+size.x/4.0f)*32.0f+y*tile_height+tile_height-tile_border);
        tile[3].position = sf::Vector2f(size.x/2.0f*16.0f+tile_border, (walls_height-1.0f+size.x/4.0f)*32.0f+y*tile_height+tile_height-tile_border);
        
        for (short i = 0; i < 4; i++)
            tile[i].texCoords = beam[i].position;

        rtex.draw(tile, rstate);
    }

    // top-center quad
    beam[0].position = sf::Vector2f(pos.x-tile_width/2.0f, pos.y-tile_height/4.0f);
    beam[1].position = sf::Vector2f(pos.x+tile_width/2.0f, pos.y-tile_height/4.0f);
    beam[2].position = sf::Vector2f(pos.x+tile_width/2.0f, pos.y);
    beam[3].position = sf::Vector2f(pos.x-tile_width/2.0f, pos.y);

    for (short i = 0; i < 4; i++)
        beam[i].texCoords = beam[i].position;

    rtex.draw(beam, rstate);

    // top-left beam
    beam[0].position = sf::Vector2f(pos.x-tile_width/2.0f-tiles_columns*tile_width, pos.y-tile_height/4.0f-size.x/4.0f*32.0f);
    beam[1].position = sf::Vector2f(pos.x-tile_width/2.0f, pos.y-tile_height/4.0f);
    beam[2].position = sf::Vector2f(pos.x-tile_width/2.0f, pos.y);
    beam[3].position = sf::Vector2f(pos.x-tile_width/2.0f-tiles_columns*tile_width, pos.y-size.x/4.0f*32.0f);
    
    for (short i = 0; i < 4; i++)
        beam[i].texCoords = beam[i].position;

    rtex.draw(beam, rstate);

    // top-right beam
    beam[0].position = sf::Vector2f(pos.x+tile_width/2.0f+tiles_columns*tile_width, pos.y-tile_height/4.0f-size.x/4.0f*32.0f);
    beam[1].position = sf::Vector2f(pos.x+tile_width/2.0f, pos.y-tile_height/4.0f);
    beam[2].position = sf::Vector2f(pos.x+tile_width/2.0f, pos.y);
    beam[3].position = sf::Vector2f(pos.x+tile_width/2.0f+tiles_columns*tile_width, pos.y-size.x/4.0f*32.0f);
    
    for (short i = 0; i < 4; i++)
        beam[i].texCoords = beam[i].position;

    rtex.draw(beam, rstate);

    sf::Image roof_image = rtex.getTexture().copyToImage();

    house_image.copy(roof_image, 0, 0, sf::IntRect(0, 0, 0, 0), true);

    // create main tex
    sf::Texture* tex = new sf::Texture();
    tex->loadFromImage(house_image);

    // create the sprite
    sprite = sf::Sprite();
    sprite.setTexture(*tex);
    sprite.setOrigin(tex->getSize().x / 2, tex->getSize().y);
    sprite.setPosition(position);

}
tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 540
0

Edytowałem generowanie dachu w taki sposób, że teraz dach jest dwukrotnie niższy oraz środkowe dachówki zaczynają się od niepełnej .. dachówki.
Mój problem polega na tym, że generowane dachy budynków w mojej grze wyglądają nienaturalnie. Nie jestem artystą i za bardzo nie jestem w stanie dostrzec błędu w swoim rozumowaniu stąd moje pytanie. Co w tym dachu jest złego i jak to naprawić ?

sss.png

flowCRANE
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Tuchów
  • Postów: 12269
0

Nie ma nic złego w tym dachu — wygląda prawidłowo, zgodnie z przyjętą perspektywą. Tzn. z jej brakiem, ale mowa o rzucie ortograficznym, pod kątem 45°. Po prostu przy takich parametrach kamery, dachy powinny wyglądać tak jak na pokazanym zrzucie.

Jeśli używasz kafli kwadratowych, to budynek o kwadratowej podstawie będzie miał kształt kwadratu na ekranie. Dach będzie zgodny z nim, więc będzie przykrywał tę kwadratową powierzchnię, przez co będzie wyglądał na nienaturalnie długi (w głąb ekranu). Matematycznie wszystko się zgadza, ale patrząc na ekran mózg sam siebie oszukuje, bo próbuje zastosować perspektywę i szuka głębi, mimo, że ta nie jest używana.

Jeśli nie podoba Ci się obecny efekt, to zamiast kwadratowych kafli, zastosuj prostokątne (niższe niż szersze), a ich zawartość namaluj w taki sposób, aby udawały, że kamera nie patrzy na nie pod kątem 45°, a mniejszym (tak jakby była niżej gruntu). W ten sposób dach na ekranie będzie miał taką samą szerokość jak obecnie, ale mniejszą wysokość, co będzie wyglądać bardziej naturalnie.

Tutaj masz przykład tego o czym piszę:

https://www.pinterest.com/pin/stoneshard-stoneshard-on-x--365354588532159360/
screenshot-20241122135132.png

Zwróć uwagę na skrzydła pionowe — wyglądają naturalnie, jako wyobrażane w głowie bryły sprawiają wrażenie wysokich i długich, ale nie nienaturalnie głębokich. Wszystko dlatego, że namalowane są w taki sposób, jakby kamera patrzyła na nie pod kątem ~30°, a nie 45°.

W razie gdybyś chciał zmienić kształt kafli z kwadratowych na prostokątne, to pamiętaj, że będziesz musiał odpowiednio przystosować do tego logikę sterowania postacią i potworkami. Wszystko co będzie się poruszać w pionie na ekranie, musi się przesuwać o mniejszy offset niż w poziomie. Dla przykładu, jeśli użyjesz kafli prostokątnych w rozmiarze 2n x n (dwukrotnie szersze niż wysokość), kamera będzie udawać że jest połową 45° (czyli 22.5°, dwa razy bliżej gruntu), a ruch gracza na pełnej prędkości w górę musi być dzielony przez pół. Czyli jeśli np. szybkość gracza wynosi 10px na klatkę, to kiedy idzie w bok, dodajesz/odejmujesz jego pozycji 10px, natomiast jeśli idzie w górę/dół, to tylko 5px. Taki dzielnik/mnożnik możesz ustalić z góry i sobie hardkodować.

Przy okazji tutaj masz ilustrację tego, co proponowałem Ci jakiś czas temu w tym wątku, czyli porzucenie renderowania dachówek z osobna na rzecz tekstur i renderowania ich w formie prostej macierzy na ekranie, zamiast przy użyciu rozciągania tekstur. Nie dość, że prześliczne te budynki, to w dodatku łatwo można zrobić tego typu zestaw kafli i wyrenderować to wszystko, bez żadnego rozciągania.

obscurity
  • Rejestracja: dni
  • Ostatnio: dni
0

Co tu się w tym temacie odwala, chcesz żeby te kafelki się oddzielnie animowały albo np psuły od tornada czy coś? Bo nie widzę sensu w tworzeniu każdej dachówki jako osobnego elementu. Czemu po prostu nie zrobisz powtarzalnej tekstury dachu? Teraz masz mnóstwo zbędnego kodu, brzydszy efekt i gorszy performance.

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 540
0

@obscurity Bo nie umiem. Z resztą już prawie zrobiłem generowanie dachu więc nie widzę sensu poprawiać kod, który wywoływany jest tylko raz :-)
Dodałem cieniowanie tak jak mi doradzano.

RPG2D 184.png

flowCRANE
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Tuchów
  • Postów: 12269
1

Ocieniowany dach wygląda dużo lepiej. Ale jest jeden problem — dach zdaje się być oświetlony z prawej strony, ale cień rzucany przez dach na jego ścianę pokazuje, że jednak źródło światła jest nad nim, a nie z boku. To samo jeśli chodzi o cienie rzucane przez inne obiekty, takie jak choćby wcześniej omawiane drzewa i potworki, których cienie rzucane są prosto w górę (na ekranie), a więc tak jakby źródło światła było nad nimi.

Światłocień dodaje realizmu i jak najbardziej jest wartością dodaną, ale musisz dbać o to, aby był spójny.

Spine
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 6968
0
tBane napisał(a):

Dobra. Udało mi się zrobić automatyczne generowanie dachów i działa dla różnych rozmiarów. Załączam dodatkowo kod, może kiedyś komuś się przyda :-)
screenshot-20241109181456.png

Kopiuj
//...

Gdybyś teksturował płaszczyznę w Blenderze, uniknąłbyś pisania tony kodu dla takich rzeczy.

Wystarczyłoby Ci, gdybyś zaimplementował obsługę plików Wavefront OBJ - najprostszy w implementacji tekstowy format modeli 3D. Wystarczy parsować od jakiej literki zaczynają się linie i wczytać dane do odpowiednich tablic.
Blender ma eksporter tego formatu. Tak wygląda wyeksportowany obiekt (przed eksportem quady zamieniłem na trójkąty):

Kopiuj
# Blender 4.0.1
# www.blender.org
mtllib dachowka.mtl
o Dach.001
v -2.500000 1.500000 0.000000
v -2.500000 -0.500000 0.000000
v -2.000000 0.000000 0.000000
v -3.000000 -1.000000 0.000000
v -2.000000 2.000000 0.000000
v -3.000000 1.000000 0.000000
v -2.250000 -0.250000 0.000000
v -2.750000 -0.750000 0.000000
v -2.250000 1.750000 0.000000
v -2.750000 1.250000 0.000000
v -2.375000 -0.375000 0.000000
v -2.875000 -0.875000 0.000000
v -2.125000 1.875000 0.000000
v -2.625000 1.375000 0.000000
v -2.125000 -0.125000 0.000000
v -2.625000 -0.625000 0.000000
v -2.375000 1.625000 0.000000
v -2.875000 1.125000 0.000000
v -3.500000 1.500000 0.000000
v -3.500000 -0.500000 0.000000
v -4.000000 0.000000 0.000000
v -4.000000 2.000000 0.000000
v -3.750000 -0.250000 0.000000
v -3.250000 -0.750000 0.000000
v -3.750000 1.750000 0.000000
v -3.250000 1.250000 0.000000
v -3.625000 -0.375000 0.000000
v -3.125000 -0.875000 0.000000
v -3.875000 1.875000 0.000000
v -3.375000 1.375000 0.000000
v -3.875000 -0.125000 0.000000
v -3.375000 -0.625000 0.000000
v -3.625000 1.625000 0.000000
v -3.125000 1.125000 0.000000
vn -0.0000 -0.0000 1.0000
vt 1.000000 -3.500000
vt 0.000000 4.500000
vt 0.000000 -3.500000
vt 1.000000 -4.000000
vt 0.000000 4.000000
vt 0.000000 -4.000000
vt 1.000000 4.500000
vt 1.000000 4.000000
s 0
usemtl DachMaterial
f 3/1/1 13/2/1 15/3/1
f 2/1/1 14/2/1 16/3/1
f 8/1/1 18/2/1 12/3/1
f 7/1/1 17/2/1 11/3/1
f 11/4/1 1/5/1 2/6/1
f 12/4/1 6/5/1 4/6/1
f 16/4/1 10/5/1 8/6/1
f 15/4/1 9/5/1 7/6/1
f 3/1/1 5/7/1 13/2/1
f 2/1/1 1/7/1 14/2/1
f 8/1/1 10/7/1 18/2/1
f 7/1/1 9/7/1 17/2/1
f 11/4/1 17/8/1 1/5/1
f 12/4/1 18/8/1 6/5/1
f 16/4/1 14/8/1 10/5/1
f 15/4/1 13/8/1 9/5/1
f 21/1/1 31/3/1 29/2/1
f 20/1/1 32/3/1 30/2/1
f 24/1/1 28/3/1 34/2/1
f 23/1/1 27/3/1 33/2/1
f 27/4/1 20/6/1 19/5/1
f 28/4/1 4/6/1 6/5/1
f 32/4/1 24/6/1 26/5/1
f 31/4/1 23/6/1 25/5/1
f 21/1/1 29/2/1 22/7/1
f 20/1/1 30/2/1 19/7/1
f 24/1/1 34/2/1 26/7/1
f 23/1/1 33/2/1 25/7/1
f 27/4/1 19/5/1 33/8/1
f 28/4/1 6/5/1 34/8/1
f 32/4/1 26/5/1 30/8/1
f 31/4/1 25/5/1 29/8/1

Twoja tekstura musiałaby być załadowana z wrap mode nie jako CLAMP, tylko REPEAT i wtedy teksturką jednej dachówki łatwo obskoczysz cały dach ustawiając UV face'ów na wartości większe niż 1.

Zrobiłem w Blenderze 4.0 przykład:
screenshot-20241123011001.png

screenshot-20241123011121.png

screenshot-20241123011206.png

screenshot-20241123011248.png

Plik *.blend: dachowka.zip
Tekstura dachówki:
dachowka.png

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.