From 297524c8ec41b585c4812494791772406653e479 Mon Sep 17 00:00:00 2001
From: Paweł Redman <pawel.redman@gmail.com>
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.
---
 assets/tiles/stone_side.png | Bin 0 -> 1644 bytes
 src/common.hpp              |  21 +++--
 src/game/assets.cpp         |   6 +-
 src/render.cpp              | 221 ++++++++++++++++++++++++++++++++------------
 src/world.cpp               |  56 +++++------
 5 files changed, 204 insertions(+), 100 deletions(-)
 create mode 100644 assets/tiles/stone_side.png

diff --git a/assets/tiles/stone_side.png b/assets/tiles/stone_side.png
new file mode 100644
index 0000000..0c9ad1f
Binary files /dev/null and b/assets/tiles/stone_side.png differ
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<entity_t*> ents;
-
-		bool empty = true;
+		sector_level_t level;
 		tile_t tiles[SECTOR_SIZE * SECTOR_SIZE];
 	};
 
@@ -101,7 +106,7 @@ namespace world {
 		std::map<sector_index_t, sector_t> 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<v2f_t> *path);
 
 		// FIXME: iterators instead of returning std::lists
-		std::list<sector_t*> get_sectors(rectf_t rect);
+		std::list<sector_t*> get_sectors(rectf_t rect, sector_level_t level);
 		std::list<entity_t*> get_entities(rectf_t rect, cflags_t cflags);
 		std::list<entity_t*> 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 <iomanip>
 
 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<world::entity_t*> 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<world::entity_t*> ents;
+	std::list<world::entity_t*>::iterator ent;
+	rect_t<world::coord_t, 2> 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 = &sectors[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<sector_t*> world_t::get_sectors(rectf_t rect)
+std::list<sector_t*> world_t::get_sectors(rectf_t rect, sector_level_t level)
 {
 	sector_index_t base, upper;
 	std::list<sector_t*> list;
@@ -165,7 +165,7 @@ std::list<sector_t*> 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<entity_t*> 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<entity_t*> 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<coord_t, 2> 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