mod_btt.cc (13102B)
1 #include "game.h" 2 #include "intrinsics.h" 3 #include "int_conversions.h" 4 #include "immediate.h" 5 #include "skybox.h" 6 #include "profiler.h" 7 #include "linear_algebra.h" 8 9 #include "libs/lz4/lz4.h" 10 11 #include "libs/stb/stb_image.h" 12 13 // #define TERRAIN_NAME "mountains512.png" 14 #define TERRAIN_NAME "grad.png" 15 16 static const char * vert_src = GLSL( 17 in vec3 position; 18 19 out vec3 smooth_position; 20 out float depth; 21 22 layout( std140 ) uniform v_hot { 23 mat4 vp; 24 }; 25 26 void main() { 27 gl_Position = vp * vec4( position, 1.0 ); 28 smooth_position = position; 29 depth = gl_Position.z; 30 } 31 ); 32 33 static const char * frag_src = GLSL( 34 in vec3 smooth_position; 35 in float depth; 36 37 out vec4 colour; 38 39 layout( std140 ) uniform f_hot { 40 vec2 dimensions; 41 float sun; 42 }; 43 44 uniform sampler2D normals; 45 uniform sampler2D horizons; 46 47 void main() { 48 vec3 normal = normalize( texture( normals, smooth_position.xy / dimensions ).xyz * 2.0 - 1.0 ); 49 float horizon = texture( horizons, smooth_position.xy / dimensions ).r; 50 51 // ground colour 52 vec3 ground; 53 if( smooth_position.z > 175 ) { 54 // snow/rocks 55 if( normal.z > 0.5 ) { 56 ground = vec3( 0.8, 0.8, 0.8 ); 57 } 58 else { 59 ground = vec3( 0.6, 0.6, 0.6 ); 60 } 61 } 62 else if( smooth_position.z < 5 ) { 63 ground = vec3( 0.0, 0.25, 1.0 ); 64 } 65 else { 66 if( normal.z > 0.8 ) { 67 ground = vec3( 0.4, 1.0, 0.4 ); 68 } 69 else { 70 ground = vec3( 0.7, 0.7, 0.5 ); 71 } 72 } 73 74 // sunlight + sky ambient lighting 75 // TODO: use formula for area of a chord? 76 float sun_visible_fraction = smoothstep( 0, 0.2, sun - horizon ); 77 sun_visible_fraction += 5.0; 78 sun_visible_fraction = min( 1.0, sun_visible_fraction ); 79 vec3 sun_direction = normalize( vec3( -1, 0, sun ) ); 80 float sunlight_lambert = max( 0, dot( normal, sun_direction ) ); 81 vec3 sunlight = sun_visible_fraction * sunlight_lambert * vec3( 0.9, 0.9, 0.5 ); 82 vec3 ambient = vec3( 0.02, 0.02, 0.15 ); 83 84 colour = vec4( ( sunlight + ambient ) * ground, 1.0 ); 85 colour = vec4( ( normal + 1.0 ) / 2.0, 1.0 ); 86 } 87 ); 88 89 static const char * vert_outline_src = GLSL( 90 in vec3 position; 91 in vec3 colour; 92 93 out vec3 frag_colour; 94 95 layout( std140 ) uniform v_hot { 96 mat4 vp; 97 }; 98 99 void main() { 100 frag_colour = colour; 101 gl_Position = vp * vec4( position, 1.0 ); 102 } 103 ); 104 105 static const char * frag_outline_src = GLSL( 106 in vec3 frag_colour; 107 108 out vec4 screen_colour; 109 110 void main() { 111 screen_colour = vec4( frag_colour, 1.0 ); 112 } 113 ); 114 115 static v3 angles_to_vector_xy( v3 angles ) { 116 return v3( sin( angles.y ), cos( angles.y ), 0 ); 117 } 118 119 v3 angles_to_vector( v3 angles ) { 120 return v3( 121 -sin( angles.y ) * sin( angles.x ), 122 -cos( angles.y ) * sin( angles.x ), 123 -cos( angles.x ) 124 ); 125 } 126 127 // static void draw_btt( 128 // const BTT * btt, const Heightmap * hm, 129 // ImmediateContext * imm, 130 // glm::ivec2 iv0, glm::ivec2 iv1, glm::ivec2 iv2 131 // ) { 132 // const glm::vec4 white( 1, 1, 1, 1 ); 133 // const glm::vec3 offset( 0.0f, 0.0f, 0.5f ); 134 // 135 // glm::vec3 v0( hm->point( iv0.x, iv0.y ) + offset ); 136 // glm::vec3 v1( hm->point( iv1.x, iv1.y ) + offset ); 137 // glm::vec3 v2( hm->point( iv2.x, iv2.y ) + offset ); 138 // 139 // if( btt->left ) { 140 // assert( btt->right ); 141 // 142 // glm::ivec2 mid = ( iv0 + iv2 ) / 2; 143 // 144 // draw_btt( btt->left, hm, imm, iv1, mid, iv0 ); 145 // draw_btt( btt->right, hm, imm, iv2, mid, iv1 ); 146 // } 147 // else { 148 // immediate_triangle( imm, v0, v1, v2, white ); 149 // } 150 // } 151 152 static void compute_normals( const array2d< u8 > heightmap, array2d< v3 > normals ) { 153 ASSERT( heightmap.w == normals.w && heightmap.h == normals.h ); 154 155 // estimate the gradient at each point by doing central differences on 156 // each axis, then cross the tangent and bitangent to find the normal 157 for( size_t y = 0; y < heightmap.h; y++ ) { 158 for( size_t x = 0; x < heightmap.w; x++ ) { 159 size_t x_plus_one = x + 1; 160 size_t x_minus_one = x - 1; 161 size_t y_plus_one = y + 1; 162 size_t y_minus_one = y - 1; 163 164 if( x == 0 ) x_minus_one = x; 165 if( x == heightmap.w - 1 ) x_plus_one = x; 166 if( y == 0 ) y_minus_one = y; 167 if( y == heightmap.h - 1 ) y_plus_one = y; 168 169 v3 tangent( 170 checked_cast< float >( x_plus_one - x_minus_one ), 171 0, 172 checked_cast< float >( heightmap( x_plus_one, y ) - heightmap( x_minus_one, y ) ) ); 173 v3 bitangent( 174 0, 175 checked_cast< float >( y_plus_one - y_minus_one ), 176 checked_cast< float >( heightmap( x, y_plus_one ) - heightmap( x, y_minus_one ) ) ); 177 178 v3 cp = cross( tangent, bitangent ); 179 v3 n = normalize( cp ); 180 normals( x, y ) = ( n + v3( 1, 1, 1 ) ) / 2; 181 } 182 } 183 } 184 185 static array2d< float > horizons; 186 static array2d< v3 > normals; 187 static HeightmapQuadTree qt; 188 189 static UB ub_fs, ub_vs; 190 191 extern "C" GAME_INIT( game_init ) { 192 PROFILE_FUNCTION(); 193 194 game->pos = v3( 100, 200, 100 ); 195 game->pitch = 0; 196 game->yaw = 45; 197 198 game->test_sun = 0.3f; 199 200 ShaderConfig terrain_config; 201 terrain_config.vertex_src = vert_src; 202 terrain_config.fragment_src = frag_src; 203 terrain_config.texture_uniform_names[ 0 ] = "normals"; 204 terrain_config.texture_uniform_names[ 1 ] = "horizons"; 205 game->test_shader = renderer_new_shader( terrain_config ); 206 207 game->test_outline_shader = renderer_new_shader( vert_outline_src, frag_outline_src ); 208 209 ub_vs = renderer_new_ub(); 210 ub_fs = renderer_new_ub(); 211 212 int w, h, channels; 213 u8 * pixels = stbi_load( "terrains/" TERRAIN_NAME, &w, &h, &channels, 1 ); 214 ASSERT( channels == 1 ); 215 heightmap_init( &game->hm, pixels, w, h ); 216 217 { 218 PROFILE_BLOCK( "Build quadtree" ); 219 size_t num_nodes = heightmap_quadtree_max_nodes( &game->hm ); 220 array< HeightmapQuadTreeNode > nodes = memarena_push_array( &mem->persistent_arena, HeightmapQuadTreeNode, num_nodes ); 221 qt = heightmap_build_quadtree( &game->hm, nodes ); 222 } 223 224 horizons = memarena_push_array2d( &mem->persistent_arena, float, w, h ); 225 226 size_t compressed_horizons_size; 227 u8 * compressed_horizons = file_get_contents( "terrains/" TERRAIN_NAME ".parts/0_0_horizons.lz4", &compressed_horizons_size ); 228 int ok_horizons = LZ4_decompress_safe( 229 ( const char * ) compressed_horizons, 230 ( char * ) horizons.ptr(), 231 compressed_horizons_size, horizons.num_bytes() ); 232 ASSERT( ok_horizons > 0 && to_unsigned( ok_horizons ) == horizons.num_bytes() ); 233 234 normals = memarena_push_array2d( &mem->persistent_arena, v3, w, h ); 235 236 // size_t compressed_normals_size; 237 // u8 * compressed_normals = file_get_contents( "terrains/" TERRAIN_NAME ".parts/0_0_normals.lz4", &compressed_normals_size ); 238 // int ok_normals = LZ4_decompress_safe( 239 // ( const char * ) compressed_normals, 240 // ( char * ) normals.ptr(), 241 // compressed_normals_size, normals.num_bytes() ); 242 // ASSERT( ok_normals > 0 && to_unsigned( ok_normals ) == normals.num_bytes() ); 243 244 array2d< u8 > heightmap( pixels, w, h ); 245 compute_normals( heightmap, normals ); 246 247 { 248 PROFILE_BLOCK( "Build BTT" ); 249 game->btt = btt_from_heightmap( &game->hm, &mem->persistent_arena ); 250 } 251 252 const OffsetHeightmap ohm = { game->hm, 0, 0 }; 253 gpubtt_init( &mem->persistent_arena, &game->gpubtt, game->btt, &ohm, normals.ptr(), horizons.ptr() ); 254 255 skybox_init( &game->skybox ); 256 } 257 258 static m4 camera_to_vp( v3 position, v3 angles ) { 259 const m4 P = m4_perspective( VERTICAL_FOV, ( float ) WIDTH / ( float ) HEIGHT, NEAR_PLANE_DEPTH, FAR_PLANE_DEPTH ); 260 261 return m4_translation( -position ) * m4_rotz( -angles.y ) * m4_rotx( -angles.x ) * P; 262 } 263 264 struct FSData { 265 v2 dimensions; 266 float sun; 267 }; 268 269 static void draw_qt( ImmediateContext * imm, AABBu32 aabb, const array< HeightmapQuadTreeNode > nodes, size_t node_idx ) { 270 if( aabb.maxs.x - aabb.mins.x < 32 ) { 271 return; 272 } 273 274 v3 mins( aabb.mins.x, aabb.mins.y, aabb.mins.z ); 275 v3 maxs( aabb.maxs.x, aabb.maxs.y, aabb.maxs.z ); 276 immediate_aabb( imm, mins, maxs, v4( 0, 0, 1, 1 ) ); 277 278 for( size_t i = 0; i < 4; i++ ) { 279 AABBu32 child_aabb = aabb.quadrant( i ).clamp_z( nodes[ node_idx * 4 + i + 1 ].min_z, nodes[ node_idx * 4 + i + 1 ].max_z ); 280 draw_qt( imm, child_aabb, nodes, node_idx * 4 + i + 1 ); 281 } 282 } 283 284 template< typename T > 285 static T lerp( T a, float t, T b ) { 286 ASSERT( t >= 0.0f && t <= 1.0f ); 287 return a * ( 1.0f - t ) + b * t; 288 } 289 290 template< typename T > 291 static T bilerp( const array2d< T > arr, float x, float y ) { 292 size_t xi = ( size_t ) x; 293 size_t yi = ( size_t ) y; 294 size_t xi1 = min( xi + 1, arr.w - 1 ); 295 size_t yi1 = min( yi + 1, arr.h - 1 ); 296 297 float xf = x - xi; 298 float yf = y - yi; 299 300 T a = arr( xi, yi ); 301 T b = arr( xi1, yi ); 302 T c = arr( xi, yi1 ); 303 T d = arr( xi1, yi1 ); 304 305 T ab = lerp( a, xf, b ); 306 T cd = lerp( c, xf, d ); 307 308 return lerp( ab, yf, cd ); 309 } 310 311 extern "C" GAME_FRAME( game_frame ) { 312 renderer_begin_frame( CLEARCOLOUR_DONT ); 313 314 int fb = input->keys[ KEY_W ] - input->keys[ KEY_S ]; 315 int lr = input->keys[ KEY_D ] - input->keys[ KEY_A ]; 316 int dz = input->keys[ KEY_SPACE ] - input->keys[ KEY_LEFTSHIFT ]; 317 318 int dpitch = input->keys[ KEY_DOWNARROW ] - input->keys[ KEY_UPARROW ]; 319 int dyaw = input->keys[ KEY_LEFTARROW ] - input->keys[ KEY_RIGHTARROW ]; 320 321 dpitch += input->keys[ KEY_K ] - input->keys[ KEY_I ]; 322 dyaw += input->keys[ KEY_J ] - input->keys[ KEY_L ]; 323 324 game->pitch += dpitch * dt * 100; 325 game->yaw += dyaw * dt * 100; 326 327 game->pitch -= float( input->mouse_dy * 0.1 ); 328 game->yaw -= float( input->mouse_dx * 0.1 ); 329 330 const float speed = 50.0f; 331 332 const v3 world_up = v3( 0, 0, 1 ); 333 v3 forward = v3_forward( game->pitch, game->yaw ); 334 v3 right = normalize( cross( forward, world_up ) ); 335 v3 up = normalize( cross( right, forward ) ); 336 337 game->pos += forward * dt * fb * speed; 338 game->pos += right * dt * lr * speed; 339 game->pos.z += dt * dz * speed; 340 341 const float dsun = ( input->keys[ KEY_EQUALS ] - input->keys[ KEY_MINUS ] ) * dt; 342 game->test_sun += dsun; 343 if( dsun != 0 ) printf( "sun: %.4f\n", game->test_sun ); 344 345 m4 P = m4_perspective( VERTICAL_FOV, ( float ) WIDTH / ( float ) HEIGHT, NEAR_PLANE_DEPTH, FAR_PLANE_DEPTH ); 346 m4 V = m4_view( forward, right, up, game->pos ); 347 m4 Vsky = m4_view( forward, right, up, v3( 0 ) ); 348 m4 VP = P * V; 349 350 skybox_render( &game->skybox, Vsky, P, game->test_sun ); 351 352 FSData fs_data = { }; 353 fs_data.dimensions = v2( game->hm.width, game->hm.height ); 354 fs_data.sun = game->test_sun; 355 356 renderer_ub_data( ub_vs, &VP, sizeof( VP ) ); 357 renderer_ub_data( ub_fs, &fs_data, sizeof( fs_data ) ); 358 359 RenderState render_state; 360 render_state.shader = game->test_shader; 361 render_state.ubs[ UB_VS_HOT ] = ub_vs; 362 render_state.ubs[ UB_FS_HOT ] = ub_fs; 363 gpubtt_render( &game->gpubtt, render_state ); 364 365 static ImmediateTriangle triangles[ megabytes( 16 ) ]; 366 ImmediateContext imm; 367 368 { 369 immediate_init( &imm, triangles, ARRAY_COUNT( triangles ) ); 370 // for( size_t i = 0; i < game->hm.width; i++ ) { 371 // v3 origin = v3( i, 1, game->hm.point( i, 1 ).z ); 372 // v3 direction = normalize( v3( -1, 0, horizons( i, 1 ) ) ); 373 // v4 white( 1, 1, 1, 1 ); 374 // immediate_arrow( &imm, origin, direction, 2, white ); 375 // } 376 377 for( u32 y = 4; y < game->hm.height; y += 8 ) { 378 for( u32 x = 4; x < game->hm.width; x += 8 ) { 379 v3 origin = game->hm.point( x, y ); 380 v3 direction = normals( x, y ) * 2 - v3( 1, 1, 1 ); 381 v4 white( 1, 1, 1, 1 ); 382 immediate_sphere( &imm, origin, 2, white, 4 ); 383 immediate_sphere( &imm, origin + 3 * direction, 2, white, 4 ); 384 } 385 } 386 387 float t; 388 v3 ray_dir = forward; 389 v3 inv_dir = v3( 1.0f / ray_dir.x, 1.0f / ray_dir.y, 1.0f / ray_dir.z ); 390 v3 xnormal; 391 if( ray_vs_quadtree( &qt, game->pos, ray_dir, inv_dir, &t, &xnormal ) ) { 392 v3 impact = game->pos + forward * t; 393 immediate_sphere( &imm, impact, 2, v4( 1, 0, 0, 1 ) ); 394 v3 smooth_normal = normalize( bilerp( normals, impact.x, impact.y ) ); 395 printf( "%.2f %.2f %.2f\n", smooth_normal.x, smooth_normal.y, smooth_normal.z ); 396 immediate_arrow( &imm, impact, smooth_normal, 8, v4( 1, 0, 0, 1 ) ); 397 } 398 else printf( "nope\n" ); 399 400 RenderState impact_render_state; 401 impact_render_state.shader = game->test_outline_shader; 402 impact_render_state.ubs[ UB_VS_HOT ] = ub_vs; 403 immediate_render( &imm, impact_render_state ); 404 } 405 406 // { 407 // immediate_init( &imm, triangles, ARRAY_COUNT( triangles ) ); 408 // v3u32 mins = v3u32( 0, 0, qt.nodes[ 0 ].min_z ); 409 // v3u32 maxs = v3u32( qt.dim, qt.dim, qt.nodes[ 0 ].max_z ); 410 // draw_qt( &imm, AABBu32( mins, maxs ), qt.nodes, 0 ); 411 // 412 // RenderState qt_render_state; 413 // qt_render_state.shader = game->test_outline_shader; 414 // qt_render_state.ubs[ UB_VS_HOT ] = ub_vs; 415 // qt_render_state.wireframe = true; 416 // immediate_render( &imm, qt_render_state ); 417 // } 418 419 // immediate_init( &imm, triangles, ARRAY_COUNT( triangles ) ); 420 // draw_btt( game->btt.left_root, &game->hm, &imm, glm::ivec2( 0, 0 ), glm::ivec2( 0, game->hm.height - 1 ), glm::ivec2( game->hm.width - 1, game->hm.height - 1 ) ); 421 // draw_btt( game->btt.right_root, &game->hm, &imm, glm::ivec2( game->hm.width - 1, game->hm.height - 1 ), glm::ivec2( game->hm.width - 1, 0 ), glm::ivec2( 0, 0 ) ); 422 423 // glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); 424 // glUseProgram( game->test_outline_shader ); 425 // glUniformMatrix4fv( game->test_outline_un_vp, 1, GL_FALSE, ( float * ) &VP ); 426 // immediate_render( &imm, game->test_outline_at_position, game->test_outline_at_colour ); 427 // glUseProgram( 0 ); 428 // glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); 429 430 // benchmark_print_timers(); 431 }