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:
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;