medfall

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit 0b62e6e88f05f743a0482dbe03eabf0f82f60626
parent 686597c8639e50cd746eef79fa9322eb8a55fff6
Author: Michael Savage <mikejsavage@gmail.com>
Date:   Wed Oct 12 22:57:55 +0300

Make preprocessor generate normal/horizon maps

Diffstat:
Makefile | 4++--
gpubtt.cc | 25++++++-------------------
gpubtt.h | 6++++--
pp.cc | 257+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
terrain_manager.cc | 182++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
terrain_manager.h | 44++++++++++++++++++++++++++++++++++----------
6 files changed, 387 insertions(+), 131 deletions(-)
diff --git a/Makefile b/Makefile @@ -6,14 +6,14 @@ all: $(BINS) $(MODULES) test_lockfree # Binary dependencies medfall: main.o gl.o log.o memory_arena.o glad.o -pp: pp.o log.o stb_image.o lz4.o lz4hc.o +pp: pp.o log.o memory_arena.o stb_image.o lz4.o lz4hc.o sound: audio.o mixer.o log.o memory_arena.o wave.o platform_audio_output.o srv: server/main.o rng/well512.o # Module dependencies bsp.so: bsp.o bsp_renderer.o hm.so: hm.o heightmap.o terrain_manager.o lz4.o btt.o gpubtt.o skybox.o -btt.so: mod_btt.o btt.o gpubtt.o heightmap.o skybox.o stb_image.o +btt.so: mod_btt.o btt.o gpubtt.o heightmap.o skybox.o stb_image.o lz4.o sm.so: shadow_map.o test_lockfree: relacy.cc diff --git a/gpubtt.cc b/gpubtt.cc @@ -8,9 +8,11 @@ #include "btt.h" #include "heightmap.h" #include "gpubtt.h" +#include "linear_algebra.h" static u32 btt_count_leaves( const BTT * btt ) { if( btt->left ) { + assert( btt->right != NULL ); return btt_count_leaves( btt->left ) + btt_count_leaves( btt->right ); } @@ -27,7 +29,7 @@ static void gpubtt_build( const glm::vec3 v1( ohm->hm.point( iv1.x, iv1.y ) + offset ); const glm::vec3 v2( ohm->hm.point( iv2.x, iv2.y ) + offset ); - if( btt->left ) { + if( btt->left != NULL ) { const glm::ivec2 mid = ( iv0 + iv2 ) / 2; gpubtt_build( verts, i, ohm, btt->left, iv1, mid, iv0 ); @@ -84,7 +86,8 @@ static void compute_horizons( } void gpubtt_init( - MemoryArena * arena, GPUBTT * gpubtt, const OffsetHeightmap * ohm, BTTs btts, + MemoryArena * arena, GPUBTT * gpubtt, BTTs btts, + const OffsetHeightmap * ohm, const v3 * normals, const float * horizons, GLuint at_position ) { MEMARENA_SCOPED_CHECKPOINT( arena ); @@ -92,7 +95,7 @@ void gpubtt_init( const u32 num_leaves = btt_count_leaves( btts.left_root ) + btt_count_leaves( btts.right_root ); - // printf( "%u to %u verts\n", ohm->hm.width * ohm->hm.height * 2, num_leaves ); + printf( "%u to %u verts\n", ohm->hm.width * ohm->hm.height * 2, num_leaves ); glm::vec3 * verts = memarena_push_many( arena, glm::vec3, num_leaves * 3 ); u32 i = 0; @@ -114,28 +117,12 @@ void gpubtt_init( glEnableVertexAttribArray( at_position ); glVertexAttribPointer( at_position, 3, GL_FLOAT, GL_FALSE, 0, 0 ); - glm::vec3 * normals = memarena_push_many( arena, glm::vec3, ohm->hm.width * ohm->hm.height ); - - for( u32 y = 0; y < ohm->hm.height; y++ ) { - for( u32 x = 0; x < ohm->hm.width; x++ ) { - const glm::vec3 normal = ohm->hm.point_normal( x, y ); - normals[ y * ohm->hm.width + x ] = normal; - } - } - glGenTextures( 1, &gpubtt->tex_normals ); glBindTexture( GL_TEXTURE_2D, gpubtt->tex_normals ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB32F, ohm->hm.width, ohm->hm.height, 0, GL_RGB, GL_FLOAT, normals ); - float * horizons = memarena_push_many( arena, float, ohm->hm.width * ohm->hm.height ); - memset( horizons, 0, ohm->hm.width * ohm->hm.height * sizeof( float ) ); - - for( u32 i = 0; i < ohm->hm.height; i++ ) { - compute_horizons( arena, &ohm->hm, horizons, glm::ivec2( 0, i ), glm::ivec2( 1, 0 ) ); - } - glGenTextures( 1, &gpubtt->tex_horizons ); glBindTexture( GL_TEXTURE_2D, gpubtt->tex_horizons ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); diff --git a/gpubtt.h b/gpubtt.h @@ -5,6 +5,7 @@ #include "heightmap.h" #include "btt.h" +#include "linear_algebra.h" struct GPUBTT { GLuint vao; @@ -14,8 +15,9 @@ struct GPUBTT { u32 num_verts; }; -void gpubtt_init( MemoryArena * arena, GPUBTT * gpubtt, - const OffsetHeightmap * ohm, BTTs btts, GLuint at_position ); +void gpubtt_init( MemoryArena * arena, GPUBTT * gpubtt, BTTs btts, + const OffsetHeightmap * ohm, const v3 * normals, const float * horizons, + GLuint at_position ); void gpubtt_destroy( GPUBTT * gpubtt ); diff --git a/pp.cc b/pp.cc @@ -1,3 +1,7 @@ +// TODO: to make iterating on huge worlds slightly quicker: +// 1. do the heighmap/horizon/normal computations separately so we don't have +// all 3 in memory at once +// 2. parallelise horizon/normal computations #include <string> #include <iostream> #include <fstream> @@ -5,69 +9,260 @@ #include <stdio.h> #include "intrinsics.h" +#include "memory_arena.h" +#include "array.h" +#include "str.h" #include "stb_image.h" #include "platform_io.h" +#include "linear_algebra.h" // #include "stb_image_write.h" #include "lz4.h" #include "lz4hc.h" -const int TILE_SIZE = 128; +static float tan_theta( v2 a, v2 b ) { + v2 d = a - b; + return d.y / d.x; +} + +static void compute_row_of_horizons( + MemoryArena * arena, + const array2d< u8 > heightmap, array2d< float > horizons, + size_t y +) { + MEMARENA_SCOPED_CHECKPOINT( arena ); + + array< v2 > hull = memarena_push_array( arena, v2, heightmap.w ); + hull[ 0 ] = v2( 0, heightmap( 0, y ) ); + + size_t hull_size = 1; + + for( size_t x = 1; x < heightmap.w; x++ ) { + v2 p( x, heightmap( x, y ) ); + + while( hull_size > 1 && tan_theta( p, hull[ hull_size - 1 ] ) <= tan_theta( hull[ hull_size - 1 ], hull[ hull_size - 2 ] ) ) { + hull_size--; + } + + horizons( x, y ) = max( 0.0f, tan_theta( p, hull[ hull_size - 1 ] ) ); + + hull[ hull_size ] = p; + hull_size++; + } +} + +static void compute_horizons( + MemoryArena * arena, + const array2d< u8 > heightmap, array2d< float > horizons +) { + assert( heightmap.w == horizons.w ); + assert( heightmap.h == horizons.h ); + + for( size_t y = 0; y < heightmap.h; y++ ) { + compute_row_of_horizons( arena, heightmap, horizons, y ); + } +} + +// CCW winding points towards the camera +static v3 triangle_normal_ccw( v3 a, v3 b, v3 c ) { + return normalize( cross( b - a, c - a ) ); +} + +static v3 normal_at_point( const array2d< u8 > heightmap, size_t x, size_t y ) { + v3 bent_normal = v3( 0 ); + + /* + * A vertex looks like this: + * + * +y + * \ 6 | + * \ | + * 5 \ | 1 + * \| + * ------X------ +x + * |\ + * 2 | \ 4 + * | \ + * | 3 \ + * + * We consider up to six triangles for the bent normal + */ + +#define P( x, y ) v3( x, y, heightmap( x, y ) ) + if( x > 0 ) { + if( y > 0 ) { // bottom left + v3 tri2a = P( x, y ); + v3 tri2b = P( x - 1, y ); + v3 tri2c = P( x, y - 1 ); + + bent_normal += triangle_normal_ccw( tri2a, tri2b, tri2c ); + } + if( y < heightmap.h - 1 ) { // top left + v3 tri5a = P( x, y ); + v3 tri5b = P( x - 1, y + 1 ); + v3 tri5c = P( x - 1, y ); + + v3 tri6a = P( x, y ); + v3 tri6b = P( x, y + 1 ); + v3 tri6c = P( x - 1, y + 1 ); + + bent_normal += triangle_normal_ccw( tri5a, tri5b, tri5c ); + bent_normal += triangle_normal_ccw( tri6a, tri6b, tri6c ); + } + } + + if( x < heightmap.w - 1 ) { + if( y > 0 ) { // bottom right + v3 tri3a = P( x, y ); + v3 tri3b = P( x, y - 1 ); + v3 tri3c = P( x + 1, y - 1 ); + + v3 tri4a = P( x, y ); + v3 tri4b = P( x + 1, y - 1 ); + v3 tri4c = P( x + 1, y ); + + bent_normal += triangle_normal_ccw( tri3a, tri3b, tri3c ); + bent_normal += triangle_normal_ccw( tri4a, tri4b, tri4c ); + } + if( y < heightmap.h - 1 ) { // top right + v3 tri1a = P( x, y ); + v3 tri1b = P( x + 1, y ); + v3 tri1c = P( x, y + 1 ); + + bent_normal += triangle_normal_ccw( tri1a, tri1b, tri1c ); + } + } +#undef P + + return normalize( bent_normal ); +} + +static void compute_normals( const array2d< u8 > heightmap, array2d< v3 > normals ) { + for( size_t y = 0; y < heightmap.h; y++ ) { + for( size_t x = 0; x < heightmap.w; x++ ) { + normals( x, y ) = normal_at_point( heightmap, x, y ); + } + } +} + +const int TILE_SIZE = 512; const int OUT_SIZE = TILE_SIZE + 1; -int w, h; -u8 * pixels; +static void write_compressed_file( const void * data, size_t len, const char * fmt, ... ) { + str< 2048 > path; + va_list argp; + va_start( argp, fmt ); + path.sprintf( fmt, argp ); + va_end( argp ); + + // compress + const int COMPRESSION_LEVEL = 6; + char * compressed = new char[ LZ4_compressBound( len ) ]; + size_t compressed_len = LZ4_compress_HC( ( const char * ) data, compressed, + len, LZ4_compressBound( len ), COMPRESSION_LEVEL ); + + // write + FILE * file = fopen( path.c_str(), "w" ); + assert( file ); + // TODO: endianness/use Stream + // fwrite( &compressed_len, sizeof( compressed_len ), 1, file ); + fwrite( compressed, 1, compressed_len, file ); + fclose( file ); + assert( ferror( file ) == 0 ); + + // clean up + delete[] compressed; +} + +static void write_tile( + const std::string & dir, + const array2d< u8 > heightmap, const array2d< float > horizons, + const array2d< v3 > normals, + int tx, int ty +) { + u8 heightmap_memory[ OUT_SIZE * OUT_SIZE ]; + float horizons_memory[ OUT_SIZE * OUT_SIZE ]; + v3 normals_memory[ OUT_SIZE * OUT_SIZE ]; -void write_tile( const std::string & dir, const int tx, const int ty ) { - u8 tile[ OUT_SIZE * OUT_SIZE ]; + array2d< u8 > tile_heightmap( heightmap_memory, OUT_SIZE, OUT_SIZE ); + array2d< float > tile_horizons( horizons_memory, OUT_SIZE, OUT_SIZE ); + array2d< v3 > tile_normals( normals_memory, OUT_SIZE, OUT_SIZE ); - for( int y = 0; y <= TILE_SIZE; y++ ) { - for( int x = 0; x <= TILE_SIZE; x++ ) { - const int gy = ty * TILE_SIZE + y; - const int gx = tx * TILE_SIZE + x; + const u32 left = tx * TILE_SIZE; + const u32 top = ty * TILE_SIZE; - if( gy >= h || gx >= w ) { - tile[ y * OUT_SIZE + x ] = 0; + for( int local_y = 0; local_y < OUT_SIZE; local_y++ ) { + for( int local_x = 0; local_x < OUT_SIZE; local_x++ ) { + int global_x = left + local_x; + int global_y = top + local_y; + + if( heightmap.in_range( global_x, global_y ) ) { + tile_heightmap( local_x, local_y ) = heightmap( global_x, global_y ); + tile_horizons( local_x, local_y ) = horizons( global_x, global_y ); + tile_normals( local_x, local_y ) = normals( global_x, global_y ); } else { - tile[ y * OUT_SIZE + x ] = pixels[ gy * w + gx ]; + tile_heightmap( local_x, local_y ) = 0; + tile_horizons( local_x, local_y ) = 0; + tile_normals( local_x, local_y ) = v3( 0 ); } } } - // const std::string file = dir + "/" + std::to_string( tx ) + "_" + std::to_string( ty ) + ".tga"; - // printf( "writing tile %s\n", file.c_str() ); - // stbi_write_tga( file.c_str(), OUT_SIZE, OUT_SIZE, 1, tile ); - - const std::string compfile = dir + "/" + std::to_string( tx ) + "_" + std::to_string( ty ) + ".lz4"; - printf( "writing tile %s\n", compfile.c_str() ); - char * compressed = new char[ LZ4_compressBound( sizeof( tile ) ) ]; - size_t compressed_len = LZ4_compress_HC( ( const char * ) tile, compressed, sizeof( tile ), LZ4_compressBound( sizeof( tile ) ), 6 ); - std::ofstream comp( compfile, std::ios::out | std::ios::binary ); - comp.write( compressed, compressed_len ); - comp.close(); - delete compressed; + printf( "writing heightmap for (%d, %d)\n", tx, ty ); + write_compressed_file( heightmap_memory, array_count( heightmap_memory ), + "%s/%d_%d_heightmap.lz4", dir.c_str(), tx, ty ); + + printf( "writing horizons for (%d, %d)\n", tx, ty ); + write_compressed_file( horizons_memory, sizeof( horizons_memory ), + "%s/%d_%d_horizons.lz4", dir.c_str(), tx, ty ); + + printf( "writing normals for (%d, %d)\n", tx, ty ); + write_compressed_file( normals_memory, sizeof( normals_memory ), + "%s/%d_%d_normals.lz4", dir.c_str(), tx, ty ); } int main( int argc, char ** argv ) { + u8 arena_memory[ megabytes( 1 ) ]; + MemoryArena arena; + memarena_init( &arena, arena_memory, array_count( arena_memory ) ); + const std::string path = argc == 2 ? argv[ 1 ] : "terrains/mountains512.png"; - pixels = stbi_load( path.c_str(), &w, &h, NULL, 1 ); + int w, h; + printf( "decoding\n" ); + u8 * heightmap_memory = stbi_load( path.c_str(), &w, &h, NULL, 1 ); + assert( heightmap_memory ); const std::string dir = path + ".parts"; mkdir( dir.c_str(), 0755 ); + std::ofstream dims( dir + "/dims.txt" ); + dims << std::to_string( w ) + " " + std::to_string( h ); + dims.close(); + + float * horizons_memory = new float[ w * h ]; + v3 * normals_memory = new v3[ w * h ]; + const array2d< u8 > heightmap( heightmap_memory, w, h ); + // TODO: quantize horizons. maybe we only need to store the convex hull too + array2d< float > horizons( horizons_memory, w, h ); + // TODO: normals can be v2 since z >= 0 on a heightmap + array2d< v3 > normals( normals_memory, w, h ); + + printf( "generating horizons\n" ); + compute_horizons( &arena, heightmap, horizons ); + printf( "generating normals\n" ); + compute_normals( heightmap, normals ); + for( int ty = 0; ty < h / TILE_SIZE; ty++ ) { for( int tx = 0; tx < w / TILE_SIZE; tx++ ) { - write_tile( dir, tx, ty ); + write_tile( dir, heightmap, horizons, normals, tx, ty ); } } - std::ofstream dims( dir + "/dims.txt" ); - dims << std::to_string( w ) + " " + std::to_string( h ); - dims.close(); - - stbi_image_free( pixels ); + stbi_image_free( heightmap_memory ); + delete[] horizons_memory; + delete[] normals_memory; return 0; } diff --git a/terrain_manager.cc b/terrain_manager.cc @@ -1,4 +1,5 @@ -#include <string.h> +#include <stdio.h> +#include <unistd.h> #include "glad.h" #include "glsl.h" @@ -14,6 +15,8 @@ #include "work_queue.h" #include "btt.h" #include "gpubtt.h" +#include "linear_algebra.h" +#include "str.h" #include "lz4.h" @@ -116,36 +119,59 @@ static WORK_QUEUE_CALLBACK( async_load_tile ) { s32 ty = alt->ty; free( alt ); - CompressedTile ct = tm->compressed_tiles[ tx ][ ty ]; + const CompressedTile & compressed_tile = tm->compressed_tiles[ tx ][ ty ]; u32 width, height; - size_t out_size = ( TILE_SIZE + 1 ) * ( TILE_SIZE + 1 ); + size_t decompressed_heightmap_size = ( TILE_SIZE + 1 ) * ( TILE_SIZE + 1 ); + size_t decompressed_normalmap_size = decompressed_heightmap_size * sizeof( v3 ); + size_t decompressed_horizonmap_size = decompressed_heightmap_size * sizeof( float ); // TODO: width/height might not be TILE_SIZE on the edges of the map width = height = TILE_SIZE + 1; - u8 * pixels = ( u8 * ) malloc( out_size ); - assert( pixels != NULL ); - int ok = LZ4_decompress_safe( ( const char * ) ct.data, ( char * ) pixels, ct.len, out_size ); - assert( ok > 0 ); - - Heightmap * hm = &tm->tiles[ tx ][ ty ]; - heightmap_init( hm, pixels, width, height ); - MEMARENA_SCOPED_CHECKPOINT( arena ); - uptr used = arena->used; - BTTs btt = btt_from_heightmap( hm, arena ); - size_t size_for_btt = arena->used - used; - - printf( "uploading tile %d %d. arena memory jumped %zu bytes from %zu to %zu\n", tx, ty, size_for_btt, used, arena->used ); - void * btt_memory = malloc( size_for_btt ); - assert( btt_memory != NULL ); - memcpy( btt_memory, arena->memory + used, size_for_btt ); - - tm->btts[ tx ][ ty ] = btt; - tm->btt_memory[ tx ][ ty ] = btt_memory; - - // TODO: this doesn't seem to be acting as release. it - // looks like btt_memory is not being fully written before - // gpu_init is called + u8 * decompressed_heightmap = ( u8 * ) malloc( decompressed_heightmap_size ); + assert( decompressed_heightmap != NULL ); + int ok_heightmap = LZ4_decompress_safe( + ( const char * ) compressed_tile.heightmap_data, + ( char * ) decompressed_heightmap, + compressed_tile.heightmap_size, decompressed_heightmap_size ); + assert( ok_heightmap > 0 ); + + u8 * decompressed_normalmap = ( u8 * ) malloc( decompressed_normalmap_size ); + assert( decompressed_normalmap != NULL ); + int ok_normalmap = LZ4_decompress_safe( + ( const char * ) compressed_tile.normalmap_data, + ( char * ) decompressed_normalmap, + compressed_tile.normalmap_size, decompressed_normalmap_size ); + assert( ok_normalmap > 0 ); + + float * decompressed_horizonmap = ( float * ) malloc( decompressed_horizonmap_size ); + assert( decompressed_horizonmap != NULL ); + int ok_horizonmap = LZ4_decompress_safe( + ( const char * ) compressed_tile.horizonmap_data, + ( char * ) decompressed_horizonmap, + compressed_tile.horizonmap_size, decompressed_horizonmap_size ); + assert( ok_horizonmap > 0 ); + + DecompressedTile decompressed_tile; + heightmap_init( &decompressed_tile.heightmap, decompressed_heightmap, width, height ); + decompressed_tile.normalmap = ( v3 * ) decompressed_normalmap; + tm->decompressed_tiles[ tx ][ ty ] = decompressed_tile; + + // TODO: should generate a triangle mesh here instead of in gpubtt_init + MemoryArena btt_arena; + void * btt_memory = malloc( megabytes( 16 ) ); + memarena_init( &btt_arena, ( u8 * ) btt_memory, megabytes( 16 ) ); + GPUTileData gpu_tile_data; + BTTs btt = btt_from_heightmap( &decompressed_tile.heightmap, &btt_arena ); + + gpu_tile_data.btt = btt; + gpu_tile_data.btt_memory = btt_memory; + gpu_tile_data.horizonmap = decompressed_horizonmap; + gpu_tile_data.horizonmap_size = decompressed_horizonmap_size; + tm->gpu_tiles[ tx ][ ty ] = gpu_tile_data; + + usleep(1000*50); // sleep for 1500ms + store_release( &tm->tile_states[ tx ][ ty ], TILE_READY_FOR_UPLOAD ); } @@ -178,10 +204,12 @@ static void terrain_unload_tile( TerrainManager * tm, s32 tx, s32 ty ) { } store_release( &tm->tile_states[ tx ][ ty ], TILE_FREEING ); + printf( "destroyed %d %d\n", tx, ty ); - Heightmap * hm = &tm->tiles[ tx ][ ty ]; - heightmap_destroy( hm ); - gpubtt_destroy( &tm->btt_tiles[ tx ][ ty ] ); + heightmap_destroy( &tm->decompressed_tiles[ tx ][ ty ].heightmap ); + free( &tm->decompressed_tiles[ tx ][ ty ].normalmap ); + gpubtt_destroy( &tm->gpubtts[ tx ][ ty ] ); + store_release( &tm->tile_states[ tx ][ ty ], TILE_EMPTY ); } @@ -203,14 +231,28 @@ void terrain_init( for( u32 ty = 0; ty < tm->height / TILE_SIZE; ty++ ) { for( u32 tx = 0; tx < tm->width / TILE_SIZE; tx++ ) { - char tile_path[ 256 ]; - // TODO: check this conversion - size_t written = sprintf( tile_path, "%s/%d_%d.lz4", tiles_dir, tx, ty ); - assert( written + 1 < sizeof( tile_path ) ); - - size_t len; - u8 * data = file_get_contents( tile_path, &len ); - tm->compressed_tiles[ tx ][ ty ] = { data, len }; + str< 512 > heightmap_path; + heightmap_path.sprintf( "%s/%d_%d_heightmap.lz4", tiles_dir, tx, ty ); + str< 512 > normalmap_path; + normalmap_path.sprintf( "%s/%d_%d_normals.lz4", tiles_dir, tx, ty ); + str< 512 > horizonmap_path; + horizonmap_path.sprintf( "%s/%d_%d_horizons.lz4", tiles_dir, tx, ty ); + + size_t heightmap_size, normalmap_size, horizonmap_size; + u8 * heightmap = file_get_contents( + heightmap_path.c_str(), &heightmap_size ); + u8 * normalmap = file_get_contents( + normalmap_path.c_str(), &normalmap_size ); + u8 * horizonmap = file_get_contents( + horizonmap_path.c_str(), &horizonmap_size ); + + CompressedTile & compressed_tile = tm->compressed_tiles[ tx ][ ty ]; + compressed_tile.heightmap_data = heightmap; + compressed_tile.heightmap_size = heightmap_size; + compressed_tile.normalmap_data = normalmap; + compressed_tile.normalmap_size = normalmap_size; + compressed_tile.horizonmap_data = horizonmap; + compressed_tile.horizonmap_size = horizonmap_size; store_release( &tm->tile_states[ tx ][ ty ], TILE_EMPTY ); } @@ -273,16 +315,12 @@ static u8 terrain_tile_lod( } static void terrain_update_lods( TerrainManager * tm ) { - u8 new_lods[ WORLD_SIZE ][ WORLD_SIZE ]; - for( u16 tx = 0; tx < WORLD_SIZE; tx++ ) { for( u16 ty = 0; ty < WORLD_SIZE; ty++ ) { - new_lods[ tx ][ ty ] = terrain_tile_lod( + tm->lods[ tx ][ ty ] = terrain_tile_lod( tm->tile_x, tm->tile_y, tx, ty ); } } - - memcpy( tm->lods, new_lods, WORLD_SIZE * WORLD_SIZE ); } void terrain_update( TerrainManager * tm, glm::vec3 position ) { @@ -349,6 +387,39 @@ void terrain_update( TerrainManager * tm, glm::vec3 position ) { } void terrain_render( TerrainManager * tm, glm::mat4 V, glm::mat4 VP, float sun ) { + u32 inited = 0; + for( u16 vy = 0; vy < VIEW_SIZE; vy++ ) { + for( u16 vx = 0; vx < VIEW_SIZE; vx++ ) { + s32 tx = vx + tm->tile_x - VIEW_HALF; + s32 ty = vy + tm->tile_y - VIEW_HALF; + + if( tx < 0 || ty < 0 ) continue; + + u32 state = load_acquire( &tm->tile_states[ tx ][ ty ] ); + if( state == TILE_READY_FOR_UPLOAD ) { + if( inited > 2 ) continue; + const GPUTileData & gpu_tile_data = tm->gpu_tiles[ tx ][ ty ]; + // TODO: move this into terrain_prepare_render? + const Heightmap * hm = &tm->decompressed_tiles[ tx ][ ty ].heightmap; + const OffsetHeightmap ohm = { *hm, tx * TILE_SIZE, ty * TILE_SIZE }; + // printf( "gpubtt_init at %d %d %p %p\n", tx, ty, tm->btts[ tx ][ ty ].left_root, tm->btts[ tx ][ ty ].right_root ); + gpubtt_init( + tm->arena, + &tm->gpubtts[ tx ][ ty ], + gpu_tile_data.btt, + &ohm, + ( v3 * ) tm->decompressed_tiles[ tx ][ ty ].normalmap, + gpu_tile_data.horizonmap, + tm->at_position + ); + free( gpu_tile_data.btt_memory ); + free( gpu_tile_data.horizonmap ); + store_release( &tm->tile_states[ tx ][ ty ], TILE_ON_GPU ); + inited++; + } + } + } + glUseProgram( tm->shader ); glUniformMatrix4fv( tm->un_V, 1, GL_FALSE, glm::value_ptr( V ) ); @@ -388,29 +459,6 @@ void terrain_render( TerrainManager * tm, glm::mat4 V, glm::mat4 VP, float sun ) glUniform1i( tm->un_point_light_colours, 2 ); /* end lighting */ - u32 inited = 0; - for( u16 vy = 0; vy < VIEW_SIZE; vy++ ) { - for( u16 vx = 0; vx < VIEW_SIZE; vx++ ) { - s32 tx = vx + tm->tile_x - VIEW_HALF; - s32 ty = vy + tm->tile_y - VIEW_HALF; - - if( tx < 0 || ty < 0 ) continue; - - u32 state = load_acquire( &tm->tile_states[ tx ][ ty ] ); - if( state == TILE_READY_FOR_UPLOAD ) { - if( inited > 0 ) continue; - // TODO: move this into terrain_prepare_render? - const Heightmap * hm = &tm->tiles[ tx ][ ty ]; - const OffsetHeightmap ohm = { *hm, tx * TILE_SIZE, ty * TILE_SIZE }; - // printf( "gpubtt_init at %d %d %p %p\n", tx, ty, tm->btts[ tx ][ ty ].left_root, tm->btts[ tx ][ ty ].right_root ); - gpubtt_init( tm->arena, &tm->btt_tiles[ tx ][ ty ], &ohm, tm->btts[ tx ][ ty ], tm->at_position ); - free( tm->btt_memory[ tx ][ ty ] ); - store_release( &tm->tile_states[ tx ][ ty ], TILE_ON_GPU ); - inited++; - } - } - } - for( u16 vy = 0; vy < VIEW_SIZE; vy++ ) { for( u16 vx = 0; vx < VIEW_SIZE; vx++ ) { s32 tx = vx + tm->tile_x - VIEW_HALF; @@ -420,7 +468,7 @@ void terrain_render( TerrainManager * tm, glm::mat4 V, glm::mat4 VP, float sun ) u32 state = load_acquire( &tm->tile_states[ tx ][ ty ] ); if( state == TILE_ON_GPU ) { - gpubtt_render( &tm->btt_tiles[ tx ][ ty ], tm->un_normals, tm->un_horizons ); + gpubtt_render( &tm->gpubtts[ tx ][ ty ], tm->un_normals, tm->un_horizons ); } } } @@ -437,7 +485,7 @@ float terrain_height( const TerrainManager * tm, glm::vec3 position ) { s32 tx = position.x / TILE_SIZE; s32 ty = position.y / TILE_SIZE; - const Heightmap * hm = &tm->tiles[ tx ][ ty ]; + const Heightmap * hm = &tm->decompressed_tiles[ tx ][ ty ].heightmap; return hm->bilerp_height( position.x - tx * TILE_SIZE, position.y - ty * TILE_SIZE ); diff --git a/terrain_manager.h b/terrain_manager.h @@ -5,21 +5,40 @@ #include <glm/glm.hpp> #include "intrinsics.h" +#include "array.h" #include "memory_arena.h" #include "heightmap.h" #include "gpubtt.h" #include "work_queue.h" #include "platform_atomic.h" +#include "linear_algebra.h" -static const u16 TILE_SIZE = 128; -static const u16 WORLD_SIZE = 170; +static const u16 TILE_SIZE = 512; +static const u16 WORLD_SIZE = 64; static const u16 VIEW_SIZE = 45; static const u16 VIEW_HALF = VIEW_SIZE / 2; struct CompressedTile { - u8 * data; - size_t len; + u8 * heightmap_data; + size_t heightmap_size; + u8 * normalmap_data; + size_t normalmap_size; + u8 * horizonmap_data; + size_t horizonmap_size; +}; + +struct GPUTileData { + BTTs btt; + void * btt_memory; + float * horizonmap; + size_t horizonmap_size; +}; + +struct DecompressedTile { + // TODO: merge this + Heightmap + Heightmap heightmap; + v3 * normalmap; }; enum TileState { @@ -65,16 +84,21 @@ struct TerrainManager { GLuint tex_point_light_colours; bool first_teleport; + // tile_x and tile_y are the coordinates of the tile we are centered on + s32 tile_x, tile_y; CompressedTile compressed_tiles[ WORLD_SIZE ][ WORLD_SIZE ]; + array2d< CompressedTile > ASDF = array2d< CompressedTile >( *compressed_tiles, WORLD_SIZE, WORLD_SIZE ); + DecompressedTile decompressed_tiles[ WORLD_SIZE ][ WORLD_SIZE ]; atomic_u32 tile_states[ WORLD_SIZE ][ WORLD_SIZE ]; - Heightmap tiles[ WORLD_SIZE ][ WORLD_SIZE ]; - BTTs btts[ WORLD_SIZE ][ WORLD_SIZE ]; - void * btt_memory[ WORLD_SIZE ][ WORLD_SIZE ]; - GPUBTT btt_tiles[ WORLD_SIZE ][ WORLD_SIZE ]; + + // holds tile data that needs to be moved to the gpu + GPUTileData gpu_tiles[ WORLD_SIZE ][ WORLD_SIZE ]; + + // opengl handles + GPUBTT gpubtts[ WORLD_SIZE ][ WORLD_SIZE ]; + u8 lods[ WORLD_SIZE ][ WORLD_SIZE ]; - // tile_x and tile_y are the coordinates of the tile we are centered on - s32 tile_x, tile_y; }; void terrain_init( TerrainManager * tm, const char * tiles_dir, MemoryArena * arena, WorkQueue * background_tasks );