#include #include "utils.h" #include "raymath.h" #include "sort.h" #include "render.h" #define ZBUF(x,y) (zbuf[x + y * 320]) void render(int *map, int mapWidth, int mapHeight, float *height, float *alt, char *texture[], int *texW, int *texH, struct Sprite *sprite, int numSprites, int w, int h, float heye, float posX, float posY, float dirX, float dirY, float planeX, float planeY, char *scrbuf, float *zbuf, int *colbuf, int res ) { //loop counters int x, y, p, q, t; //# pixels draw in column int pxcount; //wall x-intersection float wallX; //intersection loop variables int stepX; int stepY; int side; //sprite data int spriteOrder[numSprites]; float spriteDistance[numSprites]; //clear the screen memset(scrbuf, 0xFF, SCREEN_BYTES_SIZE); //clear z-buffer for (p = 0; p < 320; p++) { for (q = 0; q < 240; q++) { ZBUF(p, q) = 1000; } } for(x = 0; x < w; x += res) { float rayPosX = posX; float rayPosY = posY; int mapX = (int) rayPosX; int mapY = (int) rayPosY; float cameraX = 2 * x / (float) (w) - 1; float raydx = dirX + planeX * cameraX; float raydy = dirY + planeY * cameraX; float sideDistX; float sideDistY; float raydx2 = raydx * raydx, raydy2 = raydy * raydy; float deltaDistX = sqrt(1 + raydy2 / raydx2); float deltaDistY = sqrt(1 + raydx2 / raydy2); float perpWallDist; if (raydx < 0) { stepX = -1; sideDistX = (rayPosX - mapX) * deltaDistX; } else { stepX = 1; sideDistX = (mapX + 1.0 - rayPosX) * deltaDistX; } if (raydy < 0) { stepY = -1; sideDistY = (rayPosY - mapY) * deltaDistY; } else { stepY = 1; sideDistY = (mapY + 1.0 - rayPosY) * deltaDistY; } pxcount = 0; for (t = 0; t < 240; t++) { colbuf[t] = 0; } while (mapX > 0 && mapX < mapWidth && mapY > 0 && mapY < mapHeight ) { if (sideDistX < sideDistY) { sideDistX += deltaDistX; mapX += stepX; side = 0; } else { sideDistY += deltaDistY; mapY += stepY; side = 1; } int mapIdx = mapX * mapWidth + mapY; if (!*(map + mapIdx)) continue; if (pxcount >= 240) break; int texNum = *(map + mapIdx) - 1; float hwall = *(height + mapIdx); float awall = *(alt + mapIdx); if (side == 0) { perpWallDist = fabs((mapX - rayPosX + (1 - stepX) / 2) / raydx); } else { perpWallDist = fabs((mapY - rayPosY + (1 - stepY) / 2) / raydy); } int nextX; int nextY; int nextSide; if (sideDistX < sideDistY) { nextX = mapX + stepX; nextSide = 0; } else { nextY = mapY + stepY; nextSide = 1; } float nextD; if (nextSide == 0) { nextD = fabs((nextX - rayPosX + (1 - stepX) / 2) / raydx); } else { nextD = fabs((nextY - rayPosY + (1 - stepY) / 2) / raydy); } int lo = (int) (h * (0.5 - heye / perpWallDist + awall / perpWallDist)); int lw = (int) (h * hwall / perpWallDist); int lone = (int) (h / perpWallDist); int nlo = (int) (h * (0.5 - heye / nextD + awall / nextD)); int nlw = (int) (h * hwall / nextD); int fstart = h - nlo - nlw; int fend = h - lo - lw - 1; int wstart = h - lo - lw; int wend = h - lo - 1; int bstart = h - lo; int bend = h - nlo; if (side == 1) { wallX = rayPosX + ((mapY - rayPosY + (1 - stepY) / 2) / raydy) * raydx; } else { wallX = rayPosY + ((mapX - rayPosX + (1 - stepX) / 2) / raydx) * raydy; } wallX -= (int) wallX; int texX = (int) (wallX * (float) (texW[texNum])); if(side == 0 && raydx > 0) texX = texW[texNum] - texX - 1; if(side == 1 && raydy < 0) texX = texW[texNum] - texX - 1; //wall loop float texY = 0; float texD = ((float)texH[texNum]) / ((float)lone); int swstart = max(wstart, 0); int swend = min(wend, h - 1); if (swend == h - 1) { texY = (wend - h + 1) * texD; } for (y = swend; y >= swstart; y--) { if (!colbuf[y]) { char *thistex = texture[texNum]; int color = *(thistex + texW[texNum] * (texH[texNum] - 1 - ((int) texY) % texH[texNum]) + texX); setPixelBuf(scrbuf, x, y, color); colbuf[y] = 1; pxcount++; ZBUF(x, y) = perpWallDist; if (res == 2 && x < w - 1) { ZBUF(x + 1, y) = perpWallDist; setPixelBuf(scrbuf, x + 1, y, color); } } texY += texD; } //floor loop int sfstart = max(fstart, 0); int sfend = min(fend, h - 1); for (y = sfstart; y <= sfend; y++) { if (!colbuf[y]) { setPixelBuf(scrbuf, x, y, 8); colbuf[y] = 1; pxcount++; ZBUF(x, y) = nextD; if (res == 2 && x < w - 1) { ZBUF(x + 1, y) = nextD; setPixelBuf(scrbuf, x + 1, y, 8); } } } //bottom loop int sbstart = max(bstart, 0); int sbend = min(bend, h - 1); for (y = sbstart; y <= sbend; y++) { if (!colbuf[y]) { setPixelBuf(scrbuf, x, y, 8); colbuf[y] = 1; pxcount++; ZBUF(x, y) = nextD; if (res == 2 && x < w - 1) { setPixelBuf(scrbuf, x + 1, y, 8); ZBUF(x + 1, y) = nextD; } } } } } int i; //set up sprite distances for(i = 0; i < numSprites; i++) { spriteOrder[i] = i; spriteDistance[i] = ((posX - sprite[i].x) * (posX - sprite[i].x) + (posY - sprite[i].y) * (posY - sprite[i].y)); } //sort the sprites by distance combSort(spriteOrder, spriteDistance, numSprites); for(i = 0; i < numSprites; i++) { float spriteX = sprite[spriteOrder[i]].x - posX; float spriteY = sprite[spriteOrder[i]].y - posY; float spriteZ = sprite[spriteOrder[i]].z; int texNum = sprite[spriteOrder[i]].texNum; char *thistex = texture[texNum]; float invDet = 1.0 / (planeX * dirY - dirX * planeY); float transformX = invDet * (dirY * spriteX - dirX * spriteY); float transformY = invDet * (-planeY * spriteX + planeX * spriteY); int spriteScreenX = (int) ((w / 2) * (1 + transformX / transformY)); int spriteHeight = abs((int) (h / transformY)); //sprite offset int lo = (int) (h * (0.5 - heye / transformY + spriteZ / transformY )); int drawStartY = h - lo - spriteHeight; int drawEndY = h - lo; if (drawEndY > h - 1) drawEndY = h - 1; int texOff = 0; if (drawStartY < 0) { texOff = -drawStartY; drawStartY = 0; } int spriteWidth = abs((int) (h / (transformY))); int drawStartX = -spriteWidth / 2 + spriteScreenX; if(drawStartX < 0) drawStartX = 0; int drawEndX = spriteWidth / 2 + spriteScreenX; if(drawEndX >= w) drawEndX = w - 1; int stripe; //amount we increment texture y by each pixel float texD = ((float)texH[texNum]) / ((float)spriteHeight); texOff *= texD; for(stripe = drawStartX; stripe < drawEndX; stripe += res) { int texX = (int) (256 * (stripe - (-spriteWidth / 2 + spriteScreenX)) * texW[texNum] / spriteWidth) / 256; float texY = texOff; if(transformY > 0 && stripe > 0 && stripe < w) { for(y = drawStartY; y < drawEndY; y++) { if (transformY <= ZBUF(stripe, y)) { if (y >= 0 && y <= h - 1) { int color = thistex[(int)texY * texW[texNum] + texX]; if (color != 0) { setPixelBuf(scrbuf, stripe, y, color); if (res == 2 && stripe < w - 1) { setPixelBuf(scrbuf, stripe + 1, y, color); } } } } texY += texD; } } } } }