#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 ) { GL_3D* gl3d = (GL_3D*)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* 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 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 ) { 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 }; }