#include #include #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_trace_list( VEC3 start, F32 ang, U32 max_iter ) { WORLD* w = objl->world; if( !w ) return {}; LIST 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 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 ); }