/* * Voxel-a-tord * main.c: * Copyright (c) 2022 986-Studio. All rights reserved. * * Created by Manoƫl Trapier on 28/09/2022. */ #include #include #include #include #include #include #include #include #include bool isRunning = false; int previousFrameTime = 0; double deltaTime = 0; static double updateTime, renderTime, waitTime; static int32_t windowHeight; static int32_t windowWidth; static texture_t heightMap; static texture_t colourMap; static int32_t *yBuffer; static int32_t playerHeight = 70; static fp_num_t playerX = FP_SET_INT(444); static fp_num_t playerY = FP_SET_INT(299); static int32_t playerRot = 0; static int32_t cameraHorizon = 55; static int32_t scaleFactor = 90; static int32_t renderDistance = 300; uint32_t getMicroSecTime() { struct timeval curTime; gettimeofday(&curTime, NULL); return ((curTime.tv_sec) * 1000 * 1000) + (curTime.tv_usec); } void processInput() { SDL_Event event; double direction = FP_GET_INT(1); fp_num_t dx, dy; while(SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: isRunning = false; break; case SDL_KEYDOWN: switch (event.key.keysym.sym) { case SDLK_ESCAPE: isRunning = false; break; case SDLK_LEFT: playerRot = playerRot + 1; break; case SDLK_RIGHT: playerRot = playerRot - 1; break; case SDLK_DOWN: direction = FP_GET_INT(-1); /* fall-trough */ case SDLK_UP: dx = FP_MUL(FP_MUL(fpcos(playerRot), 75), FP_SET_FLT(deltaTime)); dy = FP_MUL(FP_MUL(fpsin(playerRot), 75), FP_SET_FLT(deltaTime)); playerX += FP_MUL(dx, direction); playerY += FP_MUL(dy, direction); break; case SDLK_KP_4: playerX -= FP_SET_INT(1); break; case SDLK_KP_6: playerX += FP_SET_INT(1); break; case SDLK_KP_8: playerY += FP_SET_INT(1); break; case SDLK_KP_2: playerY -= FP_SET_INT(1); break; case SDLK_PAGEUP: playerHeight = playerHeight + 5; break; case SDLK_PAGEDOWN: playerHeight = playerHeight - 5; break; case SDLK_HOME: cameraHorizon = cameraHorizon + 5; break; case SDLK_END: cameraHorizon = cameraHorizon - 5; break; case SDLK_KP_MULTIPLY: scaleFactor = scaleFactor + 3; break; case SDLK_KP_DIVIDE: scaleFactor = scaleFactor - 3; break; case SDLK_KP_PLUS: renderDistance = renderDistance + 3; break; case SDLK_KP_MINUS: renderDistance = renderDistance - 3; break; } break; default: break; } } } void setup() { loadTextureDateFromPng(&heightMap, "assets/D1.png"); loadTextureDateFromPng(&colourMap, "assets/C1W.png"); yBuffer = (int32_t *)calloc(windowWidth, sizeof(int32_t)); isRunning = true; } void update() { uint32_t timeToWait = FRAME_TARGET_TIME - (SDL_GetTicks() - previousFrameTime); uint32_t ticks = getMicroSecTime(); if (timeToWait <= FRAME_TARGET_TIME) { SDL_Delay(timeToWait); } deltaTime = (SDL_GetTicks() - previousFrameTime) / 1000.0; previousFrameTime = SDL_GetTicks(); waitTime = (waitTime + (getMicroSecTime() - ticks) / 1000.) / 2.0; ticks = getMicroSecTime(); /* [...] */ updateTime = (updateTime + (getMicroSecTime() - ticks) / 1000.) / 2.0; } void renderVoxels(tex2_t *p, int32_t angle, int32_t height, int32_t horizon, int32_t scaleHeight, int32_t renderDistance) { fp_num_t angleSin = fpsin(angle); fp_num_t angleCos = fpcos(angle); fp_num_t dz = FP_SET_INT(1), z = FP_SET_INT(1); int i; for(i = 0; i < windowWidth; i++) { yBuffer[i] = FP_SET_INT(windowHeight); } while( FP_GET_INT(z) < renderDistance) { tex2_t pleft; pleft.u = -FP_MUL(angleCos, z) - FP_MUL(angleSin, z) + p->u; pleft.v = FP_MUL(angleSin, z) - FP_MUL(angleCos, z) + p->v; tex2_t pright; pright.u = FP_MUL(angleCos, z) - FP_MUL(angleSin, z) + p->u; pright.v = -FP_MUL(angleSin, z) - FP_MUL(angleCos, z) + p->v; fp_num_t dx = (pright.u - pleft.u); dx = FP_DIV(dx, FP_SET_INT(windowWidth)); fp_num_t dy = (pright.v - pleft.v); dy = FP_DIV(dy, FP_SET_INT(windowWidth)); for(i = 0; i < windowWidth; i++) { int32_t voxelHeight2 = getTextureColour(&heightMap, &pleft); voxelHeight2 = height - voxelHeight2; fp_num_t temp = FP_MUL(FP_DIV(FP_SET_INT(1), z), FP_SET_INT(scaleHeight)); voxelHeight2 = FP_GET_INT(FP_MUL(temp, FP_SET_INT(voxelHeight2))); int32_t heightOnScreen = voxelHeight2 + horizon; if (heightOnScreen > 0 && heightOnScreen < yBuffer[i]) { colour_t voxelColour = getTextureColour(&colourMap, &pleft); drawVLine(i, yBuffer[i], heightOnScreen, voxelColour); yBuffer[i] = heightOnScreen; } pleft.u += dx; pleft.v += dy; } z += dz; #ifdef USE_FLOATING_POINT dz += 0.01; #else dz += 1; // (fp_num_t)(0.1 * FP_DIVISOR); #endif } } void render() { uint32_t ticks = getMicroSecTime(); tex2_t origin = { .u = playerX, .v = playerY }; clearFrameBuffer(MAKE_RGB(0, 0, 0)); drawGrid(20, MAKE_RGB(27, 2, 27)); renderVoxels(& origin, playerRot, playerHeight, cameraHorizon, scaleFactor, renderDistance); renderFrameBuffer(); renderTime = (renderTime + (getMicroSecTime() - ticks) / 1000.) / 2.0; drawText(5, 5, 0x11FF22, "Wait time: %02.2f ms", waitTime); drawText(5, 17, 0x11FF22, "Update time: %02.2f ms", updateTime); drawText(5, 29, 0x11FF22, "Render time: %02.2f ms", renderTime); drawText(5, 41, 0x11FF22, "FPS: %.1f | dT: %.3f", 1000 / (waitTime + updateTime + renderTime), deltaTime); #ifdef USE_FLOATING_POINT drawText(5, 53, 0x11FF22, "P: (%.1f;%.1f) | PH: %.1f | CH: %.1f | SF: %.1f | RD: %.1f", playerX, playerY, playerHeight, cameraHorizon, scaleFactor, renderDistance); #else drawText(5, 53, 0x11FF22, "P: (%d;%d) | PH: %d | CH: %d | SF: %d | RD: %d", FP_GET_INT(playerX), FP_GET_INT(playerY), playerHeight, cameraHorizon, scaleFactor, renderDistance); #endif displayWindowRender(); } int main(int argc, char *argv[]) { int param_i; bool fullScreen = false; MAX_DEBUG_LEVEL = TLOG_DEBUG; Log(TLOG_ALWAYS, NULL, "Starting Voxel Engine (version %s)!", VERSION); windowWidth = 800; windowHeight = 600; for (param_i = 1 ; (param_i < argc) && (argv[param_i][0] == '-') ; param_i++) { switch (argv[param_i][1]) { default: exit(-1); /* Option not recognized */ case 'f': fullScreen = true; break; case 'w': windowWidth = atoi(argv[++param_i]); break; case 'h': windowHeight = atoi(argv[++param_i]); break; #ifdef DYNA_LOG_LEVEL case 'l': MAX_DEBUG_LEVEL = atoi(argv[++param_i]); break; #endif case '-': goto no_more_params; /* Could use break, but this is more clear */ } } no_more_params: isRunning = initialiseWindow(windowWidth, windowHeight, fullScreen); setup(); while (isRunning) { processInput(); update(); render(); } destroyWindow(); return 0; }