From: hgn Date: Sat, 28 May 2022 16:15:23 +0000 (+0100) Subject: init X-Git-Url: https://skaterift.com/git/?a=commitdiff_plain;h=3f59dc191c7792947cd2d3f6588be06933fd49f0;p=carveJwlIkooP6JGAAIwe30JlM.git init --- 3f59dc191c7792947cd2d3f6588be06933fd49f0 diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..c400164 --- /dev/null +++ b/.gitignore @@ -0,0 +1,52 @@ +# Gitignore for MMV project. +# Whitelist mode + +# Ignore all but directories +* +!*/ + +build.linux/ +build.win32/ +restricted/ +.temp_textures/ + +# ALLOW ============================ +!.gitattributes +!.gitignore +!.gitmodules + +# Code sources _____________________ +# C source files +!*.c +!*.h + +# Blender projects +!*.blend + +# GLSL shader source files +!*.fs +!*.vs +!*.gls + +# Python source files +!*.py + +# Build scripts +!*.sh +!*.bat +!*.conf + +# Compiled resources _______________ +# MMV proprietary files +!*.vmd +!*.vma +!*.cfg +!*.vmv +!*.map + +# Other game assets (3rd party) +!*.bdf +!*.png +!*.ogg +!*.txt +!*.tga diff --git a/blender_export.py b/blender_export.py new file mode 100644 index 0000000..05059c2 --- /dev/null +++ b/blender_export.py @@ -0,0 +1,141 @@ +import bpy, math +from ctypes import * + +class model(Structure): + _pack_ = 1 + _fields_ = [("identifier",c_uint32), + ("vertex_count",c_uint32), + ("indice_count",c_uint32), + ("layer_count",c_uint32)] + +class sdf_primative(Structure): + _pack_ = 1 + _fields_ = [("origin",c_float*4), + ("info",c_float*4)] + +class submodel(Structure): + _pack_ = 1 + _fields_ = [("indice_start",c_uint32), + ("indice_count",c_uint32), + ("vertex_start",c_uint32), + ("vertex_count",c_uint32), + ("bbx",(c_float*3)*2), + ("sdf",sdf_primative), + ("sdf_type",c_int32), + ("name",c_char*32)] + +class model_vert(Structure): + _pack_ = 1 + _fields_ = [("co",c_float*3), + ("norm",c_float*3), + ("colour",c_float*4), + ("uv",c_float*2)] + +def fixed_string(dest,string): + return + for i in range(len(string)): + dest[i] = string[i] + +def write_model(name): + fp = open(F"/home/harry/Documents/carve/models/{name}.mdl", "wb") + collection = bpy.data.collections[name] + + header = model() + header.identifier = 0xABCD0000 + header.vertex_count = 0 + header.indice_count = 0 + header.layer_count = 0 + + layers = [] + vertex_buffer = [] + indice_buffer = [] + + for obj in collection.objects: + if obj.type == 'MESH': + dgraph = bpy.context.evaluated_depsgraph_get() + data = obj.evaluated_get(dgraph).data + data.calc_loop_triangles() + data.calc_normals_split() + + sm = submodel() + sm.indice_start = header.indice_count + sm.vertex_start = header.vertex_count + sm.vertex_count = len(data.vertices) + sm.indice_count = len(data.loop_triangles)*3 + sm.sdf_type = 0 + for i in range(3): + sm.bbx[0][i] = 999999 + sm.bbx[1][i] = -999999 + + if F"{obj.name}.sdf_cone" in bpy.data.objects: + cone = bpy.data.objects[F"{obj.name}.sdf_cone"] + sm.sdf.origin[0] = cone.location[0] + sm.sdf.origin[1] = cone.location[2] + cone.scale[1]*2.0 + sm.sdf.origin[2] = -cone.location[1] + sm.sdf.origin[3] = 0.0 + + lo = cone.scale[0] + la = cone.scale[1]*2.0 + lh = math.sqrt(lo*lo+la*la) + + sm.sdf.info[0] = lo + sm.sdf.info[1] = la + sm.sdf.info[2] = lo/lh + sm.sdf.info[3] = la/lh + + sm.sdf_type = 1 + + sm.name = obj.name.encode('utf-8') + + for vert in data.vertices: + v = model_vert() + v.co[0] = vert.co[0] + v.co[1] = vert.co[2] + v.co[2] = -vert.co[1] + v.colour[0] = 0.0 + v.colour[1] = 0.0 + v.colour[2] = 0.0 + v.colour[3] = 1.0 + vertex_buffer += [v] + + for i in range(3): + sm.bbx[0][i] = min( sm.bbx[0][i], v.co[i] ) + sm.bbx[1][i] = max( sm.bbx[1][i], v.co[i] ) + + for l in data.loops: + pvert = vertex_buffer[l.vertex_index + sm.vertex_start] + norm = l.normal + pvert.norm[0] = norm[0] + pvert.norm[1] = norm[2] + pvert.norm[2] = -norm[1] + + #if data.vertex_colors: + # colour = data.vertex_colors.active.data[ l.index ].color + # pvert.colour[0] = colour[0] + + if data.uv_layers: + uv = data.uv_layers.active.data[ l.index ].uv + pvert.uv[0] = uv[0] + pvert.uv[1] = uv[1] + + for tri in data.loop_triangles: + indice_buffer += [c_uint32(tri.vertices[_]) for _ in range(3)] + + layers += [sm] + header.layer_count += 1 + header.vertex_count += sm.vertex_count + header.indice_count += sm.indice_count + + fp.write( bytearray( header ) ) + for l in layers: + fp.write( bytearray(l) ) + for v in vertex_buffer: + fp.write( bytearray(v) ) + for i in indice_buffer: + fp.write( bytearray(i) ) + + fp.close() + +write_model( "test" ) +write_model( "free_dev" ) +write_model( "char_dev" ) diff --git a/main.c b/main.c new file mode 100644 index 0000000..b397e2e --- /dev/null +++ b/main.c @@ -0,0 +1,684 @@ +#define VG_3D +#include "vg/vg.h" + +/* Resources */ +vg_tex2d tex_norwey = { .path = "textures/norway_foliage.qoi" }; +vg_tex2d tex_grid = { .path = "textures/grid.qoi" }; +vg_tex2d tex_road = { .path = "textures/road.qoi" }; +vg_tex2d tex_gradients = { .path = "textures/gradients.qoi", + .flags = VG_TEXTURE_CLAMP }; +vg_tex2d *texture_list[] = +{ + &tex_norwey, + &tex_gradients, + &tex_grid, + &tex_road +}; + +/* Convars */ +static int freecam = 0; +static int debugview = 0; +static int debugsdf = 0; +static int debugroad = 0; + +/* Components */ +#include "road.h" +#include "scene.h" + +int main( int argc, char *argv[] ) +{ + vg_init( argc, argv, "Voyager Game Engine" ); +} + +m4x3f world_matrix; +v3f player_pos; +v3f player_head; /* Relative to pos */ +v3f player_vel = { 0.0f, 0.0f, -0.2f }; +float player_yaw; +v2f look_dir; +v3f player_accel; + +float waves[128][128]; + +road_patch road_main; +scene test_scene; +scene world_scene; +scene player_scene; +u32 world_terrain_count, + world_road_count; + +static int reset_player( int argc, char const *argv[] ) +{ + v3_zero( player_pos ); + v3_copy( (v3f){ 0.0f, 0.0f, -0.2f }, player_vel ); + player_yaw = 0.0f; + + return 0; +} + +void vg_register(void) +{ + scene_register(); +} + +void vg_start(void) +{ + for( int y=0; y<128; y++ ) + { + for( int x=0; x<128; x++ ) + { + v2f pos = {x,y}; + + waves[x][y] = sinf( pos[0] * 0.1f ) * + cosf( pos[1] * 0.3f ) * 5.0f + x*-0.2f; + } + } + + vg_tex2d_init( texture_list, vg_list_size( texture_list ) ); + + road_patch_init( &road_main ); + road_generate( &road_main ); + + vg_convar_push( (struct vg_convar){ + .name = "freecam", + .data = &freecam, + .data_type = k_convar_dtype_i32, + .opt_i32 = { .min=0, .max=1, .clamp=1 }, + .persistent = 1 + }); + + vg_convar_push( (struct vg_convar){ + .name = "debugview", + .data = &debugview, + .data_type = k_convar_dtype_i32, + .opt_i32 = { .min=0, .max=1, .clamp=0 }, + .persistent = 1 + }); + + vg_convar_push( (struct vg_convar){ + .name = "debugsdf", + .data = &debugsdf, + .data_type = k_convar_dtype_i32, + .opt_i32 = { .min=0, .max=1, .clamp=1 }, + .persistent = 1 + }); + + vg_convar_push( (struct vg_convar){ + .name = "debugroad", + .data = &debugroad, + .data_type = k_convar_dtype_i32, + .opt_i32 = { .min=0, .max=1, .clamp=1 }, + .persistent = 1 + }); + + vg_function_push( (struct vg_cmd){ + .name = "reset", + .function = reset_player + }); + + v3f lightDir = { 0.1f, 0.8f, 0.2f }; + v3_normalize( lightDir ); + + scene_init( &world_scene ); + scene_init( &test_scene ); + scene_init( &player_scene ); + model *world = vg_asset_read( "models/free_dev.mdl" ); + model *test = vg_asset_read( "models/test.mdl" ); + model *char_dev = vg_asset_read( "models/char_dev.mdl" ); + scene_add_model( &player_scene, char_dev, + submodel_get( char_dev, "joint" ), + (v3f){0.0f,0.0f,0.0f}, 0.0f, 1.0f ); + free(char_dev); + + scene_add_model( &world_scene, world, + submodel_get( world, "terrain" ), + (v3f){0.0f,0.0f,0.0f}, 0.0f, 1.0f ); + + int id_tree = submodel_get( test, "tree" ), + id_groundcover[] = + { + submodel_get( test, "bush" ), + submodel_get( test, "grass" ), + submodel_get( test, "blubber" ), + submodel_get( test, "blubber" ), + submodel_get( test, "blubber" ), + submodel_get( test, "grassthin" ) + }; + + /* Sprinkle some trees in the terrain areas */ + v3f range; + v3_sub( world_scene.bbx[1], world_scene.bbx[0], range ); + + for( int i=0; i<8000; i++ ) + { + v3f pos; + + pos[0] = vg_randf(); + pos[1] = 0.0f; + pos[2] = vg_randf(); + v3_muladd( world_scene.bbx[0], pos, range, pos ); + + if( sample_scene_height( &world_scene, pos ) ) + { + scene_add_model( &test_scene, test, + id_tree, + pos, vg_randf() * VG_TAUf, vg_randf() * 0.5f + 0.5f ); + } + } + + world_terrain_count = world_scene.indice_count; + + scene_add_model( &world_scene, world, + submodel_get( world, "road" ), + (v3f){0.0f,0.0f,0.0f}, 0.0f, 1.0f ); + world_road_count = world_scene.indice_count-world_terrain_count; + + free( test ); + free( world ); + +#if 0 + scene_compute_occlusion( &test_scene ); + scene_shadow_gradient( &test_scene, 1, 0.0f, 1.0f ); + scene_shadow_sphere( &test_scene, (v3f){ 0.0f,4.0f,0.0f}, + (v4f){1.0f, 2.0f, 0.0f, 0.0f}, lightDir ); +#endif + + scene_upload( &player_scene ); + scene_upload( &world_scene ); + scene_upload( &test_scene ); +} + +static float sample_terrain( v3f pos ) +{ + v3f local; + v3_muls( pos, 0.1f, local ); + + v2i ico = { local[0], local[2] }; + for( int i=0; i<2; i++ ) + { + if( ico[i] < 0 ) ico[i] = 0; + if( ico[i] > 126 ) ico[i] = 126; + } + + float hse = waves[ico[0]+1][ico[1]], + hsw = waves[ico[0]][ico[1]], + hne = waves[ico[0]+1][ico[1]+1], + hnw = waves[ico[0]][ico[1]+1]; + + v3f subcoord; + v3_floor( local, subcoord ); + v3_sub( local, subcoord, subcoord ); + + float hs = vg_lerpf( hse, hsw, 1.0f-subcoord[0] ), + hn = vg_lerpf( hne, hnw, 1.0f-subcoord[0] ), + sampleh = vg_lerpf( hs, hn, subcoord[2] ); + + return sampleh*10.0f; +} + +static void draw_terrain(void) +{ + float sf = 10.0f; + + /* Draw waves + */ + for( int y=0; y<63; y++ ) + { + for( int x=0; x<63; x++ ) + { + v2i pos = {x*2,y*2}; + vg_line( (v3f){ pos[0]*sf, waves[pos[0]][pos[1]]*sf, pos[1]*sf }, + (v3f){ (pos[0]+2)*sf, waves[pos[0]+2][pos[1]]*sf, pos[1]*sf }, + 0xa0ffffff ); + + vg_line( (v3f){ pos[1]*sf, waves[pos[1]][pos[0]]*sf, pos[0]*sf }, + (v3f){ pos[1]*sf, waves[pos[1]][pos[0]+2]*sf, (pos[0]+2)*sf }, + 0xa0ffffff ); + } + } + +} + +v3f head, butt, knee_r, knee_l, foot_r, foot_l, hand_r, hand_l, + shoulder_r, shoulder_l; + + +void vg_update(void) +{ + float timestep = 1.0f/60.0f; + + if( freecam ) + { + m4x3f cam_rot; + m4x3_identity( cam_rot ); + m4x3_rotate_y( cam_rot, -look_dir[0] ); + m4x3_rotate_x( cam_rot, -look_dir[1] ); + + v3f lookdir = { 0.0f, 0.0f, -1.0f }, + sidedir = { 1.0f, 0.0f, 0.0f }; + + m4x3_mulv( cam_rot, lookdir, lookdir ); + m4x3_mulv( cam_rot, sidedir, sidedir ); + + float movespeed = 5.0f; + static v2f mouse_last, + view_vel = { 0.0f, 0.0f }; + static v3f move_vel = { 0.0f, 0.0f, 0.0f }; + + if( vg_get_button_down( "primary" ) ) + { + v2_copy( vg_mouse, mouse_last ); + } + else if( vg_get_button( "primary" ) ) + { + v2f delta; + v2_sub( vg_mouse, mouse_last, delta ); + v2_copy( vg_mouse, mouse_last ); + + v2_muladds( view_vel, delta, 0.005f, view_vel ); + } + + v2_muls( view_vel, 0.75f, view_vel ); + v2_add( view_vel, look_dir, look_dir ); + look_dir[1] = vg_clampf( look_dir[1], -VG_PIf*0.5f, VG_PIf*0.5f ); + + if( vg_get_button( "forward" ) ) + v3_muladds( move_vel, lookdir, timestep * movespeed, move_vel ); + if( vg_get_button( "back" ) ) + v3_muladds( move_vel, lookdir, timestep *-movespeed, move_vel ); + if( vg_get_button( "left" ) ) + v3_muladds( move_vel, sidedir, timestep *-movespeed, move_vel ); + if( vg_get_button( "right" ) ) + v3_muladds( move_vel, sidedir, timestep * movespeed, move_vel ); + + v3_muls( move_vel, 0.75f, move_vel ); + v3_add( move_vel, player_head, player_head ); + + return; + } + + if( vg_get_button( "forward" ) ) + { + v3f accel = { sinf( player_yaw ), 0.0f, -cosf( player_yaw ) }; + v3_muladds( player_vel, accel, 5.0f * timestep, player_vel ); + } + + m4x3f player_transform, + world_to_local, local_to_world; + + m4x3_identity( player_transform ); + m4x3_translate( player_transform, player_pos ); + m4x3_rotate_y( player_transform, -player_yaw ); + + m4x3_identity( world_to_local ); + m4x3_rotate_y( world_to_local, player_yaw ); + + m4x3_identity( local_to_world ); + m4x3_rotate_y( local_to_world, -player_yaw ); + + /* Get front and back contact points */ + v3f contact_front, contact_back, fwd, fwd1, contact_norm; + + m4x3_mulv( local_to_world, (v3f){0.0f,0.0f,-1.0f}, fwd ); + m4x3_mulv( local_to_world, (v3f){0.03f,0.0f,-1.0f}, fwd1 ); + + v3_muladds( player_pos, fwd, 1.0f, contact_front ); + v3_muladds( player_pos, fwd,-1.0f, contact_back ); + v3_muladds( player_pos, fwd1, 1.0f, contact_norm ); + +#if 0 + road_patch_setplayer( &road_main, player_pos ); + sample_road_height( &road_main, contact_front ); + sample_road_height( &road_main, contact_back ); +#else + sample_scene_height( &world_scene, contact_front ); + sample_scene_height( &world_scene, contact_back ); + sample_scene_height( &world_scene, contact_norm ); +#endif + + v3f norm; + v3f v0, v1; + v3_sub( contact_back, contact_norm, v0 ); + v3_sub( contact_front, contact_norm, v1 ); + v3_cross( v1, v0, norm ); + v3_normalize( norm ); + + v3f gravity = { 0.0f, -9.6f, 0.0f }; + v3_muladds( player_vel, gravity, timestep, player_vel ); + + v3f ground_pos; + v3_copy( player_pos, ground_pos ); + sample_scene_height( &world_scene, ground_pos ); + + static int in_air = 1; + + if( in_air ) + { + if( ground_pos[1] > player_pos[1] ) + in_air = 0; + } + + if( !in_air ) + { + float resistance = v3_dot( norm, player_vel ); + + if( resistance >= 0.0f ) + in_air = 1; + else + { + v3_muladds( player_vel, norm, -resistance, player_vel ); + } + } + + /* vg_info( "%.3f | %.3f\n", player_vel[1], resistance ); */ + v3_muladds( player_pos, player_vel, timestep, player_pos ); + + float slip = 0.0f; + + if( !in_air ) + { + player_pos[1] = (contact_front[1]+contact_back[1])*0.5f; + + vg_line( player_pos, contact_front, 0xff00ffff ); + vg_line( player_pos, contact_back, 0xff00ffa0 ); + + /* Create the 'travel' vector */ + v3f travel; + v3_sub( contact_front, contact_back, travel ); + v3_normalize( travel ); + + /* Apply gravity */ +#if 0 + float gravity_conversion = -v3_dot( travel, gravity ); + vel[2] += gravity_conversion * substep; +#endif + + /* Get localized (rotated) rigidbody forces + * -z + * ^ + * -|- + * | + * +z + */ + + v3f vel; + m4x3_mulv( world_to_local, player_vel, vel ); + + /* Calculate local forces */ + slip = -vel[0] / vel[2]; + float substep = timestep * 0.2f; + + if( fabsf( slip ) > 1.2f ) + { + slip = vg_signf( slip ) * 1.2f; + } + + for( int i=0; i<5; i++ ) + { + if( fabsf(vel[2]) >= 0.02f*substep ) + vel[2] += vg_signf( vel[2] ) * -0.02f * substep; + if( fabsf(vel[0]) >= 6.0f*substep ) + vel[0] += vg_signf( vel[0] ) * -6.0f * substep; + } + + m4x3_mulv( local_to_world, vel, player_vel ); + + if( vg_get_button( "yawl" ) ) + player_yaw -= 1.6f * timestep; + if( vg_get_button( "yawr" ) ) + player_yaw += 1.6f * timestep; + + player_yaw += vg_get_axis( "horizontal" ) * 1.6f * timestep; + } + else + { + player_yaw += vg_get_axis( "horizontal" ) * 3.6f * timestep; + look_dir[0] = player_yaw; + } + + look_dir[0] = atan2f( player_vel[0], -player_vel[2] ); + + /* Creating a skeleton of the player dynamically */ + float kheight = 1.8f, + kleg = 0.6f; + + v2f ac; + + static v3f last_vel = { 0.0f, 0.0f, 0.0f }; + static v3f bob, bob1; + + v3_sub( player_vel, last_vel, player_accel ); + v3_copy( player_vel, last_vel ); + v3_add( bob, player_accel, bob ); + + bob[0] = vg_clampf( bob[0], -0.4f, 1.0f ); + bob[1] = vg_clampf( bob[1], -0.4f, 1.3f ); + bob[2] = vg_clampf( bob[2], -0.4f, 1.0f ); + + v3_lerp( bob, (v3f){ 0.0f, 0.0f, 0.0f }, 0.1f, bob ); + v3_lerp( bob1, bob, 0.1f, bob1 ); + + /* Feet */ + foot_r[0] = 0.0f; + foot_r[1] = 0.0f; + foot_r[2] = 0.3f; + + foot_l[0] = 0.0f; + foot_l[1] = 0.0f; + foot_l[2] = -0.3f; + + /* Head */ + head[0] = (-sinf(slip)*0.9f * kheight + bob1[0]*0.6f) * 0.54f; + head[1] = cosf(slip)*0.9f * kheight +-bob1[1]*1.4f; + head[2] = 0.0f; + + /* Hips */ + butt[0] = -sinf(slip)*0.2f; + butt[1] = cosf(slip); + butt[2] = 0.0f; + v2_normalize(butt); + v2_muls( butt, -0.7f, butt ); + v2_add( head, butt, butt ); + + /* Knees */ + v2_sub( butt, (v2f){0.0f,0.0f}, ac ); + float cl = v2_length( ac ), + d = acosf( (2.0f * kleg*kleg - cl*cl) / 2.0f * kleg*kleg ), + x = atan2f( ac[0], ac[1] ), + ad = (-VG_PIf-d)/2.0f; + + v2_muladds( (v2f){0.0f,0.0f}, + (v2f){ sinf( ad+x ), cosf( ad + x ) }, + kleg, knee_l ); + knee_l[2] = -0.3f; + + v2_copy( knee_l, knee_r ); + knee_r[2] = 0.3f; + + /* shoulders */ + v3_add( (v3f){0.0f,-0.1f,-0.2f}, head, shoulder_r ); + + /* Hands */ + hand_r[0] = sinf( slip ) * 0.1f; + hand_r[1] = -cosf( slip*5.0f ) * 0.5f - 0.5f; + hand_r[2] = -sinf( fabsf(slip) * 2.4f ); + v3_add( shoulder_r, hand_r, hand_r ); + + m4x3_mulv( player_transform, head, head ); + m4x3_mulv( player_transform, butt, butt ); + m4x3_mulv( player_transform, knee_l, knee_l ); + m4x3_mulv( player_transform, knee_r, knee_r ); + m4x3_mulv( player_transform, foot_l, foot_l ); + m4x3_mulv( player_transform, foot_r, foot_r ); + m4x3_mulv( player_transform, hand_r, hand_r ); + m4x3_mulv( player_transform, shoulder_r, shoulder_r ); + + v3_copy( head, player_head ); + +} + +static void debug_grid( v3f at ) +{ + v3f base; + v3_floor( at, base ); + + for( int y=0; y<16; y++ ) + { + vg_line( (v3f){ base[0] - 8, base[1], base[2]+y-8 }, + (v3f){ base[0] + 8, base[1], base[2]+y-8 }, + 0x40ffffff ); + } + for( int x=0; x<16; x++ ) + { + vg_line( (v3f){ base[0]+x-8, base[1], base[2]-8 }, + (v3f){ base[0]+x-8, base[1], base[2]+8 }, + 0x40ffffff ); + } +} + +void vg_render(void) +{ + glViewport( 0,0, vg_window_x, vg_window_y ); + + glDisable( GL_DEPTH_TEST ); + glClearColor( 0.1f, 0.0f, 0.2f, 1.0f ); + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + v3f pos_inv; + v3_negate( player_head, pos_inv ); + + float speed = v3_length( player_vel ); + v3f shake = { vg_randf()-0.5f, vg_randf()-0.5f, vg_randf()-0.5f }; + v3_muls( shake, speed*0.01f, shake ); + + m4x3_identity( world_matrix ); + m4x3_rotate_x( world_matrix, freecam? look_dir[1]: 0.3f+shake[1]*0.04f ); + m4x3_rotate_y( world_matrix, look_dir[0]+shake[0]*0.02f ); + m4x3_translate( world_matrix, pos_inv ); + + m4x4f world_4x4; + m4x3_expand( world_matrix, world_4x4 ); + + m4x4_projection( vg_pv, + freecam? 90.0f: 130.0f, + (float)vg_window_x / (float)vg_window_y, + 0.01f, 1000.0f ); + m4x4_mul( vg_pv, world_4x4, vg_pv ); + + if( debugroad ) + draw_road_patch_dev( &road_main ); + + vg_line( (v3f){ 0.0f, 0.0f, 0.0f }, (v3f){ 1.0f, 0.0f, 0.0f }, 0xffff0000 ); + vg_line( (v3f){ 0.0f, 0.0f, 0.0f }, (v3f){ 0.0f, 1.0f, 0.0f }, 0xff00ff00 ); + vg_line( (v3f){ 0.0f, 0.0f, 0.0f }, (v3f){ 0.0f, 0.0f, 1.0f }, 0xff0000ff ); + + v3f board_fwd = { -sinf( player_yaw ), 0.0f, cosf( player_yaw ) }; + v3f board_side = { -board_fwd[2], 0.0f, board_fwd[0] }; + v3f bnw, bne, bse, bsw; + + v3_muladds( player_pos, board_fwd, 0.75f, bnw ); + v3_muladds( player_pos, board_fwd, -0.75f, bsw ); + v3_muladds( bnw, board_side, 0.1f, bne ); + v3_muladds( bnw, board_side, -0.1f, bnw ); + v3_muladds( bsw, board_side, 0.1f, bse ); + v3_muladds( bsw, board_side, -0.1f, bsw ); + + vg_line( bnw, bne, 0xff00ff00 ); + vg_line( bne, bse, 0xff00ff00 ); + vg_line( bse, bsw, 0xff00ff00 ); + vg_line( bsw, bnw, 0xff00ff00 ); + + glEnable( GL_DEPTH_TEST ); + + v3f leg_dr, leg_dl, leg_ar, leg_al; + + v3_sub( foot_l, butt, leg_dl ); + v3_sub( foot_r, butt, leg_dr ); + leg_dl[1] = 0.0f; + leg_dr[1] = 0.0f; + v3_normalize( leg_dl ); + v3_normalize( leg_dr ); + + v3f v0; + v3_sub( butt, knee_l, v0 ); + float leg_pl = atan2f( v0[1], v3_dot( v0, leg_dl ) ); + + v3_sub( knee_l, foot_l, v0 ); + float knee_pl = atan2f( v0[1], v3_dot( v0, leg_dl ) ); + + float leg_yl = -atan2f( leg_dl[2], leg_dl[0] ), + leg_yr = atan2f( leg_dr[2], leg_dr[0] ); + + SHADER_USE( shader_debug_vcol ); + m4x3f temp; + m4x4f temp1; + + vg_tex2d_bind( &tex_grid, 0 ); + scene_tree_sway = 0.0f; + + vg_line( head, butt, 0xff0000ff ); + vg_line( butt, knee_l, 0xff0000ff ); + vg_line( butt, knee_r, 0xff0000ff ); + vg_line( foot_l, knee_l, 0xff00a0ff ); + vg_line( foot_r, knee_r, 0xff00a0ff ); + vg_line( head, shoulder_r, 0xa0ff00ff ); + vg_line( shoulder_r, hand_r, 0xffff00ff ); + m4x3_identity( temp ); + + m4x3_translate( temp, knee_l ); + m4x3_rotate_x( temp, knee_pl ); + m4x3_rotate_y( temp, leg_yl ); + + m4x3_expand( temp, temp1 ); + glUniformMatrix4fv( SHADER_UNIFORM( shader_debug_vcol, "uMdl" ), + 1, GL_FALSE, (float *)temp1 ); + + scene_draw( &player_scene, -1, 0 ); + + m4x3_identity( temp ); + m4x3_expand( temp, temp1 ); + glUniformMatrix4fv( SHADER_UNIFORM( shader_debug_vcol, "uMdl" ), + 1, GL_FALSE, (float *)temp1 ); + + vg_tex2d_bind( &tex_norwey, 0 ); + scene_tree_sway = 0.1f; + scene_draw( &test_scene, -1, 0 ); + + vg_tex2d_bind( &tex_grid, 0 ); + scene_tree_sway = 0.0f; + scene_draw( &world_scene, world_terrain_count, 0 ); + + vg_tex2d_bind( &tex_road, 0 ); + scene_draw( &world_scene, world_road_count, world_terrain_count ); + + glDisable( GL_DEPTH_TEST ); +} + +void vg_ui(void) +{ + char buf[20]; + + snprintf( buf, 20, "%.2fm/s", v3_length( player_vel ) ); + gui_text( (ui_px [2]){ 0, 0 }, buf, 1, k_text_align_left ); + + snprintf( buf, 20, "%.2f %.2f %.2f m/s", + player_accel[0], player_accel[1], player_accel[2] ); + + gui_text( (ui_px [2]){ 0, 20 }, buf, 1, k_text_align_left ); + + if( vg_gamepad_ready ) + { + for( int i=0; i<6; i++ ) + { + snprintf( buf, 20, "%.2f", vg_gamepad.axes[i] ); + gui_text( (ui_px [2]){ 0, (i+2)*20 }, buf, 1, k_text_align_left ); + } + } + else + { + gui_text( (ui_px [2]){ 0, 40 }, + "Gamepad not ready", 1, k_text_align_left ); + } +} + +void vg_free(void){} diff --git a/road.h b/road.h new file mode 100644 index 0000000..29a7a4d --- /dev/null +++ b/road.h @@ -0,0 +1,273 @@ +float const k_road_width = 4.0f; + +#define ROAD_PATCH_NODES 1024 +#define ROAD_SEGMENT_VERTICES 8 +#define ROAD_SEGMENT_INDICES ((3*2)*5) +#define ROAD_INDEX_BUFFER_SIZE (ROAD_SEGMENT_INDICES * (ROAD_PATCH_NODES-2)) +#define ROAD_RENDER_DIST 12 +#define ROAD_RENDER_DIST_INDICES (ROAD_SEGMENT_INDICES*ROAD_RENDER_DIST) + +typedef struct road_node +{ + v3f pos, + side, + fwd; + +} road_node; + +typedef struct road_patch +{ + road_node nodes[ ROAD_PATCH_NODES ]; + + int active_id; + road_node *active; +} +road_patch; + +static void road_get_range( road_patch *road, int range, int *start, int *end ) +{ + *start = VG_MIN( VG_MAX( 0, road->active_id-range ), ROAD_PATCH_NODES-2 ); + *end = VG_MIN( VG_MAX( 0, road->active_id+range ), ROAD_PATCH_NODES-2 ); +} + +static int test_edge_with_norm( v3f p0, v3f p1, v3f norm, v3f p ) +{ + v3f edge, edge_norm, v1; + + v3_sub( p1, p0, edge ); + v3_cross( edge, norm, edge_norm ); + v3_sub( p0, p, v1 ); + + if( v3_dot( edge_norm, v1 ) > 0.0f ) + { + vg_line( p0, p1, 0xff0000ff ); + return 0; + } + else + { + vg_line( p0, p1, 0xff00ff00 ); + return 1; + } +} + +static int patch_seg_test_inside( road_node *node, v3f p ) +{ + road_node *next = node + 1; + + v3f norm, + sa, sb, sc, sd; + + v3_muls( node->side, -k_road_width, sa ); + v3_add( node->pos, sa, sa ); + + v3_muls( next->side, -k_road_width, sb ); + v3_add( next->pos, sb, sb ); + + v3_muls( next->side, k_road_width, sc ); + v3_add( next->pos, sc, sc ); + + v3_muls( node->side, k_road_width, sd ); + v3_add( node->pos, sd, sd ); + + v3_cross( node->side, node->fwd, norm ); + + if( !test_edge_with_norm( sa, sb, norm, p ) ) return 0; + if( !test_edge_with_norm( sb, sc, norm, p ) ) return 0; + if( !test_edge_with_norm( sc, sd, norm, p ) ) return 0; + if( !test_edge_with_norm( sd, sa, norm, p ) ) return 0; + + return 1; +} + +static void road_patch_setplayer( road_patch *road, v3f pos ) +{ + int idx_start, idx_end; + road_get_range( road, ROAD_RENDER_DIST, &idx_start, &idx_end ); + + for( int i = idx_start; i < idx_end; i ++ ) + { + if( patch_seg_test_inside( road->nodes + i, pos ) ) + { + road->active_id = i; + road->active = road->nodes + i; + return; + } + } +} + +static void road_patch_init( road_patch *road ) +{ + road->active = road->nodes + road->active_id; +} + +static void road_generate( road_patch *road ) +{ + v3f dir_fwd = { 1.0f, 0.0f, 0.0f }, + dir_up = { 0.0f, 1.0f, 0.0f }, + dir_side = { 0.0f, 0.0f, 1.0f }, + current_point = { 0.0f, 0.0f, 0.0f }; + + float current_rotation_amt = 0.0f, + current_pitch_amt = -0.2f, + current_roll_amt = 0.00f; + + for( int i = 0; i < ROAD_PATCH_NODES; i ++ ) + { + road_node *node = road->nodes + i; + + if( (float)rand()/(float)(RAND_MAX) < 0.3f ) + { + current_rotation_amt = (float)rand()/(float)(RAND_MAX)*0.6f-0.3f; + current_pitch_amt = (float)rand()/(float)(RAND_MAX)*0.03f-0.015f; + } + + v3_rotate( dir_up, current_roll_amt, dir_fwd, dir_up ); + v3_cross( dir_fwd, dir_up, dir_side ); + dir_side[1] = 0.0f; + + v3_rotate( dir_fwd, current_rotation_amt, (v3f){0.f, 1.f, 0.f}, dir_fwd ); + v3_rotate( dir_fwd, current_pitch_amt, dir_side, dir_fwd ); + v3_rotate( dir_up, current_pitch_amt, dir_side, dir_up ); + + v3_muladds( current_point, dir_fwd, 7.0f, current_point ); + + v3_copy( current_point, node->pos ); + v3_copy( dir_side, node->side ); + v3_copy( dir_fwd, node->fwd ); + current_pitch_amt = 0.f; + + node->pos[1] += (float)rand()/(float)(RAND_MAX)*0.2f; + } + + road->active_id = 0; +} + +void draw_road_patch_dev( road_patch *road ) +{ + v3f dir; + v3f norm; + v3f p0 = { 0.0f, 0.0f, 0.0f }, p1 = { 0.0f, 0.0f, 0.0f }; + v3f p2; v3f p3; + + for( int i = 0; i < ROAD_PATCH_NODES-1; i ++ ) + { + road_node *node = &road->nodes[i]; + road_node *next = &road->nodes[i+1]; + + vg_line( node->pos, next->pos, 0x55ffcc22 ); + + // Get line dir + v3_sub( next->pos, node->pos, dir ); + v3_normalize( dir ); + + // Perpendicular vector + norm[0] = -dir[2]; + norm[1] = 0.f; + norm[2] = dir[0]; + + v3_muls( node->side, k_road_width, p2 ); + v3_add( p2, node->pos, p2 ); + v3_muls( node->side, -k_road_width, p3 ); + v3_add( p3, node->pos, p3 ); + + vg_line( p3, p1, 0xccffcc22 ); + vg_line( p2, p0, 0xccffcc22 ); + + v3_copy( p3, p1 ); + v3_copy( p2, p0 ); + } +} + +static void sample_road( road_patch *patch, v3f pos ) +{ + v3f v1, norm; + v3_sub( patch->active->pos, pos, v1 ); + v3_cross( patch->active->side, patch->active->fwd, norm ); + + float d = v3_dot( norm, v1 ); + v3_muladds( pos, norm, d, pos ); +} + +static int triangle_raycast( v3f pA, v3f pB, v3f pC, v3f ray, float *height ) +{ + v2f v0, v1, v2, vp, vp2; + float d, bca = 0.f, bcb = 0.f, bcc = 0.f; + + v0[0] = pB[0] - pA[0]; + v0[1] = pB[2] - pA[2]; + v1[0] = pC[0] - pA[0]; + v1[1] = pC[2] - pA[2]; + v2[0] = pB[0] - pC[0]; + v2[1] = pB[2] - pC[2]; + + d = 1.f / (v0[0]*v1[1] - v1[0]*v0[1]); + +#if 0 + /* Backface culling */ + if( v2_cross( v0, v1 ) > 0.f ) + return; +#endif + + vp[0] = ray[0] - pA[0]; + vp[1] = ray[2] - pA[2]; + + if( v2_cross( v0, vp ) > 0.f ) return 0; + if( v2_cross( vp, v1 ) > 0.f ) return 0; + + vp2[0] = ray[0] - pB[0]; + vp2[1] = ray[2] - pB[2]; + + if( v2_cross( vp2, v2 ) > 0.f ) return 0; + + bcb = (vp[0]*v1[1] - v1[0]*vp[1]) * d; + bcc = (v0[0]*vp[1] - vp[0]*v0[1]) * d; + bca = 1.f - bcb - bcc; + + *height = pA[1]*bca + pB[1]*bcb + pC[1]*bcc; + return 1; +} + + +static int sample_road_height( road_patch *road, v3f pos ) +{ + v3f norm, + sa, sb, sc, sd; + + int idx_start, idx_end; + road_get_range( road, ROAD_RENDER_DIST, &idx_start, &idx_end ); + + for( int i = idx_start; i < idx_end; i ++ ) + { + road_node *node = &road->nodes[i], + *next = &road->nodes[i+1]; + + v3_muls( node->side, -k_road_width, sa ); + v3_add( node->pos, sa, sa ); + + v3_muls( next->side, -k_road_width, sb ); + v3_add( next->pos, sb, sb ); + + v3_muls( next->side, k_road_width, sc ); + v3_add( next->pos, sc, sc ); + + v3_muls( node->side, k_road_width, sd ); + v3_add( node->pos, sd, sd ); + + /* Triangle 1 */ + float height; + + if( triangle_raycast( sa, sc, sb, pos, &height ) ) + { + pos[1] = height; + return 1; + } + + if( triangle_raycast( sa, sd, sc, pos, &height ) ) + { + pos[1] = height; + return 1; + } + } + + return 0; +} diff --git a/scene.h b/scene.h new file mode 100644 index 0000000..af9e26a --- /dev/null +++ b/scene.h @@ -0,0 +1,884 @@ +typedef struct model model; +typedef struct submodel submodel; +typedef struct model_vert model_vert; +typedef struct scene scene; +typedef struct sdf_primative sdf_primative; +typedef enum esdf_type esdf_type; + +GLuint tex_dual_noise; + +#pragma pack(push,1) +struct model +{ + u32 identifier; + + u32 vertex_count, + indice_count, + layer_count; +}; + +struct sdf_primative +{ + v4f origin; /* xyz, yaw */ + /* Cone: + x base scale + y height + */ + v4f info; +}; + +struct submodel +{ + u32 indice_start, + indice_count, + vertex_start, + vertex_count; + + boxf bbx; + sdf_primative sdf; + + enum esdf_type + { + k_sdf_none = 0, + k_sdf_cone, + k_sdf_sphere, + k_sdf_box + } + sdf_type; + + char name[32]; +}; + +struct model_vert +{ + v3f co, + norm; + v4f colour; + v2f uv; +}; +#pragma pack(pop) + +struct scene +{ + GLuint vao, vbo, ebo; + + model_vert *verts; + u32 *indices; + + u32 vertex_count, + indice_count, + vertex_cap, + indice_cap; + + boxf bbx; + + struct shadower + { + sdf_primative sdf; + esdf_type sdf_type; + } + *shadowers; + + u32 shadower_count, + shadower_cap; +}; + +static void scene_init( scene *pscene ) +{ + pscene->verts = NULL; + pscene->indices = NULL; + pscene->vertex_count = 0; + pscene->indice_count = 0; + pscene->shadowers = NULL; + pscene->shadower_count = 0; + pscene->shadower_cap = 0; + + v3_fill( pscene->bbx[0], 999999.9f ); + v3_fill( pscene->bbx[1], -999999.9f ); + + static int noise_ready = 0; + if( !noise_ready ) + { + noise_ready = 1; + + u8 *buf = malloc( 256*256*2 ); + + for( int i=0; i<256*256; i++ ) + { + u8 val = rand()&0xff; + buf[i*2] = val; + } + + for( int y=0; y<256; y++ ) + { + for( int x=0; x<256; x++ ) + { + u8 *pr = &buf[(y*256+x)*2], + *pg = &buf[(((y+17)&0xff)*256+((x+37)&0xff))*2+1]; + *pg = *pr; + } + } + + /* TODO: This texture should be delted somewhere */ + glGenTextures( 1, &tex_dual_noise ); + glBindTexture( GL_TEXTURE_2D, tex_dual_noise ); + glTexImage2D( GL_TEXTURE_2D, 0, GL_RG, 256, 256, 0, GL_RG, + GL_UNSIGNED_BYTE, buf ); + + vg_tex2d_linear(); + vg_tex2d_repeat(); + + free( buf ); + } +} + +/* https://www.shadertoy.com/view/4sfGzS */ +#define SHADER_VALUE_NOISE_3D \ +"uniform sampler2D uTexNoise;" \ +"" \ +"float noise( vec3 x )" \ +"{" \ + "vec3 i = floor(x);" \ + "vec3 f = fract(x);" \ + "f = f*f*(3.0-2.0*f);" \ + "vec2 uv = (i.xy+vec2(37.0,17.0)*i.z) + f.xy;" \ + "vec2 rg = texture( uTexNoise, (uv+0.5)/256.0).yx;"\ + "return mix( rg.x, rg.y, f.z );" \ +"}" \ +"" \ +"const mat3 m = mat3( 0.00, 0.80, 0.60," \ + "-0.80, 0.36, -0.48," \ + "-0.60, -0.48, 0.64 );" \ +"" \ +"float fractalNoise( vec3 x )" \ +"{" \ + "vec3 q = 8.0*x;" \ + "float f;" \ + "f = 0.5000*noise( q ); q = m*q*2.01;" \ + "f += 0.2500*noise( q ); q = m*q*2.02;" \ + "f += 0.1250*noise( q ); q = m*q*2.03;" \ + "f += 0.0625*noise( q ); q = m*q*2.01;" \ + "return f;" \ +"}" + +SHADER_DEFINE( shader_debug_vcol, + "layout (location=0) in vec3 a_co;" + "layout (location=1) in vec3 a_norm;" + "layout (location=2) in vec4 a_colour;" + "layout (location=3) in vec2 a_uv;" + "" + "uniform mat4 uPv;" + "uniform mat4 uMdl;" + "uniform float uTime;" + "uniform float uSwayAmt;" + "" + "out vec4 aColour;" + "out vec2 aUv;" + "out vec3 aNorm;" + "out vec3 aCo;" + "" + "vec3 compute_sway( vec3 pos )" + "{" + "vec4 sines = vec4( sin(uTime + pos.x)*1.0," + "sin(uTime*1.2 + pos.z*2.0)*1.1," + "sin(uTime*2.33)*0.5," + "sin(uTime*0.6 + pos.x*0.3)*1.3 );" + + "vec3 offset = vec3( sines.x+sines.y*sines.w, 0.0, sines.x+sines.z );" + "return pos + offset*a_colour.r*uSwayAmt;" + "}" + "" + "void main()" + "{" + "vec3 swaypos = compute_sway( a_co );" + "gl_Position = uPv * uMdl * vec4( swaypos, 1.0 );" + "aColour = a_colour;" + "aUv = a_uv;" + "aNorm = normalize(mat3(uMdl) * a_norm);" + "aCo = a_co;" + "}", + /* Fragment */ + "out vec4 FragColor;" + "" + "uniform int uMode;" + "uniform sampler2D uTexMain;" + "uniform sampler2D uTexGradients;" + "" + /* Include */ SHADER_VALUE_NOISE_3D + "" + "in vec4 aColour;" + "in vec2 aUv;" + "in vec3 aNorm;" + "in vec3 aCo;" + "" + "void main()" + "{" + "vec4 colour = vec4(1.0,0.0,0.5,1.0);" + "vec4 diffuse = texture( uTexMain, aUv );" + + "if( uMode == 1 )" + "{" + "colour = vec4(aNorm * 0.5 + 0.5, 1.0);" + "}" + "if( uMode == 2 )" + "{" + "colour = aColour;" + "}" + "if( uMode == 3 )" + "{" + "float light = dot(aNorm, vec3(0.2,0.8,0.1));" + "vec3 grid3 = fract(aCo);" + + "colour = vec4(vec3(light)*(1.0-grid3*0.3),1.0);" + "}" + "if( uMode == 4 )" + "{" + "colour = vec4( aUv, 0.0, 1.0 );" + "}" + "if( uMode == 5 )" + "{" + "if( diffuse.a < 0.45 ) discard;" + "colour = diffuse;" + "}" + "if( uMode == 6 )" + "{" + "float r1 = fractalNoise(aCo);" + "colour = vec4( vec3(r1), 1.0 );" + "}" + "if( uMode == 7 )" + "{" + "if( diffuse.a < 0.45 ) discard;" + "float lighting = 1.0 - aColour.g;" + "colour = vec4(vec3(pow(lighting,1.6)*(diffuse.r*0.7+0.5)),1.0);" + "}" + "if( uMode == 8 )" + "{" + "if( diffuse.a < 0.45 ) discard;" + "float light = 1.0 - aColour.g;" + "light = pow(light,1.6)*(diffuse.r*0.7+0.5);" + "float r1 = fractalNoise(aCo*0.01);" + + "vec2 gradUV = vec2(light*1.9,r1+aColour.b*0.1);" + "vec4 gradient_sample = texture( uTexGradients, gradUV );" + "colour = aColour*light;" + "}" + + "FragColor = colour;" + "}" + , + UNIFORMS({ "uPv", "uMode", "uTexMain", "uTexGradients", "uTexNoise", \ + "uTime", "uSwayAmt", "uMdl" }) +) + +/* + * Helper functions for file offsets + */ +static submodel *model_get_submodel( model *mdl, int id ) +{ + return ((submodel*)(mdl+1)) + id; +} + +static model_vert *model_vertex_base( model *mdl ) +{ + return (model_vert *)model_get_submodel( mdl, mdl->layer_count ); +} + +static u32 *model_indice_base( model *mdl ) +{ + return (u32 *)(model_vertex_base( mdl ) + mdl->vertex_count); +} + +static model_vert *submodel_vert_data( model *mdl, submodel *sub ) +{ + return model_vertex_base(mdl) + sub->vertex_start; +} + +static u32 *submodel_indice_data( model *mdl, submodel *sub ) +{ + return model_indice_base(mdl) + sub->indice_start; +} + +/* Returns -1 if not found */ +static int submodel_get( model *mdl, const char *name ) +{ + for( int i=0; ilayer_count; i++ ) + { + if( !strcmp( model_get_submodel(mdl,i)->name, name )) + { + return i; + } + } + + return -1; +} + +static void *buffer_reserve( void *buffer, u32 count, u32 *cap, u32 amount, + size_t emsize ) +{ + if( count+amount > *cap ) + { + *cap = VG_MAX( (*cap)*2, (*cap)+amount ); + + return realloc( buffer, (*cap) * emsize ); + } + + return buffer; +} + +/* + * Append a model into the scene with a given transform + */ +static void scene_add_model( scene *pscene, model *mdl, int id, + v3f pos, float yaw, float scale ) +{ + submodel *submodel = model_get_submodel( mdl, id ); + + pscene->verts = buffer_reserve( pscene->verts, pscene->vertex_count, + &pscene->vertex_cap, submodel->vertex_count, sizeof(model_vert) ); + pscene->indices = buffer_reserve( pscene->indices, pscene->indice_count, + &pscene->indice_cap, submodel->indice_count, sizeof(u32) ); + + if( submodel->sdf_type ) + { + pscene->shadowers = buffer_reserve( pscene->shadowers, + pscene->shadower_count, &pscene->shadower_cap, 1, + sizeof( struct shadower )); + + struct shadower *shadower = + &pscene->shadowers[ pscene->shadower_count ++ ]; + + shadower->sdf = submodel->sdf; + shadower->sdf_type = submodel->sdf_type; + + v2_muls( shadower->sdf.info, scale, shadower->sdf.info ); + v3_muls( shadower->sdf.origin, scale, shadower->sdf.origin ); + v3_add( pos, shadower->sdf.origin, shadower->sdf.origin ); + } + + /* Transform and place vertices */ + model_vert *src_verts = submodel_vert_data( mdl, submodel ); + u32 *src_indices = submodel_indice_data( mdl, submodel ); + + m4x3f mtx; + m4x3_identity( mtx ); + m4x3_translate( mtx, pos ); + m4x3_rotate_y( mtx, yaw ); + m4x3_scale( mtx, scale ); + + boxf bbxnew; + box_copy( submodel->bbx, bbxnew ); + m4x3_transform_aabb( mtx, bbxnew ); + box_concat( pscene->bbx, bbxnew ); + + m3x3f rotation; + m4x3_to_3x3( mtx, rotation ); + + float rand_hue = vg_randf(); + + for( u32 i=0; ivertex_count; i++ ) + { + model_vert *pvert = &pscene->verts[ pscene->vertex_count+i ], + *src = &src_verts[ i ]; + + m4x3_mulv( mtx, src->co, pvert->co ); + m3x3_mulv( rotation, src->norm, pvert->norm ); + + v4_copy( src->colour, pvert->colour ); + v2_copy( src->uv, pvert->uv ); + + float rel_y = src->co[1] / submodel->bbx[1][1]; + pvert->colour[0] = rel_y; + pvert->colour[2] = rand_hue; + } + + for( u32 i=0; iindice_count; i++ ) + { + u32 *pidx = &pscene->indices[ pscene->indice_count+i ]; + *pidx = src_indices[i] + pscene->vertex_count; + } + + pscene->vertex_count += submodel->vertex_count; + pscene->indice_count += submodel->indice_count; +} + +static void scene_shadow_sphere( scene *pscene, v3f sphere, + v4f params, v3f lightdir ) +{ + for( int i=0; ivertex_count; i++ ) + { + model_vert *vert = &pscene->verts[i]; + + v3f delta; + v3_sub( sphere, vert->co, delta ); + + float d = v3_dot( lightdir, delta ); + v3f closest; + + v3_muls( lightdir, d, closest ); + float dist = v3_dist( closest, delta ), + shading = vg_maxf( dist - params[0], 0.0f ); + + shading = vg_minf( shading * params[1], 1.0f ); + vert->colour[1] *= shading; + } +} + +static void scene_shadow_gradient( scene *pscene, int comp, + float start, float length ) +{ + float scale = 1.0f / length; + + for( int i=0; ivertex_count; i++ ) + { + model_vert *vert = &pscene->verts[i]; + float shading = start + vert->co[comp] * scale; + + vert->colour[1] = shading; + } +} + +/* Temporary */ +static int sample_scene_height( scene *pscene, v3f pos ) +{ + for( int i=0; iindice_count/3; i++ ) + { + u32 *tri = &pscene->indices[i*3]; + + float height; + if( triangle_raycast( + pscene->verts[ tri[0] ].co, + pscene->verts[ tri[1] ].co, + pscene->verts[ tri[2] ].co, pos, &height )) + { + pos[1] = height; + return 1; + } + } + return 0; +} + +static void sample_scene_normal( scene *pscene, v3f pos, v3f normal ) +{ + for( int i=0; iindice_count/3; i++ ) + { + u32 *tri = &pscene->indices[i*3]; + + float height; + if( triangle_raycast( + pscene->verts[ tri[0] ].co, + pscene->verts[ tri[1] ].co, + pscene->verts[ tri[2] ].co, pos, &height )) + { + v3f v0, v1; + + v3_sub( pscene->verts[ tri[1] ].co, + pscene->verts[ tri[0] ].co, + v0 ); + + v3_sub( pscene->verts[ tri[2] ].co, + pscene->verts[ tri[0] ].co, + v1 ); + + v3_cross( v0, v1, normal ); + v3_normalize( normal ); + return; + } + } + + normal[0] = 0.0f; + normal[1] = 1.0f; + normal[2] = 0.0f; +} + +/* + * Experimental SDF based shadows + * + * https://iquilezles.org/articles/distfunctions/ + */ +static float sd_cone( v3f co, sdf_primative *prim ) +{ + float bound = prim->info[1]*1.75f; + if( v3_dist2( prim->origin, co ) > bound*bound ) + return 999999.9f; + + v3f p; + v3_sub( co, prim->origin, p ); + + float h = prim->info[1]; + v2f c = { prim->info[2], prim->info[3] }; + + v2f q, w, a, b; + v2_muls( (v2f){ c[0]/c[1], -1.0f }, h, q ); + + w[0] = v2_length( (v2f){ p[0], p[2] } ); + w[1] = p[1]; + + v2_muladds( w, q, -vg_clampf( v2_dot(w,q)/v2_dot(q,q), 0.0f, 1.0f ), a ); + v2_muladd( w, q, (v2f){ vg_clampf( w[0]/q[0], 0.0f, 1.0f ), 1.0f }, b ); + + float k = vg_signf( q[1] ), + d = vg_minf( v2_dot( a,a ), v2_dot( b,b ) ), + s = vg_maxf( k*(w[0]*q[1]-w[1]*q[0]), k*(w[1]-q[1]) ); + + return sqrtf(d)*vg_signf(s); +} + +#define CACHE_AMBIENT_SHAPES + +static float scene_ambient_sample( scene *pscene, v3f pos, v3f dir ) +{ + float accum = 0.0f; + +#ifdef CACHE_AMBIENT_SHAPES + static struct shadower *local_shadowers[32]; + static int local_shadower_count = 0; + static v3f local_shadower_last = { -99999.9f, -999999.9f, -9999999.9f }; + + if( v3_dist2( pos, local_shadower_last ) > 10.0f*10.0f ) + { + local_shadower_count = 0; + v3_copy( pos, local_shadower_last ); + + for( int k=0; kshadower_count; k++ ) + { + struct shadower *shadower = &pscene->shadowers[k]; + + if( sd_cone( pos, &shadower->sdf ) <= 20.0f ) + { + local_shadowers[ local_shadower_count ++ ] = shadower; + if( local_shadower_count == vg_list_size( local_shadowers ) ) + break; + } + } + } +#endif + + for( int j=0; j<5; j++ ) + { + v3f tracepos; + v3_muladds( pos, dir, 1.5f*(float)j, tracepos ); + + float mindist = 99999.9f; + +#ifndef CACHE_AMBIENT_SHAPES + + for( int k=0; kshadower_count; k++ ){ + struct shadower *shadower = &pscene->shadowers[k]; +#else + + for( int k=0; ksdf )); + mindist = vg_minf( mindist, dist ); + } + + + accum += vg_clampf( 1.0f - mindist, 0.0f, 1.0f )*0.2f; + } + + return accum; +} + +#define DYNAMIC_GRID +#define JUST_DO_EVERY_VERT + +static void scene_compute_occlusion( scene *pscene ) +{ + v3f sundir = { 0.2f, 0.9f, 0.2f }; + v3_normalize( sundir ); + + /* TODO: Make this sample grid be dynamically required. + * + * 1. Only resample the light grid (1x1x1), when a vertex is outside the + * current cube + * + * 2. Reorder all vertices so that each group of vertices that fit in a + * cube are next to eachother in the buffer. This will save cache + * misses. + * + * for the sorting algorithm, i think we can already assume that *most + * vertices will be quite close to eachother. so instead of doing an + * exhaustive search we can reorder 1k chunks at a time. + */ + + v3f sample_area; + v3_sub( pscene->bbx[1], pscene->bbx[0], sample_area ); + v3_ceil( sample_area, sample_area ); + int ax = sample_area[0], + ay = sample_area[1], + az = sample_area[2]; + +#ifndef DYNAMIC_GRID + float *samplegrid = malloc( ax*ay*az* sizeof(float) ); + + for( int x=0; xbbx[0], sample_pos, sample_pos ); + float accum = scene_ambient_sample( pscene, sample_pos, sundir ); + + samplegrid[x + y*ax + z*ax*ay] = accum; + }}} +#else + v3i cube_pos = { -999999, -999999, -999999 }; + int cube_resamples = 0, hits = 0, misses = 0; + + float s0=0.0f,s1=0.0f,s2=0.0f,s3=0.0f,s4=0.0f,s5=0.0f,s6=0.0f,s7=0.0f; +#endif + + for( int i=0; ivertex_count; i++ ) + { + model_vert *vert = &pscene->verts[i]; + v3f rel, q; + +#ifndef DYNAMIC_GRID + v3_sub( vert->co, pscene->bbx[0], q ); +#else + v3_copy( vert->co, q ); +#endif + + v3_floor( q, rel ); + v3_sub( q, rel, q ); + + int x=rel[0], + y=rel[1], + z=rel[2]; + +#ifndef JUST_DO_EVERY_VERT +#ifndef DYNAMIC_GRID + x = VG_MIN(x,ax-2); + y = VG_MIN(y,ay-2); + z = VG_MIN(z,az-2); + x = VG_MAX(x,0); + y = VG_MAX(y,0); + z = VG_MAX(z,0); + + float + s0 = samplegrid[ x + y*ax + z*ax*ay], + s1 = samplegrid[(x+1) + y*ax + z*ax*ay], + s2 = samplegrid[ x + (y+1)*ax + z*ax*ay], + s3 = samplegrid[(x+1) + (y+1)*ax + z*ax*ay], + s4 = samplegrid[ x + y*ax + (z+1)*ax*ay], + s5 = samplegrid[(x+1) + y*ax + (z+1)*ax*ay], + s6 = samplegrid[ x + (y+1)*ax + (z+1)*ax*ay], + s7 = samplegrid[(x+1) + (y+1)*ax + (z+1)*ax*ay], +#else + if( x!=cube_pos[0] || y!=cube_pos[1] || z!=cube_pos[2] ) + { + cube_pos[0] = x; + cube_pos[1] = y; + cube_pos[2] = z; + + s0 = scene_ambient_sample( pscene, (v3f){ x,y,z }, sundir ); + s1 = scene_ambient_sample( pscene, (v3f){ x+1,y,z }, sundir ); + s2 = scene_ambient_sample( pscene, (v3f){ x,y+1,z }, sundir ); + s3 = scene_ambient_sample( pscene, (v3f){ x+1,y+1,z }, sundir ); + s4 = scene_ambient_sample( pscene, (v3f){ x,y,z+1 }, sundir ); + s5 = scene_ambient_sample( pscene, (v3f){ x+1,y,z+1 }, sundir ); + s6 = scene_ambient_sample( pscene, (v3f){ x,y+1,z+1 }, sundir ); + s7 = scene_ambient_sample( pscene, (v3f){ x+1,y+1,z+1 }, sundir ); + + cube_resamples += 8; + misses ++; + } + else + hits ++; + + float +#endif + + s0_s1 = vg_lerpf( s0, s1, q[0] ), + s2_s3 = vg_lerpf( s2, s3, q[0] ), + s4_s5 = vg_lerpf( s4, s5, q[0] ), + s6_s7 = vg_lerpf( s6, s7, q[0] ), + + s0s1_s2s3 = vg_lerpf( s0_s1, s2_s3, q[1] ), + s4s5_s6s7 = vg_lerpf( s4_s5, s6_s7, q[1] ), + s0s1s2s3_s4s5s6s7 = vg_lerpf( s0s1_s2s3, s4s5_s6s7, q[2] ); + + vert->colour[1] = s0s1s2s3_s4s5s6s7; +#else + vert->colour[1] = scene_ambient_sample( pscene, vert->co, sundir ); +#endif + } + +#ifndef DYNAMIC_GRID + int cube_resamples = -1, misses = 0, hits = 0; +#endif + + int static_samples = ax*ay*az, + vertex_samples = pscene->vertex_count; + + if( cube_resamples < static_samples ) + vg_success( "Walking cube beat static grid (%d<%d. %d)!\n", + cube_resamples, static_samples, vertex_samples ); + else + vg_warn( "Walking cube was worse than static grid (%d<%d. %d).\n", + cube_resamples, static_samples, vertex_samples ); + + vg_info( "Hits; %d, misses: %d\n", hits, misses ); + +#ifndef DYNAMIC_GRID + free( samplegrid ); +#endif + + return; + + for( int i=0; ivertex_count; i++ ) + { + model_vert *vert = &pscene->verts[i]; + float accum = 0.0f; + + for( int j=0; j<5; j++ ) + { + v3f tracepos; + v3_copy( vert->co, tracepos ); + v3_muladds( tracepos, sundir, 1.5f*(float)j, tracepos ); + + float mindist = 99999.9f; + + for( int k=0; kshadower_count; k++ ) + { + struct shadower *shadower = &pscene->shadowers[k]; + float dist = vg_maxf( 0.0f, sd_cone( tracepos, &shadower->sdf )); + mindist = vg_minf( mindist, dist ); + } + + accum += vg_clampf( 1.0f - mindist, 0.0f, 1.0f )*0.2f; + } + + vert->colour[1] = vg_minf( accum, 1.0f ); + } +} + +static void scene_upload( scene *pscene ) +{ + glGenVertexArrays( 1, &pscene->vao ); + glGenBuffers( 1, &pscene->vbo ); + glGenBuffers( 1, &pscene->ebo ); + glBindVertexArray( pscene->vao ); + + glBindBuffer( GL_ARRAY_BUFFER, pscene->vbo ); + glBufferData( GL_ARRAY_BUFFER, pscene->vertex_count*sizeof(model_vert), + pscene->verts, GL_STATIC_DRAW ); + + glBindVertexArray( pscene->vao ); + glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, pscene->ebo ); + glBufferData( GL_ELEMENT_ARRAY_BUFFER, pscene->indice_count*sizeof(u32), + pscene->indices, GL_STATIC_DRAW ); + + glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, + sizeof(model_vert), (void*)0 ); + glEnableVertexAttribArray( 0 ); + + glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, + sizeof(model_vert), (void *)offsetof(model_vert, norm) ); + glEnableVertexAttribArray( 1 ); + + glVertexAttribPointer( 2, 4, GL_FLOAT, GL_FALSE, + sizeof(model_vert), (void *)offsetof(model_vert, colour) ); + glEnableVertexAttribArray( 2 ); + + glVertexAttribPointer( 3, 2, GL_FLOAT, GL_FALSE, + sizeof(model_vert), (void *)offsetof(model_vert, uv) ); + glEnableVertexAttribArray( 3 ); + + VG_CHECK_GL(); + + vg_info( "Scene upload\n" ); + vg_info( " indices:%u\n", pscene->indice_count ); + vg_info( " verts:%u\n", pscene->vertex_count ); +} + +float scene_tree_sway = 0.1f; +static void scene_draw( scene *pscene, int count, int start ) +{ + SHADER_USE( shader_debug_vcol ); + + glUniformMatrix4fv( SHADER_UNIFORM( shader_debug_vcol, "uPv" ), + 1, GL_FALSE, (float *)vg_pv ); + + glUniform1i( SHADER_UNIFORM( shader_debug_vcol, "uMode" ), debugview ); + glUniform1i( SHADER_UNIFORM( shader_debug_vcol, "uTexMain" ), 0 ); + + glUniform1i( SHADER_UNIFORM( shader_debug_vcol, "uTexGradients" ), 1 ); + vg_tex2d_bind( &tex_gradients, 1 ); + + glUniform1i( SHADER_UNIFORM( shader_debug_vcol, "uTexNoise" ), 2 ); + glActiveTexture( GL_TEXTURE2 ); + glBindTexture( GL_TEXTURE_2D, tex_dual_noise ); + + glUniform1f( SHADER_UNIFORM( shader_debug_vcol, "uTime" ), vg_time ); + glUniform1f( SHADER_UNIFORM( shader_debug_vcol, "uSwayAmt" ), + scene_tree_sway ); + + glBindVertexArray( pscene->vao ); + + if( count == -1 ) + { + glDrawElements( GL_TRIANGLES, pscene->indice_count, + GL_UNSIGNED_INT, + (void *)0 + ); + } + else + { + glDrawElements( GL_TRIANGLES, count, + GL_UNSIGNED_INT, + (void *)(start*sizeof(u32)) + ); + } + + if( debugsdf ) + { + for( int i=0; ishadower_count; i++ ) + { + struct shadower *shadower = &pscene->shadowers[i]; + + v3f base, side; + v3_copy( shadower->sdf.origin, base ); + base[1] -= shadower->sdf.info[1]; + v3_copy( base, side ); + side[0] += shadower->sdf.info[0]; + + vg_line2( shadower->sdf.origin, base, 0xff00ff00, 0xff0000ff ); + vg_line2( side, base, 0xff00ff00, 0xff0000ff ); + vg_line( side, shadower->sdf.origin, 0xff00ff00 ); + } + + v3f p0 = { pscene->bbx[0][0], pscene->bbx[0][1], pscene->bbx[0][2] }, + p1 = { pscene->bbx[0][0], pscene->bbx[1][1], pscene->bbx[0][2] }, + p2 = { pscene->bbx[1][0], pscene->bbx[1][1], pscene->bbx[0][2] }, + p3 = { pscene->bbx[1][0], pscene->bbx[0][1], pscene->bbx[0][2] }, + + p4 = { pscene->bbx[0][0], pscene->bbx[0][1], pscene->bbx[1][2] }, + p5 = { pscene->bbx[0][0], pscene->bbx[1][1], pscene->bbx[1][2] }, + p6 = { pscene->bbx[1][0], pscene->bbx[1][1], pscene->bbx[1][2] }, + p7 = { pscene->bbx[1][0], pscene->bbx[0][1], pscene->bbx[1][2] }; + + u32 col = 0xffff00c8; + vg_line( p0, p1, col ); + vg_line( p1, p2, col ); + vg_line( p2, p3, col ); + vg_line( p3, p0, col ); + + vg_line( p4, p5, col ); + vg_line( p5, p6, col ); + vg_line( p6, p7, col ); + vg_line( p7, p4, col ); + + vg_line( p0, p4, col ); + vg_line( p1, p5, col ); + vg_line( p2, p6, col ); + vg_line( p3, p7, col ); + } +} + +static void scene_register(void) +{ + SHADER_INIT( shader_debug_vcol ); +} diff --git a/sprites_autocombine.h b/sprites_autocombine.h new file mode 100644 index 0000000..cb1229f --- /dev/null +++ b/sprites_autocombine.h @@ -0,0 +1,8 @@ +enum sprites_auto_combine_index +{ + k_sprite_*, +}; + +static struct vg_sprite sprites_auto_combine[] = +{ +}; \ No newline at end of file diff --git a/textures/gradients.png b/textures/gradients.png new file mode 100644 index 0000000..8b60c3b Binary files /dev/null and b/textures/gradients.png differ diff --git a/textures/grid.png b/textures/grid.png new file mode 100644 index 0000000..3b9d99d Binary files /dev/null and b/textures/grid.png differ diff --git a/textures/norway_foliage.png b/textures/norway_foliage.png new file mode 100644 index 0000000..707558a Binary files /dev/null and b/textures/norway_foliage.png differ diff --git a/textures/norwey.png b/textures/norwey.png new file mode 100644 index 0000000..9ac01ff Binary files /dev/null and b/textures/norwey.png differ diff --git a/textures/road.png b/textures/road.png new file mode 100644 index 0000000..06c95cc Binary files /dev/null and b/textures/road.png differ diff --git a/vg.conf b/vg.conf new file mode 100644 index 0000000..896d68a --- /dev/null +++ b/vg.conf @@ -0,0 +1,2 @@ +vg_src="main.c" +vg_target="game" diff --git a/vg_config.h b/vg_config.h new file mode 100644 index 0000000..b1348a0 --- /dev/null +++ b/vg_config.h @@ -0,0 +1,26 @@ +// Copyright (C) 2021 Harry Godden (hgn) - All Rights Reserved +#define VG_CONFIG + +static struct button_binding vg_button_binds[] = +{ + { .name = "primary", .bind = GLFW_MOUSE_BUTTON_LEFT }, + { .name = "secondary", .bind = GLFW_MOUSE_BUTTON_RIGHT }, + { .name = "left", .bind = GLFW_KEY_A }, + { .name = "right", .bind = GLFW_KEY_D }, + { .name = "forward", .bind = GLFW_KEY_W }, + { .name = "back", .bind = GLFW_KEY_S }, + { .name = "up", .bind = GLFW_KEY_R }, + { .name = "down", .bind = GLFW_KEY_F }, + { .name = "yawl", .bind = GLFW_KEY_Q }, + { .name = "yawr", .bind = GLFW_KEY_E }, +}; + +static struct axis_binding vg_axis_binds[] = +{ + { .name = "horizontal", .axis = GLFW_GAMEPAD_AXIS_LEFT_X }, + { .name = "vertical", .axis = GLFW_GAMEPAD_AXIS_LEFT_Y } +}; + +static struct vg_achievement vg_achievements[] = +{ +};