summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoraura <nw@moneybot.cc>2026-02-25 08:58:39 +0100
committeraura <nw@moneybot.cc>2026-02-25 08:58:39 +0100
commit3e5e15a4923c752be703d7afb1214d5e5a767fad (patch)
treed76049a556bd0fff581a018408faa6bed23b74eb /src
parent25da8e01a0499c273357cb2feb2825b53e86795b (diff)
finish wall collisions (clipvelocity), some utils
Diffstat (limited to 'src')
-rw-r--r--src/editor/view3d.cpp4
-rw-r--r--src/game.cpp8
-rw-r--r--src/game/physics/movement.cpp157
-rw-r--r--src/game/physics/movement.h16
-rw-r--r--src/game/player.cpp95
-rw-r--r--src/game/player.h2
-rw-r--r--src/game/world/bsp.cpp1
-rw-r--r--src/game/world/map.cpp4
-rw-r--r--src/game/world/trace.cpp7
-rw-r--r--src/util/fnv.h8
-rw-r--r--src/util/input.cpp2
-rw-r--r--src/util/input.h4
-rw-r--r--src/util/string.h30
13 files changed, 249 insertions, 89 deletions
diff --git a/src/editor/view3d.cpp b/src/editor/view3d.cpp
index f8d1def..656eda9 100644
--- a/src/editor/view3d.cpp
+++ b/src/editor/view3d.cpp
@@ -93,7 +93,7 @@ void gui_editor_3dview_input_fn( void* ptr ) {
if( input.mouse.pos.x >= view_x && input.mouse.pos.x < view_x + view->w &&
input.mouse.pos.y >= view_y && input.mouse.pos.y < view_y + view->h - EDITORVIEW_TOOLBAR_OFFSET ) {
if( !input.mouselock && !view->heldoutbounds )
- input_capture_mouse( true );
+ input_capture_mouse( 1 );
} else {
view->heldoutbounds = 1;
}
@@ -109,7 +109,7 @@ void gui_editor_3dview_create_toolbar( GUI_EDITOR_3DVIEW* view ) {
gui_button( x, y, 90, 18, "compile bsp", pfn( void* b ) {
if( editor->map->bsp )
bsp_free( editor->map->bsp );
- editor->map->bsp = bsp_build_map( editor->map );
+ bsp_build_map( editor->map );
} ); x += 100;
gui_checkbox( x, y, "draw bsp", &e->drawbsp );
diff --git a/src/game.cpp b/src/game.cpp
index 285f288..6202145 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -1,6 +1,8 @@
#include <unistd.h>
#include "game.h"
+#include "game/physics/movement.h"
+#include "game/world/bsp.h"
#include "game/world/draw.h"
#include "render/gl_2d.h"
#include "render/gl_3d.h"
@@ -35,6 +37,8 @@ GAME_DATA* game_init( GL_DATA* gl ) {
varl = vars_init();
objl = objl_init();
+ gmove_init( game );
+
#if IS_EDITOR
game->editor = editor_create( game );
#else
@@ -88,6 +92,7 @@ WORLD_MAP* game_load_map( GAME_DATA* game, const char* mapname ) {
return 0;
}
+ bsp_build_map( m );
objl_load_world( game, m );
game->state.map = m;
return m;
@@ -102,7 +107,8 @@ void game_unload_map( GAME_DATA* game ) {
void game_draw( GAME_DATA* game ) {
if( !objl->pl || !objl->world ) {
- game_load_map( game, "../assets/maps/test.hmap" );
+ dlog( "game_draw() : no world" );
+ return;
}
VEC2 window = { 270.f, 25.f };
diff --git a/src/game/physics/movement.cpp b/src/game/physics/movement.cpp
new file mode 100644
index 0000000..e9e0132
--- /dev/null
+++ b/src/game/physics/movement.cpp
@@ -0,0 +1,157 @@
+#include "movement.h"
+#include "../world/trace.h"
+#include "../../util/math.h"
+#include "../../util/string.h"
+#include "../../game.h"
+
+GAME_MOVEMENT* gmove;
+
+void gmove_init( GAME_DATA* game ) {
+ gmove = new GAME_MOVEMENT;
+ gmove->game = game;
+}
+
+U8 gmove_check_valid( STR<256> from ) {
+ if( !gmove->game ) {
+ dlog( "%s : game not init\n", from.data );
+ return 0;
+ }
+
+ if( !gmove->pl ) {
+ dlog( "%s : player null\n", from.data );
+ return 0;
+ }
+
+ if( !gmove->input ) {
+ dlog( "%s : input null\n", from.data );
+ return 0;
+ }
+
+ if( !gmove->game->state.map ) {
+ dlog( "%s : no map loaded\n", from.data );
+ return 0;
+ }
+
+ if( !gmove->game->state.map->bsp ) {
+ dlog( "%s : no bsp loaded\n", from.data );
+ return 0;
+ }
+
+ return 1;
+}
+
+void gmove_set_player( PLAYER* player ) {
+ gmove->pl = player;
+ gmove->input = &player->input;
+}
+
+VEC3 gmove_clip_velocity( VEC3 in, VEC3 norm, F32 overbounce ) {
+ F32 blocked = vec_dot( in, norm );
+ F32 backoff = blocked * overbounce;
+ VEC3 out = in - norm * backoff;
+
+ F32 adjust = vec_dot( out, norm );
+ if( adjust < 0.f )
+ out -= (norm * adjust);
+
+ // uncomment to enable quake/hl1-like wallboosting
+ // F32 len = vec_len( out );
+ // if( len > 0.f )
+ // out *= ( -1.f * blocked + len ) / len;
+
+ return out;
+}
+
+VEC3 gmove_clip_planes( VEC3 vel, LIST<VEC3>* planes, F32 overbounce ) {
+ if( planes->size > 2 )
+ vel = {};
+
+ planes->each( fn( VEC3* p ) {
+ vel = gmove_clip_velocity( vel, *p, overbounce );
+ } );
+
+ if( planes->size > 1 ) {
+ for( U32 i = 0; i < planes->size; ++i ) {
+ if( vec_dot( vel, planes->data[i] ) < 0.f ) {
+ VEC3 dir = vec_cross( planes->data[0], planes->data[1] );
+ F32 len = vec_len( dir );
+ if( len > 0.00001f ) {
+ dir *= 1.0f / len;
+ vel = dir * vec_dot( vel, dir );
+ } else {
+ vel = {};
+ }
+
+ break;
+ }
+ }
+ }
+
+ return vel;
+}
+
+F32 gmove_try_move( BSP_TRACE* t, AABB aabb, VEC3* pos, VEC3 vel ) {
+ F32 dt = TICK_INTERVAL;
+ LIST<VEC3> planes;
+
+ for( U32 bump = 0; bump < 4; ++bump ) {
+ VEC3 wishmove = vel * dt;
+ bsp_trace( t, gmove->game->state.map->bsp, aabb, *pos, *pos + wishmove );
+ if( !t->hit ) {
+ *pos = t->point;
+ break;
+ }
+
+ *pos += wishmove * t->frac;
+ // nudge player away from wall
+ *pos += t->normal * (BSP_TRACE_EPSILON * 2.f);
+
+ planes.push( t->normal );
+
+ dt *= (1.f - t->frac);
+ if( dt <= 0.0001f )
+ break;
+
+ vel = gmove_clip_planes( vel, &planes, 1.f /* surface friction */ );
+ }
+
+ return dt < 0 ? 0 : dt;
+}
+
+void gmove_walk_move() {
+ PLAYER* p = gmove->pl;
+ VEC2 move = gmove->input->move;
+ F32 yawrad = m_deg2rad( p->rot.y );
+ VEC3 wishdir = {
+ move.x * cosf( yawrad ) - move.y * sinf( yawrad ),
+ move.y * cosf( yawrad ) + move.x * sinf( yawrad ),
+ 0
+ };
+
+ VEC3 vel = wishdir * p->maxspeed;
+ VEC3 wishmove = vel * TICK_INTERVAL;
+ p->velocity = vel;
+
+ if( vec_len( wishmove ) < BSP_TRACE_EPSILON )
+ return;
+
+ BSP_TRACE tr{};
+ AABB aabb{};
+ aabb.min = p->mins;
+ aabb.max = p->maxs;
+ VEC3 npos = p->pos;
+
+ F32 frac = gmove_try_move( &tr, aabb, &npos, vel );
+ p->velocity *= frac / TICK_INTERVAL;
+ p->pos = npos;
+}
+
+void gmove_process_move() {
+ gmove_walk_move();
+}
+
+void gmove_tick() {
+ if( !gmove_check_valid( "gmove_tick()" ) )
+ return;
+ gmove_process_move();
+}
diff --git a/src/game/physics/movement.h b/src/game/physics/movement.h
index e69de29..7c78d79 100644
--- a/src/game/physics/movement.h
+++ b/src/game/physics/movement.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "../player.h"
+
+struct GAME_MOVEMENT {
+ PLAYER* pl;
+ PLAYER_INPUT* input;
+ GAME_DATA* game;
+};
+
+extern void gmove_init( GAME_DATA* game );
+extern void gmove_set_player( PLAYER* player );
+extern void gmove_tick();
+extern void gmove_process_move();
+
+extern GAME_MOVEMENT* gmove;
diff --git a/src/game/player.cpp b/src/game/player.cpp
index 51d1cf8..65daa9b 100644
--- a/src/game/player.cpp
+++ b/src/game/player.cpp
@@ -1,6 +1,7 @@
#include "../game.h"
#include "SDL_scancode.h"
#include "objlist.h"
+#include "physics/movement.h"
#include "world/trace.h"
#include <cmath>
#include "player.h"
@@ -34,8 +35,9 @@ PLAYER* player_create( VEC3 origin, F32 yaw ) {
void capture_mouse( PLAYER* p ) {
F32* yaw = &p->input.cam.pos.y;
F32* pitch = &p->input.cam.pos.x;
+
if( input.keys[SDL_SCANCODE_F1] ) {
- input_capture_mouse( false );
+ input_capture_mouse( 0 );
}
if( input.mouselock ) {
@@ -56,59 +58,47 @@ void capture_mouse( PLAYER* p ) {
void capture_move_keys( PLAYER* p ) {
VEC2* move = &p->input.move;
if( input.keys[input.binds.fwd] ) {
- if( !p->input.fwd_held )
- move->x = 1.f;
- p->input.fwd_held = true;
+ if( !p->input.fwd_held ) move->x = 1.f;
+ p->input.fwd_held = 1;
} else {
if( p->input.fwd_held ) {
- if( p->input.bk_held )
- move->x = -1.f;
- else
- move->x = 0.f;
+ if( p->input.bk_held ) move->x = -1.f;
+ else move->x = 0.f;
}
- p->input.fwd_held = false;
+ p->input.fwd_held = 0;
}
if( input.keys[input.binds.back] ) {
- if( !p->input.bk_held )
- move->x = -1.f;
- p->input.bk_held = true;
+ if( !p->input.bk_held ) move->x = -1.f;
+ p->input.bk_held = 1;
} else {
if( p->input.bk_held ) {
- if( p->input.fwd_held )
- move->x = 1.f;
- else
- move->x = 0.f;
+ if( p->input.fwd_held ) move->x = 1.f;
+ else move->x = 0.f;
}
- p->input.bk_held = false;
+ p->input.bk_held = 0;
}
if( input.keys[input.binds.left] ) {
- if( !p->input.left_held )
- move->y = -1.f;
- p->input.left_held = true;
+ if( !p->input.left_held ) move->y = -1.f;
+ p->input.left_held = 1;
} else {
if( p->input.left_held ) {
- if( p->input.right_held )
- move->y = 1.f;
- else
- move->y = 0.f;
+ if( p->input.right_held ) move->y = 1.f;
+ else move->y = 0.f;
}
- p->input.left_held = false;
+ p->input.left_held = 0;
}
if( input.keys[input.binds.right] ) {
- if( !p->input.right_held )
- move->y = 1.f;
- p->input.right_held = true;
+ if( !p->input.right_held ) move->y = 1.f;
+ p->input.right_held = 1;
} else {
if( p->input.right_held ) {
- if( p->input.left_held )
- move->y = -1.f;
- else
- move->y = 0.f;
+ if( p->input.left_held ) move->y = -1.f;
+ else move->y = 0.f;
}
- p->input.right_held = false;
+ p->input.right_held = 0;
}
}
@@ -121,43 +111,8 @@ void player_input( GAME_DATA* game, PLAYER* p ) {
}
void player_move( GAME_DATA* game, PLAYER* p ) {
- VEC2 move = p->input.move;
- F32 yawrad = m_deg2rad( p->rot.y );
-
- VEC3 wishdir = {
- move.x * cosf( yawrad ) - move.y * sinf( yawrad ),
- move.y * cosf( yawrad ) + move.x * sinf( yawrad ),
- 0
- };
-
- VEC3 vel = wishdir * 70.f;
- p->velocity = vel;
- VEC3 wishmove = vel * TICK_INTERVAL;
- // todo : should never be false
- if( vec_len( wishmove ) > BSP_TRACE_EPSILON && game->state.map->bsp ) {
- BSP_TRACE tr{};
- AABB aabb{};
- tr.in_start = p->pos;
- tr.in_end = tr.in_start + wishmove;
- aabb.min = p->mins;
- aabb.max = p->maxs;
-
- bsp_trace( &tr, game->state.map->bsp, aabb );
- if( !tr.hit )
- p->pos += wishmove;
- else {
- p->pos += wishmove * tr.frac;
-
-
- // TODO: clipvelocity fixes this, clips velocity to 1 wall (should do 4)
- VEC3 left = wishmove * (1.f - tr.frac);
- F32 into = vec_dot( left, tr.normal );
- if( into < 0.f )
- left -= tr.normal * into;
-
- p->pos += left;
- }
- }
+ gmove_set_player( p );
+ gmove_tick();
}
VEC3 player_get_view_pos( PLAYER* p ) {
diff --git a/src/game/player.h b/src/game/player.h
index 6617f01..c0e126d 100644
--- a/src/game/player.h
+++ b/src/game/player.h
@@ -20,10 +20,10 @@ struct PLAYER : OBJECT {
F32 eyeoffset{40.f};
PLAYER_INPUT input;
-
VEC3 mins;
VEC3 maxs;
+ F32 maxspeed{70.f};
VEC3 velocity;
};
diff --git a/src/game/world/bsp.cpp b/src/game/world/bsp.cpp
index a95a693..c6c9726 100644
--- a/src/game/world/bsp.cpp
+++ b/src/game/world/bsp.cpp
@@ -736,6 +736,7 @@ BSP* bsp_build_map( WORLD_MAP* m ) {
bsp_build_portals( bsp );
bsp_build_pvs( bsp );
+ m->bsp = bsp;
return bsp;
}
diff --git a/src/game/world/map.cpp b/src/game/world/map.cpp
index aba283d..53cfe22 100644
--- a/src/game/world/map.cpp
+++ b/src/game/world/map.cpp
@@ -518,13 +518,13 @@ WORLD_MAP* map_from_file( GAME_DATA* game, const char* path ) {
CFG_SECTION* skybox = cfg_section( s, "skybox" );
if( !skybox ) {
dlog( errstr, path, "skybox" );
- dlog( "using default skybox" );
+ dlog( "using default skybox\n" );
m->skybox = map_default_skybox();
}
else {
if( !OK( map_skybox_from_section( skybox, game, m ) ) ) {
dlog( errstr, path, "skybox" );
- dlog( "using default skybox" );
+ dlog( "using default skybox\n" );
m->skybox = map_default_skybox();
}
}
diff --git a/src/game/world/trace.cpp b/src/game/world/trace.cpp
index c8ad3be..43f5f68 100644
--- a/src/game/world/trace.cpp
+++ b/src/game/world/trace.cpp
@@ -152,7 +152,10 @@ U8 bsp_trace( BSP_TRACE* trace, BSP* bsp, VEC3 start, VEC3 end ) {
trace->in_start = start;
trace->in_end = end;
- return bsp_trace( trace, bsp );
+ U8 ret = bsp_trace( trace, bsp );
+ if( !ret )
+ trace->point = trace->in_end;
+ return ret;
}
U8 bsp_trace( BSP_TRACE* trace, BSP* bsp ) {
@@ -378,6 +381,8 @@ U8 bsp_trace( BSP_TRACE* trace, BSP* bsp, AABB hull ) {
VEC3 end = trace->in_end;
U8 ret = bsp_trace_sweep_aabb( trace, bsp, hull, start, end, bsp->root );
+ if( !ret )
+ trace->point = trace->in_end;
return ret;
}
diff --git a/src/util/fnv.h b/src/util/fnv.h
index 534063d..679bea9 100644
--- a/src/util/fnv.h
+++ b/src/util/fnv.h
@@ -1,4 +1,5 @@
#pragma once
+#include "string.h"
#include "typedef.h"
typedef U32 FNV1A;
@@ -8,13 +9,6 @@ enum : FNV1A {
FNV1A_BASIS = 0x811C9DC5
};
-inline constexpr U32 strlen_ct( const char* str ) {
- U32 s = 0;
- for( ; !!str[s]; ++s );
-
- return s;
-}
-
inline constexpr FNV1A fnv1a( const U8* data, const U32 size ) {
FNV1A out = FNV1A_BASIS;
diff --git a/src/util/input.cpp b/src/util/input.cpp
index 284ebfe..1222658 100644
--- a/src/util/input.cpp
+++ b/src/util/input.cpp
@@ -83,7 +83,7 @@ U8 input_is_key_down( U32 key ) {
return input.keys[key];
}
-void input_capture_mouse( bool capture ) {
+void input_capture_mouse( U8 capture ) {
input_reset_mouse_accumulator();
input.mouselock = capture;
SDL_SetRelativeMouseMode( capture ? SDL_TRUE : SDL_FALSE );
diff --git a/src/util/input.h b/src/util/input.h
index 86d715c..1317f94 100644
--- a/src/util/input.h
+++ b/src/util/input.h
@@ -38,7 +38,7 @@ using ON_INPUT_FN = std::function<void( SDL_Event* )>;
struct INPUT_DATA {
MOUSE_DATA mouse;
U8 keys[0xff];
- bool mouselock;
+ U8 mouselock;
F32 msens = .3f;
F32 myaw = 1.f;
F32 mpitch = 1.f;
@@ -54,7 +54,7 @@ extern void input_frame_end();
extern void input_on_event( SDL_Event* e );
extern void input_on_mouse( I32 type, I32 x, I32 y );
extern U8 input_is_key_down( U32 key );
-extern void input_capture_mouse( bool capture );
+extern void input_capture_mouse( U8 capture );
extern void input_reset_mouse_accumulator();
#define kb_down( key ) input_is_key_down( key )
diff --git a/src/util/string.h b/src/util/string.h
index b261e76..47da1a0 100644
--- a/src/util/string.h
+++ b/src/util/string.h
@@ -3,9 +3,15 @@
#include "typedef.h"
+constexpr U32 strlen_ct( const char* str ) {
+ U32 len = 0;
+ for( ; !!str[len]; ++len );
+ return len;
+}
+
template <U32 N>
struct STR {
- char data[N];
+ char data[N]{ 0 };
enum {
size = N
};
@@ -13,11 +19,31 @@ struct STR {
memset( data, 0, N );
}
STR( const char* str ) {
- memcpy( data, str, N );
+ memcpy( data, str, strlen_ct( str ) );
}
STR( const STR<N>& str ) {
memcpy( data, str.data, N );
}
+ template <U32 other>
+ auto operator+( const STR<other>& rhs ) {
+ constexpr U32 l1 = strlen_ct( data );
+ constexpr U32 l2 = strlen_ct( rhs.data );
+
+ constexpr U32 high = N > other ? N : other;
+ constexpr U32 max = (l1 + l2 > high) ? l1 + l2 + 1 : high;
+
+ STR<max> result;
+ memcpy( result.data, data, l1 );
+ memcpy( result.data + l1, rhs.data, l2 );
+ result.data[l1 + l2] = '\0';
+ return result;
+ }
+
+ template <U32 other>
+ auto concat( const STR<other>& str ) {
+ return *this + str;
+ }
+
operator char*() { return data; }
};