summaryrefslogtreecommitdiff
path: root/src/game/raycast.cpp
blob: d38eb0ab40a724c788e16c157ebbf71268797bf4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
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_SHADER_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 );
}