#ifdef _WIN32 // грубый способ определения; на деле разница не в ОС, а в компиляторe #include #include #include #else #include #include #include #endif #include #include #include #include /* TILESIZE (и SQRTTILE) лучше не трогать - или быть готовым гадать, где 32 заменить, а где не надо */ /* При изменении размера окна не забыть поменять скайбокс (или добавить масштабируемость) */ #define PI 3.1415926535 // значение числа пи #define DEG 0.01745329 // знаечние 1 градуса в радианах #include "./var.conf" #include "levels/level_test.dat" #define TILESIZE 32 // размер одной клетки карты; также "реальная" высота стены #define SQRTTILE 5 // двоичный логарифм TILESIZE - используется для побитовых операций #if (MAPX > MAPY) // дальность прорисовки #define DOF MAPX #else #define DOF MAPY #endif SDL_Texture *skyT, *wallT, *spriT; typedef struct { float x, y; } vec; float dist(float x1, float y1, float x2, float y2) { return ( sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)) ); } void rotateVector(vec *v, float ang) { /* Матрица поворота в левосторонней системе координат: */ // | cos(a) sin(a) | // | -sin(a) cos(a) | float xr = (v->x)*cos(ang) - (v->y)*sin(ang); float yr = (v->x)*sin(ang) + (v->y)*cos(ang); v->x = xr; v->y = yr; } void sortSprites(int order[], float dist[], int amount) { double factor = 1.24733095; int step = amount-1; double tmp; while(step >= 1) { for(int i = 0; i+step < amount; i++) { if(dist[i] > dist[i+step]) { tmp = dist[i]; dist[i] = dist[i+step]; dist[i+step] = tmp; tmp = (double)order[i]; order[i] = order[i+step]; order[i+step] = (int)tmp; } } step /= factor; } } void scalePPM(int *img) { ; } int processEvents(SDL_Window *window, struct player *P, int map[], int *mpbool, clock_t *prevframe) { // Вычисление FPS clock_t frame; float fps; frame = clock(); fps = CLOCKS_PER_SEC/(double)(frame - *prevframe); *prevframe = frame; printf("%d\t\t\r", (int)fps); int done = 0; SDL_Event event; int xoff = 0; if((P->dx)>0) {xoff = 20;} else {xoff = -20;} int yoff = 0; if((P->dy)>0) {yoff = 20;} else {yoff = -20;} int px64 = (int)(P->x) >> SQRTTILE; int py64 = (int)(P->y) >> SQRTTILE; int pxadd = (int)(P->x + xoff) >> SQRTTILE, pyadd = (int)(P->y + yoff) >> SQRTTILE; int pxsub = (int)(P->x - xoff) >> SQRTTILE, pysub = (int)(P->y - yoff) >> SQRTTILE; while(SDL_PollEvent(&event)) { switch(event.type) { case SDL_WINDOWEVENT_CLOSE: { if(window) { SDL_DestroyWindow(window); window = NULL; done = 1; } } break; case SDL_KEYDOWN: { switch(event.key.keysym.sym) { case SDLK_ESCAPE: done = 1; break; case SDLK_m: // карта *mpbool = !*mpbool; break; case SDLK_e: // взаимодействие int xeoff = 0; if((P->th)<(PI/4) || (P->th)>(7*PI/4)) {xeoff = 25;} else if((P->th)>(3*PI/4) && (P->th)<(5*PI/4)){xeoff = -25;} int yeoff = 0; if((P->th)>(PI/4) && (P->th)<(3*PI/4)) {yeoff = 25;} else if((P->th)>(5*PI/4) && (P->th)<(7*PI/4)) {yeoff = -25;} int exadd = ((int)P->x + xeoff) >> SQRTTILE, eyadd = ((int)P->y + yeoff) >> SQRTTILE; if(map[eyadd*MAPX + exadd] == 4) // дверь map[eyadd*MAPX + exadd] = 0; } } break; case SDL_QUIT: done = 1; break; } } const Uint8 *state = SDL_GetKeyboardState(NULL); if(state[SDL_SCANCODE_A]) P->th -= (RADSPD/fps); if(state[SDL_SCANCODE_W]) { if(map[py64*MAPX + pxadd]==0) {P->x += P->dx;} if(map[pyadd*MAPX + px64]==0) {P->y += P->dy;} // P->x += P->dx; // P->y += P->dy; } if(state[SDL_SCANCODE_S]) { if(map[py64*MAPX + pxsub]==0) {P->x -= P->dx;} if(map[pysub*MAPX + px64]==0) {P->y -= P->dy;} // P->x -= P->dx; // P->y -= P->dy; } if(state[SDL_SCANCODE_D]) P->th += (RADSPD/fps); if(P->th < 0) P->th = 2*PI - 0.0001; if(P->th > 2*PI) P->th = 0.0; P->dx = cos(P->th)*(SPEED/fps); P->dy = sin(P->th)*(SPEED/fps); return done; } void drawMinimap(SDL_Renderer *renderer, int map[], struct player P) { for(int y = 0; y 0) { SDL_SetRenderDrawColor(renderer, 255, 255, 255, 128); } else { SDL_SetRenderDrawColor(renderer, 0, 0, 0, 128); } SDL_Rect wall = {x*TILESIZE/MAPSCALE, y*TILESIZE/MAPSCALE, (TILESIZE-4)/MAPSCALE, (TILESIZE-4)/MAPSCALE}; SDL_RenderFillRect(renderer, &wall); } SDL_Vertex arrow[3]; arrow[0].position.x = (P.x+7*cos(P.th-PI/2))/MAPSCALE; arrow[0].position.y = (P.y+7*sin(P.th-PI/2))/MAPSCALE; arrow[0].color.r = 255; arrow[0].color.g = 220; arrow[0].color.b = 0; arrow[0].color.a = 128; arrow[1].position.x = (P.x+7*cos(P.th+PI/2))/MAPSCALE; arrow[1].position.y = (P.y+7*sin(P.th+PI/2))/MAPSCALE; arrow[1].color.r = 255; arrow[1].color.g = 220; arrow[1].color.b = 0; arrow[1].color.a = 128; arrow[2].position.x = (P.x+20*cos(P.th))/MAPSCALE; arrow[2].position.y = (P.y+20*sin(P.th))/MAPSCALE; arrow[2].color.r = 255; arrow[2].color.g = 220; arrow[2].color.b = 0; arrow[2].color.a = 128; SDL_RenderGeometry(renderer, NULL, arrow, 3, NULL, 0); } void drawSky(SDL_Surface *surface, struct player P) { void *tpixels = NULL; int pitch = 0; SDL_LockTexture(skyT, NULL, &tpixels, &pitch); Uint32 *edited = (Uint32 *)malloc(WIDTH*HEIGHT/2*sizeof(Uint32)); for(int y = 1; y < HEIGHT/2; y++) for(int x = 0; x < WIDTH; x++) { int xo = (int)(-P.th*WIDTH/(FOV*DEG)) - x; if(xo < 0) {xo += WIDTH;} xo %= WIDTH; int index = (y*WIDTH + xo)*3; char red = sky[index]; char green = sky[index +1]; char blue = sky[index +2]; edited[y*WIDTH + x] = SDL_MapRGB(surface->format, red, green, blue); } memcpy(tpixels, edited, sizeof(WIDTH*HEIGHT/2*sizeof(Uint32))); SDL_UnlockTexture(skyT); free(edited); } void drawRays(SDL_Renderer *renderer, SDL_Surface *surface, struct player P, int map[], int mapF[], int mapC[], float zbuffer[]) { int mx, my, mp, dof, texture_iv = 4, texture_ih = 4, texture_i; float rx, ry, ra, xo, yo, px, py, hlen, vlen, sinra, cosra, hx = 0, hy = 0, vx = 0, vy = 0, rdist; void *tpixels = NULL; int pitch = 0; SDL_LockTexture(wallT, NULL, &tpixels, &pitch); Uint32 *pixels = (Uint32 *)malloc(WIDTH*HEIGHT*sizeof(Uint32)); ra=P.th-FOV/2*DEG; if(ra<0) ra += 2*PI; if(ra>2*PI) ra -= 2*PI; px = P.x; py = P.y; float antan; for(int r = 0; r>SQRTTILE)<0.001) { ry = (((int)py>>SQRTTILE)<>SQRTTILE; my = (int)(ry)>>SQRTTILE; mp = my*MAPX+mx; if(mp > 0 && mp < MAPX*MAPY && map[mp]>0){ hx = rx; hy = ry; hlen = dist(px, py, hx, hy); texture_ih = map[mp]-1; break; } else { rx += xo; ry += yo; dof++; } } // ----- по вертикальным ----- dof = 0; vlen = 100000; cosra = cos(ra); antan=-tan(ra); if(cosra<-0.001) // "влево" { rx = (((int)px>>SQRTTILE)<0.001) { rx = (((int)px>>SQRTTILE)<>SQRTTILE; my = (int)(ry)>>SQRTTILE; mp = my*MAPX+mx; if(mp > 0 && mp < MAPX*MAPY && map[mp]>0){ vx = rx; vy = ry; vlen = dist(px, py, vx, vy); texture_iv = map[mp]-1; break; } else { rx += xo; ry += yo; dof++; } } float shade = 1; if(vlen < hlen) { rx = vx; ry = vy; rdist = vlen; texture_i = texture_iv; } else { rx = hx; ry = hy; rdist = hlen; shade = 0.6; texture_i = texture_ih; } // ----- Рисуем стены ----- float corran = P.th - ra; if(corran<0) corran += 2*PI; if(corran>2*PI) corran -= 2*PI; rdist *= cos(corran); // рыбий глаз float lineh = (TILESIZE*HEIGHT)/rdist; float ty_step = 32.0 / lineh; float ty_offset = 0; if(lineh>HEIGHT){ ty_offset = (lineh-HEIGHT)/2.0; lineh=HEIGHT; } float lineo = HEIGHT/2 - lineh/2; float ty = ty_offset*ty_step; // Текстурирование float tx = 0; if(vlen > hlen) { tx = (int)(rx/(TILESIZE/32)) % 32; if(ra < 180*DEG) {tx = 31-tx;} } else { tx = (int)(ry/(TILESIZE/32)) % 32; if(ra>90*DEG && ra < 270*DEG) {tx = 31-tx;} } int y = 0; for(y = lineo; yformat, red, green, blue); } // ----- рисуем пол ----- for(y = lineo+lineh; yformat, red, green, blue); // ----- рисуем потолок ----- mp = mapC[( (int)(ty/32.0)*MAPX + (int)(tx/32.0) )%(MAPX*MAPY)]; if(mp > 0) { index = (((int)(ty)&31)*32 + ((int)(tx)&31))*3 + (mp-1)*32*32*3; // index %= sizeof(textures)/sizeof(textures[0]); red = textures[index]*0.7; green = textures[index+1]*0.7; blue = textures[index+2]*0.7; for(int i = 0; iformat, red, green, blue); } } for(int p = 0; p < PIXELSIZE; p++){ zbuffer[(int)(r*PIXELSIZE)+p] = rdist; } ra += (double)FOV/((double)WIDTH/(double)PIXELSIZE)*DEG; if(ra<0) ra += 2*PI; if(ra>2*PI) ra -= 2*PI; } memcpy(tpixels, pixels, (size_t)(surface->pitch * surface->h)); SDL_UnlockTexture(wallT); free(pixels); } void drawSprites(SDL_Renderer *renderer, SDL_Surface *surface, struct player P, float zbuffer[]) { void *tpixels = NULL; int pitch = 0; SDL_LockTexture(spriT, NULL, &tpixels, &pitch); Uint32 *pixels = (Uint32 *)malloc(WIDTH*HEIGHT*sizeof(Uint32)); int spnum = sizeof(sp)/sizeof(sp[0]); int sp_order[spnum]; float sp_dist[spnum]; for(int i = 0; i < spnum; i++) { sp_order[i] = i; sp_dist[i] = ( (P.x - sp[i].x)*(P.x - sp[i].x) + (P.y - sp[i].y)*(P.y - sp[i].y) ); } sortSprites(sp_order, sp_dist, spnum); for(int i = 0; i < spnum; i++) { double relX = sp[sp_order[i]].x - P.x; // относительные координаты спрайта double relY = sp[sp_order[i]].y - P.y; /* трансформируем координаты в экранные засчет умножения на обратную матрицу камеры */ // | нпY -нпX | // -1/(плX * нпY - нпX * плY) * | | // | -плY плX | // где (напX, напY) - вектор направления, (плX, плY) - вектор плоскости камеры // __ ___ // |пл| = |нап| * tg(FOV/2) // |нап| = HEIGHT // нап = (HEIGHT, 0) // пл = (0, HEIGHT*tg(FOV/2)) // vec dir = {HEIGHT, 0}; // vec plane = {0, HEIGHT*tan(FOV/2*DEG)}; vec dir = {TILESIZE, 0}; vec plane = {0, TILESIZE*tan(FOV/2*DEG)}; rotateVector(&dir, P.th); rotateVector(&plane, P.th); double invDet = 1.0 / (plane.x * dir.y - plane.y * dir.x); // | нпY -нпХ | | x_отн | | x_тр | // 1/(плХ * нпY - нпХ * плY) * | | * | | = | | // | плY плX | | y_отн | | y_тр | // x_тр - координата X в плоскости камеры, y_тр - фактор масштабирования double trX = invDet * (dir.y * relX - dir.x * relY); // трансформируем координаты в координаты камеры double trY = invDet * ((-plane.y) * relX + plane.x * relY); int screenX = (int)((WIDTH/2) * (1 + trX / trY)); int sp_height = abs((int)(HEIGHT / trY)); int startY = HEIGHT/2 - sp_height/2; if(startY < 0) {startY = 0;} int endY = HEIGHT/2 + sp_height/2; if(endY >= HEIGHT){endY = HEIGHT-1;} int sp_width = abs((int)(WIDTH / trY)); // возможно, тут WIDTH? int startX = screenX - sp_width/2; if(startX < 0) {startX = 0;} int endX = screenX + sp_width/2; if(endX >= WIDTH){endX = WIDTH-1;} for(int s = startX; s < endX; s++) { int tx = (int)(32 * (s - (screenX - sp_width/2)) * 32 / sp_width) /32; if(trY > 0 && s > 0 && s < WIDTH && trY*TILESIZE < zbuffer[s]) { for(int y = startY; y < endY; y++) { int d = (y*2 - HEIGHT + sp_height)*(32/2); int ty = (d * 32 / sp_height) /32; int mp = 4; int index = (((int)(ty)&31)*32 + ((int)(tx)&31))*3 + (mp-1)*32*32*3; int red = textures[index]; int green = textures[index+1]; int blue = textures[index+2]; for(int i = 0; iformat, red, green, blue); } } } } } memcpy(tpixels, pixels, (size_t)(surface->pitch * surface->h)); SDL_UnlockTexture(spriT); free(pixels); } int main(int argc, char *argv[]) { SDL_Window *window = NULL; SDL_Renderer *renderer = NULL; SDL_Surface *surface = NULL; // SDL_Texture *texture = NULL; SDL_Init(SDL_INIT_VIDEO); window = SDL_CreateWindow("Raycast", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WIDTH, HEIGHT, 0); renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); surface = SDL_GetWindowSurface(window); // SDL_SetSurfaceRLE(surface, 1); // texture = SDL_CreateTexture(renderer, SDL_GetWindowPixelFormat(window), SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT); skyT = SDL_CreateTexture(renderer, SDL_GetWindowPixelFormat(window), SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT); wallT = SDL_CreateTexture(renderer, SDL_GetWindowPixelFormat(window), SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT); spriT = SDL_CreateTexture(renderer, SDL_GetWindowPixelFormat(window), SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT); clock_t prevframe = clock(); clock_t *pnt_pframe = &prevframe; int done = 0; // bool завершения программы int mpbool = 0; // bool вкл/выкл карты // void *tpixels = NULL; // int pitch = 0; float zbuffer[WIDTH*PIXELSIZE+1] = {0}; while(!done) { // --- ПРОВЕРКА СОБЫТИЙ --- if(processEvents(window, &P, layoutW, &mpbool, pnt_pframe) == 1) done = 1; // --- РЕНДЕР --- SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); // фон SDL_RenderClear(renderer); // SDL_LockTexture(texture, NULL, &tpixels, &pitch); drawSky(surface, P); drawRays(renderer, surface, P, layoutW, layoutF, layoutC, zbuffer); drawSprites(renderer, surface, P, zbuffer); // SDL_UnlockTexture(texture); SDL_RenderCopy(renderer, skyT, NULL, NULL); SDL_RenderCopy(renderer, wallT, NULL, NULL); SDL_RenderCopy(renderer, spriT, NULL, NULL); // SDL_RenderCopy(renderer, texture, NULL, NULL); if(mpbool) drawMinimap(renderer, layoutW, P); SDL_RenderPresent(renderer); // SDL_Delay(DELAY); } SDL_DestroyWindow(window); SDL_DestroyRenderer(renderer); // SDL_DestroyTexture(texture); SDL_Quit(); printf("!%d!", errno); return 0; }