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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
|
#include "draw.h"
#include "map.h"
#include "../../game.h"
#include "../../render/gl_3d.h"
#include "../objlist.h"
#include "../raycast.h"
VEC2 world_project_pos(
GAME_DATA* game,
VEC3 pos
) {
GL3D* gl3d = (GL3D*)game->shaders.gl3d;
VEC4 clip = { pos.x, pos.z, pos.y, 1.f };
VEC4 ndc;
mat4_mul_vec4( &gl3d->projmat, &clip, &ndc );
if( ndc.w <= 0 )
return { -INFINITY, -INFINITY };
ndc.x /= ndc.w;
ndc.y /= ndc.w;
VEC2 screen = {
( ndc.x * 0.5f + 0.5f ) * gl3d->winsize.x,
( -( ndc.y * 0.5f ) + 0.5f ) * gl3d->winsize.y
};
return screen;
}
void world_draw_wall(
GAME_DATA* game,
WORLD* world,
VERTEX3D* verts,
MAP_WALL* s
) {
SURF_PROPS* p = wall_get_props( world->map, s );
gl_3d_polygon( game->shaders.gl3d, verts, 4, p->tex );
}
void world_draw_walls( GAME_DATA* game, WORLD* world ) {
world->map->walls.each( fn( MAP_WALL* s ) {
SURF_PROPS* props = wall_get_props( world->map, s );
VEC3 start = s->start;
VEC3 end = s->end;
VERTEX3D verts[4]{};
verts[0].pos = { start.x, start.z, start.y };
verts[1].pos = { start.x, start.z + end.z, start.y };
verts[2].pos = { end.x, start.z + end.z, end.y };
verts[3].pos = { end.x, start.z, end.y };
verts[0].uv = { 0, 1 };
verts[1].uv = { 0, 0 };
verts[2].uv = { 1, 0 };
verts[3].uv = { 1, 1 };
verts[0].clr = verts[1].clr = verts[2].clr = verts[3].clr = props->clr;
world_draw_wall( game, world, verts, s );
} );
}
void world_draw_polygon(
GAME_DATA* game,
WORLD* world,
MAP_POLYGON* p,
LIST<VERTEX3D>* verts
) {
SURF_PROPS* props = polygon_get_props( world->map, p );
gl_3d_polygon(
game->shaders.gl3d,
verts->data,
verts->size,
props->tex
);
}
void world_draw_polygons( GAME_DATA* game, WORLD* world ) {
for( U32 i = 0; i < world->map->polygons.size; ++i ) {
MAP_POLYGON* p = &world->map->polygons[i];
SURF_PROPS* props = polygon_get_props( world->map, p );
LIST<VERTEX3D> proj_verts;
U8 visc = p->vertices.size; // todo: visc
p->vertices.each( fn( MAP_VERTEX* mv ) {
VERTEX3D v;
v.pos = { mv->pos.x, mv->pos.z, mv->pos.y };
v.uv = mv->uv;
v.clr = mv->clr * props->clr;
v.sampler = 0;
proj_verts.push( v );
} );
if( proj_verts.size > 2 && visc > 0 ) {
world_draw_polygon( game, world, p, &proj_verts );
}
}
}
U8 sprite_is_occluded(
VEC3 player_pos,
MAP_SPRITE* sprite,
WORLD* world
) {
VEC3 ray_dir = sprite->pos - player_pos;
F32 dist_to_sprite = vec_len2d( ray_dir );
vec_normalize( &ray_dir );
TRACE trace;
trace.startpos = player_pos;
U32 hit = ray_trace( &trace, sprite->pos );
F32 dist = vec_len( trace.endpos - player_pos );
return hit != TRACE_HIT_NONE && dist < dist_to_sprite;
}
void world_draw_sprite(
GAME_DATA* game,
MAP_SPRITE* sprite,
VEC3 player_pos,
WORLD* world
) {
if( sprite_is_occluded( player_pos, sprite, world ) )
return;
VEC3 angle = vec_angles( sprite->pos, player_pos );
gl_3d_plane(
game->shaders.gl3d,
sprite->pos,
sprite->size,
{ -angle.x + 90.f, angle.y - 90.f },
sprite->tex,
sprite->clr
);
}
void world_draw_sprites(
GAME_DATA* game,
WORLD* world
) {
VEC3 player_pos = player_get_view_pos( objl->pl );
for( U32 i = 0; i < world->map->sprites.size; ++i ) {
MAP_SPRITE* sprite = &world->map->sprites[i];
world_draw_sprite( game, sprite, player_pos, world );
}
}
void world_draw( GAME_DATA* game, WORLD* world, VEC2 window, VEC2 winsize ) { _profiled
PLAYER* pl = objl->pl;
VEC2 start = { pl->pos.x, pl->pos.y };
F32 fov = pl->fov;
gl_set_viewport( game->gl, window, winsize );
gl_3d_projection_setup(
game->shaders.gl3d,
{ pl->pos.x, pl->pos.y, pl->pos.z + pl->eyeoffset },
fov,
pl->rot.y,
pl->rot.x,
1.f,
9999.f,
winsize
);
gl_set_clip( game->gl, window, winsize );
glEnable( GL_DEPTH_TEST );
glDepthFunc( GL_LESS );
world_draw_polygons( game, world );
world_draw_walls( game, world );
glDisable( GL_DEPTH_TEST );
world_draw_sprites( game, world );
gl_reset_clip( game->gl );
VEC2 canvas = { (F32)game->gl->canvas_size[0], (F32)game->gl->canvas_size[1] };
gl_set_viewport( game->gl, {}, canvas );
}
VEC2 world_draw_project_point(
VEC3 vertex_pos,
VEC3 player_pos,
F32 player_angle_deg,
F32 fov_deg,
VEC2 window,
VEC2 winsize,
U8* in_view
) {
F32 rx = vertex_pos.x;
F32 ry = vertex_pos.y;
F32 dist = sqrtf( rx*rx + ry*ry );
if( dist < 0.001f )
return { -99999.0f, -99999.0f };
F32 ang = atan2f( ry, rx );
F32 half_fov_rad = m_deg2rad( fov_deg * 0.5f );
if( in_view ) *in_view = (ang >= -half_fov_rad && ang <= half_fov_rad);
F32 cosang = cosf( ang );
F32 normalized_x = (ang + half_fov_rad) / (2.f * half_fov_rad);
F32 screen_x = window.x + normalized_x * winsize.x;
F32 screen_y_center = window.y + winsize.y * 0.5f;
F32 vz = ( vertex_pos.z - player_pos.z );
F32 screen_y = screen_y_center + (vz * winsize.y) / (dist * -cosang);
return { screen_x, screen_y };
}
|