medfall

A super great game engine
Log | Files | Refs

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 }