From e7e6a79f8bf2855b5d1d432613151e48d2d685da Mon Sep 17 00:00:00 2001 From: Jon Bergli Heier Date: Sat, 14 May 2011 23:22:05 +0200 Subject: Implemented terrain chunk caching. --- Makefile.win32 | 3 +- scene.cpp | 4 +- terrain.cpp | 122 +++++++---------------------------------------------- terrain.h | 13 ++---- terrain_cache.cpp | 57 +++++++++++++++++++++++++ terrain_cache.h | 45 ++++++++++++++++++++ terrain_loader.cpp | 90 +++++++++++++++++++++++++++++++++++++++ terrain_loader.h | 25 +++++++++++ 8 files changed, 238 insertions(+), 121 deletions(-) create mode 100644 terrain_cache.cpp create mode 100644 terrain_cache.h create mode 100644 terrain_loader.cpp create mode 100644 terrain_loader.h diff --git a/Makefile.win32 b/Makefile.win32 index 91e3f22..c15c41c 100644 --- a/Makefile.win32 +++ b/Makefile.win32 @@ -11,8 +11,7 @@ else endif TARGET=foo.exe -#OBJECTS=$(shell ls *.cpp | sed 's/cpp/o/') -OBJECTS=gl.o gui.o main.o terrain.o scene.o shader.o tool.o vector.o video.o +OBJECTS=gl.o gui.o main.o scene.o shader.o terrain_cache.o terrain.o terrain_loader.o tool.o vector.o video.o ifeq ($(shell uname), Linux) OBJECTS+=noiseutils/noiseutils.o diff --git a/scene.cpp b/scene.cpp index e371f74..f7db5c4 100644 --- a/scene.cpp +++ b/scene.cpp @@ -444,8 +444,8 @@ void Scene::render() { float height = font->LineHeight(); glColor3f(1, 1, 1); glTranslatef(0, video::height-height, 0); - font->Render((boost::format("chunks: %d gravity: %d steps: %d") - % terrain->chunks.size() % gravity % steps).str().c_str()); + font->Render((boost::format("chunks: %d cache: %d gravity: %d steps: %d") + % terrain->chunks.size() % terrain->tc->get_size() % gravity % steps).str().c_str()); //font->Render((boost::format("%dx%d %d levels %d nodes tree creation time: %f steps: %d update: %d") // % scene.qt->width % scene.qt->height % scene.qt->levels % scene.qt->nodes % scene.qt->init_time % steps % scene.qt->thread_running).str().c_str()); //glTranslatef(0, height, 0); diff --git a/terrain.cpp b/terrain.cpp index 1fd75db..f3f573d 100644 --- a/terrain.cpp +++ b/terrain.cpp @@ -3,20 +3,10 @@ #include "gl.h" -#include -#include "noiseutils/noiseutils.h" -#include -#include -#include - #include #include #include -using namespace noise; - -namespace fs = boost::filesystem; - using std::min; using std::max; @@ -129,7 +119,7 @@ float Terrain::Node::get_height(float px, float py) { /* Chunk */ -Terrain::Chunk::Chunk(Terrain *terrain, float x, float y, float width, float height) { +Terrain::Chunk::Chunk(Terrain *terrain, float x, float y, float width, float height) : cache_obj(terrain->tc->get_chunk(x, y, width+1, height+1)) { this->terrain = terrain; this->x = x; this->y = y; @@ -139,7 +129,7 @@ Terrain::Chunk::Chunk(Terrain *terrain, float x, float y, float width, float hei this->h_height = height+1; this->vbo_object = this->node_count = this->vertices = 0; this->nodes = NULL; - heights = terrain->get_chunk(x, y, h_width, h_height); + heights = cache_obj->heights; normals = new Vector3[(int)((h_width)*(h_height))]; calc_normals(); @@ -158,7 +148,6 @@ Terrain::Chunk::~Chunk() { for(unsigned int i = 0; i < node_count; i++) delete nodes[i]; delete[] nodes; - delete[] heights; delete[] normals; } @@ -281,11 +270,16 @@ Terrain::Node* Terrain::Chunk::find(float x, float y) { void Terrain::Chunk::calc_normals() { float *right, *left, *up, *down; - right = left = up = down = NULL; - right = terrain->get_chunk(this->x - chunk_size, this->y, h_width, h_height); - left = terrain->get_chunk(this->x + chunk_size, this->y, h_width, h_height); - up = terrain->get_chunk(this->x, this->y + chunk_size, h_width, h_height); - down = terrain->get_chunk(this->x, this->y - chunk_size, h_width, h_height); + + TerrainCacheObject::p right_ob = terrain->tc->get_chunk(this->x - chunk_size, this->y, h_width, h_height), + left_ob = terrain->tc->get_chunk(this->x + chunk_size, this->y, h_width, h_height), + up_ob = terrain->tc->get_chunk(this->x, this->y + chunk_size, h_width, h_height), + down_ob = terrain->tc->get_chunk(this->x, this->y - chunk_size, h_width, h_height); + + right = right_ob->heights; + left = left_ob->heights; + up = up_ob->heights; + down = down_ob->heights; for(int x = 0; x < h_width; x++) { for(int y = 0; y < h_height; y++) { @@ -343,98 +337,17 @@ void Terrain::Chunk::calc_normals() { normals[x*h_height + y] = N; } } - - if(right) - delete[] right; - if(left) - delete[] left; - if(up) - delete[] up; - if(down) - delete[] down; } Terrain::Terrain() { + tc = new TerrainCache(0, "map", 120); } Terrain::~Terrain() { for(std::list::iterator it = chunks.begin(); it != chunks.end(); it++) { delete *it; } -} - -float *Terrain::generate_heights(int x, int y, int width, int height) { - module::Perlin mod; - mod.SetSeed(0); - - utils::NoiseMap heightmap; - utils::NoiseMapBuilderPlane heightmap_builder; - - heightmap_builder.SetSourceModule(mod); - heightmap_builder.SetDestNoiseMap(heightmap); - - heightmap_builder.SetDestSize(width, height); - heightmap_builder.SetBounds((double)x / 100, (double)(x+width) / 100, (double)y / 100, (double)(y+height) / 100); - heightmap_builder.Build(); - - float *heights = new float[width*height]; - for(int i = 0; i < width; i++) { - for(int j = 0; j < height; j++) { - heights[i*height + j] = 10*(1+heightmap.GetValue(i, j)); - } - } - - chunk_indices.insert(std::pair(x, y)); - save_chunk(heights, x, y, width, height); - - return heights; -} - -float *Terrain::get_chunk(int x, int y, int width, int height) { - if(has_chunk(x, y)) - return load_chunk(x, y, width, height); - else - return generate_heights(x, y, width, height); -} - -bool Terrain::has_chunk(int x, int y) { - return fs::exists((boost::format("map/%d.%d.chunk") % x % y).str()); - //return chunk_indices.find(std::pair(x, y)) != chunk_indices.end(); -} - -void Terrain::save_chunk(float *chunk, int x, int y, int width, int height) { - fs::path p = (boost::format("map/%d.%d.chunk") % x % y).str(); - fs::ofstream os(p); - - os << width << std::endl; - os << height << std::endl; - - for(int x = 0; x < width; x++) - for(int y = 0; y < height; y++) - os << chunk[x*height + y] << std::endl; - os.close(); -} - -// NOTE: assumes width <= chunk_size+, likewise for height -float *Terrain::load_chunk(int x, int y, int width, int height) { - fs::path p = (boost::format("map/%d.%d.chunk") % x % y).str(); - fs::ifstream is(p); - - int w, h; - is >> w; - is >> h; - - float *chunk = new float[width*height]; - for(int x = 0; x < w; x++) - for(int y = 0; y < h; y++) { - float v; - is >> v; - if(x < width && y < height) - chunk[x*height + y] = v; - } - is.close(); - - return chunk; + delete tc; } void Terrain::raise(float x, float z, float radius, float focus, float strength, bool up) { @@ -498,12 +411,6 @@ void Terrain::raise(float x, float z, float radius, float focus, float strength, } } - // save chunks - for(std::set::iterator it = changed_chunks.begin(); it != changed_chunks.end(); it++) { - Chunk *chunk = *it; - save_chunk(chunk->heights, chunk->x, chunk->y, chunk->h_width, chunk->h_height); - } - /* recalculate normals */ for(std::set::iterator it = changed_chunks.begin(); it != changed_chunks.end(); it++) { (*it)->calc_normals(); @@ -540,6 +447,7 @@ void Terrain::update(float x, float z) { chunk_indices.erase(ind_it); } if((*it)->distance(x, z) > chunk_dist_threshold) { + delete *it; it = chunks.erase(it); } } diff --git a/terrain.h b/terrain.h index 84abe26..6fa39b3 100644 --- a/terrain.h +++ b/terrain.h @@ -2,14 +2,11 @@ #define TERRAIN_H #include "vector.h" +#include "terrain_cache.h" #include -#include class Terrain { - private: - std::set > chunk_indices; - public: struct Chunk; @@ -36,6 +33,7 @@ class Terrain { float x, y, width, height; int h_width, h_height; float *heights; + TerrainCacheObject::p cache_obj; Vector3 *normals; size_t buf_size; unsigned int vbo_object; @@ -55,15 +53,10 @@ class Terrain { static const int chunk_size = 32; std::list chunks; + TerrainCache *tc; Terrain(); virtual ~Terrain(); - float *generate_heights(int x, int y, int width, int height); - float *get_chunk(int x, int y, int width, int height); - bool has_chunk(int x, int y); - void save_chunk(float *chunk, int x, int y, int width, int height); - float *load_chunk(int x, int y, int width, int height); - void raise(float x, float z, float radius, float focus, float strength, bool up = true); void update(float x, float z); diff --git a/terrain_cache.cpp b/terrain_cache.cpp new file mode 100644 index 0000000..c40a1d6 --- /dev/null +++ b/terrain_cache.cpp @@ -0,0 +1,57 @@ +#include "terrain_cache.h" + +/* TerrainCacheObject */ + +TerrainCacheObject::TerrainCacheObject(TerrainCache *cache, int x, int y, int width, int height) { + this->cache = cache; + this->x = x; + this->y = y; + this->width = width; + this->height = height; + this->heights = cache->tl->get_chunk(x, y, width, height); +} + +TerrainCacheObject::~TerrainCacheObject() { + cache->tl->save_chunk(heights, x, y, width, height); + delete[] heights; +} + +/* TerrainCache */ + +TerrainCache::TerrainCache(int seed, fs::path root, size_t max_size) { + this->max_size = max_size; + tl = new TerrainLoader(seed, root); +} + +TerrainCache::~TerrainCache() { + caches.clear(); + delete tl; +} + +TerrainCacheObject::p TerrainCache::make_object(int x, int y, int width, int height) { + TerrainCacheObject::p ob(new TerrainCacheObject(this, x, y, width, height)); + + if(caches.size() >= max_size) { + for(cache_map::iterator it = caches.begin(); it != caches.end(); it++) { + if(it->second.use_count() == 1) { + caches.erase(it); + break; + } + } + } + + caches.insert(std::pair(intpair(x, y), ob)); + return ob; +} + +TerrainCacheObject::p TerrainCache::get_chunk(int x, int y, int width, int height) { + cache_map::iterator it = caches.find(intpair(x, y)); + if(it != caches.end()) + return it->second; + + return make_object(x, y, width, height); +} + +size_t TerrainCache::get_size() { + return caches.size(); +} diff --git a/terrain_cache.h b/terrain_cache.h new file mode 100644 index 0000000..2ea4e15 --- /dev/null +++ b/terrain_cache.h @@ -0,0 +1,45 @@ +#ifndef TERRAIN_CACHE_H +#define TERRAIN_CACHE_H + +#include "terrain_loader.h" + +#include + +#include +#include + +class TerrainCache; + +struct TerrainCacheObject { + typedef boost::shared_ptr p; + + TerrainCache *cache; + float *heights; + int x, y, width, height; + + TerrainCacheObject(TerrainCache *cache, int x, int y, int width, int height); + virtual ~TerrainCacheObject(); +}; + +class TerrainCache { + friend class TerrainCacheObject; + + private: + typedef std::pair intpair; + typedef std::map cache_map; + + cache_map caches; + TerrainLoader *tl; + size_t max_size; + + TerrainCacheObject::p make_object(int x, int y, int width, int height); + + public: + TerrainCache(int seed, fs::path root, size_t max_size); + virtual ~TerrainCache(); + + TerrainCacheObject::p get_chunk(int x, int y, int width, int height); + size_t get_size(); +}; + +#endif diff --git a/terrain_loader.cpp b/terrain_loader.cpp new file mode 100644 index 0000000..d908aca --- /dev/null +++ b/terrain_loader.cpp @@ -0,0 +1,90 @@ +#include "terrain_loader.h" + +#include +#include "noiseutils/noiseutils.h" +#include +#include + +using namespace noise; + +TerrainLoader::TerrainLoader(int seed, fs::path root) { + this->seed = seed; + this->root = root; +} + +TerrainLoader::~TerrainLoader() { +} + +float *TerrainLoader::generate_heights(int x, int y, int width, int height) { + module::Perlin mod; + mod.SetSeed(seed); + + utils::NoiseMap heightmap; + utils::NoiseMapBuilderPlane heightmap_builder; + + heightmap_builder.SetSourceModule(mod); + heightmap_builder.SetDestNoiseMap(heightmap); + + heightmap_builder.SetDestSize(width, height); + heightmap_builder.SetBounds((double)x / 100, (double)(x+width) / 100, (double)y / 100, (double)(y+height) / 100); + heightmap_builder.Build(); + + float *heights = new float[width*height]; + for(int i = 0; i < width; i++) { + for(int j = 0; j < height; j++) { + heights[i*height + j] = 10*(1+heightmap.GetValue(i, j)); + } + } + + save_chunk(heights, x, y, width, height); + + return heights; +} + +float *TerrainLoader::get_chunk(int x, int y, int width, int height) { + float *h; + if(has_chunk(x, y)) + h = load_chunk(x, y, width, height); + else + h = generate_heights(x, y, width, height); + return h; +} + +bool TerrainLoader::has_chunk(int x, int y) { + return fs::exists(root / (boost::format("%d.%d.chunk") % x % y).str()); +} + +void TerrainLoader::save_chunk(float *chunk, int x, int y, int width, int height) { + fs::path p = root / (boost::format("%d.%d.chunk") % x % y).str(); + fs::ofstream os(p); + + os << width << std::endl; + os << height << std::endl; + + for(int x = 0; x < width; x++) + for(int y = 0; y < height; y++) + os << chunk[x*height + y] << std::endl; + os.close(); +} + +// NOTE: assumes width <= chunk_size+1, likewise for height +float *TerrainLoader::load_chunk(int x, int y, int width, int height) { + fs::path p = root / (boost::format("%d.%d.chunk") % x % y).str(); + fs::ifstream is(p); + + int w, h; + is >> w; + is >> h; + + float *chunk = new float[width*height]; + for(int x = 0; x < w; x++) + for(int y = 0; y < h; y++) { + float v; + is >> v; + if(x < width && y < height) + chunk[x*height + y] = v; + } + is.close(); + + return chunk; +} diff --git a/terrain_loader.h b/terrain_loader.h new file mode 100644 index 0000000..7efa596 --- /dev/null +++ b/terrain_loader.h @@ -0,0 +1,25 @@ +#ifndef TERRAIN_LOADER_H +#define TERRAIN_LOADER_H + +#include + +namespace fs = boost::filesystem; + +class TerrainLoader { + private: + int seed; + fs::path root; + + public: + TerrainLoader(int seed, fs::path root); + virtual ~TerrainLoader(); + + float *generate_heights(int x, int y, int width, int height); + float *get_chunk(int x, int y, int width, int height); + bool has_chunk(int x, int y); + void save_chunk(float *chunk, int x, int y, int width, int height); + float *load_chunk(int x, int y, int width, int height); + +}; + +#endif -- cgit v1.2.3