From f8b92ce3aa08b1445c9f956d8166830946562d12 Mon Sep 17 00:00:00 2001 From: navewindre Date: Wed, 3 Sep 2025 20:10:09 +0200 Subject: a --- src/game/world/draw.cpp | 216 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 src/game/world/draw.cpp (limited to 'src/game/world/draw.cpp') diff --git a/src/game/world/draw.cpp b/src/game/world/draw.cpp new file mode 100644 index 0000000..9269a21 --- /dev/null +++ b/src/game/world/draw.cpp @@ -0,0 +1,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 +) { + 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 }; +} -- cgit v1.2.3