// Voir README.md pour license précise, par Fcalva 2023-2024 et est sous GPLv3 #include #include #include #include #include #include #include #include "render-fx.h" #include #include #include "fixed.h" #include "gint/display-cg.h" #include "gint/display-fx.h" #include "moteur.h" #include "map.h" // moteur.c : // ici se trouvent tout ce qui concerne les graphismes, mouvement et collisions // // void move() { extern int frame_time; fixed_t moveSpeed = fmul(fix(frame_time), 0x148); //frame_time * fix(carrés/seconde/1000) là carrés/seconde = 5 fixed_t rotSpeed = fmul(fix(frame_time), 0x83); //frame_time * fix(radians/seconde/1000) là radians/seconde = 2 fixed_t c_rotSpeed = fix(cos(f2float(rotSpeed))); fixed_t s_rotSpeed = fix(sin(f2float(rotSpeed))); extern uint8_t map_test[map_w][map_h]; extern fixed_t planeX; extern fixed_t planeY; extern fixed_t dirX; extern fixed_t dirY; extern fixed_t posX; extern fixed_t posY; fixed_t oldDirX; fixed_t oldPlaneX; int xtemp1; int ytemp1; int xtemp2; int ytemp2; if (keydown(KEY_UP)) { xtemp1 = f2int(posX + fmul(dirX, moveSpeed) - 0xF); ytemp1 = f2int(posY); xtemp2 = f2int(posX); ytemp2 = f2int(posY + fmul(dirY, moveSpeed) - 0xF); if(map_test[xtemp1][ytemp1] == 0) posX += fmul(dirX, moveSpeed); if(map_test[xtemp2][ytemp2] == 0) posY += fmul(dirY, moveSpeed); } //move backwards if no wall behind you if (keydown(KEY_DOWN)) { xtemp1 = f2int(posX - fmul(dirX, moveSpeed) + 0xF); ytemp1 = f2int(posY); xtemp2 = f2int(posX); ytemp2 = f2int(posY - fmul(dirY, moveSpeed) + 0xF); if(map_test[xtemp1][ytemp1] == 0) posX -= fmul(dirX, moveSpeed); if(map_test[xtemp2][ytemp2] == 0) posY -= fmul(dirY, moveSpeed); } //rotate to the rightdouble sin_rotspeed; if (keydown(KEY_RIGHT)) { //both camera direction and camera plane must be rotated oldDirX = dirX; dirX = (fmul(dirX, c_rotSpeed)+1) - (fmul(dirY, -s_rotSpeed)+1); dirY = (fmul(oldDirX, -s_rotSpeed)+1) + (fmul(dirY, c_rotSpeed)+1); oldPlaneX = planeX; planeX = (fmul(planeX, c_rotSpeed)+1) - (fmul(planeY, -s_rotSpeed)+1); planeY = (fmul(oldPlaneX, -s_rotSpeed)+1) + (fmul(planeY, c_rotSpeed)+1); } //rotate to the left if (keydown(KEY_LEFT)) { //both camera direction and camera plane must be rotated oldDirX = dirX; dirX = (fmul(dirX, c_rotSpeed)-1) - (fmul(dirY, s_rotSpeed)-1); dirY = (fmul(oldDirX, s_rotSpeed)+1) + (fmul(dirY, c_rotSpeed)+1); oldPlaneX = planeX; planeX = (fmul(planeX, c_rotSpeed)-1) - (fmul(planeY, s_rotSpeed) - 1); planeY = (fmul(oldPlaneX, s_rotSpeed)+1) + (fmul(planeY, c_rotSpeed) + 1); } if (dirX > 0xFFFF) dirX = 0xFFFF; if (dirY > 0xFFFF) dirY = 0xFFFF; if (dirX < -0xFFFF) dirX = -0xFFFF; if (dirY < -0xFFFF) dirY = -0xFFFF; } void spawn_gen(){ extern fixed_t posX; extern fixed_t posY; extern fixed_t dirX; extern fixed_t dirY; extern fixed_t planeX; extern fixed_t planeY; extern uint8_t map_test[map_w][map_h]; int seed, seed_x, seed_y; int SeedSeed; time_t timeN; time(&timeN); SeedSeed = timeN; srand(SeedSeed); while(1){ seed = rand() * 0.5 + rand() * 0.5; seed_x = seed & 0b1111111; seed_y = (seed >> 8) & 0b111111; if (seed_x < map_w && seed_y < map_h && (seed_x < 111 && seed_y > 15)){ if(map_test[seed_x][seed_y] == 0){ break; } } } fixed_t start_dirX, start_dirY; int dirSeed = (seed >> 9) & 0b11 ; switch(dirSeed){ case 0: { start_dirX = 0x0; start_dirY = 0xFFFF; planeX = fix(0.66); planeY = 0; break; } case 1: { start_dirX = 0xFFFF; start_dirY = 0x0; planeX = 0; planeY = fix(-0.66); break; } case 2: { start_dirX = -0xFFFF; start_dirY = 0x0; planeX = 0; planeY = fix(0.66); break; } case 3: { start_dirX = 0x0; start_dirY = -0xFFFF; planeX = fix(-0.66); planeY = 0; break; } default : { start_dirX = 0xFFFF; start_dirY = 0x0; planeX = 0; planeY = fix(-0.66); break; } } posX = fix(seed_x) + 0x7FFF; posY = fix(seed_y) + 0x7FFF; //x and y start position dirX = start_dirX; dirY = start_dirY; //initial direction vector } void load_map(){ spawn_gen(); } #if asm_opti #else //Fonction fournie par le grand Lephé :D inline int __attribute__((always_inline)) get_pixel(bopti_image_t const *img, int u, int v) { int layers = 2; int columns = (img->width + 31) >> 5; uint32_t const *layer_ptr = (void *)img->data; layer_ptr += (v * columns + (u >> 5)) * layers; uint32_t mask = 0x80000000u >> (u & 31); int light = (layer_ptr[0] & mask) != 0; int dark = (layer_ptr[1] & mask) != 0; return (dark << 1) + light; } uint32_t *light, *dark; //Version simplifiée de gpixel dans gint/src/gray/gpixel.c //Tout le crédit revient à Lephé inline void __attribute__((always_inline)) cust_dpixel(int x, int y, color_t color){ if(y >= viewport_h) return; int offset = (y << 2) + (x >> 5); uint32_t mask = 1 << (~x & 31); switch(color){ case C_WHITE: light[offset] &= ~mask; dark [offset] &= ~mask; break; case C_LIGHT: light[offset] |= mask; dark [offset] &= ~mask; break; case C_DARK: light[offset] &= ~mask; dark [offset] |= mask; break; case C_BLACK: light[offset] |= mask; dark [offset] |= mask; break; default: break; } } void dscale_bopti(bopti_image_t *tex, fixed_t scale_x, fixed_t scale_y, int x, int y){ dgray_getvram(&light,&dark); fixed_t screen_y = fix(y); fixed_t tex_y = 0; do{ fixed_t oldy = screen_y; if(screen_y < 0 || screen_y+scale_x > viewport_h) return; do{ fixed_t screen_x = fix(x); fixed_t tex_x = 0; do{ fixed_t oldx = screen_x; int tpix = get_pixel(tex, tex_x, tex_y); if(screen_x < 0 || screen_x+scale_x > viewport_w) return; do{ cust_dpixel(ffloor(oldx), ffloor(oldy), tpix); oldx += 0xFFFF; } while(oldx < screen_x+scale_x); screen_x += scale_x; tex_x++; } while(tex_x < tex->width); oldy += 0xFFFF; } while(oldy < screen_y+scale_y); screen_y += scale_y; tex_y++; } while(tex_y < tex->height); } inline void __attribute__((always_inline)) draw_stripe(bopti_image_t *tex, int texSampleY, int linePos, fixed_t texSize, int texX, int x){ fixed_t screenPos = fix(linePos); for(int texPos = texSampleY; texPos < TSIZE; ++texPos){ if(screenPos >= 0){ fixed_t oldPos = screenPos; int tpix = get_pixel(tex, texX, texPos); do{ oldPos += 0xFFFF; cust_dpixel(x, fround(oldPos), tpix); } while(oldPos < screenPos+texSize); } screenPos += texSize; } } #endif void draw_walls( #if debug EngineTimers *timers #endif ){ extern fixed_t posX; extern fixed_t posY; extern fixed_t dirX; extern fixed_t dirY; extern fixed_t planeX; extern fixed_t planeY; extern bopti_image_t *tex_index[TINDEX_S]; extern uint8_t map_test[map_w][map_h]; fixed_t cameraX; fixed_t rayDirX; fixed_t rayDirY; fixed_t sideDistX;//length of ray from current position to next x or y-side fixed_t sideDistY; fixed_t deltaDistX; fixed_t deltaDistY; fixed_t perpWallDist; fixed_t texSize; int x; int i; int mapX; int mapY; int stepX; //what direction to step in x or y-direction (either +1 or -1) int stepY; int side; //was a NS or a EW wall hit? int lineHeight; int texX; int texSample; int texSampleY; dgray_getvram(&light,&dark); int v_offset = 0; fixed_t h_offset = 0; //struct image_linear_map temp; for(x = 0; x < viewport_w; x++) { #if debug prof_enter(timers->raycast_time); #endif //calculate ray position and direction cameraX = fdiv(fix(x*2), fix(viewport_w)) - 0xFFFF + h_offset; //x-coordinate in camera space rayDirX = dirX + fmul(planeX, cameraX); rayDirY = dirY + fmul(planeY, cameraX); //which box of the map we're in mapX = f2int(posX); mapY = f2int(posY); // length of ray from one x or y-side to next x or y-side // these are derived as: // deltaDistX = sqrt(1 + (rayDirY * rayDirY) / (rayDirX * rayDirX)) // deltaDistY = sqrt(1 + (rayDirX * rayDirX) / (rayDirY * rayDirY)) // which can be simplified to abs(|rayDir| / rayDirX) and abs(|rayDir| / rayDirY) // where |rayDir| is the length of the vector (rayDirX, rayDirY). Its length, // unlike (dirX, dirY) is not 1, however this does not matter, only the // ratio between deltaDistX and deltaDistY matters, due to the way the DDA // stepping further below works. So the values can be computed as below. // Division through zero is prevented, even though technically that's not // needed in C++ with IEEE 754 floating point values. //Fcalva : It is with fp32s ! rayDirX = rayDirX == 0 ? 1 : rayDirX; rayDirY = rayDirY == 0 ? 1 : rayDirY; deltaDistX = abs(fdiv(0xFFFF, rayDirX)); deltaDistY = abs(fdiv(0xFFFF, rayDirY)); //calculate step and initial sideDist if (rayDirX < 0) { stepX = -1; sideDistX = fmul(posX - fix(mapX), deltaDistX); } else { stepX = 1; sideDistX = fmul( fix(mapX + 1) - posX, deltaDistX); } if (rayDirY == 0) { stepY = 0; sideDistY = 0; } else if (rayDirY < 0) { stepY = -1; sideDistY = fmul(posY - fix(mapY), deltaDistY); } else { stepY = 1; sideDistY = fmul( fix(mapY + 1) - posY, deltaDistY); } //perform DDA while(true) { //Check if the ray is out of range/bounds if (sideDistX >= max_dist || sideDistY >= max_dist || mapX < 0 || mapY < 0 || mapX >= map_w || mapY >= map_h) { break; } //Otherwise check if ray has hit a wall if (map_test[mapX][mapY] > 0) { break; } //jump to next map square, either in x-direction, or in y-direction if (sideDistX < sideDistY) { sideDistX += deltaDistX; mapX += stepX; side = 0; } else { sideDistY += deltaDistY; mapY += stepY; side = 1; } } //Calculate distance projected on camera direction. This is the shortest distance from the point where the wall is //hit to the camera plane. Euclidean to center camera point would give fisheye effect! //This can be computed as (mapX - posX + (1 - stepX) / 2) / rayDirX for side == 0, or same formula with Y //for size == 1, but can be simplified to the code below thanks to how sideDist and deltaDist are computed: //because they were left scaled to |rayDir|. sideDist is the entire length of the ray above after the multiple //steps, but we subtract deltaDist once because one step more into the wall was taken above. if (side == 0) perpWallDist = (sideDistX - deltaDistX); else perpWallDist = (sideDistY - deltaDistY); #if debug prof_leave(timers->raycast_time); prof_enter(timers->draw_time); #endif //texturing calculations //calculate value of wallX fixed_t wallX; //where exactly the wall was hit if (side == 0) wallX = posY + fmul(perpWallDist, rayDirY); else wallX = posX + fmul(perpWallDist, rayDirX); wallX -= fix(floor(f2int(wallX))); //x coordinate on the texture texX = fmul(wallX, TSIZE); texX %= TSIZE; if(side == 0 && rayDirX > 0) texX = 64 - texX - 1; if(side == 1 && rayDirY < 0) texX = 64 - texX - 1; lineHeight = f2int(fdiv(fix(viewport_h), perpWallDist)); //Taille en px de la ligne if (lineHeight < 1) lineHeight = 1; //if (lineHeight > viewport_h) lineHeight = viewport_h - 1; fixed_t texSize = fix(lineHeight) / TSIZE; //taille proportionelle de la ligne a la tex if (texSize < fix(1.0/(float)TSIZE)) texSize = fix(1.0/(float)TSIZE); if (texSize > fix((float)viewport_h/(float)TSIZE)) { texSample = fceil(fdiv(fix(viewport_h), texSize)); texSampleY = TSIZE/2 - texSample/2; } else { texSample = TSIZE; texSampleY = 0; } int linePos = viewport_h/2 - lineHeight/2; bopti_image_t *tex = tex_index[map_test[mapX][mapY]]; draw_stripe(tex, texSampleY, linePos, texSize, texX, x); #if debug prof_leave(timers->draw_time); #endif } }