medfall

A super great game engine
Log | Files | Refs

commit cde158d1aef0a788c72490185217d42346a47e0b
parent f28eac3058adc63af6c2660636d93f4a0fff7c55
Author: Michael Savage <mikejsavage@gmail.com>
Date:   Fri, 10 Nov 2017 20:39:46 +0200

Update TerrainManager to use the new heightmap stuff

Diffstat:
btt.cc | 10+++++-----
btt.h | 2+-
clipmap.cc | 1-
game.h | 13++++++-------
gpubtt.cc | 41++++++++++++++++++++---------------------
gpubtt.h | 2+-
heightmap.h | 1-
intrinsics.h | 2--
make.lua | 8++++----
terrain_manager.cc | 72+++++++++++++++++++++++++++---------------------------------------------
terrain_manager.h | 6++----
11 files changed, 66 insertions(+), 92 deletions(-)

diff --git a/btt.cc b/btt.cc @@ -68,7 +68,7 @@ static s32 square_distance( v2s32 u, v2s32 v ) { return dot( d, d ); } -static bool btt_should_split( const Heightmap * hm, v2s32 v0, v2s32 v1, v2s32 v2, v2s32 mid ) { +static bool btt_should_split( const array2d< u16 > hm, v2s32 v0, v2s32 v1, v2s32 v2, v2s32 mid ) { if( square_distance( v0, v2 ) <= 4 ) return false; const float avg_height = ( heightmap_height( hm, v0.x, v0.y ) + heightmap_height( hm, v2.x, v2.y ) ) * 0.5f; @@ -80,7 +80,7 @@ static bool btt_should_split( const Heightmap * hm, v2s32 v0, v2s32 v1, v2s32 v2 } static void btt_build( - const Heightmap * hm, MemoryArena * arena, + const array2d< u16 > hm, MemoryArena * arena, BTT * node, v2s32 v0, v2s32 v1, v2s32 v2 ) { v2s32 mid = ( v0 + v2 ) / 2; @@ -101,7 +101,7 @@ static void btt_build( } } -BTTs btt_from_heightmap( const Heightmap * hm, MemoryArena * arena ) { +BTTs btt_from_heightmap( const array2d< u16 > hm, MemoryArena * arena ) { BTTs roots; roots.left_root = alloc< BTT >( arena ); @@ -113,8 +113,8 @@ BTTs btt_from_heightmap( const Heightmap * hm, MemoryArena * arena ) { roots.left_root->bottom = roots.right_root; roots.right_root->bottom = roots.left_root; - btt_build( hm, arena, roots.left_root, v2s32( 0, 0 ), v2s32( 0, hm->height - 1 ), v2s32( hm->width - 1, hm->height - 1 ) ); - btt_build( hm, arena, roots.right_root, v2s32( hm->width - 1, hm->height - 1 ), v2s32( hm->width - 1, 0 ), v2s32( 0, 0 ) ); + btt_build( hm, arena, roots.left_root, v2s32( 0, 0 ), v2s32( 0, hm.h - 1 ), v2s32( hm.w - 1, hm.h - 1 ) ); + btt_build( hm, arena, roots.right_root, v2s32( hm.w - 1, hm.h - 1 ), v2s32( hm.w - 1, 0 ), v2s32( 0, 0 ) ); return roots; } diff --git a/btt.h b/btt.h @@ -19,4 +19,4 @@ struct BTTs { BTT * right_root; }; -BTTs btt_from_heightmap( const Heightmap * hm, MemoryArena * arena ); +BTTs btt_from_heightmap( const array2d< u16 > hm, MemoryArena * arena ); diff --git a/clipmap.cc b/clipmap.cc @@ -120,7 +120,6 @@ GAME_INIT( game_init ) { array< QuadTreeNode > nodes = alloc_array< QuadTreeNode >( &mem->persistent_arena, 4096 * 4096 / 3 ); file_get_contents_and_decompress( "terrains/gta16.png.parts/quadtree.lz4", ( u8 * ) nodes.ptr(), nodes.num_bytes() ); - clipmap.quadtree.nodes_memory = nodes.ptr(); clipmap.quadtree.nodes = nodes; clipmap.quadtree.heightmap = clipmap.heightmap; } diff --git a/game.h b/game.h @@ -3,9 +3,9 @@ // TODO: this whole file blows #include "intrinsics.h" #include "linear_algebra.h" -// #include "terrain_manager.h" -// #include "btt.h" -// #include "gpubtt.h" +#include "terrain_manager.h" +#include "btt.h" +#include "gpubtt.h" #include "heightmap.h" #include "bsp.h" #include "bsp_renderer.h" @@ -28,7 +28,7 @@ struct GameState { bool noclip; float pitch, yaw; - // TerrainManager tm; + TerrainManager tm; BSP bsp; BSPRenderer bspr; @@ -39,9 +39,8 @@ struct GameState { float sun_angle; - // BTTs btt; - // GPUBTT gpubtt; - // Heightmap hm; + BTTs btt; + GPUBTT gpubtt; bool draw_wireframe; bool draw_quadtree; diff --git a/gpubtt.cc b/gpubtt.cc @@ -16,58 +16,57 @@ static u32 btt_count_leaves( const BTT * btt ) { } static void gpubtt_build( - v3 * verts, u32 * i, const OffsetHeightmap * ohm, const BTT * btt, - v2s32 p0, v2s32 p1, v2s32 p2 + v3 * verts, u32 * i, const array2d< u16 > hm, v3 offset, + const BTT * btt, v2s32 p0, v2s32 p1, v2s32 p2 ) { - v3 offset( ohm->x_offset, ohm->y_offset, 0.0f ); - if( btt->left != NULL ) { v2s32 mid = ( p0 + p2 ) / 2; - gpubtt_build( verts, i, ohm, btt->left, p1, mid, p0 ); + gpubtt_build( verts, i, hm, offset, btt->left, p1, mid, p0 ); ASSERT( btt->right != NULL ); - gpubtt_build( verts, i, ohm, btt->right, p2, mid, p1 ); + gpubtt_build( verts, i, hm, offset, btt->right, p2, mid, p1 ); } else { - verts[ *i + 0 ] = ohm->hm.point( p0.x, p0.y ) + offset; - verts[ *i + 1 ] = ohm->hm.point( p1.x, p1.y ) + offset; - verts[ *i + 2 ] = ohm->hm.point( p2.x, p2.y ) + offset; + verts[ *i + 0 ] = heightmap_point( hm, p0.x, p0.y ) + offset; + verts[ *i + 1 ] = heightmap_point( hm, p1.x, p1.y ) + offset; + verts[ *i + 2 ] = heightmap_point( hm, p2.x, p2.y ) + offset; *i += 3; } } void gpubtt_init( MemoryArena * arena, GPUBTT * gpubtt, BTTs btts, - const OffsetHeightmap * ohm, const v3 * normals, const u8 * horizons + const array2d< u16 > hm, v2u32 offset, const v3 * normals, const u8 * horizons ) { PROFILE_FUNCTION(); MEMARENA_SCOPED_CHECKPOINT( arena ); - const u32 num_leaves = btt_count_leaves( btts.left_root ) - + btt_count_leaves( btts.right_root ); + const u32 num_leaves = btt_count_leaves( btts.left_root ) + btt_count_leaves( btts.right_root ); v3 * verts = memarena_push_many( arena, v3, num_leaves * 3 ); u32 num_vertices = 0; // bottom left, bottom right, top left, top right const v2s32 bl( 0, 0 ); - const v2s32 br( ohm->hm.width - 1, 0 ); - const v2s32 tl( 0, ohm->hm.height - 1 ); - const v2s32 tr( ohm->hm.width - 1, ohm->hm.height - 1 ); + const v2s32 br( hm.w - 1, 0 ); + const v2s32 tl( 0, hm.h - 1 ); + const v2s32 tr( hm.w - 1, hm.h - 1 ); + + v3 offset_v3( offset.x, offset.y, 0 ); { PROFILE_BLOCK( "Convert BTT to mesh" ); - gpubtt_build( verts, &num_vertices, ohm, btts.left_root, tr, tl, bl ); - gpubtt_build( verts, &num_vertices, ohm, btts.right_root, bl, br, tr ); + gpubtt_build( verts, &num_vertices, hm, offset_v3, btts.left_root, tr, tl, bl ); + gpubtt_build( verts, &num_vertices, hm, offset_v3, btts.right_root, bl, br, tr ); ASSERT( num_leaves * 3 == num_vertices ); } { PROFILE_BLOCK( "Upload normal map" ); TextureConfig normals_config; - normals_config.width = ohm->hm.width; - normals_config.height = ohm->hm.height; + normals_config.width = hm.w; + normals_config.height = hm.h; normals_config.format = TEXFMT_RGB_FLOAT; normals_config.data = normals; normals_config.wrap = TEXWRAP_CLAMP; // TODO: this is a hack and should be removed @@ -77,8 +76,8 @@ void gpubtt_init( { PROFILE_BLOCK( "Upload horizon map" ); TextureConfig horizons_config; - horizons_config.width = ohm->hm.width; - horizons_config.height = ohm->hm.height; + horizons_config.width = hm.w; + horizons_config.height = hm.h; horizons_config.format = TEXFMT_R_U8; horizons_config.data = horizons; horizons_config.wrap = TEXWRAP_CLAMP; // TODO: this is a hack and should be removed diff --git a/gpubtt.h b/gpubtt.h @@ -12,6 +12,6 @@ struct GPUBTT { }; void gpubtt_init( MemoryArena * arena, GPUBTT * gpubtt, BTTs btts, - const OffsetHeightmap * ohm, const v3 * normals, const u8 * horizons ); + const array2d< u16 > hm, v2u32 offset, const v3 * normals, const u8 * horizons ); void gpubtt_destroy( GPUBTT * gpubtt ); void gpubtt_render( const GPUBTT * gpubtt, RenderState render_state ); diff --git a/heightmap.h b/heightmap.h @@ -10,7 +10,6 @@ struct QuadTreeNode { }; struct QuadTree { - QuadTreeNode * nodes_memory; array< QuadTreeNode > nodes; array2d< u16 > heightmap; }; diff --git a/intrinsics.h b/intrinsics.h @@ -226,7 +226,6 @@ DEF_VISIT( double ) #undef DEF_VISIT -#if !PLATFORM_RELACY template< typename T > inline T * malloc_array( size_t count ) { ASSERT( SIZE_MAX / count >= sizeof( T ) ); @@ -238,7 +237,6 @@ inline T * realloc_array( T * old, size_t count ) { ASSERT( SIZE_MAX / count >= sizeof( T ) ); return ( T * ) realloc( old, count * sizeof( T ) ); } -#endif template< typename T > static T lerp( T a, float t, T b ) { diff --git a/make.lua b/make.lua @@ -18,9 +18,9 @@ if OS == "macos" then game_ldflags = "-framework Cocoa -framework CoreVideo -framework IOKit" end --- bin( "medfall", { "main", "hm", "heightmap", "terrain_manager", "btt", "gpubtt", "skybox", "http", "platform_network", game_objs }, { "lz4", game_libs } ) --- msvc_bin_ldflags( "medfall", "opengl32.lib gdi32.lib Ws2_32.lib" ) --- gcc_bin_ldflags( "medfall", game_ldflags ) +bin( "medfall", { "main", "hm", "heightmap", "terrain_manager", "btt", "gpubtt", "decompress_bc", "skybox", "http", "platform_network", game_objs }, { "lz4", game_libs } ) +msvc_bin_ldflags( "medfall", "opengl32.lib gdi32.lib Ws2_32.lib" ) +gcc_bin_ldflags( "medfall", game_ldflags ) bin( "launch", { "launcher/main", "http", "sha2", "platform_network", game_objs }, { "imgui", "monocypher", "whereami", game_libs } ) msvc_bin_ldflags( "launch", "opengl32.lib gdi32.lib Ws2_32.lib" ) @@ -68,7 +68,7 @@ end -- gcc_obj_cxxflags( "pp", "-O2" ) -- msvc_obj_cxxflags( "pp", "/O2" ) -bin( "pp2", { "pp2", "heightmap", "decompress_bc", common_objs }, { "lz4", "squish", "stb_image", "stb_image_write" } ) +bin( "pp2", { "pp2", "decompress_bc", common_objs }, { "lz4", "squish", "stb_image", "stb_image_write" } ) gcc_obj_cxxflags( "pp2", "-O2" ) msvc_obj_cxxflags( "pp2", "/O2" ) diff --git a/terrain_manager.cc b/terrain_manager.cc @@ -27,6 +27,12 @@ struct AsyncLoadTile { s32 tx, ty; }; +static size_t quadtree_max_nodes( size_t w, size_t h ) { + // with a power of 2 + 1 sized quadtree with a 2x2 bottom level it + // should be exactly 1/3 (1/4 + 1/16 + ...) + return ( ( w - 1 ) * ( h - 1 ) ) / 3; +} + static WORK_QUEUE_CALLBACK( async_load_tile ) { AsyncLoadTile * alt = ( AsyncLoadTile * ) data; TerrainManager * tm = alt->tm; @@ -40,23 +46,20 @@ static WORK_QUEUE_CALLBACK( async_load_tile ) { size_t num_points = ( TILE_SIZE + 1 ) * ( TILE_SIZE + 1 ); // heightmap - size_t decompressed_heightmap_size = num_points * sizeof( u16 ); - u16 * decompressed_heightmap = ( u16 * ) malloc( decompressed_heightmap_size ); - ASSERT( decompressed_heightmap != NULL ); + dt.heightmap = array2d< u16 >( malloc_array< u16 >( num_points ), TILE_SIZE + 1, TILE_SIZE + 1 ); + ASSERT( dt.heightmap.ptr() != NULL ); int ok_heightmap = LZ4_decompress_safe( ct.heightmap.ptr(), - ( char * ) decompressed_heightmap, - ct.heightmap.num_bytes(), decompressed_heightmap_size ); + ( char * ) dt.heightmap.ptr(), + ct.heightmap.num_bytes(), dt.heightmap.num_bytes() ); ASSERT( ok_heightmap > 0 ); - heightmap_init( &dt.heightmap, decompressed_heightmap, TILE_SIZE + 1, TILE_SIZE + 1 ); - // normalmap size_t decompressed_normalmap_size = num_points * sizeof( v3 ); v3 * decompressed_normalmap = ( v3 * ) malloc( decompressed_normalmap_size ); ASSERT( decompressed_normalmap != NULL ); - array2d< v3 > normals( decompressed_normalmap, dt.heightmap.width, dt.heightmap.height ); + array2d< v3 > normals( decompressed_normalmap, dt.heightmap.w, dt.heightmap.h ); // compute centre region for( size_t y = 1; y < normals.h - 1; y++ ) { @@ -64,11 +67,11 @@ static WORK_QUEUE_CALLBACK( async_load_tile ) { v3 tangent( 2.0f, 0, - heightmap_height( &dt.heightmap, x + 1, y ) - heightmap_height( &dt.heightmap, x - 1, y ) ); + heightmap_height( dt.heightmap, x + 1, y ) - heightmap_height( dt.heightmap, x - 1, y ) ); v3 bitangent( 0, 2.0f, - heightmap_height( &dt.heightmap, x, y + 1 ) - heightmap_height( &dt.heightmap, x, y - 1 ) ); + heightmap_height( dt.heightmap, x, y + 1 ) - heightmap_height( dt.heightmap, x, y - 1 ) ); normals( x, y ) = normalize( cross( tangent, bitangent ) ); } @@ -107,20 +110,16 @@ static WORK_QUEUE_CALLBACK( async_load_tile ) { ASSERT( decompressed_horizonmap != NULL ); // quadtree - size_t max_decompressed_quadtree_size = heightmap_quadtree_max_nodes( &dt.heightmap ) * sizeof( HeightmapQuadTreeNode ); - HeightmapQuadTreeNode * decompressed_quadtree = ( HeightmapQuadTreeNode * ) malloc( max_decompressed_quadtree_size ); - ASSERT( decompressed_quadtree != NULL ); + size_t num_nodes = quadtree_max_nodes( dt.heightmap.w, dt.heightmap.h ); + dt.quadtree.nodes = array< QuadTreeNode >( malloc_array< QuadTreeNode >( num_nodes ), num_nodes ); + ASSERT( dt.quadtree.nodes.ptr() != NULL ); int ok_quadtree = LZ4_decompress_safe( ct.quadtree.ptr(), - ( char * ) decompressed_quadtree, - ct.quadtree.num_bytes(), max_decompressed_quadtree_size ); + ( char * ) dt.quadtree.nodes.ptr(), + ct.quadtree.num_bytes(), dt.quadtree.nodes.num_bytes() ); ASSERT( ok_quadtree > 0 ); - size_t decompressed_quadtree_size = checked_cast< size_t >( ok_quadtree ); - dt.quadtree.dim = TILE_SIZE; - dt.quadtree.nodes_memory = decompressed_quadtree; - dt.quadtree.nodes = array< HeightmapQuadTreeNode >( decompressed_quadtree, decompressed_quadtree_size / sizeof( HeightmapQuadTreeNode ) ); - dt.quadtree.hm = &dt.heightmap; + dt.quadtree.heightmap = dt.heightmap; // generate btt // TODO: should generate a triangle mesh here instead of in gpubtt_init @@ -128,7 +127,7 @@ static WORK_QUEUE_CALLBACK( async_load_tile ) { void * btt_memory = malloc( megabytes( 16 ) ); memarena_init( &btt_arena, ( u8 * ) btt_memory, megabytes( 16 ) ); GPUTileData gpu_tile_data; - BTTs btt = btt_from_heightmap( &dt.heightmap, &btt_arena ); + BTTs btt = btt_from_heightmap( dt.heightmap, &btt_arena ); gpu_tile_data.btt = btt; gpu_tile_data.btt_memory = btt_memory; @@ -173,9 +172,9 @@ static void terrain_unload_tile( TerrainManager * tm, s32 tx, s32 ty ) { return; } - heightmap_destroy( &tm->decompressed_tiles[ tx ][ ty ].heightmap ); + free( tm->decompressed_tiles[ tx ][ ty ].heightmap.ptr() ); free( tm->decompressed_tiles[ tx ][ ty ].normalmap ); - free( tm->decompressed_tiles[ tx ][ ty ].quadtree.nodes_memory ); + free( tm->decompressed_tiles[ tx ][ ty ].quadtree.nodes.ptr() ); gpubtt_destroy( &tm->gpubtts[ tx ][ ty ] ); tm->tile_states[ tx ][ ty ] = TILE_EMPTY; @@ -308,13 +307,13 @@ void terrain_render( TerrainManager * tm, const m4 & V, const m4 & P, float sun_ s32 ty = ready_tile.ty; const GPUTileData & gpu_tile_data = tm->gpu_tiles[ tx ][ ty ]; - const Heightmap * hm = &tm->decompressed_tiles[ tx ][ ty ].heightmap; - const OffsetHeightmap ohm = { *hm, checked_cast< float >( tx * TILE_SIZE ), checked_cast< float >( ty * TILE_SIZE ) }; + const array2d< u16 > hm = tm->decompressed_tiles[ tx ][ ty ].heightmap; + v2u32 offset( tx * TILE_SIZE, ty * TILE_SIZE ); gpubtt_init( tm->arena, &tm->gpubtts[ tx ][ ty ], gpu_tile_data.btt, - &ohm, + hm, offset, tm->decompressed_tiles[ tx ][ ty ].normalmap, gpu_tile_data.horizonmap ); @@ -366,23 +365,6 @@ void terrain_render( TerrainManager * tm, const m4 & V, const m4 & P, float sun_ } } -float terrain_height( const TerrainManager * tm, v3 position ) { - ASSERT( position.x >= 0 ); - ASSERT( position.y >= 0 ); - ASSERT( position.x < tm->width ); - ASSERT( position.y < tm->height ); - - s32 tx = s32( position.x / TILE_SIZE ); - s32 ty = s32( position.y / TILE_SIZE ); - - if( tm->tile_states[ tx ][ ty ] != TILE_LOADED ) { - return 0.0f; - } - - const Heightmap * hm = &tm->decompressed_tiles[ tx ][ ty ].heightmap; - return hm->bilerp_height( position.x - tx * TILE_SIZE, position.y - ty * TILE_SIZE ); -} - v3 terrain_normal( const TerrainManager * tm, v3 position ) { ASSERT( position.x >= 0 ); ASSERT( position.y >= 0 ); @@ -433,10 +415,10 @@ bool segment_vs_terrain( const TerrainManager * tm, v3 seg_origin, v3 seg_end, f v3 local_seg_origin = seg_origin - v3( checked_cast< float >( tx * TILE_SIZE ), checked_cast< float >( ty * TILE_SIZE ), 0 ); ray.origin = local_seg_origin; - const HeightmapQuadTree * qt = &tm->decompressed_tiles[ tx ][ ty ].quadtree; + const QuadTree * qt = &tm->decompressed_tiles[ tx ][ ty ].quadtree; // TODO: convert from tile t to global t float tile_t; - bool tile_hit = ray_vs_quadtree( qt, ray, &tile_t, xnormal ); + bool tile_hit = ray_vs_quadtree( *qt, ray, &tile_t, xnormal ); if( tile_hit && tile_t <= segment_length && tile_t < *t ) { *t = tile_t; diff --git a/terrain_manager.h b/terrain_manager.h @@ -30,10 +30,9 @@ struct GPUTileData { }; struct DecompressedTile { - // TODO: merge this + Heightmap - Heightmap heightmap; + array2d< u16 > heightmap; v3 * normalmap; - HeightmapQuadTree quadtree; + QuadTree quadtree; }; enum TileState { @@ -76,7 +75,6 @@ void terrain_init( TerrainManager * tm, const char * tiles_dir, MemoryArena * ar void terrain_teleport( TerrainManager * tm, v3 position ); void terrain_update( TerrainManager * tm, v3 position ); void terrain_render( TerrainManager * tm, const m4 & V, const m4 & P, float sun_angle, v3 sun_dir, double current_time ); -float terrain_height( const TerrainManager * tm, v3 position ); v3 terrain_normal( const TerrainManager * tm, v3 position ); bool segment_vs_terrain( const TerrainManager * tm, v3 seg_origin, v3 seg_end, float * t, v3 * xnormal );