#include #include "gif.c" #include #include "jdebug.h" #define M_PI 3.14159265358979323846 #define DEG_TO_RAD M_PI/180.0f struct vec2 { float x, y; }; struct wall { Uint16 v1, v2; vec2 normal; float u1, u2; int portal; }; struct sector { float floor_height; float ceiling_height; std::vector walls; std::vector verts; }; SDL_Window *sdl_window; SDL_Renderer *sdl_renderer; SDL_Texture *sdl_texture; Uint32 *palette; Uint8 *gif; Uint8 screen[320*240]; std::vector sectors; int current_sector; vec2 position = { 128.0f, 192.0f }; float height = 48.0f; float real_height = 48.0f; float orientation = 90.0f; float accel = 500.0f; float speed = 0.0f; float max_speed = 200.0f; float rspeed = 200.0f; float bobbing = 0.0f; float dt; // Returns 1 if the lines intersect, otherwise 0. In addition, if the lines // intersect the intersection point may be stored in the floats i_x and i_y. const bool get_line_intersection(vec2 p0, vec2 p1, vec2 p2, vec2 p3, vec2 *i) { vec2 s1, s2; s1.x = p1.x - p0.x; s1.y = p1.y - p0.y; s2.x = p3.x - p2.x; s2.y = p3.y - p2.y; float s, t; s = (-s1.y * (p0.x - p2.x) + s1.x * (p0.y - p2.y)) / (-s2.x * s1.y + s1.x * s2.y); t = ( s2.x * (p0.y - p2.y) - s2.y * (p0.x - p2.x)) / (-s2.x * s1.y + s1.x * s2.y); if (s >= 0 && s <= 1 && t >= 0 && t <= 1) { // Collision detected if (i != NULL) { i->x = p0.x + (t * s1.x); i->y = p0.y + (t * s1.y); } return true; } return false; // No collision } inline const float distance(vec2 p1, vec2 p2) { const float xx = p2.x-p1.x; const float yy = p2.y-p1.y; return SDL_sqrtf(xx*xx+yy*yy); } inline const void normalize(vec2 *v) { const float length = SDL_sqrtf(v->x*v->x + v->y*v->y); if (length > 0.0f) { v->x /= length; v->y /= length; } } inline const float dot(vec2 v1, vec2 v2) { return v1.x*v2.x + v1.y*v2.y; } void putp(int x, int y, Uint8 color) { if (x<0 || y<0 || x>=320 || y>=240) return; screen[x+y*320]=color; } void line(int x1, int y1, int x2, int y2, Uint8 color) { float dx = float(x2-x1); float dy = float(y2-y1); float steps = SDL_max(SDL_fabsf(dx), SDL_fabsf(dy)); if (steps==0) { putp(x1,y1,color); return; } float incx = dx / steps; float incy = dy / steps; float x = (float)x1; float y = (float)y1; for (int i=0; i<=int(steps); ++i) { putp(int(x+0.5f),int(y+0.5f),color); x += incx; y += incy; } } namespace map { void putp(int x, int y, Uint8 color) { ::putp(160+(x/8), 120+(y/8), color); } void line(int x1, int y1, int x2, int y2, Uint8 color) { ::line(160+(x1/8), 120+(y1/8), 160+(x2/8), 120+(y2/8), color); } } void createMap() { current_sector = 0; { sector s; s.floor_height = 32.0f; s.ceiling_height = 128.0f; s.verts.push_back({ 64.0f, 0.0f}); s.verts.push_back({256.0f, 0.0f}); s.verts.push_back({256.0f, 64.0f}); s.verts.push_back({320.0f, 64.0f}); s.verts.push_back({320.0f, 320.0f}); s.verts.push_back({ 0.0f, 320.0f}); s.verts.push_back({ 0.0f, 64.0f}); s.verts.push_back({ 64.0f, 64.0f}); s.walls.push_back({0,1,{0,0},0.0f,0.0f,1}); s.walls.push_back({1,2,{0,0},0.0f,0.0f,-1}); s.walls.push_back({2,3,{0,0},0.0f,0.0f,-1}); s.walls.push_back({3,4,{0,0},0.0f,0.0f,-1}); s.walls.push_back({4,5,{0,0},0.0f,0.0f,-1}); s.walls.push_back({5,6,{0,0},0.0f,0.0f,-1}); s.walls.push_back({6,7,{0,0},0.0f,0.0f,-1}); s.walls.push_back({7,0,{0,0},0.0f,0.0f,-1}); sectors.push_back(s); } { sector s; s.floor_height = 0.0f; s.ceiling_height = 96.0f; s.verts.push_back({256.0f, 0.0f}); s.verts.push_back({ 64.0f, 0.0f}); s.verts.push_back({ 64.0f,-256.0f}); s.verts.push_back({256.0f,-256.0f}); s.walls.push_back({0,1,{0,0},0.0f,0.0f,0}); s.walls.push_back({1,2,{0,0},0.0f,0.0f,-1}); s.walls.push_back({2,3,{0,0},0.0f,0.0f,-1}); s.walls.push_back({3,0,{0,0},0.0f,0.0f,-1}); sectors.push_back(s); } for (auto &s : sectors ) { for (auto &w : s.walls ) { w.u2 = distance(s.verts[w.v1], s.verts[w.v2]) / 64.0f; vec2 norm = { s.verts[w.v2].x - s.verts[w.v1].x, s.verts[w.v2].y - s.verts[w.v1].y}; normalize(&norm); const float tmp = norm.x; norm.x = -norm.y; norm.y = tmp; w.normal = norm; } } } void drawColumn(sector &s, int screen_column, int start, int end, float a_inc, vec2 infi) { const float angle = orientation + a_inc; vec2 normal = { SDL_cosf(angle*DEG_TO_RAD), SDL_sinf(angle*DEG_TO_RAD) }; vec2 result, tmp_result; wall *w = nullptr; float dist=100000.0f; for (auto &wall : s.walls) { if (dot(normal, wall.normal) >= 0) continue; if (get_line_intersection(position, infi, s.verts[wall.v1], s.verts[wall.v2], &tmp_result)) { const float d = distance(position, tmp_result);// * SDL_cosf(a_inc*DEG_TO_RAD); if (dv2].x-s.verts[w->v1].x, s.verts[w->v2].y-s.verts[w->v1].y}; const vec2 AP = {result.x-s.verts[w->v1].x, result.y-s.verts[w->v1].y}; float v = dot(AP,AB) / dot(AB,AB); v *= w->u2; v = (v-int(v))*tex_height; float wall_height = (sector_height*277)/dist; // [64=altura sector] float wall_cut = 0.0f; float dpix = tex_height/wall_height; // [64=crec que altura sector] float cpix = 0; float wall_start = 120-(wall_height/sector_height)*(sector_height-(height-s.floor_height)); // [64=els dos crec que altura sector] if (wall_start> 1)); float actual_dist = straight_dist / SDL_cosf(a_inc*DEG_TO_RAD); int tx = abs(int(actual_dist * SDL_cosf(angle*DEG_TO_RAD) - position.x)) % tex_height; int ty = abs(int(actual_dist * SDL_sinf(angle*DEG_TO_RAD) - position.y)) % tex_height; putp(screen_column, y, gif[tx+ty*tex_height]); } if (w->portal == -1) { // Pinta la pared for (int i=0; i=end) break; putp(screen_column, wall_start+i, gif[(int(v)%tex_height)+int(cpix)*tex_height]); cpix += dpix; } } else { if (sectors[w->portal].ceiling_height < s.ceiling_height) { float upper_height = s.ceiling_height - sectors[w->portal].ceiling_height; float upper_wall_height = (upper_height*277)/dist; upper_wall_height -= wall_cut; if (upper_wall_height>0.0f) { // Pinta la pared for (int i=0; i=end) break; putp(screen_column, wall_start+i, gif[(int(v)%tex_height)+int(cpix)*tex_height]); cpix += dpix; } wall_start += upper_wall_height; wall_height -= upper_wall_height; } } float lower_height = sectors[w->portal].floor_height - s.floor_height; float lower_wall_height = (lower_height*277)/dist; int wall_end = wall_start+wall_height+1; if (lower_wall_height>0.0f) wall_end -= lower_wall_height; wall_end = SDL_min(wall_end, end); drawColumn(sectors[w->portal], screen_column, wall_start, wall_end, a_inc, infi); if (lower_wall_height>0.0f) { cpix += (wall_height-lower_wall_height)*dpix; // Pinta la pared for (int i=0; i=end) break; putp(screen_column, wall_end+i, gif[(int(v)%tex_height)+int(cpix)*tex_height]); cpix += dpix; } //wall_start += upper_wall_height; //wall_height -= upper_wall_height; } } // Pinta el piso int paint_end = wall_start+wall_height-1; for (int y=paint_end+1; y> 1)); float actual_dist = straight_dist / SDL_cosf(a_inc*DEG_TO_RAD); int tx = abs(int(actual_dist * SDL_cosf(angle*DEG_TO_RAD) + position.x)) % tex_height; int ty = abs(int(actual_dist * SDL_sinf(angle*DEG_TO_RAD) + position.y)) % tex_height; putp(screen_column, y, gif[tx+ty*tex_height]); } //line(screen_column, 120-(wall_height), screen_column, 120+(wall_height), 5); } } bool tryMove(float angle, float speed) { float sign = speed > 0 ? 1.0f : -1.0f; bool moved = false; sector &s = sectors[current_sector]; vec2 newpos = { position.x + SDL_cosf(angle*DEG_TO_RAD)*5*sign, position.y }; bool collision=false; for (auto w : s.walls) { if (get_line_intersection(position, newpos, s.verts[w.v1], s.verts[w.v2], NULL)) { if (w.portal != -1) { newpos.x = position.x + SDL_cosf(angle*DEG_TO_RAD)*dt*speed; if (get_line_intersection(position, newpos, s.verts[w.v1], s.verts[w.v2], NULL) ) current_sector = w.portal; } else { collision=true; } break; } } if (!collision) { moved = true; position.x += SDL_cosf(angle*DEG_TO_RAD)*dt*speed; } newpos = { position.x, position.y + SDL_sinf(angle*DEG_TO_RAD)*5*sign }; collision=false; for (auto w : s.walls) { if (get_line_intersection(position, newpos, s.verts[w.v1], s.verts[w.v2], NULL)) { if (w.portal != -1) { newpos.y = position.y + SDL_sinf(angle*DEG_TO_RAD)*dt*speed; if (get_line_intersection(position, newpos, s.verts[w.v1], s.verts[w.v2], NULL) ) current_sector = w.portal; } else { collision=true; } break; } } if (!collision) { moved = true; position.y += SDL_sinf(angle*DEG_TO_RAD)*dt*speed; } return moved; } int main(int argc, char *argv[]) { SDL_Init(SDL_INIT_VIDEO); sdl_window = SDL_CreateWindow("WOLF", 640, 480, 0); SDL_SetWindowPosition(sdl_window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); sdl_renderer = SDL_CreateRenderer(sdl_window, NULL); sdl_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 320, 240); debug::init(sdl_renderer); FILE *f = fopen("walls.gif", "rb"); fseek(f, 0, SEEK_END); int filesize = ftell(f); fseek(f, 0, SEEK_SET); Uint8 *buffer = (Uint8*)malloc(filesize); fread(buffer, filesize, 1, f); fclose(f); Uint16 w, h; gif = LoadGif(buffer, &w, &h); palette = LoadPalette(buffer); free(buffer); createMap(); SDL_Event e; bool should_exit = false; Uint32 millis = SDL_GetTicks(); int fps = 0; int fps_count = 0; int fps_time = SDL_GetTicks(); while (!should_exit) { dt = float(SDL_GetTicks() - millis)/1000.0f; millis = SDL_GetTicks(); bobbing += dt*1000.0f; height = real_height + SDL_sinf(bobbing*DEG_TO_RAD)*(speed/100.0f); while (SDL_PollEvent(&e)) { if (e.type==SDL_EVENT_QUIT) { should_exit=true; break; } if (e.type==SDL_EVENT_KEY_DOWN && e.key.scancode==SDL_SCANCODE_ESCAPE) { should_exit=true; break; } } const bool *keys = SDL_GetKeyboardState(NULL); if (keys[SDL_SCANCODE_Q]) { real_height += dt*100.0f; } if (keys[SDL_SCANCODE_A]) { real_height -= dt*100.0f; } if (keys[SDL_SCANCODE_W]) { //real_height += dt*100.0f; sectors[0].ceiling_height -= dt*100.0f; sectors[1].ceiling_height -= dt*100.0f; sectors[0].floor_height -= dt*100.0f; sectors[1].floor_height -= dt*100.0f; } if (keys[SDL_SCANCODE_S]) { //real_height -= dt*100.0f; sectors[0].ceiling_height += dt*100.0f; sectors[1].ceiling_height += dt*100.0f; sectors[0].floor_height += dt*100.0f; sectors[1].floor_height += dt*100.0f; } if (keys[SDL_SCANCODE_RIGHT]) { orientation += dt*rspeed; } else if (keys[SDL_SCANCODE_LEFT]) { orientation -= dt*rspeed; } if (keys[SDL_SCANCODE_UP]) { if (speed < max_speed) speed += dt*accel; } else if (keys[SDL_SCANCODE_DOWN]) { if (speed > -max_speed) speed -= dt*accel; } else { if (speed > 0.0f) { speed -= dt*accel; if (speed < 0.0f) speed = 0.0f; } if (speed < 0.0f) { speed += dt*accel; if (speed > 0.0f) speed = 0.0f; } } if (speed != 0.0f) if (!tryMove(orientation, speed)) speed = 0.0f; sector &s = sectors[current_sector]; // Clear screen SDL_memset4(screen, 0x00000000, (320*240)>>2); int screen_column = 0; for (float a_inc=-32.0f; a_inc<=32.0f; a_inc+=0.2f) { const float angle = orientation + a_inc; vec2 infi; infi.x = position.x + SDL_cosf(angle*DEG_TO_RAD)*40000; infi.y = position.y + SDL_sinf(angle*DEG_TO_RAD)*40000; drawColumn(s, screen_column, 0, 240, a_inc, infi); screen_column++; } // Draw map walls int sec = 0; for (auto &s : sectors) { for (auto &w : s.walls) { map::line(s.verts[w.v1].x, s.verts[w.v1].y, s.verts[w.v2].x, s.verts[w.v2].y, sec==current_sector?3:4); } sec++; } // Draw map hero vec2 lookat; lookat.x = position.x + SDL_cosf(orientation*DEG_TO_RAD)*20; lookat.y = position.y + SDL_sinf(orientation*DEG_TO_RAD)*20; map::line(position.x, position.y, lookat.x, lookat.y, 3); map::putp(position.x, position.y, 7); // Send to texture and render Uint32 *pixels; int pitch; SDL_LockTexture(sdl_texture, NULL, (void**)&pixels, &pitch); for (int i=0; i<(320*240); ++i) { pixels[i] = 0xFF000000 | palette[screen[i]]; } SDL_UnlockTexture(sdl_texture); SDL_RenderTexture(sdl_renderer, sdl_texture, NULL, NULL); fps_count++; if (SDL_GetTicks()-fps_time>=1000) { fps = fps_count; fps_count = 0; fps_time = SDL_GetTicks(); } debug::println("fps:", fps); debug::println("sector:", current_sector); debug::println("height:", real_height); debug::println("ceil_height:", sectors[0].ceiling_height); debug::println("floor_height:", sectors[0].floor_height); debug::render(sdl_renderer); SDL_RenderPresent(sdl_renderer); } return 0; }