medfall

A super great game engine
Log | Files | Refs

commit fbc90f2a75ca30a3a6aea4edd78f0ec9e6a46584
parent 5db3a20a4396d2dd2cefcc55f20c799d4363778d
Author: Michael Savage <mikejsavage@gmail.com>
Date:   Sat, 11 Nov 2017 23:50:14 +0200

Only draw two triangles when the tile doesn't intersect the world, and draw skirt geometry

Diffstat:
clipmap.cc | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
linear_algebra.h | 4++++
renderer.cc | 5+++--
renderer.h | 22+++++++++++++---------
4 files changed, 119 insertions(+), 15 deletions(-)

diff --git a/clipmap.cc b/clipmap.cc @@ -22,7 +22,9 @@ struct ClipmapTerrain { struct { Mesh tile; + Mesh empty_tile; Mesh seam; + Mesh skirt; Texture heightmap; Texture normalmap; Texture horizonmap; @@ -219,6 +221,51 @@ GAME_INIT( game_init ) { clipmap.gpu.tile = renderer_new_mesh( mesh_config ); } + // generate empty tile mesh + { + StaticArray< v3, 4 > vertices; + vertices[ 0 ] = v3( 0, 0, 0 ); + vertices[ 1 ] = v3( PATCH_RESOLUTION, 0, 0 ); + vertices[ 2 ] = v3( 0, PATCH_RESOLUTION, 0 ); + vertices[ 3 ] = v3( PATCH_RESOLUTION, PATCH_RESOLUTION, 0 ); + + u32 indices[] = { 0, 1, 2, 2, 1, 3 }; + + MeshConfig mesh_config; + mesh_config.positions = renderer_new_vb( vertices.ptr(), vertices.num_bytes() ); + mesh_config.indices = renderer_new_ib( indices, sizeof( indices ) ); + mesh_config.num_vertices = ARRAY_COUNT( indices ); + clipmap.gpu.empty_tile = renderer_new_mesh( mesh_config ); + } + + // generate skirt mesh + { + StaticArray< v3, 8 > vertices; + float big = 10000000.0; + vertices[ 0 ] = v3( -1, -1, 0 ) * big; + vertices[ 1 ] = v3( +1, -1, 0 ) * big; + vertices[ 2 ] = v3( -1, +1, 0 ) * big; + vertices[ 3 ] = v3( +1, +1, 0 ) * big; + // these get moved to the edge of the terrain in the vertex shader + vertices[ 4 ] = v3( 0, 0, 0 ); + vertices[ 5 ] = v3( 0, 0, 0 ); + vertices[ 6 ] = v3( 0, 0, 0 ); + vertices[ 7 ] = v3( 0, 0, 0 ); + + u32 indices[] = { + 0, 1, 4, 4, 1, 5, + 1, 3, 5, 5, 3, 7, + 3, 2, 7, 7, 2, 6, + 4, 6, 0, 0, 6, 2, + }; + + MeshConfig mesh_config; + mesh_config.positions = renderer_new_vb( vertices.ptr(), vertices.num_bytes() ); + mesh_config.indices = renderer_new_ib( indices, sizeof( indices ) ); + mesh_config.num_vertices = ARRAY_COUNT( indices ); + clipmap.gpu.skirt = renderer_new_mesh( mesh_config ); + } + game->pos = v3( 2056, 2056, 70 ); game->pitch = 0; game->yaw = 250; @@ -251,6 +298,12 @@ static void draw_qt( MinMaxu32 aabb, const array< QuadTreeNode > nodes, size_t n } } +static bool intervals_overlap( float a0, float a1, float b0, float b1 ) { + ASSERT( a0 <= a1 ); + ASSERT( b0 <= b1 ); + return a0 <= b1 && b0 <= a1; +} + GAME_FRAME( game_frame ) { float fb = float( input->keys[ KEY_W ] - input->keys[ KEY_S ] ); float lr = float( input->keys[ KEY_D ] - input->keys[ KEY_A ] ); @@ -448,22 +501,37 @@ GAME_FRAME( game_frame ) { UniformBinding inf_view_uniforms = renderer_uniforms( V, Pinf, game->pos ); UniformBinding sun_uniforms = renderer_uniforms( sun_dir, game->sun_angle ); + v2 clipmap_tl; + v2 clipmap_br; + float max_scale; + // draw terrain for( u32 l = 0; l < NUM_LODS; l++ ) { float scale = 1.0f; - v2 base = v2( -float( PATCH_RESOLUTION * 2 ), -float( PATCH_RESOLUTION * 2 ) ); + v2 base = v2( -float( PATCH_RESOLUTION * 2 ) ); for( u32 i = 0; i < l; i++ ) { scale *= 2.0f; base *= 2.0f; } + v2 tile_size = v2( scale * float( PATCH_RESOLUTION ) ); for( int x = 0; x < 4; x++ ) { for( int y = 0; y < 4; y++ ) { + // draw a 4x4 set of tiles. cut out the middle 2x2 unless we're at the coarsest level if( l != 0 && ( x == 1 || x == 2 ) && ( y == 1 || y == 2 ) ) { continue; } - v2 offset = base + v2( x * scale * float( PATCH_RESOLUTION ), y * scale * float( PATCH_RESOLUTION ) ); + v2 offset = base + v2( x, y ) * tile_size; + + // draw a 2 poly tile if the tile is entirely outside the world + v2 tile_tl = offset + game->pos.xy(); + v2 tile_br = tile_tl + tile_size; + bool inside = true; + if( !intervals_overlap( tile_tl.x, tile_br.x, 0, clipmap.heightmap.w ) ) + inside = false; + if( !intervals_overlap( tile_tl.y, tile_br.y, 0, clipmap.heightmap.h ) ) + inside = false; RenderState render_state; render_state.shader = get_shader( SHADER_CLIPMAP ); @@ -475,9 +543,36 @@ GAME_FRAME( game_frame ) { render_state.textures[ 2 ] = clipmap.gpu.horizonmap; render_state.textures[ 3 ] = renderer_blue_noise(); render_state.wireframe = game->draw_wireframe; - renderer_draw_mesh( clipmap.gpu.tile, render_state ); + + Mesh mesh = inside ? clipmap.gpu.tile : clipmap.gpu.empty_tile; + renderer_draw_mesh( mesh, render_state ); } } + + clipmap_tl = base + game->pos.xy(); + max_scale = scale; + } + + clipmap_tl.x = floorf( clipmap_tl.x / max_scale ) * max_scale; + clipmap_tl.y = floorf( clipmap_tl.y / max_scale ) * max_scale; + clipmap_br = clipmap_tl + v2( 4 * max_scale * float( PATCH_RESOLUTION ) ); + + // draw skirts + { + // need to be v4 because bounds is an array! + v4 bounds0 = v4( clipmap_tl.x, clipmap_tl.y, 0.0, 0.0 ); + v4 bounds1 = v4( clipmap_br.x, clipmap_tl.y, 0.0, 0.0 ); + v4 bounds2 = v4( clipmap_tl.x, clipmap_br.y, 0.0, 0.0 ); + v4 bounds3 = v4( clipmap_br.x, clipmap_br.y, 0.0, 0.0 ); + + RenderState render_state; + render_state.shader = get_shader( SHADER_CLIPMAP_SKIRT ); + render_state.uniforms[ UNIFORMS_VIEW ] = inf_view_uniforms; + render_state.uniforms[ UNIFORMS_SUN ] = sun_uniforms; + render_state.uniforms[ UNIFORMS_CLIPMAP_SKIRT ] = renderer_uniforms( bounds0, bounds1, bounds2, bounds3 ); + render_state.textures[ 0 ] = renderer_blue_noise(); + render_state.wireframe = game->draw_wireframe; + renderer_draw_mesh( clipmap.gpu.skirt, render_state ); } // draw trees @@ -707,7 +802,7 @@ GAME_FRAME( game_frame ) { connected ? "connected!" : "connecting" ); draw_text( status.c_str(), 2, 2, 16.0f ); draw_text( str< 128 >( "pos = {.1}", game->pos ).c_str(), 2, 20, 16 ); - draw_text( str< 128 >( "drawcalls = {}, verts = {}", renderer_num_draw_calls(), renderer_num_vertices() ).c_str(), 2, 38, 16 ); + draw_text( str< 128 >( "drawcalls = {}, tris = {}", renderer_num_draw_calls(), renderer_num_vertices() / 3 ).c_str(), 2, 38, 16 ); } renderer_end_pass(); diff --git a/linear_algebra.h b/linear_algebra.h @@ -319,6 +319,10 @@ forceinline v2 operator*( float scale, v2 v ) { return v * scale; } +forceinline v2 operator*( v2 lhs, v2 rhs ) { + return v2( lhs.x * rhs.x, lhs.y * rhs.y ); +} + forceinline v2 operator/( v2 v, float scale ) { float inv_scale = 1.0f / scale; return v * inv_scale; diff --git a/renderer.cc b/renderer.cc @@ -157,7 +157,7 @@ static void set_render_state( const RenderState & state ) { } // uniforms - for( GLuint i = 0; i < RENDERER_MAX_UNIFORMS; i++ ) { + for( GLuint i = 0; i < UNIFORMS_COUNT; i++ ) { if( state.uniforms[ i ] != previous_render_state.uniforms[ i ] ) { UniformBinding binding = state.uniforms[ i ]; if( binding.size == 0 ) { @@ -708,7 +708,8 @@ Shader renderer_new_shader( ShaderConfig config ) { return INVALID_SHADER; } - const char * ubo_names[] = { "view", "model", "light_view", "window", "sun", "sky", "clipmap" }; + const char * ubo_names[] = { "view", "model", "light_view", "window", "sun", "sky", "clipmap", "clipmap_skirt" }; + STATIC_ASSERT( ARRAY_COUNT( ubo_names ) == UNIFORMS_COUNT ); for( GLuint i = 0; i < ARRAY_COUNT( ubo_names ); i++ ) { GLuint idx = glGetUniformBlockIndex( program, ubo_names[ i ] ); if( idx != GL_INVALID_INDEX ) { diff --git a/renderer.h b/renderer.h @@ -15,17 +15,21 @@ typedef u32 Texture; typedef u32 TextureBufferObject; typedef u32 FramebufferObject; -const u32 UNIFORMS_VIEW = 0; -const u32 UNIFORMS_MODEL = 1; -const u32 UNIFORMS_LIGHT_VIEW = 2; -const u32 UNIFORMS_WINDOW = 3; -const u32 UNIFORMS_SUN = 4; -const u32 UNIFORMS_SKY = 5; -const u32 UNIFORMS_CLIPMAP = 6; +enum UniformSlots { + UNIFORMS_VIEW, + UNIFORMS_MODEL, + UNIFORMS_LIGHT_VIEW, + UNIFORMS_WINDOW, + UNIFORMS_SUN, + UNIFORMS_SKY, + UNIFORMS_CLIPMAP, + UNIFORMS_CLIPMAP_SKIRT, + + UNIFORMS_COUNT, +}; #define RENDERER_MAX_TEXTURES 4 #define RENDERER_MAX_TEXTURE_BUFFERS 4 -#define RENDERER_MAX_UNIFORMS 8 const u32 INVALID_SHADER = 0; @@ -115,7 +119,7 @@ struct UniformBinding { }; struct RenderState { - StaticArray< UniformBinding, RENDERER_MAX_UNIFORMS > uniforms = { }; + StaticArray< UniformBinding, UNIFORMS_COUNT > uniforms = { }; StaticArray< Texture, RENDERER_MAX_TEXTURES > textures = { }; StaticArray< TB, RENDERER_MAX_TEXTURE_BUFFERS > tbs = { }; Shader shader = INVALID_SHADER;