diff options
| author | navewindre <boneyaard@gmail.com> | 2025-09-03 20:10:09 +0200 |
|---|---|---|
| committer | navewindre <boneyaard@gmail.com> | 2025-09-03 20:10:09 +0200 |
| commit | f8b92ce3aa08b1445c9f956d8166830946562d12 (patch) | |
| tree | 94e63a5aec9f8f52b577f56799e0c9201fd976a5 /src/game/raycast.cpp | |
a
Diffstat (limited to 'src/game/raycast.cpp')
| -rw-r--r-- | src/game/raycast.cpp | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/src/game/raycast.cpp b/src/game/raycast.cpp new file mode 100644 index 0000000..c82a3ed --- /dev/null +++ b/src/game/raycast.cpp @@ -0,0 +1,162 @@ +#include <cmath> +#include <math.h> + +#include "raycast.h" +#include "../game.h" + +#include "../render/gl_2d.h" + +#include "objlist.h" +#include "player.h" +#include "world/world.h" +#include "world/map.h" + +const U32 RAY_DEPTH_MAX = 8; + +U8 line_intersects( VEC3 ray_start, VEC2 ray_dir, MAP_WALL* s, VEC3* p, F32* t ) { + VEC2 v1 = VEC2{ ray_start.x, ray_start.y } - VEC2{ s->start.x, s->start.y }; + VEC2 v2 = VEC2{ s->end.x, s->end.y } - VEC2{ s->start.x, s->start.y }; + VEC2 v3 = { -ray_dir.y, ray_dir.x }; + + F32 dot = vec_dot( v2, v3 ); + if( fabsf(dot) < 0.001f ) + return 0; + + F32 t1 = vec_cross( v2, v1 ) / dot; + F32 t2 = vec_dot( v1, v3 ) / dot; + + if( t1 >= 0.0f && ( t2 >= 0.0f && t2 <= 1.0f ) ) { + *t = t1; + *p = ray_start + VEC3{ ray_dir } * (*t); + return 1; + } + + return 0; +} + +F32 ray_calc_wall_hit_dir( VEC3 start, VEC3 end, F32 ang ) { + VEC3 walld = start - end; + vec_normalize( &walld ); + F32 wallang = atan2f( walld.y, walld.x ); + wallang += ang; + if( wallang < 0.f ) wallang += 2.f * M_PI; + if( wallang > 2.f * M_PI ) wallang -= 2.f * M_PI; + return wallang; +} + +LIST<RAY_HITDATA> ray_trace_list( VEC3 start, F32 ang, U32 max_iter ) { + WORLD* w = objl->world; + if( !w ) return {}; + LIST<RAY_HITDATA> hits; + + F32 rayang = m_deg2rad( ang ); + VEC2 raydir = { cos( rayang ), sin( rayang ) }; + VEC3 minp; + + for( U32 i = 0; i < w->map->walls.size; ++i ) { + VEC3 p; + F32 dist; + if( line_intersects( start, raydir, &w->map->walls[i], &p, &dist ) ) { + dist = vec_dist( start, p ); + + VEC3 start = w->map->walls[i].start; + VEC3 end = w->map->walls[i].end; + F32 hitang = ray_calc_wall_hit_dir( start, end, ang ); + hits.push( RAY_HITDATA{ p, dist, hitang, TH_WALL, i } ); + if( hits.size > max_iter ) + break; + } + } + + return hits; +} + +U32 ray_trace( TRACE* tr, F32 ang ) { + F32 rayang = m_deg2rad( ang ); + VEC3 start = tr->startpos; + VEC3 raydir = { cos( rayang ), sin( rayang ), 0.f }; + raydir *= RAY_DEPTH_MAX; + + return ray_trace( tr, raydir ); +} + +U32 ray_trace( TRACE* tr, VEC3 end ) { + WORLD* w = objl->world; + if( !w ) return TRACE_HIT_NONE; + WORLD_MAP* map = w->map; + + VEC3 start = tr->startpos; + VEC3 diff = end - start; + F32 trdist = vec_lensq( diff ); + vec_normalize( &diff ); + VEC2 raydir = { diff.x, diff.y }; + F32 ang = atan2f( raydir.y, raydir.x ); + + F32 mindist = INFINITY; + VEC3 minp; + U32 hitwall = TRACE_HIT_NONE; + + for( U32 i = 0; i < map->walls.size; ++i ) { + VEC3 p; + F32 dist; + + if( line_intersects( start, raydir, &map->walls[i], &p, &dist ) ) { + if( dist <= trdist && dist < mindist ) { + mindist = dist; + minp = p; + hitwall = i; + } + } + } + + if( hitwall != -1 ) { + tr->endpos = { minp.x, minp.y, 0.0f }; + tr->hitwall = hitwall; + + VEC3 start = map->walls[hitwall].start; + VEC3 end = map->walls[hitwall].end; + tr->hitang = ray_calc_wall_hit_dir( start, end, ang ); + return hitwall; + } + + tr->hitwall = -1; + tr->fract = -1.f; + tr->endpos = { start.x + RAY_DEPTH_MAX * raydir.x, start.y + RAY_DEPTH_MAX * raydir.y, 0.0f }; + return TRACE_HIT_NONE; +} + +void draw_player( GAME_DATA* game, PLAYER* pl ) { + GL_PROGRAM* gl2d = game->shaders.gl2d; + VEC2 xy = { pl->pos.x, pl->pos.y }; + + gl_2d_frect( gl2d, xy - VEC2{ 5.f, 5.f }, { 10.f, 10.f }, CLR::RED() ); +} + +void ray_draw_world2d( GAME_DATA* game, WORLD* world ) { + for( U32 i = 0; i < world->map->polygons.size; ++i ) { + MAP_POLYGON* p = &world->map->polygons[i]; + + LIST<VERTEX> verts{}; + SURF_PROPS* props = polygon_get_props( world->map, p ); + p->vertices.each( fn( MAP_VERTEX* mv ) { + VERTEX v{}; + v.pos = VEC2{ mv->pos.x, mv->pos.y }; + v.uv = mv->uv; + v.clr = props->clr; + verts.push( v ); + } ); + + if( props->tex ) + gl_textured_polygon( game->shaders.gl2d_texcoord, verts.data, verts.size, props->tex ); + else + gl_polygon( game->shaders.gl2d, verts.data, verts.size ); + } + + for( U32 i = 0; i < world->map->walls.size; ++i ) { + MAP_WALL* s = &world->map->walls[i]; + SURF_PROPS* p = &world->map->props[s->propid]; + gl_2d_line( game->shaders.gl2d, VEC2( s->start.x, s->start.y ), VEC2( s->end.x, s->end.y ), p->clr ); + } + + draw_player( game, objl->pl ); +} |
