commit 4901289a005db1d8f281c2b1287a7acd267aa476 parent b596eab93d7f4ac287a103ed342e8cb891b5ce95 Author: Michael Savage <mikejsavage@gmail.com> Date: Wed Jun 28 21:58:58 +0300 Hacky support for 16bit heightmaps. Collision still broken Diffstat:
aabb.h | | | 4 | ++-- |
btt.cc | | | 2 | +- |
heightmap.cc | | | 27 | +++++++++++++++------------ |
heightmap.h | | | 8 | ++++---- |
make.lua | | | 6 | ++++-- |
pp.cc | | | 68 | ++++++++++++++++++++++++++++++++++++++++++-------------------------- |
terrain_manager.cc | | | 6 | +++--- |
diff --git a/aabb.h b/aabb.h @@ -46,10 +46,10 @@ struct AABB { explicit AABB( const AABBu32 & aabb ) { mins.x = checked_cast< float >( aabb.mins.x ); mins.y = checked_cast< float >( aabb.mins.y ); - mins.z = checked_cast< float >( aabb.mins.z ); + mins.z = checked_cast< float >( aabb.mins.z / 256.0f ); // TODO: huge hack maxs.x = checked_cast< float >( aabb.maxs.x ); maxs.y = checked_cast< float >( aabb.maxs.y ); - maxs.z = checked_cast< float >( aabb.maxs.z ); + maxs.z = checked_cast< float >( aabb.maxs.z / 256.0f ); } bool contains( v3 p ) { diff --git a/btt.cc b/btt.cc @@ -71,7 +71,7 @@ static s32 square_distance( v2s32 u, v2s32 v ) { static bool btt_should_split( const Heightmap * hm, v2s32 v0, v2s32 v1, v2s32 v2, v2s32 mid ) { if( square_distance( v0, v2 ) <= 4 ) return false; - const float avg_height = ( float( heightmap_height( hm, v0.x, v0.y ) ) + float( heightmap_height( hm, v2.x, v2.y ) ) ) * 0.5f; + const float avg_height = ( heightmap_height( hm, v0.x, v0.y ) + heightmap_height( hm, v2.x, v2.y ) ) * 0.5f; const float error = fabsf( avg_height - heightmap_height( hm, mid.x, mid.y ) ); if( error > 2.0f ) return true; diff --git a/heightmap.cc b/heightmap.cc @@ -22,7 +22,7 @@ static float bilerp( v3 p1, v3 p2, v3 p3, v3 p4, v2 p ) { return lerp( mx1, mx2, ty ); } -void heightmap_init( Heightmap * hm, u8 * pixels, u32 width, u32 height ) { +void heightmap_init( Heightmap * hm, u16 * pixels, u32 width, u32 height ) { hm->pixels = pixels; hm->width = width; hm->height = height; @@ -35,13 +35,13 @@ void heightmap_destroy( Heightmap * hm ) { } v3 Heightmap::point( u32 x, u32 y ) const { - return v3( checked_cast< float >( x ), checked_cast< float >( y ), pixels[ y * width + x ] ); + return v3( checked_cast< float >( x ), checked_cast< float >( y ), pixels[ y * width + x ] / 256.0f ); } -u8 heightmap_height( const Heightmap * hm, u32 x, u32 y ) { +float heightmap_height( const Heightmap * hm, u32 x, u32 y ) { ASSERT( x < hm->width ); ASSERT( y < hm->height ); - return hm->pixels[ y * hm->width + x ]; + return hm->pixels[ y * hm->width + x ] / 256.0f; } float Heightmap::bilerp_height( float x, float y ) const { @@ -186,18 +186,19 @@ static void heightmap_build_quadtree_node( HeightmapQuadTree * qt, size_t node_i if( aabb.maxs.x - aabb.mins.x == 1 || aabb.maxs.y - aabb.mins.y == 1 ) { u32 x = aabb.mins.x; u32 y = aabb.mins.y; - u8 a = heightmap_height( qt->hm, x, y ); - u8 b = heightmap_height( qt->hm, x + 1, y ); - u8 c = heightmap_height( qt->hm, x, y + 1 ); - u8 d = heightmap_height( qt->hm, x + 1, y + 1 ); + // TODO: this needs changing for 16bit heights + u16 a = heightmap_height( qt->hm, x, y ); + u16 b = heightmap_height( qt->hm, x + 1, y ); + u16 c = heightmap_height( qt->hm, x, y + 1 ); + u16 d = heightmap_height( qt->hm, x + 1, y + 1 ); qt->nodes[ node_idx ].min_z = min( a, b, c, d ); qt->nodes[ node_idx ].max_z = max( a, b, c, d ); return; } - u8 lo = 255; - u8 hi = 0; + u16 lo = U16_MAX; + u16 hi = 0; for( int i = 0; i < 4; i++ ) { heightmap_build_quadtree_node( qt, child_idx( node_idx, i ), aabb.quadrant( i ) ); lo = min( lo, qt->nodes[ child_idx( node_idx, i ) ].min_z ); @@ -302,7 +303,8 @@ static bool ray_vs_quadtree_node( AABBu32 aabbs[ 4 ]; float ts[ 4 ]; for( int i = 0; i < 4; i++ ) { - aabbs[ i ] = aabb.quadrant( i ).clamp_z( qt->nodes[ child_idx( node_idx, i ) ].min_z, qt->nodes[ child_idx( node_idx, i ) ].max_z ); + HeightmapQuadTreeNode child = qt->nodes[ child_idx( node_idx, i ) ]; + aabbs[ i ] = aabb.quadrant( i ).clamp_z( child.min_z, child.max_z ); ts[ i ] = FLT_MAX; ray_vs_aabb( AABB( aabbs[ i ] ), ray_origin, inv_dir, &ts[ i ] ); } @@ -312,7 +314,8 @@ static bool ray_vs_quadtree_node( for( int i = 0; i < 4; i++ ) { int idx = sorted[ i ]; - if( ts[ idx ] == FLT_MAX ) break; + if( ts[ idx ] == FLT_MAX ) + break; if( ray_vs_quadtree_node( qt, child_idx( node_idx, idx ), aabbs[ idx ], ray_origin, ray_dir, inv_dir, t, xnormal ) ) { return true; diff --git a/heightmap.h b/heightmap.h @@ -7,7 +7,7 @@ class Heightmap { public: - u8 * pixels = NULL; + u16 * pixels = NULL; u32 width, height; v3 point( u32 x, u32 y ) const; @@ -20,12 +20,12 @@ struct OffsetHeightmap { float x_offset, y_offset; }; -void heightmap_init( Heightmap * hm, u8 * pixels, u32 width, u32 height ); -u8 heightmap_height( const Heightmap * hm, u32 x, u32 y ); +void heightmap_init( Heightmap * hm, u16 * pixels, u32 width, u32 height ); +float heightmap_height( const Heightmap * hm, u32 x, u32 y ); void heightmap_destroy( Heightmap * hm ); struct HeightmapQuadTreeNode { - u8 min_z, max_z; + u16 min_z, max_z; }; struct HeightmapQuadTree { diff --git a/make.lua b/make.lua @@ -30,7 +30,6 @@ require( "libs/imgui" ) require( "libs/glfw" ) require( "libs/lz4" ) require( "libs/monocypher" ) -require( "libs/squish" ) require( "libs/stb" ) require( "libs/tinyobjloader" ) require( "libs/whereami" ) @@ -39,6 +38,9 @@ if config == "release" then return end +require( "libs/squish" ) +require( "libs/lodepng" ) + bin( "bsp", { "main", "bsp", "bsp_renderer", game_objs }, { game_libs } ) -- TODO: fix btt build -- bin( "btt", { "main", "mod_btt", "btt", "heightmap", "skybox", "lz4", game_objs }, { "stb_image", game_libs } ) @@ -57,7 +59,7 @@ if OS == "macos" then bin_ldflags( "sound", "-framework AudioUnit" ) end -bin( "pp", { "pp", "heightmap", common_objs }, { "lz4", "squish", "stb_image" } ) +bin( "pp", { "pp", "heightmap", common_objs }, { "lodepng", "lz4", "squish", "stb_image", "stb_image_write" } ) if OS == "linux" then bin( "utils/genkeys/genkeys", { "utils/genkeys/genkeys", common_objs }, { "monocypher" } ) diff --git a/pp.cc b/pp.cc @@ -15,11 +15,13 @@ #include "rng/well512.h" +#define LODEPNG_NO_COMPILE_CPP +#define LODEPNG_NO_COMPILE_ENCODER +#include "libs/lodepng/lodepng.h" + #include "libs/lz4/lz4.h" #include "libs/lz4/lz4hc.h" -#include "libs/stb/stb_image.h" - static float tan_theta( v2 a, v2 b ) { v2 d = a - b; return -d.y / d.x; @@ -27,19 +29,19 @@ static float tan_theta( v2 a, v2 b ) { static void compute_row_of_horizons( MemoryArena * arena, - const array2d< u8 > heightmap, array2d< float > horizons, + const array2d< u16 > 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 ) ); + hull[ 0 ] = v2( 0, heightmap( 0, y ) / 256.0f ); horizons( 0, y ) = 0.0f; size_t hull_size = 1; for( size_t x = 1; x < heightmap.w; x++ ) { - v2 p( checked_cast< float >( x ), heightmap( x, y ) ); + v2 p( checked_cast< float >( x ), heightmap( x, y ) / 256.0f ); while( hull_size > 1 && tan_theta( p, hull[ hull_size - 1 ] ) <= tan_theta( hull[ hull_size - 1 ], hull[ hull_size - 2 ] ) ) { hull_size--; @@ -54,7 +56,7 @@ static void compute_row_of_horizons( static void compute_horizons( MemoryArena * arena, - const array2d< u8 > heightmap, array2d< float > horizons + const array2d< u16 > heightmap, array2d< float > horizons ) { ASSERT( heightmap.w == horizons.w ); ASSERT( heightmap.h == horizons.h ); @@ -64,7 +66,8 @@ static void compute_horizons( } } -static void compute_normals( const array2d< u8 > heightmap, array2d< v3 > normals ) { +static void compute_normals( const array2d< u16 > heightmap, array2d< v3 > normals ) { + // TODO: sobel operator? // estimate the gradient at each point by doing central differences on // each axis, then cross the tangent and bitangent to find the normal for( size_t y = 0; y < heightmap.h; y++ ) { @@ -82,11 +85,11 @@ static void compute_normals( const array2d< u8 > heightmap, array2d< v3 > normal v3 tangent( checked_cast< float >( x_plus_one - x_minus_one ), 0, - checked_cast< float >( heightmap( x_plus_one, y ) - heightmap( x_minus_one, y ) ) ); + heightmap( x_plus_one, y ) / 256.0f - heightmap( x_minus_one, y ) / 256.0f ); v3 bitangent( 0, checked_cast< float >( y_plus_one - y_minus_one ), - checked_cast< float >( heightmap( x, y_plus_one ) - heightmap( x, y_minus_one ) ) ); + heightmap( x, y_plus_one ) / 256.0f - heightmap( x, y_minus_one ) / 256.0f ); normals( x, y ) = normalize( cross( tangent, bitangent ) ); } @@ -114,13 +117,13 @@ const int TILE_SIZE = 512; const int OUT_SIZE = TILE_SIZE + 1; const size_t MAX_TREES = 262144 / 4; -static bool ok_tree_position( RNGWell512 * rng, const array2d< u8 > heightmap, const array2d< v3 > normals, v2 pos, v3 * tree ) { - u8 height = bilerp01( heightmap, pos.x, pos.y ); +static bool ok_tree_position( RNGWell512 * rng, const array2d< u16 > heightmap, const array2d< v3 > normals, v2 pos, v3 * tree ) { + u16 height = bilerp01( heightmap, pos.x, pos.y ); double p = 1; - if( height <= 5 ) return false; - if( height > 235 ) return false; - if( height >= 200 ) { - p *= double( 235 - height ) / double( 235 - 200 ); + if( height <= 5 * 5 ) return false; + if( height > 235 * 235 ) return false; + if( height >= 200 * 200 ) { + p *= double( 235 * 235 - height ) / double( 235 * 235 - 200 * 200 ); } v3 normal = normalize( bilerp01( normals, pos.x, pos.y ) ); @@ -130,11 +133,11 @@ static bool ok_tree_position( RNGWell512 * rng, const array2d< u8 > heightmap, c if( !rng_p( rng, p ) ) return false; - *tree = v3( pos.x * heightmap.w, pos.y * heightmap.h, height ); + *tree = v3( pos.x * heightmap.w, pos.y * heightmap.h, height / 256 ); return true; } -static size_t place_trees( MemoryArena * arena, const array2d< u8 > heightmap, const array2d< v3 > normals, array< v3 > trees ) { +static size_t place_trees( MemoryArena * arena, const array2d< u16 > heightmap, const array2d< v3 > normals, array< v3 > trees ) { MEMARENA_SCOPED_CHECKPOINT( arena ); array< v2 > points = memarena_push_array( arena, v2, trees.n ); @@ -221,10 +224,10 @@ static void write_tile( static void write_quadtree( MemoryArena * arena, const std::string & dir, - const array2d< u8 > heightmap, int tx, int ty + const array2d< u16 > heightmap, int tx, int ty ) { MEMARENA_SCOPED_CHECKPOINT( arena ); - array2d< u8 > tile_heightmap = memarena_push_array2d( arena, u8, OUT_SIZE, OUT_SIZE ); + array2d< u16 > tile_heightmap = memarena_push_array2d( arena, u16, OUT_SIZE, OUT_SIZE ); const u32 left = tx * TILE_SIZE; const u32 top = ty * TILE_SIZE; @@ -244,7 +247,7 @@ static void write_quadtree( } Heightmap hm; - heightmap_init( &hm, ( u8 * ) tile_heightmap.ptr(), OUT_SIZE, OUT_SIZE ); + heightmap_init( &hm, tile_heightmap.ptr(), OUT_SIZE, OUT_SIZE ); size_t num_nodes = heightmap_quadtree_max_nodes( &hm ); array< HeightmapQuadTreeNode > nodes = memarena_push_array( arena, HeightmapQuadTreeNode, num_nodes ); @@ -259,6 +262,16 @@ static void write_quadtree( #include "autogdb.h" #endif +static u16 bswap16( u16 x ) { + return ( x >> 8 ) | ( x << 8 ); +} + +static void bswap16s( u16 * xs, size_t n ) { + for( size_t i = 0; i < n; i++ ) { + xs[ i ] = bswap16( xs[ i ] ); + } +} + int main( int argc, char ** argv ) { #if PLATFORM_LINUX install_debug_signal_handlers( true ); @@ -269,10 +282,13 @@ int main( int argc, char ** argv ) { memarena_init( &arena, arena_memory, ARRAY_COUNT( arena_memory ) ); const std::string path = argc == 2 ? argv[ 1 ] : "terrains/mountains512.png"; - int w, h; printf( "decoding\n" ); - u8 * heightmap_memory = stbi_load( path.c_str(), &w, &h, NULL, 1 ); - ASSERT( heightmap_memory != NULL ); + u32 w, h; + u16 * heightmap_memory; + u32 ok = lodepng_decode_file( ( u8 ** ) &heightmap_memory, &w, &h, path.c_str(), LCT_GREY, 16 ); + ASSERT( ok == 0 ); + + bswap16s( heightmap_memory, w * h ); const std::string dir = path + ".parts"; @@ -283,7 +299,7 @@ int main( int argc, char ** argv ) { ASSERT( ferror( dims ) == 0 ); fclose( dims ); - const array2d< u8 > heightmap( heightmap_memory, w, h ); + const array2d< u16 > heightmap( heightmap_memory, w, h ); int xtiles = ( w + TILE_SIZE - 2 ) / TILE_SIZE; int ytiles = ( h + TILE_SIZE - 2 ) / TILE_SIZE; @@ -291,7 +307,7 @@ int main( int argc, char ** argv ) { { for( int ty = 0; ty < ytiles; ty++ ) { for( int tx = 0; tx < xtiles; tx++ ) { - write_tile( &arena, dir, "_heightmap.lz4", heightmap, u8( 0 ), tx, ty ); + write_tile( &arena, dir, "_heightmap.lz4", heightmap, u16( 0 ), tx, ty ); } } } @@ -346,7 +362,7 @@ int main( int argc, char ** argv ) { } } - stbi_image_free( heightmap_memory ); + free( heightmap_memory ); return 0; } diff --git a/terrain_manager.cc b/terrain_manager.cc @@ -36,8 +36,8 @@ 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( u8 ); - u8 * decompressed_heightmap = ( u8 * ) malloc( decompressed_heightmap_size ); + size_t decompressed_heightmap_size = num_points * sizeof( u16 ); + u16 * decompressed_heightmap = ( u16 * ) malloc( decompressed_heightmap_size ); ASSERT( decompressed_heightmap != NULL ); int ok_heightmap = LZ4_decompress_safe( ct.heightmap.ptr(), @@ -376,7 +376,7 @@ 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 ) { PROFILE_FUNCTION(); - v3 dont_care; + v3 dont_care = v3( 0, 0, 0 ); if( xnormal == NULL ) { xnormal = &dont_care; }