summaryrefslogtreecommitdiff
path: root/src/game/world/draw.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/game/world/draw.cpp')
-rw-r--r--src/game/world/draw.cpp216
1 files changed, 216 insertions, 0 deletions
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<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 ) {
+ 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 };
+}