medfall

A super great game engine
Log | Files | Refs

commit ad8b543a9928bb37631fe3a0cb7c83066001f623
parent f2a1403ddd1978a96860c1f7ce87c953166db227
Author: Michael Savage <mikejsavage@gmail.com>
Date:   Sat, 18 Nov 2017 15:21:30 +0200

Insert 1xRES filler tiles to get rid of tile overlap

Diffstat:
clipmap.cc | 179++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
linear_algebra.h | 4++++
shaders/clipmap.glsl | 9++++-----
3 files changed, 144 insertions(+), 48 deletions(-)

diff --git a/clipmap.cc b/clipmap.cc @@ -23,6 +23,8 @@ struct ClipmapTerrain { struct { Mesh tile; Mesh empty_tile; + Mesh horizontal_filler; + Mesh vertical_filler; Mesh seam; Mesh skirt; Texture heightmap; @@ -69,6 +71,7 @@ static VB trees_instance_data; static u32 num_trees; static Mesh tree_mesh; +// PATCH_RESOLUTION is the number of squares per dimension, PATCH_RESOLUTION + 1 is the number of verts per dimension static constexpr u32 PATCH_RESOLUTION = 48; static constexpr u32 NUM_LODS = 7; @@ -114,6 +117,13 @@ struct RGBA { u8 r, g, b, a; }; +template< typename T > +void swap( T & a, T & b ) { + T t = a; + a = b; + b = t; +} + GAME_INIT( game_init ) { net_init(); @@ -223,7 +233,11 @@ GAME_INIT( game_init ) { // generate empty tile mesh { - StaticArray< v3, PATCH_RESOLUTION * 4 + 1 > vertices; + MEMARENA_SCOPED_CHECKPOINT( &mem->persistent_arena ); + + array< v3 > vertices = alloc_array< v3 >( &mem->persistent_arena, PATCH_RESOLUTION * 4 + 1 ); + + // vertices[ 0 ] is the centre of the fan vertices[ 0 ] = v3( PATCH_RESOLUTION / 2.0f, PATCH_RESOLUTION / 2.0f, 0 ); for( u32 i = 0; i < PATCH_RESOLUTION; i++ ) { @@ -233,7 +247,7 @@ GAME_INIT( game_init ) { vertices[ i + 1 + 3 * PATCH_RESOLUTION ] = v3( 0, PATCH_RESOLUTION - i, 0 ); } - StaticArray< u32, PATCH_RESOLUTION * 12 > indices; + array< u32 > indices = alloc_array< u32 >( &mem->persistent_arena, PATCH_RESOLUTION * 12 ); for( u32 i = 0; i < PATCH_RESOLUTION * 4; i++ ) { indices[ i * 3 + 0 ] = 0; @@ -241,15 +255,68 @@ GAME_INIT( game_init ) { indices[ i * 3 + 2 ] = i + 2; } - indices[ indices.size() - 1 ] = 1; + // make the last triangle wrap around + indices[ indices.n - 1 ] = 1; MeshConfig mesh_config; - 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(); + mesh_config.positions = renderer_new_vb( vertices ); + mesh_config.indices = renderer_new_ib( indices ); + mesh_config.num_vertices = indices.n; clipmap.gpu.empty_tile = renderer_new_mesh( mesh_config ); } + // generate filler meshes + { + MEMARENA_SCOPED_CHECKPOINT( &mem->persistent_arena ); + + array< v3 > horizontal_vertices = alloc_array< v3 >( &mem->persistent_arena, ( PATCH_RESOLUTION + 1 ) * 2 ); + array< v3 > vertical_vertices = alloc_array< v3 >( &mem->persistent_arena, ( PATCH_RESOLUTION + 1 ) * 2 ); + + for( u32 i = 0; i < PATCH_RESOLUTION + 1; i++ ) { + horizontal_vertices[ i * 2 + 0 ] = v3( i, 0, 0 ); + horizontal_vertices[ i * 2 + 1 ] = v3( i, 1, 0 ); + + vertical_vertices[ i * 2 + 0 ] = v3( 0, i, 0 ); + vertical_vertices[ i * 2 + 1 ] = v3( 1, i, 0 ); + } + + array< u32 > horizontal_indices = alloc_array< u32 >( &mem->persistent_arena, PATCH_RESOLUTION * 6 ); + array< u32 > vertical_indices = alloc_array< u32 >( &mem->persistent_arena, PATCH_RESOLUTION * 6 ); + + for( u32 i = 0; i < PATCH_RESOLUTION; i++ ) { + u32 bl = i * 2 + 0; + u32 br = i * 2 + 1; + u32 tl = i * 2 + 2; + u32 tr = i * 2 + 3; + + horizontal_indices[ i * 6 + 0 ] = br; + horizontal_indices[ i * 6 + 1 ] = bl; + horizontal_indices[ i * 6 + 2 ] = tl; + horizontal_indices[ i * 6 + 3 ] = tl; + horizontal_indices[ i * 6 + 4 ] = tr; + horizontal_indices[ i * 6 + 5 ] = br; + } + + memcpy( vertical_indices.ptr(), horizontal_indices.ptr(), horizontal_indices.num_bytes() ); + + for( size_t i = 0; i < vertical_indices.n; i += 6 ) { + swap( vertical_indices[ i + 0 ], vertical_indices[ i + 1 ] ); + swap( vertical_indices[ i + 4 ], vertical_indices[ i + 5 ] ); + } + + MeshConfig horizontal_mesh_config; + horizontal_mesh_config.positions = renderer_new_vb( horizontal_vertices ); + horizontal_mesh_config.indices = renderer_new_ib( horizontal_indices ); + horizontal_mesh_config.num_vertices = horizontal_indices.n; + clipmap.gpu.horizontal_filler = renderer_new_mesh( horizontal_mesh_config ); + + MeshConfig vertical_mesh_config; + vertical_mesh_config.positions = renderer_new_vb( vertical_vertices ); + vertical_mesh_config.indices = renderer_new_ib( vertical_indices ); + vertical_mesh_config.num_vertices = vertical_indices.n; + clipmap.gpu.vertical_filler = renderer_new_mesh( vertical_mesh_config ); + } + // generate skirt mesh { StaticArray< v3, 8 > vertices; @@ -518,52 +585,78 @@ GAME_FRAME( game_frame ) { v2 base = v2( -float( PATCH_RESOLUTION * 2 ) ); // draw terrain - for( u32 l = 0; l < NUM_LODS; l++ ) { - 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; + { + RenderState render_state; + render_state.shader = get_shader( SHADER_CLIPMAP ); + render_state.uniforms[ UNIFORMS_VIEW ] = view_uniforms; + render_state.uniforms[ UNIFORMS_SUN ] = sun_uniforms; + render_state.textures[ 0 ] = clipmap.gpu.heightmap; + render_state.textures[ 1 ] = clipmap.gpu.normalmap; + render_state.textures[ 2 ] = clipmap.gpu.horizonmap; + render_state.textures[ 3 ] = renderer_blue_noise(); + render_state.wireframe = game->draw_wireframe; + + for( u32 l = 0; l < NUM_LODS; l++ ) { + v2 tile_size = v2( scale * float( PATCH_RESOLUTION ) ); + + // draw tiles + 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 fill = v2( x >= 2 ? 1 : 0, y >= 2 ? 1 : 0 ) * scale; + v2 tile_tl = base + v2( x, y ) * tile_size + fill + game->pos.xy(); + v2 snapped = floorf( tile_tl / scale ) * scale; + + // draw a low poly tile if the tile is entirely outside the world + 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; + + Mesh mesh = inside ? clipmap.gpu.tile : clipmap.gpu.empty_tile; + render_state.uniforms[ UNIFORMS_CLIPMAP ] = renderer_uniforms( snapped, scale ); + renderer_draw_mesh( mesh, render_state ); + } + } + + // draw fillers + for( u32 i = 0; i < 2; i++ ) { + // horizontal + { + v2 tile_tl = base + v2( i * 3, 2 ) * tile_size + v2( i * scale, 0 ) + game->pos.xy(); + v2 snapped = floorf( tile_tl / scale ) * scale; + + render_state.uniforms[ UNIFORMS_CLIPMAP ] = renderer_uniforms( snapped, scale ); + renderer_draw_mesh( clipmap.gpu.horizontal_filler, render_state ); } - 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 ); - 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 ] = clipmap.gpu.heightmap; - render_state.textures[ 1 ] = clipmap.gpu.normalmap; - render_state.textures[ 2 ] = clipmap.gpu.horizonmap; - render_state.textures[ 3 ] = renderer_blue_noise(); - render_state.wireframe = game->draw_wireframe; - - Mesh mesh = inside ? clipmap.gpu.tile : clipmap.gpu.empty_tile; - renderer_draw_mesh( mesh, render_state ); + // vertical + { + v2 tile_tl = base + v2( 2, i * 3 ) * tile_size + v2( 0, i * scale ) + game->pos.xy(); + v2 snapped = floorf( tile_tl / scale ) * scale; + + render_state.uniforms[ UNIFORMS_CLIPMAP ] = renderer_uniforms( snapped, scale ); + renderer_draw_mesh( clipmap.gpu.vertical_filler, render_state ); + } } - } - clipmap_tl = base + game->pos.xy(); - scale *= 2.0f; - base *= 2.0f; + + clipmap_tl = base + game->pos.xy(); + scale *= 2.0f; + base *= 2.0f; + } } scale /= 2.0f; clipmap_tl.x = floorf( clipmap_tl.x / scale ) * scale; clipmap_tl.y = floorf( clipmap_tl.y / scale ) * scale; - v2 clipmap_br = clipmap_tl + v2( 4 * scale * float( PATCH_RESOLUTION ) ); + v2 clipmap_br = clipmap_tl + v2( ( 4.0f * PATCH_RESOLUTION + 1.0f ) * scale ); // draw skirts { diff --git a/linear_algebra.h b/linear_algebra.h @@ -344,6 +344,10 @@ forceinline v2 normalize( v2 v ) { return v / length( v ); } +forceinline v2 floorf( v2 v ) { + return v2( floorf( v.x ), floorf( v.y ) ); +} + /* * v2s32 */ diff --git a/shaders/clipmap.glsl b/shaders/clipmap.glsl @@ -31,13 +31,12 @@ in vec3 position; out VSOut v2f; void main() { - vec2 xy = offset + position.xy * scale + camera_pos.xy; - vec2 snapped_xy = floor( xy / scale ) * scale; - vec2 uv = ( snapped_xy + 0.5 ) / textureSize( heightmap, 0 ); - vec2 height_sample = texelFetch( heightmap, ivec2( snapped_xy ), 0 ).rg; + vec2 xy = offset + position.xy * scale; + vec2 uv = ( xy + 0.5 ) / textureSize( heightmap, 0 ); + vec2 height_sample = texelFetch( heightmap, ivec2( xy ), 0 ).rg; float z = 256.0 * height_sample.r + height_sample.g; - v2f.view_position = V * vec4( snapped_xy, z, 1.0 ); + v2f.view_position = V * vec4( xy, z, 1.0 ); v2f.world_position = vec3( xy, z ); v2f.uv = uv; gl_Position = P * v2f.view_position;