medfall

A super great game engine
Log | Files | Refs

commit 4a650ec06feada62f855dd8e04b2a0758c6a8b47
parent 24df7e4f19e86902d654c4fd0ef5687be9d2ace2
Author: Michael Savage <mikejsavage@gmail.com>
Date:   Sun, 29 Oct 2017 23:05:40 +0200

Semi-functional collision detection in the clipmap engine

Diffstat:
clipmap.cc | 91++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
main.cc | 2+-
make.lua | 2+-
pp2.cc | 85++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
4 files changed, 152 insertions(+), 28 deletions(-)

diff --git a/clipmap.cc b/clipmap.cc @@ -3,16 +3,26 @@ #include "game.h" #include "renderer.h" #include "shaders.h" +#include "immediate.h" #include "text_renderer.h" #include "gl.h" #include "skybox.h" #include "libs/lz4/lz4.h" -static Mesh patch; -static Texture heightmap; -static Texture normalmap; -static Texture horizonmap; +#include "libs/squish/squish.h" + +struct ClipmapTerrain +{ + Mesh tile; + Mesh seam; + Texture heightmap; + Texture normalmap; + Texture horizonmap; + HeightmapQuadTree quadtree; +}; + +static ClipmapTerrain clipmap; static u32 PATCH_RESOLUTION = 80; static u32 NUM_LODS = 5; @@ -29,19 +39,32 @@ static void file_get_contents_and_decompress( const char * path, u8 * decompress free( compressed ); } +struct RGBA { + u8 r, g, b, a; +}; + GAME_INIT( game_init ) { { MEMARENA_SCOPED_CHECKPOINT( &mem->persistent_arena ); - u8 * heightmap_data = memarena_push_size( &mem->persistent_arena, 4096 * 4096 ); - u8 * normalmap_data = memarena_push_size( &mem->persistent_arena, 4096 * 4096 ); - u8 * horizonmap_data = memarena_push_size( &mem->persistent_arena, 4096 * 4096 / 2 ); + Heightmap * hm = alloc< Heightmap >( &mem->persistent_arena ); + hm->width = hm->height = 4096; + hm->pixels = memarena_push_many( &mem->persistent_arena, u16, 4096 * 4096 ); - file_get_contents_and_decompress( "terrains/gta16.png.parts/heightmap.bc5.lz4", heightmap_data, 4096 * 4096 ); - file_get_contents_and_decompress( "terrains/gta16.png.parts/normalmap.bc5.lz4", normalmap_data, 4096 * 4096 ); - file_get_contents_and_decompress( "terrains/gta16.png.parts/horizonmap.bc4.lz4", horizonmap_data, 4096 * 4096 / 2 ); + array< HeightmapQuadTreeNode > nodes = alloc_array< HeightmapQuadTreeNode >( &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.dim = 4096; + clipmap.quadtree.hm = hm; + clipmap.quadtree.nodes_memory = nodes.ptr(); + clipmap.quadtree.nodes = nodes; { + MEMARENA_SCOPED_CHECKPOINT( &mem->persistent_arena ); + + u8 * heightmap_data = memarena_push_size( &mem->persistent_arena, 4096 * 4096 ); + file_get_contents_and_decompress( "terrains/gta16.png.parts/heightmap.bc5.lz4", heightmap_data, 4096 * 4096 ); + TextureConfig texture_config; texture_config.width = 4096; texture_config.height = 4096; @@ -49,10 +72,22 @@ GAME_INIT( game_init ) { texture_config.format = TEXFMT_BC5; texture_config.wrap = TEXWRAP_BORDER; texture_config.border_colour = v4( 0 ); - heightmap = renderer_new_texture( texture_config ); + clipmap.heightmap = renderer_new_texture( texture_config ); + + RGBA * rgba = memarena_push_many( &mem->persistent_arena, RGBA, 4096 * 4096 ); + squish::DecompressImage( ( u8 * ) rgba, 4096, 4096, heightmap_data, squish::kBc5 ); + + for( size_t i = 0; i < 4096 * 4096; i++ ) { + clipmap.quadtree.hm->pixels[ i ] = u16( rgba[ i ].r ) * u16( 255 ) + u16( rgba[ i ].g ); + } } { + MEMARENA_SCOPED_CHECKPOINT( &mem->persistent_arena ); + + u8 * normalmap_data = memarena_push_size( &mem->persistent_arena, 4096 * 4096 ); + file_get_contents_and_decompress( "terrains/gta16.png.parts/normalmap.bc5.lz4", normalmap_data, 4096 * 4096 ); + TextureConfig texture_config; texture_config.width = 4096; texture_config.height = 4096; @@ -60,10 +95,15 @@ GAME_INIT( game_init ) { texture_config.format = TEXFMT_BC5; texture_config.wrap = TEXWRAP_BORDER; texture_config.border_colour = v4( 0.5f ); - normalmap = renderer_new_texture( texture_config ); + clipmap.normalmap = renderer_new_texture( texture_config ); } { + MEMARENA_SCOPED_CHECKPOINT( &mem->persistent_arena ); + + u8 * horizonmap_data = memarena_push_size( &mem->persistent_arena, 4096 * 4096 / 2 ); + file_get_contents_and_decompress( "terrains/gta16.png.parts/horizonmap.bc4.lz4", horizonmap_data, 4096 * 4096 / 2 ); + TextureConfig texture_config; texture_config.width = 4096; texture_config.height = 4096; @@ -71,7 +111,7 @@ GAME_INIT( game_init ) { texture_config.format = TEXFMT_BC4; texture_config.wrap = TEXWRAP_BORDER; texture_config.border_colour = v4( 0 ); - horizonmap = renderer_new_texture( texture_config ); + clipmap.horizonmap = renderer_new_texture( texture_config ); } } @@ -100,7 +140,7 @@ GAME_INIT( game_init ) { mesh_config.positions = renderer_new_vb( vertices.ptr(), vertices.num_bytes() ); mesh_config.indices = renderer_new_ib( indices.ptr(), indices.num_bytes() ); mesh_config.num_vertices = indices.size(); - patch = renderer_new_mesh( mesh_config ); + clipmap.tile = renderer_new_mesh( mesh_config ); } game->pos = v3( 0, 0, 100 ); @@ -137,7 +177,6 @@ GAME_FRAME( game_frame ) { v3 up = normalize( cross( right, forward ) ); const float speed = 100.0f; - // const float speed = 1.0f; game->pos += forward * dt * fb * speed; game->pos += right * dt * lr * speed; game->pos.z += dt * dz * speed; @@ -176,17 +215,31 @@ GAME_FRAME( game_frame ) { render_state.uniforms[ UNIFORMS_VIEW ] = view_uniforms; render_state.uniforms[ UNIFORMS_SUN ] = sun_uniforms; render_state.uniforms[ UNIFORMS_CLIPMAP ] = renderer_uniforms( offset, scale ); - render_state.textures[ 0 ] = heightmap; - render_state.textures[ 1 ] = normalmap; - render_state.textures[ 2 ] = horizonmap; + render_state.textures[ 0 ] = clipmap.heightmap; + render_state.textures[ 1 ] = clipmap.normalmap; + render_state.textures[ 2 ] = clipmap.horizonmap; render_state.textures[ 3 ] = renderer_blue_noise(); // render_state.wireframe = true; - renderer_draw_mesh( patch, render_state ); + renderer_draw_mesh( clipmap.tile, render_state ); } } } { + float t; + v3 normal; + if( ray_vs_quadtree( &clipmap.quadtree, Ray3( game->pos + v3( 2048, 2048, 0 ), forward ), &t, &normal ) ) { + v3 impact = game->pos + forward * t; + immediate_arrow( impact, v3( 0, 0, 1 ), 16, v4( 0, 1, 1, 1 ) ); + } + + RenderState impact_render_state; + impact_render_state.shader = get_shader( SHADER_FLAT_VERTEX_COLOURS ); + impact_render_state.uniforms[ UNIFORMS_VIEW ] = view_uniforms; + immediate_render( impact_render_state ); + } + + { const str< 128 > status( "Frame time: {.1}ms FPS: {.1}", dt * 1000.0f, 1.0f / dt ); draw_text( status.c_str(), 2, 2, 16.0f ); draw_text( str< 128 >( "pos = {.1}", game->pos ).c_str(), 2, 20, 16 ); diff --git a/main.cc b/main.cc @@ -22,7 +22,7 @@ GameFrame game_frame; int main( int argc, char ** argv ) { install_debug_signal_handlers( true ); - size_t persistent_size = megabytes( 64 ); + size_t persistent_size = megabytes( 150 ); u8 * persistent_memory = ( u8 * ) malloc( persistent_size ); if( persistent_memory == NULL ) { FATAL( "couldn't allocate persistent memory" ); diff --git a/make.lua b/make.lua @@ -46,7 +46,7 @@ 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 } ) bin( "sm", { "main", "shadow_map", game_objs }, { game_libs } ) -bin( "clipmap", { "main", "clipmap", "skybox", game_objs }, { "lz4", game_libs } ) +bin( "clipmap", { "main", "clipmap", "heightmap", "skybox", game_objs }, { "lz4", "squish", game_libs } ) msvc_bin_ldflags( "bsp", "opengl32.lib gdi32.lib" ) gcc_bin_ldflags( "bsp", game_ldflags ) diff --git a/pp2.cc b/pp2.cc @@ -4,6 +4,7 @@ #include "linear_algebra.h" #include "dynstr.h" #include "int_conversions.h" +#include "heightmap.h" #include "autogdb.h" #include "libs/squish/squish.h" @@ -87,6 +88,53 @@ static void compute_normals( const array2d< u16 > heightmap, array2d< v3 > norma } } +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 size_t child_idx( size_t node_idx, size_t quadrant ) { + return node_idx * 4 + quadrant + 1; +} + +static void build_quadtree_node( const array2d< u16 > heightmap, array< HeightmapQuadTreeNode > & nodes, size_t node_idx, MinMaxu32 aabb ) { + nodes[ node_idx ].min_z = U16_MAX; + nodes[ node_idx ].max_z = 0; + + if( aabb.maxs.x - aabb.mins.x == 2 || aabb.maxs.y - aabb.mins.y == 2 ) { + for( size_t y = aabb.mins.y; y <= aabb.maxs.y; y++ ) { + for( size_t x = aabb.mins.x; x <= aabb.maxs.x; x++ ) { + u16 h = heightmap( x, y ); + nodes[ node_idx ].min_z = min( h, nodes[ node_idx ].min_z ); + nodes[ node_idx ].max_z = max( h, nodes[ node_idx ].max_z ); + } + } + + return; + } + + u16 lo = U16_MAX; + u16 hi = 0; + for( int i = 0; i < 4; i++ ) { + build_quadtree_node( heightmap, nodes, child_idx( node_idx, i ), aabb.quadrant( i ) ); + lo = min( lo, nodes[ child_idx( node_idx, i ) ].min_z ); + hi = max( hi, nodes[ child_idx( node_idx, i ) ].max_z ); + } + + nodes[ node_idx ].min_z = lo; + nodes[ node_idx ].max_z = hi; +} + +static void build_quadtree( const array2d< u16 > heightmap, array< HeightmapQuadTreeNode > & nodes ) { + ASSERT( nodes.n >= quadtree_max_nodes( heightmap.w, heightmap.h ) ); + ASSERT( heightmap.w == heightmap.h ); + + size_t dim = heightmap.w - 1; + MinMaxu32 aabb( v3u32( 0, 0, 0 ), v3u32( dim, dim, U16_MAX ) ); + build_quadtree_node( heightmap, nodes, 0, aabb ); +} + static void write_compressed_file( MemoryArena * arena, const DynamicString & path, const void * data, size_t len ) { MEMARENA_SCOPED_CHECKPOINT( arena ); @@ -136,17 +184,13 @@ int main( int argc, char ** argv ) { ASSERT( png != NULL ); array2d< u16 > heightmap( png, w, h ); - array2d< RGBA > heightmap_split = alloc_array2d< RGBA >( &arena, w, h ); - - array2d< v3 > normalmap_v3 = alloc_array2d< v3 >( &arena, w, h ); - array2d< RGBA > normalmap = alloc_array2d< RGBA >( &arena, w, h ); - - array2d< float > horizonmap_float = alloc_array2d< float >( &arena, w, h ); - array2d< RGBA > horizonmap = alloc_array2d< RGBA >( &arena, w, h ); { MEMARENA_SCOPED_CHECKPOINT( &arena ); printf( "computing heightmaps\n" ); + + array2d< RGBA > heightmap_split = alloc_array2d< RGBA >( &arena, w, h ); + for( int y = 0; y < h; y++ ) { for( int x = 0; x < w; x++ ) { heightmap_split( x, y ).r = heightmap( x, y ) / 256; @@ -165,6 +209,9 @@ int main( int argc, char ** argv ) { MEMARENA_SCOPED_CHECKPOINT( &arena ); printf( "computing normalmap\n" ); + array2d< v3 > normalmap_v3 = alloc_array2d< v3 >( &arena, w, h ); + array2d< RGBA > normalmap = alloc_array2d< RGBA >( &arena, w, h ); + compute_normals( heightmap, normalmap_v3 ); for( int y = 0; y < h; y++ ) { for( int x = 0; x < w; x++ ) { @@ -184,6 +231,9 @@ int main( int argc, char ** argv ) { MEMARENA_SCOPED_CHECKPOINT( &arena ); printf( "computing horizonmap\n" ); + array2d< float > horizonmap_float = alloc_array2d< float >( &arena, w, h ); + array2d< RGBA > horizonmap = alloc_array2d< RGBA >( &arena, w, h ); + compute_horizons( &arena, heightmap, horizonmap_float ); for( int y = 0; y < h; y++ ) { for( int x = 0; x < w; x++ ) { @@ -200,6 +250,27 @@ int main( int argc, char ** argv ) { write_compressed_texture( &arena, horizonmap_path, horizonmap, squish::kBc4 ); } + { + MEMARENA_SCOPED_CHECKPOINT( &arena ); + printf( "computing quadtree\n" ); + + array2d< u16 > heightmap1 = alloc_array2d< u16 >( &arena, heightmap.w + 1, heightmap.h + 1 ); + for( size_t y = 0; y < heightmap1.h; y++ ) { + for( size_t x = 0; x < heightmap1.w; x++ ) { + heightmap1( x, y ) = heightmap.try_get( x, y, 0 ); + } + } + + size_t num_nodes = quadtree_max_nodes( heightmap1.w, heightmap1.h ); + array< HeightmapQuadTreeNode > nodes = alloc_array< HeightmapQuadTreeNode >( &arena, num_nodes ); + + build_quadtree( heightmap1, nodes ); + + printf( "compressing\n" ); + DynamicString quadtree_path( "{}/quadtree.lz4", output_path ); + write_compressed_file( &arena, quadtree_path, nodes.ptr(), nodes.num_bytes() ); + } + stbi_image_free( png ); return 0;