From 297524c8ec41b585c4812494791772406653e479 Mon Sep 17 00:00:00 2001 From: PaweÅ‚ Redman Date: Tue, 19 Dec 2017 19:27:06 +0100 Subject: Introduce tall tiles. Some features are still missing but the commit is large enough as it is. TODO: render layers and fix minor artifacts. --- src/common.hpp | 21 +++-- src/game/assets.cpp | 6 +- src/render.cpp | 221 +++++++++++++++++++++++++++++++++++++--------------- src/world.cpp | 56 ++++++------- 4 files changed, 204 insertions(+), 100 deletions(-) (limited to 'src') diff --git a/src/common.hpp b/src/common.hpp index a253da5..71bc695 100644 --- a/src/common.hpp +++ b/src/common.hpp @@ -70,13 +70,18 @@ namespace world { class entity_t; + typedef enum { + SECTOR_EMPTY, + SECTOR_PARTIAL, + SECTOR_FULL + } sector_level_t; + class sector_t { public: sector_index_t index; rectf_t bounds; std::unordered_set ents; - - bool empty = true; + sector_level_t level; tile_t tiles[SECTOR_SIZE * SECTOR_SIZE]; }; @@ -101,7 +106,7 @@ namespace world { std::map sectors; void generate_tile(tile_t *tile, tile_index_t index); - void generate(sector_t *sector, sector_index_t index, bool partial); + void generate(sector_t *sector, sector_index_t index, sector_level_t level); protected: friend render::state_t; @@ -115,13 +120,13 @@ namespace world { world_t(void); - sector_t *get_sector(sector_index_t index, bool partial = false); - tile_t *get_tile(tile_index_t index, bool partial = false); + sector_t *get_sector(sector_index_t index, sector_level_t level); + tile_t *get_tile(tile_index_t index, sector_level_t level); bool find_path(v2f_t src, v2f_t dst, cmodel_t *cmodel, entity_t *ignore, std::list *path); // FIXME: iterators instead of returning std::lists - std::list get_sectors(rectf_t rect); + std::list get_sectors(rectf_t rect, sector_level_t level); std::list get_entities(rectf_t rect, cflags_t cflags); std::list get_render_entities(rectf_t rect); @@ -313,12 +318,12 @@ namespace render { ALIGN_CENTER_BOTTOM } text_align_t; - void register_tile(uint8_t type, const char *path); + void register_tile(uint8_t type, const char *top, const char *side, float height); class state_t { sf::RenderWindow *window; - void render_sector(world::world_t *world, world::sector_t *sector); + void render_tile(world::world_t *world, v2f_t tx, world::tile_t *tile); void drender_text(rectf_t rect, std::string str); void drender_entity(world::entity_t *ent); public: diff --git a/src/game/assets.cpp b/src/game/assets.cpp index 1bc8948..046934b 100644 --- a/src/game/assets.cpp +++ b/src/game/assets.cpp @@ -38,11 +38,11 @@ void load(void) unit_selected.load("assets/units/selected_", 1); world::register_tile(TILE_DIRT, 0); - render::register_tile(TILE_DIRT, "assets/tiles/dirt.png"); + render::register_tile(TILE_DIRT, "assets/tiles/dirt.png", NULL, 0.0f); world::register_tile(TILE_STONE, CF_SOLID); - render::register_tile(TILE_STONE, "assets/tiles/stone.png"); + render::register_tile(TILE_STONE, "assets/tiles/stone.png", "assets/tiles/stone_side.png", 1.0f); world::register_tile(TILE_WATER, CF_WATER); - render::register_tile(TILE_WATER, "assets/tiles/water.png"); + render::register_tile(TILE_WATER, "assets/tiles/water.png", NULL, 0.0f); } } // namespace game::assets diff --git a/src/render.cpp b/src/render.cpp index 76701b6..6c47ab7 100644 --- a/src/render.cpp +++ b/src/render.cpp @@ -1,4 +1,5 @@ #include "common.hpp" +#include static sf::RectangleShape wot_rect; static sf::Font font; @@ -12,17 +13,31 @@ state_t::state_t(sf::RenderWindow *window_) font.loadFromFile("assets/FanwoodText.otf"); } -// FIXME: rename -static sf::Texture *tiles[256] = {0}; +typedef struct { + sf::Texture *top, *side; + float height; +} tiledata_t; +static tiledata_t tiledata[256] = {0}; -void register_tile(uint8_t type, const char *path) +void register_tile(uint8_t type, const char *top, const char *side, float height) { - printf("load %s\n", path); - tiles[type] = new sf::Texture; - tiles[type]->loadFromFile(path); - tiles[type]->setRepeated(true); + tiledata_t *tile = tiledata + type; + + printf("load %s\n", top); + tile->top = new sf::Texture; + tile->top->loadFromFile(top); + tile->top->setRepeated(true); + + if (height > 0.0f) { + tile->height = height; + printf("load %s\n", side); + tile->side = new sf::Texture; + tile->side->loadFromFile(side); + tile->side->setRepeated(true); + } } + void state_t::begin_frame(double now_, double dt_) { now = now_; @@ -71,17 +86,17 @@ void state_t::drender_entity(world::entity_t *ent) drender_text(ent->render_bounds, ss.str()); } -static const v2f_t tile_base[8] ={ - {1.0f, 0.5f}, {1.0f, 1.0f}, {0.5f, 1.0f}, {0.0f, 1.0f}, - {0.0f, 0.5f}, {0.0f, 0.0f}, {0.5f, 0.0f}, {1.0f, 0.0f} -}; - static void generate_tile_verts(sf::Vertex *verts, v2f_t tx, procgen::perlin_noise_t *perlin) { + static const v2f_t local_base[8] ={ + {1.0f, 0.5f}, {1.0f, 1.0f}, {0.5f, 1.0f}, {0.0f, 1.0f}, + {0.0f, 0.5f}, {0.0f, 0.0f}, {0.5f, 0.0f}, {1.0f, 0.0f} + }; + for (size_t i = 0; i < 8; i++) { v2f_t base, turb; - base = tile_base[i] + tx; + base = local_base[i] + tx; turb[0] = perlin->get(base, 1.0f) * 0.5f; turb[1] = perlin->get(base, 2.0f) * 0.5f; @@ -92,89 +107,173 @@ static void generate_tile_verts(sf::Vertex *verts, v2f_t tx, procgen::perlin_noi } } -void state_t::render_sector(world::world_t *world, world::sector_t *sector) +void state_t::render_tile(world::world_t *world, v2f_t tx, world::tile_t *tile) { - for (ssize_t y = 0; y < SECTOR_SIZE; y++) - for (ssize_t x = 0; x < SECTOR_SIZE; x++) { - sf::Vertex verts[8]; - sf::Texture *texture; - v2f_t tx; - world::tile_t *tile; - - tx = sector->bounds.v[0] + v2f_t(x, y); - tile = sector->tiles + y * SECTOR_SIZE + x; - - texture = tiles[tile->type]; - if (!texture) { - printf("draw_tile: tile %i not registered\n", tile->type); - abort(); - } + static const size_t side_order[6] = {4, 0, 7, 3, 2, 1}; + static const size_t side_tests[8] = {1, 4, 4, 16, 16, 64, 64, 1}; - sf::RenderStates states(texture); - generate_tile_verts(verts, tx, &world->perlin); - window->draw(verts, 8, sf::TrianglesFan, states); + sf::Vertex verts[8]; + tiledata_t *data; - if (debug_draw_tile_coords) { - world::tile_index_t local(x, y); - std::stringstream ss; + data = tiledata + tile->type; - ss << "L=" << local; - sf::Text text(ss.str(), font, 20); - text.setPosition(tx); - text.setScale(0.005, 0.005); - window->draw(text); + generate_tile_verts(verts, tx, &world->perlin); + + if (data->height) { + sf::RenderStates states(data->side); + + for (size_t i = 0; i < 6; i++) { + size_t j = side_order[i]; + size_t k = (j + 1) % 8; + sf::Vertex quad[4]; + + if (tile->neighbors & side_tests[j]) + continue; + + quad[0] = verts[j]; + quad[0].texCoords.y = 0; + quad[1] = verts[k]; + quad[1].texCoords.y = 0; + quad[2] = quad[1]; + quad[3] = quad[0]; + + quad[2].position.y -= data->height; + quad[2].texCoords.y -= data->height * 32; + quad[3].position.y -= data->height; + quad[3].texCoords.y -= data->height * 32; + + window->draw(quad, 4, sf::TrianglesFan, states); } - stats.tiles++; + for (size_t i = 0; i < 8; i++) + verts[i].position.y -= data->height; } - stats.sectors++; + sf::RenderStates states(data->top); + window->draw(verts, 8, sf::TrianglesFan, states); + stats.tiles++; } -void state_t::render(game::state_t *game) +static rectf_t window_bounds(sf::RenderWindow *window) { + const v2f_t margin(1.5f, 1.5f); + sf::Vector2u size = window->getSize(); v2f_t A, B, C, D; - const v2f_t margin(1.0f, 1.0f); - rectf_t bbox; - std::list ents; + rectf_t bounds; A = window->mapPixelToCoords(sf::Vector2i(0, 0)); B = window->mapPixelToCoords(sf::Vector2i(size.x, 0)); C = window->mapPixelToCoords(sf::Vector2i(0, size.y)); D = window->mapPixelToCoords(sf::Vector2i(size.x, size.y)); - bbox[0][0] = std::min({A[0], B[0], C[0], D[0]}); - bbox[0][1] = std::min({A[1], B[1], C[1], D[1]}); - bbox[1][0] = std::max({A[0], B[0], C[0], D[0]}); - bbox[1][1] = std::max({A[1], B[1], C[1], D[1]}); + bounds[0][0] = std::min({A[0], B[0], C[0], D[0]}); + bounds[0][1] = std::min({A[1], B[1], C[1], D[1]}); + bounds[1][0] = std::max({A[0], B[0], C[0], D[0]}); + bounds[1][1] = std::max({A[1], B[1], C[1], D[1]}); + bounds[0] -= margin; + bounds[1] += margin; + + return bounds; +} - bbox[0] -= margin; - bbox[1] += margin; +void state_t::render(game::state_t *game) +{ + rectf_t bounds; + std::list ents; + std::list::iterator ent; + rect_t sectors; - for (world::sector_t *sector : game->world.get_sectors(bbox)) - render_sector(&game->world, sector); + bounds = window_bounds(window); + sectors[0] = world::sector_index_at(bounds[0]); + sectors[1] = world::sector_index_at(bounds[1]); - ents = game->world.get_render_entities(bbox); + ents = game->world.get_render_entities(bounds); ents.sort( [](const world::entity_t *x, const world::entity_t *y) -> bool { + return x->render_bounds[1][1] < y->render_bounds[1][1]; + // FIXME: bring render layers back + /* if (x->render_layer < y->render_layer) return true; else if (x->render_layer > y->render_layer) return false; else - return x->render_bounds[1][1] < y->render_bounds[1][1]; + return x->render_bounds[1][1] < y->render_bounds[1][1];*/ }); - for (world::entity_t *ent : ents) { - ent->render_to(this); + ent = ents.begin(); - if (debug_draw_cmodels) - drender_entity(ent); + for (world::coord_t sy = sectors[0][1]; sy <= sectors[1][1]; sy++) + for (world::coord_t ty = 0; ty < SECTOR_SIZE; ty++) + for (world::coord_t sx = sectors[0][0]; sx <= sectors[1][0]; sx++) { + world::sector_index_t sector_index(sx, sy); + world::sector_t *sector; + + sector = game->world.get_sector(sector_index, world::SECTOR_FULL); + + while (ent != ents.end() && + (*ent)->render_bounds[1][1] < sy * SECTOR_SIZE + ty) { + (*ent)->render_to(this); + stats.entities++; + ent++; + } + + for (world::coord_t tx = 0; tx < SECTOR_SIZE; tx++) { + world::tile_index_t index; + world::tile_t *tile; - stats.entities++; + index = sector_index * SECTOR_SIZE + + world::tile_index_t(tx, ty); + tile = sector->tiles + ty * SECTOR_SIZE + tx; + + render_tile(&game->world, index, tile); + } + + stats.sectors++; } + + // Every sector is iterated SECTOR_SIZE times. + stats.sectors /= SECTOR_SIZE; + + if (debug_draw_tile_coords) { + for (world::sector_t *sector : game->world.get_sectors(bounds, world::SECTOR_FULL)) + for (world::coord_t ty = 0; ty < SECTOR_SIZE; ty++) + for (world::coord_t tx = 0; tx < SECTOR_SIZE; tx++) { + std::stringstream ss; + world::tile_index_t local, index; + world::tile_t *tile; + int neighbors; + + local = world::tile_index_t(tx, ty); + index = sector->index * SECTOR_SIZE + local; + tile = sector->tiles + ty * SECTOR_SIZE + tx; + + ss << "SI: " << sector->index << "\n"; + ss << "GI: " << index << "\n"; + ss << "LI: " << local << "\n"; + ss << "T: " << tile->type << "\n"; + ss << "N: "; + + neighbors = tile->neighbors; + for (size_t i = 0; i < 8; i++) { + ss << ((neighbors & 1) ? 'P' : 'A'); + neighbors >>= 1; + } + + ss << " (" << tile->neighbors << ")"; + + sf::Text text(ss.str(), font, 20); + text.setPosition(index); + text.setScale(0.005, 0.005); + window->draw(text); + } + } + + if (debug_draw_cmodels) + for (world::entity_t *ent : ents) + drender_entity(ent); } void state_t::render(double phase, animated_texture_t *anim, rectf_t bounds, sf::Color color, bool mirror){ diff --git a/src/world.cpp b/src/world.cpp index 5c2d7b3..6a0ba97 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -27,50 +27,50 @@ void register_tile(uint8_t type, cflags_t cflags) tiles[type] = cflags; } -void world_t::generate(sector_t *sector, sector_index_t index, bool partial) +void world_t::generate(sector_t *sector, sector_index_t index, sector_level_t level) { bool gen_tiles = false, gen_decos = false; sector->index = index; - sector->bounds.v[0] = (v2f_t)index * SECTOR_SIZE; sector->bounds.v[1] = sector->bounds.v[0] + v2f_t(SECTOR_SIZE, SECTOR_SIZE); - if (sector->empty) { - if (partial) + if (sector->level == SECTOR_EMPTY) { + if (level == SECTOR_PARTIAL) gen_tiles = true; - else + else if (level == SECTOR_FULL) gen_tiles = gen_decos = true; - } else + } else if (sector->level == SECTOR_PARTIAL) gen_decos = true; - sector->empty = false; + sector->level = level; generator(this, index, sector, gen_tiles, gen_decos, generator_data); stats.sectors++; stats.tiles += SECTOR_SIZE * SECTOR_SIZE; - // Unused, for now. - /* - for (coord_t ly = 0; ly < SECTOR_SIZE; ly++) - for (coord_t lx = 0; lx < SECTOR_SIZE; lx++) { + if (sector->level < SECTOR_FULL) + return; + + for (coord_t ty = 0; ty < SECTOR_SIZE; ty++) + for (coord_t tx = 0; tx < SECTOR_SIZE; tx++) { tile_t *tile; + tile_index_t local(tx, ty); - tile = sector->tiles + ly * SECTOR_SIZE + lx; + tile = sector->tiles + ty * SECTOR_SIZE + tx; tile->neighbors = 0; for (size_t i = 0; i < 8; i++) { tile_index_t neighbor_index; tile_t *neighbor; - neighbor_index = index * SECTOR_SIZE + tile_index_t(lx, ly) + neighbor_offsets[i]; - neighbor = get_tile(neighbor_index, true); + neighbor_index = index * SECTOR_SIZE + local + neighbor_offsets[i]; + neighbor = get_tile(neighbor_index, SECTOR_PARTIAL); if (neighbor->type == tile->type) tile->neighbors |= (1 << i); } } - */ } bool world_t::find_path(v2f_t src, v2f_t dst, cmodel_t *cmodel, entity_t *ignore, @@ -91,7 +91,7 @@ bool world_t::find_path(v2f_t src, v2f_t dst, cmodel_t *cmodel, entity_t *ignore index = finder.base + tile_index_t(x, y); - if (!(tiles[get_tile(index)->type] & cmodel->cflags)) + if (!(tiles[get_tile(index, SECTOR_FULL)->type] & cmodel->cflags)) continue; combined[0] = v2f_t(index) - cmodel_dims / 2; @@ -129,19 +129,19 @@ bool world_t::find_path(v2f_t src, v2f_t dst, cmodel_t *cmodel, entity_t *ignore return true; } -sector_t *world_t::get_sector(sector_index_t index, bool partial) +sector_t *world_t::get_sector(sector_index_t index, sector_level_t level) { sector_t *sector; sector = §ors[index]; - if (sector->empty) - generate(sector, index, partial); + if (sector->level < level) + generate(sector, index, level); return sector; } -tile_t *world_t::get_tile(tile_index_t index, bool partial) +tile_t *world_t::get_tile(tile_index_t index, sector_level_t level) { sector_index_t sector_index; sector_t *sector; @@ -149,12 +149,12 @@ tile_t *world_t::get_tile(tile_index_t index, bool partial) sector_index[0] = divide_rmi(index[0], (int64_t)SECTOR_SIZE, &tx); sector_index[1] = divide_rmi(index[1], (int64_t)SECTOR_SIZE, &ty); - sector = get_sector(sector_index, partial); + sector = get_sector(sector_index, level); return sector->tiles + ty * SECTOR_SIZE + tx; } -std::list world_t::get_sectors(rectf_t rect) +std::list world_t::get_sectors(rectf_t rect, sector_level_t level) { sector_index_t base, upper; std::list list; @@ -165,7 +165,7 @@ std::list world_t::get_sectors(rectf_t rect) for (int64_t y = base[1]; y <= upper[1]; y++) for (int64_t x = base[0]; x <= upper[0]; x++) { sector_index_t index(x, y); - list.push_back(get_sector(index)); + list.push_back(get_sector(index, level)); } return list; @@ -179,7 +179,7 @@ std::list world_t::get_entities(rectf_t rect, cflags_t cflags) cookie++; - for (sector_t *sector : get_sectors(rect)) + for (sector_t *sector : get_sectors(rect, SECTOR_FULL)) for (entity_t *ent : sector->ents) { if (ent->cookie == cookie) continue; @@ -204,7 +204,7 @@ std::list world_t::get_render_entities(rectf_t rect) cookie++; - for (sector_t *sector : get_sectors(rect)) + for (sector_t *sector : get_sectors(rect, SECTOR_FULL)) for (entity_t *ent : sector->ents) { if (ent->cookie == cookie) continue; @@ -224,7 +224,7 @@ bool world_t::test_rect(const cmodel_t *cmodel, const entity_t *ignore) { cookie++; - for (sector_t *sector : get_sectors(cmodel->bounds)) { + for (sector_t *sector : get_sectors(cmodel->bounds, SECTOR_FULL)) { rect_t bounds; tile_index_t index; @@ -338,7 +338,7 @@ trace_t world_t::trace(v2f_t start, v2f_t end, cflags_t cflags) index = (tile_index_t(x.floor()) ^ transforms_index[quad]) + offsets_index[quad]; - if (tiles[get_tile(index, false)->type] & cflags) { + if (tiles[get_tile(index, SECTOR_FULL)->type] & cflags) { res.hit = true; res.end = x ^ transforms[quad]; res.frac = (x - start).len() / (end - start).len(); @@ -423,7 +423,7 @@ void entity_t::link(world_t *world_) sector_index_t index = base + sector_index_t(x, y); sector_t *sector; - sector = world->get_sector(index); + sector = world->get_sector(index, SECTOR_FULL); link_to_sector(sector); } -- cgit