#include "haloo3d/haloo3d.h" #include "haloo3d/haloo3dex_console.h" #include "haloo3d/haloo3dex_easy.h" #include "haloo3d/haloo3dex_gen.h" // #include "haloo3d/haloo3dex_img.h" #include "haloo3d/haloo3dex_obj.h" #include "haloo3d/haloo3dex_print.h" #include "unigi/unigi.headers/src/main.h" #include "unigi/unigi.platform.sdl1/src/main.c" #include "ecs2.h" // #include "ecs2_comps.h" #include "keys.h" #include // INteresting flags for performance comparisons #define FASTFILL #define WIDTH 480 #define HEIGHT 300 #define ASPECT ((float)WIDTH / HEIGHT) #define SCREENSCALE 2 #define SWIDTH (WIDTH * SCREENSCALE) #define SHEIGHT (HEIGHT * SCREENSCALE) #define NEARCLIP 0.01 #define FARCLIP 100.0 #define LIGHTANG -MPI / 4.0 #define AVGWEIGHT 0.85 // Game options #define MAZESIZE 15 #define HSCALE 1.5 // Maze grows in the positive direction #define MAZENORTH 1 #define MAZEEAST 2 #define MAZEVISIT 4 // When you rightshift these values, you "turn right". // NOTE: north in this case is "towards the screen" because it moves in the // positive direction. In this case, it's actually wound in the opposite // direction of what you'd expect #define DIRNORTH 8 #define DIRWEST 4 #define DIRSOUTH 2 #define DIREAST 1 #define TURNRIGHT(d) (d == 1 ? 8 : (d >> 1)) #define TURNLEFT(d) (d == 8 ? 1 : (d << 1)) #define STACKPUSH(s, t, v) s[t++] = v; // Store all the values users can change at the beginning float ditherstart = -1; float ditherend = 8; float fov = 90.0; float minlight = 0.25; int fps = 30; uint16_t sky = 0xF000; struct vec2i dirtovec(uint8_t dir) { struct vec2i result; switch (dir) { case DIREAST: vec2i(result.v, 1, 0); break; case DIRWEST: vec2i(result.v, -1, 0); break; case DIRNORTH: vec2i(result.v, 0, 1); break; case DIRSOUTH: vec2i(result.v, 0, -1); break; default: vec2i(result.v, 0, 0); } return result; } mfloat_t dirtoyaw(uint8_t dir) { switch (dir) { case DIREAST: return MPI_2; case DIRWEST: return -MPI_2; case DIRNORTH: return 0; case DIRSOUTH: return MPI; default: return 0; } } int maze_visited(uint8_t *maze, int x, int y, int size) { return (maze[x + y * size] & MAZEVISIT) > 0; } int maze_connected(uint8_t *maze, int x, int y, int size, uint8_t move) { // eprintf("CHECKING DIR %d at (%d,%d), it is %d\n", move, x, y, // maze[x + y * size]); if (move == DIREAST) { return (maze[x + y * size] & MAZEEAST) == 0; } else if (move == DIRWEST) { return (x > 0) && ((maze[x - 1 + y * size] & MAZEEAST) == 0); } else if (move == DIRNORTH) { return (maze[x + y * size] & MAZENORTH) == 0; } else if (move == DIRSOUTH) { return (y > 0) && ((maze[x + (y - 1) * size] & MAZENORTH) == 0); } return 0; } // Calculate which direction from the given position would have you // facing down the longest hallway uint8_t maze_longesthallway(uint8_t *maze, int size, int x, int y) { uint8_t face = DIRNORTH; uint8_t maxface = face; uint16_t maxdist = 0; while (face) { uint16_t dist = 0; int dx = x, dy = y; struct vec2i move = dirtovec(face); while (maze_connected(maze, dx, dy, size, face)) { dx += move.x; dy += move.y; dist++; } if (dist > maxdist) { maxface = face; maxdist = dist; } face >>= 1; } return maxface; } // Generate a (square) maze. Utilize one bit of the maze (#2) to // indicate whether it is visited void maze_generate(uint8_t *maze, int size, struct vec2i *start, struct vec2i *end) { const int mazesquare = (size) * (size); for (int i = 0; i < mazesquare; i++) { maze[i] = MAZENORTH | MAZEEAST; } int *mazestack; mallocordie(mazestack, sizeof(int) * mazesquare); for (int i = 0; i < size * size; i++) { mazestack[i] = -1; } // Push current cell onto stack, mark as visited start->x = rand() % size; start->y = rand() % size; int x = start->x; int y = start->y; int mazetop = 0; int maxmazetop = 0; STACKPUSH(mazestack, mazetop, x + y * size); maze[x + y * size] |= MAZEVISIT; uint8_t visitable[4]; int visittop = 0; // Now let's make a maze! while (mazetop) { // The end of the maze is the furthest into the stack we go. This should // somewhat maximize the complexity of start to finish? if (mazetop > maxmazetop) { end->x = x; end->y = y; maxmazetop = mazetop; } mazetop--; visittop = 0; x = mazestack[mazetop] % size; y = mazestack[mazetop] / size; if (x > 0 && !maze_visited(maze, x - 1, y, size)) { visitable[visittop++] = DIRWEST; } if (x < size - 1 && !maze_visited(maze, x + 1, y, size)) { visitable[visittop++] = DIREAST; } if (y > 0 && !maze_visited(maze, x, y - 1, size)) { visitable[visittop++] = DIRSOUTH; } if (y < size - 1 && !maze_visited(maze, x, y + 1, size)) { visitable[visittop++] = DIRNORTH; } // You can generate a random location! if (visittop) { // Readd ourselves, we're moving STACKPUSH(mazestack, mazetop, x + y * size); uint8_t dir = visitable[rand() % visittop]; struct vec2i movedir = dirtovec(dir); int nx = x + movedir.x; int ny = y + movedir.y; // Trust that the visitable array is always valid if (dir == DIREAST) { // Tear down east wall maze[x + y * size] &= ~MAZEEAST; } else if (dir == DIRWEST) { // Move left and tear down east wall maze[(x - 1) + y * size] &= ~MAZEEAST; } else if (dir == DIRNORTH) { // tear down north wall maze[x + y * size] &= ~MAZENORTH; } else if (dir == DIRSOUTH) { // move down and tear down north wall maze[x + (y - 1) * size] &= ~MAZENORTH; } // Push onto stack and set visited STACKPUSH(mazestack, mazetop, nx + ny * size); maze[nx + ny * size] |= MAZEVISIT; } } eprintf("Maze generate: %d,%d -> %d,%d maxdepth: %d\n", start->x, start->y, end->x, end->y, maxmazetop); free(mazestack); } void maze_wall_generate(uint8_t *maze, int size, haloo3d_obj *obj) { // Simple: for each cell, we check if north or east is a wall. If so, // generate it. Also, generate walls for the south and west global wall for (int y = 0; y < size; y++) { for (int x = 0; x < size; x++) { if (!maze_connected(maze, x, y, size, DIREAST)) { haloo3d_gen_grid_quad(obj, x, y, dirtovec(DIREAST)); } if (!maze_connected(maze, x, y, size, DIRNORTH)) { haloo3d_gen_grid_quad(obj, x, y, dirtovec(DIRNORTH)); } } } for (int i = 0; i < size; i++) { haloo3d_gen_grid_quad(obj, i, 0, dirtovec(DIRSOUTH)); haloo3d_gen_grid_quad(obj, 0, i, dirtovec(DIRWEST)); } } // Turning into a blob? IDK. Maybe certain things should be // global state, which is this. typedef struct { // float timedelta; int fps; uint8_t state; uint8_t maze[MAZESIZE * MAZESIZE]; // int size; // A suggested start and end. The end is the actual // end, where we put the ending indicator struct vec2i start; struct vec2i end; // Some simple calcs for you to use mfloat_t cellsize; } worldstate; enum { WSTATE_INIT = 0, WSTATE_SPINUP = 1, WSTATE_GAMEPLAY = 2, WSTATE_GAMEOVER = 3, WSTATE_SPINDOWN = 4 }; // A component that stores a position and a facing direction // as an angle typedef struct { struct vec3 pos; struct vec2 rot; // x is yaw, y is pitch } ecs_placement; // A component representing movement through the scene. // typedef struct { // struct vec3 posvel; // mfloat_t yawvel; // } ecs_movement; // A component which links back to a camera typedef struct { haloo3d_camera *camera; } ecs_camera; // A component which allows automatic navigation // of position through the use of timers. typedef struct { struct vec3 dest; int timer; } ecs_autonav; // A component which allows automatic rotation // through the use of timers. typedef struct { struct vec2 dest; // x is yaw, y is pitch int timer; } ecs_autorotate; // A component which has pointers back to global world state. typedef struct { worldstate *state; } ecs_worldstate; // A component which holds onto data specifically for world // entities. You can think of it as private data so people // can't poke at the worldstate data typedef struct { worldstate *state; haloo3d_obj_instance *walls; uint16_t timer; } ecs_world; // void sys_move_placement(ecs_movement *m, ecs_placement *p) { // vec3_add(p->pos.v, p->pos.v, m->posvel.v); // p->yaw += m->yawvel; // } void sys_autonav(ecs_autonav *nav, ecs_placement *p) { if (nav->timer <= 0) { p->pos = nav->dest; return; } mfloat_t xdiff = nav->dest.x - p->pos.x; mfloat_t ydiff = nav->dest.y - p->pos.y; mfloat_t zdiff = nav->dest.z - p->pos.z; p->pos.x += xdiff / nav->timer; p->pos.y += ydiff / nav->timer; p->pos.z += zdiff / nav->timer; nav->timer--; } void sys_autorotate(ecs_autorotate *arot, ecs_placement *p) { if (arot->timer <= 0) { p->rot = arot->dest; return; } mfloat_t xdiff = arot->dest.x - p->rot.x; mfloat_t ydiff = arot->dest.y - p->rot.y; p->rot.x += xdiff / arot->timer; p->rot.y += ydiff / arot->timer; arot->timer--; } void sys_camera(ecs_camera *cam, ecs_placement *p) { cam->camera->pos = p->pos; cam->camera->yaw = p->rot.x; cam->camera->pitch = p->rot.y; // eprintf("CAM: %f %f POS: %f %f %f\n", cam->camera->yaw, cam->camera->pitch, // cam->camera->pos.x, cam->camera->pos.y, cam->camera->pos.z); } void sys_world(ecs_world *w) { float scaleleft; const int spinspeed = w->state->fps * 4 / 5; switch (w->state->state) { case WSTATE_INIT: maze_generate(w->state->maze, MAZESIZE, &w->state->start, &w->state->end); maze_wall_generate(w->state->maze, MAZESIZE, w->walls->model); eprintf("INIT MAZE COMPLETE, spinning up walls\n"); w->state->state = WSTATE_SPINUP; w->timer = spinspeed; w->walls->scale.y = 0; break; case WSTATE_SPINUP: // Bring walls up, timer down scaleleft = 1 - w->walls->scale.y; w->walls->scale.y += scaleleft / w->timer; w->timer--; if (w->timer == 0) { eprintf("SPINUP COMPLETE, starting gameplay\n"); w->walls->scale.y = 1; // Start gameplay. We won't know when it's done w->state->state = WSTATE_GAMEPLAY; } break; case WSTATE_GAMEOVER: w->timer = spinspeed; eprintf("GAME OVER, spinning down\n"); w->state->state = WSTATE_SPINDOWN; break; case WSTATE_SPINDOWN: // Bring walls sown, timer down scaleleft = w->walls->scale.y; w->walls->scale.y -= scaleleft / w->timer; w->timer--; if (w->timer == 0) { eprintf("SPINDOWN COMPLETE, reinitializing\n"); w->walls->scale.y = 0; // Start gameplay. We won't know when it's done w->state->state = WSTATE_INIT; } break; } } enum { SAI_INIT, SAI_READY, SAI_GAMEPLAY, }; // State for tracking a smart ai moving through the maze. typedef struct { uint8_t state; uint8_t rotstate; uint8_t dir; uint32_t timer; struct vec2i mpos; } ecs_smartai; static void sys_smartai(ecs_smartai *smartai, ecs_worldstate *ws, ecs_autonav *anav, ecs_autorotate *arot) { const int actiontime = ws->state->fps / 2; const int rotdelaytime = 2 * actiontime / 5; switch (smartai->state) { case SAI_INIT: // Here, we wait until the world state is spinup, in which // case we can spawn and face if (ws->state->state == WSTATE_SPINUP) { smartai->mpos = ws->state->start; anav->dest.x = ws->state->cellsize * (ws->state->start.x + 0.5); anav->dest.z = ws->state->cellsize * (ws->state->start.y + 0.5); anav->timer = 0; smartai->dir = maze_longesthallway( ws->state->maze, MAZESIZE, ws->state->start.x, ws->state->start.y); arot->dest.x = dirtoyaw(smartai->dir); arot->timer = 0; smartai->state = SAI_READY; eprintf("PLAYER READY: %f %f (%f), waiting for spinup\n", anav->dest.x, anav->dest.z, arot->dest.x); } break; case SAI_READY: if (ws->state->state == WSTATE_GAMEPLAY) { smartai->state = SAI_GAMEPLAY; eprintf("PLAYER STARTING GAMEPLAY\n"); } break; case SAI_GAMEPLAY: // Normal gameplay: move through the maze, etc. // Some states are triggered based on the timer if (smartai->timer > 0) { smartai->timer--; } // The rotation is delayed to make it feel a bit more like the original // maze, which I think determined rotation and direction upon entering // a tile. I instead calculate that in the middle of the tile. It doesn't // really line up like it does on the windows screensaver but it's // close enough for me. if (smartai->timer == 0) { if (smartai->rotstate == 1) { eprintf("TURNING RIGHT\n"); arot->dest.x += MPI_2; arot->timer = actiontime; smartai->dir = TURNRIGHT(smartai->dir); smartai->rotstate = 0; } else if (smartai->rotstate == 2) { eprintf("TURNING LEFT\n"); arot->dest.x -= MPI_2; arot->timer = actiontime; smartai->dir = TURNLEFT(smartai->dir); smartai->rotstate = 0; } } // Only decide to do things if you're not moving anymore. Movement is the // most important thing if (anav->timer == 0) { eprintf("SMARTAI: %d TIMER: %d DIR: %d POS: (%f, %f)\n", smartai->rotstate, smartai->timer, smartai->dir, anav->dest.x, anav->dest.z); // Player can only move forward if there's nothing in front of them if (maze_connected(ws->state->maze, smartai->mpos.x, smartai->mpos.y, MAZESIZE, smartai->dir)) { struct vec2i movement = dirtovec(smartai->dir); smartai->mpos.x += movement.x; smartai->mpos.y += movement.y; anav->timer = actiontime; anav->dest.x += ws->state->cellsize * movement.x; anav->dest.z += ws->state->cellsize * movement.y; smartai->rotstate = 0; } // Figure out if a rotation should be scheduled if (smartai->timer <= 0) { // Ok we might be moving, we might not be. Let's go ahead and calculate // rotation based on the FUTURE direction we want to turn. uint8_t rightdir = TURNRIGHT(smartai->dir); uint8_t leftdir = TURNLEFT(smartai->dir); if (maze_connected(ws->state->maze, smartai->mpos.x, smartai->mpos.y, MAZESIZE, rightdir)) { // Always choose right over left smartai->rotstate = 1; smartai->timer = rotdelaytime; eprintf("WILL TURN RIGHT TO: %d\n", rightdir); } else if (!maze_connected(ws->state->maze, smartai->mpos.x, smartai->mpos.y, MAZESIZE, smartai->dir)) { // We only move left if the player can't move forward or right smartai->rotstate = 2; smartai->timer = rotdelaytime; eprintf("WILL TURN LEFT (stuck) TO: %d\n", leftdir); } } } break; } } // static void sys_ecs_world(ecs_world *world, ecs_maze *maze, ecs_moveto *mt, // ecs_rotateto *rt) {} // Setup ECS system for our game ECS_START(mecs) ECS_COMPONENT(ecs_worldstate); ECS_COMPONENT(ecs_world); ECS_COMPONENT(ecs_autonav); ECS_COMPONENT(ecs_autorotate); ECS_COMPONENT(ecs_placement); ECS_COMPONENT(ecs_camera); ECS_COMPONENT(ecs_smartai); // ECS_COMPONENT(ecs_moveto); // ECS_COMPONENT(ecs_rotateto); // ECS_COMPONENT(ecs_camera); // ECS_COMPONENT(ecs_maze); ECS_END(mecs) // And then a copy of the components here... that sucksssss ECS_CID(ecs_worldstate, 0); ECS_CID(ecs_world, 1); ECS_CID(ecs_autonav, 2); ECS_CID(ecs_autorotate, 3); ECS_CID(ecs_placement, 4); ECS_CID(ecs_camera, 5); ECS_CID(ecs_smartai, 6); // ECS_CID(ecs_moveto, 0); // ECS_CID(ecs_rotateto, 1); // ECS_CID(ecs_camera, 3); // ECS_CID(ecs_maze, 4); ECS_SYSTEM1(mecs, sys_world, ecs_world); ECS_SYSTEM2(mecs, sys_autonav, ecs_autonav, ecs_placement); ECS_SYSTEM2(mecs, sys_autorotate, ecs_autorotate, ecs_placement); ECS_SYSTEM2(mecs, sys_camera, ecs_camera, ecs_placement); ECS_SYSTEM4(mecs, sys_smartai, ecs_smartai, ecs_worldstate, ecs_autonav, ecs_autorotate); // ECS_SYSTEM1(mecs, sys_ecs_moveto, ecs_moveto); // ECS_SYSTEM1(mecs, sys_ecs_rotateto, ecs_rotateto); // ECS_SYSTEM2(mecs, sys_ecs_moveto_camera, ecs_moveto, ecs_camera); // ECS_SYSTEM2(mecs, sys_ecs_rotateto_camera, ecs_rotateto, ecs_camera); // ECS_SYSTEM4(mecs, sys_ecs_smartai, ecs_smartai, ecs_maze, ecs_moveto, // ecs_rotateto); void init_floortexture(haloo3d_fb *floort) { // uint16_t cols[4] = {0xFD93, 0xFB83, 0xFFFF}; //, 0xFDDD}; uint16_t cols[1] = {0xFD93}; haloo3d_fb_init_tex(floort, 64, 64); haloo3d_apply_alternating(floort, cols, 1); haloo3d_apply_noise(floort, NULL, 1.0 / 6); } void init_ceilingtexture(haloo3d_fb *ceilt) { uint16_t cols[1] = {0xFFFF}; //, 0xFDDD}; // haloo3d_apply_alternating(floort, cols, 2); haloo3d_fb_init_tex(ceilt, 64, 64); haloo3d_apply_alternating(ceilt, cols, 1); haloo3d_apply_noise(ceilt, NULL, 1.0 / 4); haloo3d_apply_brick(ceilt, 16, 8, 0xFAAA); } void init_walltexture(haloo3d_fb *wallt) { haloo3d_fb_init_tex(wallt, 64, 64); uint16_t wallcols[] = {0xFA22}; haloo3d_apply_alternating(wallt, wallcols, 1); haloo3d_apply_noise(wallt, NULL, 1.0 / 8); haloo3d_apply_brick(wallt, 18, 13, 0xFEEE); // haloo3d_apply_brick(wallt, 14, 8, 0xFD94); haloo3d_apply_noise(wallt, NULL, 1.0 / 8); } int main() { // int argc, char **argv) { haloo3d_easystore storage; haloo3d_easystore_init(&storage); haloo3d_fb screen; haloo3d_fb_init(&screen, SWIDTH, SHEIGHT); haloo3d_easyrender render; haloo3d_easyrender_init(&render, WIDTH, HEIGHT); render.camera.pos.y = 0.5; render.tprint.fb = &screen; eprintf("Initialized renderer\n"); haloo3d_easytimer frametimer, sdltimer, filltimer; haloo3d_easytimer_init(&frametimer, AVGWEIGHT); haloo3d_easytimer_init(&sdltimer, AVGWEIGHT); haloo3d_easytimer_init(&filltimer, AVGWEIGHT); // Load the junk + generate stuff haloo3d_obj *flooro = haloo3d_easystore_addobj(&storage, "floor"); haloo3d_obj *ceilo = haloo3d_easystore_addobj(&storage, "ceiling"); haloo3d_obj *wallo = haloo3d_easystore_addobj(&storage, "walls"); haloo3d_fb *floort = haloo3d_easystore_addtex(&storage, "floor"); haloo3d_fb *ceilt = haloo3d_easystore_addtex(&storage, "ceiling"); haloo3d_fb *wallt = haloo3d_easystore_addtex(&storage, "walls"); haloo3d_gen_plane(flooro, MAZESIZE); haloo3d_gen_plane(ceilo, MAZESIZE); haloo3d_gen_grid(wallo, MAZESIZE, 0); init_floortexture(floort); init_ceilingtexture(ceilt); init_walltexture(wallt); eprintf("Initialized models and textures\n"); worldstate wstate; memset(&wstate, 0, sizeof(worldstate)); wstate.fps = fps; wstate.state = WSTATE_INIT; wstate.cellsize = HSCALE; // Lighting. Note that for performance, the lighting is always calculated // against the base model, and is thus not realistic if the object rotates in // the world. This can be fixed easily, since each object gets its own // lighting vector, which can easily be rotated in the opposite direction of // the model struct vec3 light; vec3(light.v, 0, -MCOS(LIGHTANG), MSIN(LIGHTANG)); haloo3d_obj_instance *floori = haloo3d_easyrender_addinstance(&render, flooro, floort); haloo3d_obj_instance *walli = haloo3d_easyrender_addinstance(&render, wallo, wallt); haloo3d_obj_instance *ceili = haloo3d_easyrender_addinstance(&render, ceilo, ceilt); floori->cullbackface = 0; ceili->cullbackface = 0; walli->cullbackface = 0; vec3(floori->scale.v, HSCALE, 1, HSCALE); vec3(ceili->scale.v, HSCALE, 1, HSCALE); vec3(walli->scale.v, HSCALE, 1, HSCALE); // vec3(walli->scale.v, HSCALE, 0, HSCALE); floori->pos.x += MAZESIZE / 2.0 * HSCALE; floori->pos.z += MAZESIZE / 2.0 * HSCALE; ceili->pos.x += MAZESIZE / 2.0 * HSCALE; ceili->pos.z += MAZESIZE / 2.0 * HSCALE; walli->pos.x += MAZESIZE / 2.0 * HSCALE; walli->pos.z += MAZESIZE / 2.0 * HSCALE; ceili->pos.y = 1; //-1; eprintf("Setup all object instances\n"); unigi_type_event event; unigi_type_resolution res; res.width = SWIDTH; res.height = SHEIGHT; res.depth = 0; int totaldrawn = 0; eprintf("Scene has %d tris, %d verts\n", render.totalfaces, render.totalverts); // Init unigi system unigi_graphics_init(); unigi_window_create(res, "maze.exe"); // render.printbuf); // render.camera.pos.y = 4; // 5; // render.camera.pitch = MPI - 0.1; // 2.2; // ceili->pos.y = -10; haloo3d_debugconsole dc; haloo3d_debugconsole_init(&dc); haloo3d_debugconsole_set(&dc, "render/fps.i", &fps); haloo3d_debugconsole_set(&dc, "render/fov.f", &fov); haloo3d_debugconsole_set(&dc, "render/trifunc.i", &render.trifunc); haloo3d_debugconsole_set(&dc, "render/ditherstart.f", &ditherstart); haloo3d_debugconsole_set(&dc, "render/ditherend.f", &ditherend); haloo3d_debugconsole_set(&dc, "render/sky.u16x", &sky); haloo3d_debugconsole_set(&dc, "camera/pos_y.f", &render.camera.pos.y); haloo3d_debugconsole_set(&dc, "camera/pitch.f", &render.camera.pitch); haloo3d_debugconsole_set(&dc, "obj/ceil/pos_y.f", &ceili->pos.y); // Set up ECS entities. For this game, we mostly have global entities. mecs ecs; mecs_init(&ecs); ecs_eid worldid = mecs_newentity(&ecs, 0); eprintf("World eid: %d\n", worldid); ECS_SETCOMPONENT(&ecs, worldid, ecs_world){.state = &wstate, .walls = walli, .timer = 0}; ecs_eid playerid = mecs_newentity(&ecs, 0); eprintf("Player eid: %d\n", playerid); ECS_SETCOMPONENT(&ecs, playerid, ecs_worldstate){.state = &wstate}; ECS_SETCOMPONENT(&ecs, playerid, ecs_placement){.pos = render.camera.pos}; ECS_SETCOMPONENT(&ecs, playerid, ecs_camera){.camera = &render.camera}; ECS_SETCOMPONENT(&ecs, playerid, ecs_autonav){.timer = 0, .dest = render.camera.pos}; ECS_SETCOMPONENT(&ecs, playerid, ecs_autorotate){.timer = 0}; ECS_SETCOMPONENT(&ecs, playerid, ecs_smartai){.state = SAI_INIT, .rotstate = 0, .timer = 0}; // if (playerid == -1) { // dieerr("WHY IS PLAYERID -1???\n"); // } // // System is setup such that camera position matches maze index // render.camera.pos.x = 0.5 * HSCALE; // render.camera.pos.z = 0.5 * HSCALE; // struct vec2i playerstart = {.x = 0, .y = 0}; // struct vec2 playerrotation = {.x = render.camera.yaw, // .y = render.camera.pitch}; // ECS_SETCOMPONENT(&ecs, playerid, ecs_rotateto){ // .rot = playerrotation, .dstrot = playerrotation, .timer = 0}; // ECS_SETCOMPONENT(&ecs, playerid, ecs_maze){ // .maze = maze, .pos = playerstart, .size = MAZESIZE, .dir = DIRSOUTH}; // ECS_SETCOMPONENT(&ecs, playerid, ecs_camera) & render.camera; // ECS_SETCOMPONENT(&ecs, playerid, ecs_smartai){.state = 0, .timer = 0}; // eprintf("Player component mask: %lx\n", ecs.entities[playerid]); // ----------------------------------- // Actual rendering // ----------------------------------- // ceili->texture = &render.window; while (1) { haloo3d_easytimer_start(&frametimer); // render.camera.yaw += 0.008; haloo3d_perspective(render.perspective, fov, ASPECT, NEARCLIP, FARCLIP); haloo3d_easyrender_beginframe(&render); haloo3d_fb_clear(&render.window, sky); // walli->scale.y = fabs(sin(3 * render.camera.yaw)); // render.camera.up.x = sin(render.camera.yaw); // render.camera.up.y = cos(render.camera.yaw); // walli->up.x = sin(3 * render.camera.yaw); // walli->up.y = cos(4 * render.camera.yaw); do { unigi_event_get(&event); switch (event.type) { case unigi_enum_event_input_keyboard: if (event.data.input_keyboard.down) { switch (event.data.input_keyboard.button) { case KEY_SPACE: haloo3d_debugconsole_beginprompt(&dc); break; default: exit(0); } } break; } } while (event.type != unigi_enum_event_none); // --------------------------- // Game logic? // --------------------------- for (int i = 0; i < ECS_MAXENTITIES; i++) { sys_world_run(&ecs, i); sys_smartai_run(&ecs, i); sys_autonav_run(&ecs, i); sys_autorotate_run(&ecs, i); sys_camera_run(&ecs, i); } totaldrawn = 0; haloo3d_obj_instance *object = NULL; // Iterate over objects while ((object = haloo3d_easyrender_nextinstance(&render, object)) != NULL) { // Setup final model matrix and the precalced vertices haloo3d_easyrender_beginmodel(&render, object); // Iterate over object faces for (int fi = 0; fi < object->model->numfaces; fi++) { totaldrawn += haloo3d_easyrender_renderface( &render, object, fi, ditherstart, ditherend, minlight); } } haloo3d_easytimer_start(&filltimer); #ifdef FASTFILL haloo3d_fb_fill(&screen, &render.window); #else haloo3d_recti texrect = {.x1 = 0, .y1 = 0, .x2 = WIDTH, .y2 = HEIGHT}; haloo3d_recti screenrect = {.x1 = 0, .y1 = 0, .x2 = SWIDTH, .y2 = SHEIGHT}; haloo3d_sprite(&screen, &render.window, texrect, screenrect); #endif haloo3d_easytimer_end(&filltimer); haloo3d_print(&render.tprint, "Last frame: %05.2f (%05.2f)\nLast fill: %05.2f " "(%05.2f)\nLast SDLFl: %05.2f " "(%05.2f)\nTris: %d / %d\nVerts: %d\n", frametimer.last * 1000, frametimer.sum * 1000, filltimer.last * 1000, filltimer.sum * 1000, sdltimer.last * 1000, sdltimer.sum * 1000, totaldrawn, render.totalfaces, render.totalverts); haloo3d_easytimer_start(&sdltimer); unigi_graphics_blit(0, (unigi_type_color *)screen.buffer, res.width * res.height); unigi_graphics_flush(); haloo3d_easytimer_end(&sdltimer); haloo3d_easytimer_end(&frametimer); float waittime = (1.0 / fps) - frametimer.last; if (waittime > 0) { unigi_time_sleep(waittime * unigi_time_clocks_per_s); } } // Just to get the compiler to STOP COMPLAINING about unused mecs_deleteentity(&ecs, worldid); haloo3d_easystore_deleteallobj(&storage, haloo3d_obj_free); haloo3d_easystore_deletealltex(&storage, haloo3d_fb_free); }