medfall

A super great game engine
Log | Files | Refs

commit f1df84a73dfd033ca9deda09f262ddac0232560d
parent 0f693ea29f192245376b54b2b50c920856d1aa01
Author: Michael Savage <mikejsavage@gmail.com>
Date:   Thu Sep 14 23:39:05 +0300

Basic clipmap implementation

Diffstat:
.gitignore | 1+
clipmap.cc | 161+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
linear_algebra.h | 4++++
make.lua | 3+++
renderer.cc | 4++--
renderer.h | 1+
shaders.cc | 3+++
shaders.h | 1+
shaders/clipmap.glsl | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 227 insertions(+), 2 deletions(-)
diff --git a/.gitignore b/.gitignore @@ -12,6 +12,7 @@ launch pp bsp sm +clipmap sound srv test_lockfree diff --git a/clipmap.cc b/clipmap.cc @@ -0,0 +1,161 @@ +#include "intrinsics.h" +#include "game.h" +#include "renderer.h" +#include "shaders.h" +#include "text_renderer.h" +#include "gl.h" +#include "skybox.h" + +#include "libs/lodepng/lodepng.h" + +static Mesh patch; +static Texture heightmap; + +static u32 PATCH_RESOLUTION = 64; +static u32 NUM_LODS = 6; + +static size_t patch2d( size_t x, size_t y ) { + return y * ( PATCH_RESOLUTION + 1 ) + x; +} + +static u16 bswap16( u16 x ) { + return ( x >> 8 ) | ( x << 8 ); +} + +static void bswap16s( u16 * xs, size_t n ) { + for( size_t i = 0; i < n; i++ ) { + xs[ i ] = bswap16( xs[ i ] ); + } +} + +GAME_INIT( game_init ) { + { + u32 w, h; + u16 * heightmap_memory; + u32 ok = lodepng_decode_file( ( u8 ** ) &heightmap_memory, &w, &h, "terrains/gta16.png", LCT_GREY, 16 ); + ASSERT( ok == 0 ); + bswap16s( heightmap_memory, w * h ); + + TextureConfig texture_config; + texture_config.width = w; + texture_config.height = h; + texture_config.data = heightmap_memory; + texture_config.format = TEXFMT_R_U16; + texture_config.wrap = TEXWRAP_BORDER; + texture_config.border_colour = v4( 0 ); + texture_config.srgb = false; + heightmap = renderer_new_texture( texture_config ); + } + + { + DynamicArray< v3 > vertices; + for( u32 y = 0; y < PATCH_RESOLUTION + 1; y++ ) { + for( u32 x = 0; x < PATCH_RESOLUTION + 1; x++ ) { + vertices.append( v3( x, y, 0 ) ); + } + } + + DynamicArray< u32 > indices; + for( u32 y = 0; y < PATCH_RESOLUTION; y++ ) { + for( u32 x = 0; x < PATCH_RESOLUTION; x++ ) { + indices.append( patch2d( x, y ) ); + indices.append( patch2d( x, y + 1 ) ); + indices.append( patch2d( x + 1, y + 1 ) ); + + indices.append( patch2d( x, y ) ); + indices.append( patch2d( x + 1, y + 1 ) ); + indices.append( patch2d( x + 1, y ) ); + } + } + + 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(); + patch = renderer_new_mesh( mesh_config ); + } + + game->pos = v3( 0, 0, 100 ); + game->pitch = 0; + game->yaw = 45; + game->sun_angle = 0.3f; + + skybox_init( &game->skybox ); +} + +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 ] ); + float dz = float( input->keys[ KEY_SPACE ] - input->keys[ KEY_LEFTSHIFT ] ); + + float dpitch = input->keys[ KEY_DOWNARROW ] - input->keys[ KEY_UPARROW ]; + float dyaw = input->keys[ KEY_LEFTARROW ] - input->keys[ KEY_RIGHTARROW ]; + + dpitch = input->keys[ KEY_K ] - input->keys[ KEY_I ]; + dyaw += input->keys[ KEY_J ] - input->keys[ KEY_L ]; + + dpitch -= float( input->mouse_dy * 0.25 ); + dyaw -= float( input->mouse_dx * 0.25 ); + + game->pitch = clamp( game->pitch + dpitch * dt * 100, -89.9f, 89.9f ); + game->yaw += dyaw * dt * 100; + + const v3 world_up = v3( 0, 0, 1 ); + v3 forward = v3_forward( game->pitch, game->yaw ); + v3 right = normalize( cross( forward, world_up ) ); + v3 up = normalize( cross( right, forward ) ); + + const float speed = 100.0f; + game->pos += forward * dt * fb * speed; + game->pos += right * dt * lr * speed; + game->pos.z += dt * dz * speed; + + m4 P = m4_perspective( VERTICAL_FOV, get_aspect_ratio(), NEAR_PLANE_DEPTH, FAR_PLANE_DEPTH ); + m4 V = m4_view( forward, right, up, game->pos ); + m4 Vsky = m4_view( forward, right, up, v3( 0 ) ); + v3 sun_dir = v3( -cosf( game->sun_angle ), 0, sinf( game->sun_angle ) ); + + renderer_begin_frame(); + renderer_begin_pass( RENDERER_CLEAR_COLOUR_DO, RENDERER_CLEAR_DEPTH_DO ); + + skybox_render( &game->skybox, Vsky, P, game->sun_angle, sun_dir ); + + UniformBinding view_uniforms = renderer_uniforms( V, P, game->pos ); + + for( int l = 0; l < NUM_LODS; l++ ) { + float scale = 1.0f; + v2 base = v2( -float( PATCH_RESOLUTION * 2 ), -float( PATCH_RESOLUTION * 2 ) ); + for( int i = 0; i < l; i++ ) { + scale *= 2.0f; + base *= 2.0f; + } + + for( int x = 0; x < 4; x++ ) { + for( int y = 0; y < 4; y++ ) { + 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 ) ); + + RenderState render_state; + render_state.shader = get_shader( SHADER_CLIPMAP ); + render_state.uniforms[ UNIFORMS_VIEW ] = view_uniforms; + render_state.uniforms[ UNIFORMS_CLIPMAP ] = renderer_uniforms( offset, scale ); + render_state.textures[ 0 ] = heightmap; + render_state.wireframe = true; + renderer_draw_mesh( patch, render_state ); + } + } + } + + { + const str< 128 > status( "Frame time: {.1}ms FPS: {.1}", dt * 1000.0f, 1.0f / dt ); + 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 ); + } + + renderer_end_pass(); + renderer_end_frame(); +} diff --git a/linear_algebra.h b/linear_algebra.h @@ -311,6 +311,10 @@ forceinline v2 operator*( v2 v, float scale ) { return v2( v.x * scale, v.y * scale ); } +forceinline void operator*=( v2 & v, float scale ) { + v = v * scale; +} + forceinline v2 operator*( float scale, v2 v ) { return v * scale; } diff --git a/make.lua b/make.lua @@ -46,6 +46,7 @@ bin( "bsp", { "main", "bsp", "bsp_renderer", game_objs }, { game_libs } ) -- TODO: fix btt build -- bin( "btt", { "main", "mod_btt", "btt", "heightmap", "skybox", "lz4", game_objs }, { "stb_image", game_libs } ) bin( "sm", { "main", "shadow_map", game_objs }, { game_libs } ) +bin( "clipmap", { "main", "clipmap", "skybox", game_objs }, { game_libs } ) msvc_bin_ldflags( "bsp", "opengl32.lib gdi32.lib" ) gcc_bin_ldflags( "bsp", game_ldflags ) @@ -53,6 +54,8 @@ msvc_bin_ldflags( "btt", "opengl32.lib gdi32.lib" ) gcc_bin_ldflags( "btt", game_ldflags ) msvc_bin_ldflags( "sm", "opengl32.lib gdi32.lib" ) gcc_bin_ldflags( "sm", game_ldflags ) +msvc_bin_ldflags( "clipmap", "opengl32.lib gdi32.lib" ) +gcc_bin_ldflags( "clipmap", game_ldflags ) bin( "sound", { "audio", "mixer", "wave", "platform_audio_output", common_objs } ) msvc_bin_ldflags( "sound", "ole32.lib" ) diff --git a/renderer.cc b/renderer.cc @@ -24,7 +24,7 @@ static const GLuint ATTR_TEX_COORD1 = 3; static const GLuint ATTR_COLOUR = 4; static const GLuint ATTR_MODEL_TO_WORLD_COL0 = 5; -static const u32 UNIFORM_BUFFER_SIZE = kilobytes( 16 ); +static const u32 UNIFORM_BUFFER_SIZE = kilobytes( 32 ); struct DrawCall { RenderState render_state; @@ -568,7 +568,7 @@ Shader renderer_new_shader( ShaderConfig config ) { return INVALID_SHADER; } - const char * ubo_names[] = { "view", "model", "light_view", "window", "sun", "sky" }; + const char * ubo_names[] = { "view", "model", "light_view", "window", "sun", "sky", "clipmap" }; 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 @@ -21,6 +21,7 @@ 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; #define RENDERER_MAX_TEXTURES 4 #define RENDERER_MAX_TEXTURE_BUFFERS 4 diff --git a/shaders.cc b/shaders.cc @@ -69,6 +69,9 @@ void shaders_init() { shaders[ SHADER_WIREFRAME ].path = "shaders/wireframe.glsl"; + shaders[ SHADER_CLIPMAP ].path = "shaders/clipmap.glsl"; + shaders[ SHADER_CLIPMAP ].texture_uniform_names[ 0 ] = "heightmap"; + int failed = hotload_shaders(); if( failed != 0 ) { FATAL( "failed to load shaders" ); diff --git a/shaders.h b/shaders.h @@ -14,6 +14,7 @@ enum ShaderID { SHADER_DEBUG_RENDER_SHADOW_MAP, SHADER_SHADOWED_VERTEX_COLOURS, SHADER_WIREFRAME, + SHADER_CLIPMAP, SHADER_COUNT, }; diff --git a/shaders/clipmap.glsl b/shaders/clipmap.glsl @@ -0,0 +1,51 @@ +layout( std140 ) uniform view { + mat4 V; + mat4 P; + vec3 camera_pos; +}; + +layout( std140 ) uniform clipmap { + vec2 offset; + float scale; +}; + +struct VSOut { + vec3 world_pos; + vec3 world_normal; +}; + +uniform sampler2D heightmap; + +#ifdef VERTEX_SHADER + +in vec3 position; +out VSOut v2f; + +void main() { + vec2 xy = offset + position.xy * scale + camera_pos.xy; + vec2 uv = floor( xy ) / textureSize( heightmap, 0 ) + 0.5; + float z = 255.0 * texture2D( heightmap, uv ).r; + + vec3 normal = normalize( cross( + vec3( 2.0, 0.0, ( 255.0 * texture2D( heightmap, ( floor( xy ) + vec2( 1.0, 0.0 ) ) / textureSize( heightmap, 0 ) + 0.5 ).r + - texture2D( heightmap, ( floor( xy ) - vec2( 1.0, 0.0 ) ) / textureSize( heightmap, 0 ) + 0.5 ).r ) ), + vec3( 0.0, 2.0, ( 255.0 * texture2D( heightmap, ( floor( xy ) + vec2( 0.0, 1.0 ) ) / textureSize( heightmap, 0 ) + 0.5 ).r + - texture2D( heightmap, ( floor( xy ) - vec2( 0.0, 1.0 ) ) / textureSize( heightmap, 0 ) + 0.5 ).r ) ) + ) ); + + v2f.world_pos = vec3( xy, z ); + v2f.world_normal = normal; + gl_Position = P * V * vec4( floor( xy ), z, 1.0 ); +} + +#else + +in VSOut v2f; +out vec4 screen_colour; + +void main() { + vec4 colour = vec4( 0.3, 0.3, 1.0, 1.0 ); + screen_colour = colour * v2f.world_normal.z; +} + +#endif