summaryrefslogtreecommitdiff
path: root/src/game/raycast.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/game/raycast.cpp')
-rw-r--r--src/game/raycast.cpp162
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 );
+}