#ifdef _EE #define DIRECTBUILD #define H3D_VOLATILE_FLOATS #endif #ifdef DIRECTBUILD // clang-format off #define MATHC_USE_UNIONS #define MATHC_NO_STRUCT_FUNCTIONS #include "haloo3d/lib/mathc.c" #define FNL_IMPL #include "haloo3d/lib/FastNoiseLite.h" #include "haloo3d/haloo3d.c" #include "haloo3d/haloo3dex_easy.c" #include "haloo3d/haloo3dex_gen.c" #include "haloo3d/haloo3dex_obj.c" #include "haloo3d/haloo3dex_print.c" #include "unigi.platform.sdl2/main.c" // clang-format on #else #include "haloo3d/haloo3d.h" #include "haloo3d/haloo3dex_easy.h" #include "haloo3d/haloo3dex_gen.h" #include "haloo3d/haloo3dex_obj.h" #include "haloo3d/haloo3dex_print.h" #include "haloo3d/lib/FastNoiseLite.h" #include "haloo3d/lib/mathc.h" #include "unigi/main.h" #endif // #include "keys.h" // #define ECS_MAXENTITIES 30000 #include "terrain_ecstypes.h" #include #include // #define LIMITEDPRINT #ifdef _EE #define WIDTH 160 #define HEIGHT 120 #define SCREENSCALE 4 #else #define WIDTH 480 #define HEIGHT 300 #define SCREENSCALE 2 #endif #define SWIDTH (WIDTH * SCREENSCALE) #define SHEIGHT (HEIGHT * SCREENSCALE) #define AVGWEIGHT 0.85 #define DEFAULTUP \ { .x = 0, .y = 1, .z = 0 } // Lookvec for objects which all face forward along with the player #define DEFAULTLOOK \ { .x = 0, .y = 0, .z = -1 } // How big each chunk is (x and y, square). MUST BE POWER OF 2 - 1 #define CHUNKSIZE 7 // This is the width in tiles #define CHUNKSCALE (1.0 / CHUNKSIZE) // This is how many vertices across a chunk is #define CHUNKVSIZE (CHUNKSIZE + 1) #define VIEWDISTANCE 10 #define PLBOXEDGE (VIEWDISTANCE * 2 + 1) #define INIT_NEARCLIP 0.01 #define INIT_FARCLIP 100.0 #define MAXCHUNKPERFRAME PLBOXEDGE // Generation consts / things for looks #define LANDHEIGHT 4.0 #define LANDCOL 0xF6D2 // 0xF4E1 #define SEACOL 0xF28D // 0xF26C #define SKYCOL 0xFA7F // 0xF77F #define CLOUDCOL 0xFFFF #define TREECOL 0xF070 #define TREETRUNKCOL 0xF950 #define REDFLOWERCOL 0xFF46 // 0xFF9A #define YELLOWFLOWERCOL 0xFEE0 #define TOWERCOL 0xF111 #define LIGHTCOL 0xFF00 #define TOWERLIGHTMINSIZE (CHUNKSCALE * 0.1) #define TOWERLIGHTMAXSIZE (CHUNKSCALE * 0.5) #define TOWERLIGHTMINTRANS 0 #define TOWERLIGHTMAXTRANS 0.4 #define TOWERLIGHTRATE 1.5 #define TREEBOTTOM -0.333 #define TREETRUNKWIDTH 0.2 #define SEATRANS 0.75 #define CLOUDTRANSMIN 0.1 #define CLOUDTRANSMAX 0.2 #define CLOUDHEIGHT 2 #define CLOUDSTRETCH 2 #define CLOUDCHANCE 0.1 #define INIT_LIGHTPITCH (MPI_2 * 1.65) #define INIT_LIGHTYAW (MPI_2 * 0.65) #define INIT_MINLIGHT 0.2 #define INIT_DITHEREND (VIEWDISTANCE - 1.0) #define INIT_DITHERSTART (INIT_DITHEREND * 0.33) #define MAXTREEPERCELL 9 #define MAXTREEACROSS 3 #define MAXTREECLOSENESS 0.2 // Player things #define INIT_FOV 90.0 #define INIT_CAMPITCH (MPI_2 * 1.3) // MPI_2 * 1.6 // 1.575 #define INIT_PLAYERHEIGHT 0.65 #define INIT_PLAYERSPEED \ { .x = 0, .y = 0, .z = -1.5 } #define RANDF() ((float)rand() / RAND_MAX) #define OFLOOR(v) (floor(fabs(v)) * ((v) < 0 ? -1 : 1)) // Some globals you can mess around with potentially int fps = 60; #define PALETTEKEY "palette" #define TREEKEY "tree" #define CLOUDKEY "cloud" #define LIGHTKEY "light" #define REDFLOWERKEY "redflower" #define YELLOWFLOWERKEY "yellowflower" #define TOWERKEY "tower" #include "commonobj.c" static int gen_terrain_seed = 0; static inline mfloat_t calc_barycentric(mfloat_t *point, mfloat_t *values, struct vec2 *triedge, int count) { mfloat_t we[3]; for (int i = 0; i < count; i++) { int ofs = i * 3; we[0] = haloo3d_edgefunc(triedge[ofs + 1].v, triedge[ofs + 2].v, point); we[1] = haloo3d_edgefunc(triedge[ofs + 2].v, triedge[ofs + 0].v, point); we[2] = haloo3d_edgefunc(triedge[ofs + 0].v, triedge[ofs + 1].v, point); if (we[0] < 0 || we[1] < 0 || we[2] < 0) { continue; } return we[0] * values[ofs] + we[1] * values[ofs + 1] + we[2] * values[ofs + 2]; } return 0; } void gen_tower(haloo3d_obj *model, haloo3d_obj *tower, tecs *ecs, render_context *ctx, struct vec3i chunk, struct vec3 pos) { const float scale = 0.5; pos.x += 0.5; pos.y += scale; pos.z -= 0.5; haloo3d_obj_addobj(model, tower, pos, (struct vec3)DEFAULTLOOK, (struct vec3)DEFAULTUP, (struct vec3){.x = scale, .y = scale, .z = scale}); // Put a nice little light on top pos.y += scale; haloo3d_obj *light = haloo3d_easystore_getobj(&ecs->storage, LIGHTKEY); ecs_eid lightid = tecs_newentity(ecs, 0); eprintf("LIGHTING SPAWN: %d (%d,%d)\n", lightid, chunk.x, chunk.z); ECS_SETCOMPONENT(ecs, lightid, ecs_playergarbage){}; ECS_SETCOMPONENT(ecs, lightid, ecs_towerlight){.timer = 0}; ECS_SETCOMPONENT(ecs, lightid, ecs_placement){ .up = DEFAULTUP, .lookvec = DEFAULTLOOK, .pos = (struct vec3){.x = chunk.x + pos.x * CHUNKSCALE, .y = pos.y * CHUNKSCALE * LANDHEIGHT, .z = chunk.z + pos.z * CHUNKSCALE}}; ECS_SETCOMPONENT(ecs, lightid, ecs_object){ .flatdither = 0.2, .texture = haloo3d_easystore_gettex(&ecs->storage, PALETTEKEY), .scale = {.x = CHUNKSCALE, .y = CHUNKSCALE, .z = CHUNKSCALE}, .lighting = NULL, //&ecs->chunklight, .stoplighting = 0, .model = light, .cullbackface = 0, .context = {ctx}, .contextcount = 1}; } void gen_cloud(render_context *ctx, tecs *ecs, struct vec3i pos) { haloo3d_obj *cloud = haloo3d_easystore_getobj(&ecs->storage, CLOUDKEY); ecs_eid cloudid = tecs_newentity(ecs, 0); ECS_SETCOMPONENT(ecs, cloudid, ecs_playergarbage){}; ECS_SETCOMPONENT(ecs, cloudid, ecs_placement){.up = DEFAULTUP, .lookvec = DEFAULTLOOK, .pos = {.x = pos.x + RANDF(), .y = CLOUDHEIGHT + 0.5 * RANDF(), .z = pos.z - 0.9}}; ECS_SETCOMPONENT(ecs, cloudid, ecs_object){ .flatdither = CLOUDTRANSMIN + (CLOUDTRANSMAX - CLOUDTRANSMIN) * RANDF(), .texture = haloo3d_easystore_gettex(&ecs->storage, PALETTEKEY), .scale = {.x = 1 + RANDF(), .y = 1.0 / CLOUDSTRETCH, .z = 1}, .lighting = NULL, //&ecs->chunklight, .stoplighting = 0, .model = cloud, .cullbackface = 0, .context = {ctx}, .contextcount = 1}; } uint16_t gen_terrain(struct vec3i pos, haloo3d_obj *model, tecs *ecs, render_context *ctx) { haloo3d_easystore *storage = &ecs->storage; eprintf("Generating terrain at %d,%d\n", pos.x, pos.z); haloo3d_obj *tree = haloo3d_easystore_getobj(storage, TREEKEY); haloo3d_obj *tower = haloo3d_easystore_getobj(storage, TOWERKEY); haloo3d_obj *flowers[2]; flowers[0] = haloo3d_easystore_getobj(storage, REDFLOWERKEY); flowers[1] = haloo3d_easystore_getobj(storage, YELLOWFLOWERKEY); // Don't allow the model to have more than some amount of faces/vertices. haloo3d_obj_resetfixed(model, H3D_OBJ_MAXFACES, H3D_OBJ_MAXVERTICES); struct vec3 landcol = haloo3d_gen_paletteuv(LANDCOL); // 0xF2C0); int landuv = haloo3d_obj_addvtexture(model, landcol); fnl_state ns = fnlCreateState(); ns.noise_type = FNL_NOISE_OPENSIMPLEX2; ns.seed = gen_terrain_seed; ns.frequency = 0.025; ns.fractal_type = FNL_FRACTAL_RIDGED; float noisex = pos.x * CHUNKSIZE; float noisey = -pos.z * CHUNKSIZE; for (int z = 0; z < CHUNKVSIZE; z++) { for (int x = 0; x < CHUNKVSIZE; x++) { // clang-format off haloo3d_obj_addvertex(model, (struct vec4){.x = x, .y = fnlGetNoise2D(&ns, noisex + x, noisey + z), .z = -z, .w = 1 }); // clang-format on } } struct vec2 triedge[6]; mfloat_t heights[6]; uint16_t faces[2]; uint8_t gentower = 0; // A funny little thing... we're going to add flowers to this alternative // model, then add them to the end of the real model with a pointer saying // "don't do lighting past this point" haloo3d_obj nl_model; haloo3d_obj_resetfixed(&nl_model, H3D_OBJ_MAXFACES, H3D_OBJ_MAXVERTICES); for (int i = 0; i < CHUNKVSIZE * (CHUNKVSIZE - 1); i++) { if ((i & CHUNKSIZE) == CHUNKSIZE) { continue; } uint16_t bl = i; uint16_t br = i + 1; uint16_t tl = i + CHUNKVSIZE; uint16_t tr = i + CHUNKVSIZE + 1; faces[0] = fastface2(model, landuv, bl, br, tl); faces[1] = fastface2(model, landuv, br, tr, tl); float lowy = 999; for (int t = 0; t < 2; t++) { for (int v = 0; v < 3; v++) { int ofs = t * 3; // NOTE: WE SWTICH Y AND Z TO MAKE THE EDGE FUNCTION WORK triedge[ofs + v].x = model->vertices[model->faces[faces[t]][v].posi].x; triedge[ofs + v].y = model->vertices[model->faces[faces[t]][v].posi].z; heights[ofs + v] = model->vertices[model->faces[faces[t]][v].posi].y; if (heights[ofs + v] < lowy) lowy = heights[ofs + v]; } } // Change frequency for subthings // ns.seed = 0; ns.frequency = 0.02; ns.fractal_type = FNL_FRACTAL_NONE; int treecount = 0; for (int t = 0; t < MAXTREEPERCELL; t++) { // We know that the first vec in the first triangle is bl, which is our // base to start generating trees struct vec2 treepos = { .x = triedge[0].x + (1.0 / MAXTREEACROSS) * ((t % MAXTREEACROSS) + (1.0 - MAXTREECLOSENESS) * RANDF()), .y = triedge[0].y - ((1.0 / MAXTREEACROSS) * ((int)(t / MAXTREEACROSS) + (1.0 - MAXTREECLOSENESS) * RANDF()))}; float y = calc_barycentric(treepos.v, heights, triedge, 2); float pick = RANDF(); // pow(RANDF(), 2); float nlayer = fnlGetNoise2D(&ns, noisex + treepos.x, noisey + treepos.y); if (pick < y * (1 + nlayer) * 0.5) { float scale = 0.1 + RANDF() * 0.05; float height = 0.5 + RANDF() * 0.5; haloo3d_obj_addobj( model, tree, (struct vec3){ .x = treepos.x, .y = y + scale * height, .z = treepos.y}, (struct vec3)DEFAULTLOOK, (struct vec3)DEFAULTUP, (struct vec3){.x = scale, .y = scale * height, .z = scale}); treecount++; } else if ((rand() & 3) == 0 && y < 0.4 && y > 0 && RANDF() < -nlayer) { float scale = 0.04; float vertscale = scale * (1 - 0.5 * RANDF()); // * CHUNKSCALE * 4; haloo3d_obj_addobj( &nl_model, flowers[nlayer > -0.9], // flowers[RANDF() * 0.4 > y], (struct vec3){ .x = treepos.x, .y = y + vertscale * 0.5, .z = treepos.y}, (struct vec3)DEFAULTLOOK, //(struct vec3){.x = -sin(MPI * 0.25), .y = 0, .z = -cos(MPI * // 0.25)}, (struct vec3)DEFAULTUP, (struct vec3){.x = scale, .y = vertscale, .z = scale}); } } if (treecount == 0 && lowy > 0.75 && !gentower) { gentower = 1; gen_tower(&nl_model, tower, ecs, ctx, pos, (struct vec3){.x = triedge[0].x, .y = lowy, .z = triedge[0].y}); } } uint16_t stoplighting = model->numfaces; haloo3d_obj_addobj(model, &nl_model, (struct vec3){.x = 0, .y = 0, .z = 0}, (struct vec3)DEFAULTLOOK, (struct vec3)DEFAULTUP, (struct vec3){.x = 1, .y = 1, .z = 1}); haloo3d_obj_free(&nl_model); // Generate a cloud if (RANDF() < CLOUDCHANCE) gen_cloud(ctx, ecs, pos); return stoplighting; } // Generate the entity/components/terrain for the chunk at the given // coordinates. // NOTE: chunks are per x/y tile (terrain inside is just scaled // down to fit inside the 1x1 box) void gen_chunk(render_context *ctx, tecs *ecs, struct vec3i pos) { // eprintf("Generating chunk at %d,%d\n", pos.x, pos.y); ecs_eid id = tecs_newentity(ecs, 0); ecs_playergarbage garbage; sprintf(garbage.dynmodel, "e%d", id); haloo3d_obj *model = haloo3d_easystore_addobj(&ecs->storage, garbage.dynmodel); uint16_t stoplighting = gen_terrain(pos, model, ecs, ctx); ECS_SETCOMPONENT(ecs, id, ecs_playergarbage) garbage; ECS_SETCOMPONENT(ecs, id, ecs_chunk){.pos = pos}; ECS_SETCOMPONENT(ecs, id, ecs_placement){.up = DEFAULTUP, .lookvec = DEFAULTLOOK, .pos = {.x = pos.x, .y = pos.y, .z = pos.z}}; // TODO: an easy place for optimization (if it's even needed) // eprintf("TEX: %p\n", haloo3d_easystore_gettex(&ecs->storage, PALETTEKEY)); ECS_SETCOMPONENT(ecs, id, ecs_object){ .flatdither = 0, .texture = haloo3d_easystore_gettex(&ecs->storage, PALETTEKEY), .scale = {.x = CHUNKSCALE, .y = CHUNKSCALE * LANDHEIGHT, .z = CHUNKSCALE}, .lighting = &ecs->chunklight, .stoplighting = stoplighting, .model = model, .cullbackface = 1, .context = {ctx}, .contextcount = 1}; } void player_chunkload(ecs_placement *ppos, render_context *ctx, tecs *ecs) { struct vec3i pchunk = {.x = OFLOOR(ppos->pos.x), .y = OFLOOR(ppos->pos.y), .z = OFLOOR(ppos->pos.z)}; // This is our little array of "filled chunks" around the player. We // use this to figure out what to generate ecs_eid surround[PLBOXEDGE][PLBOXEDGE] = {0}; // Need to pull all existing objects that are killable ecs_eid matches[ECS_MAXENTITIES]; // Find + iterate over killable objects placed in world int mcount = tecs_query( ecs, ecs_playergarbage_fl | ecs_placement_fl | ecs_object_fl, matches); int lostobjects = 0; for (int i = 0; i < mcount; i++) { ecs_placement *placement = &ECS_GETCOMPONENT(ecs, matches[i], ecs_placement); ecs_object *obj = &ECS_GETCOMPONENT(ecs, matches[i], ecs_object); // If this object is outside the view distance, we no longer have access to // it if (fabs(OFLOOR(placement->pos.x) - pchunk.x) > VIEWDISTANCE || fabs(OFLOOR(placement->pos.z) - pchunk.z) > VIEWDISTANCE) { // OK, reduce the item refcount, since we're no longer close to it obj->contextcount--; lostobjects++; } // This is specifically a chunk, it might be within our view radius. if (ECS_HASCOMPONENT(ecs, matches[i], ecs_chunk)) { ecs_chunk *chunk = &ECS_GETCOMPONENT(ecs, matches[i], ecs_chunk); int surx = chunk->pos.x - pchunk.x + VIEWDISTANCE; int surz = chunk->pos.z - pchunk.z + VIEWDISTANCE; if (surx >= 0 && surz >= 0 && surx < PLBOXEDGE && surz < PLBOXEDGE) { // eprintf("SUR: %d %d\n", surx, sury); surround[surz][surx] = 1; // matches[i]; } } } if (lostobjects) { eprintf("KILLABLE OBJS: %d PCHUNK: %d,%d\n", mcount, pchunk.x, pchunk.y); } int chunkgen = 0; // We now have our view radius box and which chunks are not loaded. Up to // some amount, let's load them for (int z = pchunk.z - VIEWDISTANCE; z <= pchunk.z + VIEWDISTANCE; z++) { for (int x = pchunk.x - VIEWDISTANCE; x <= pchunk.x + VIEWDISTANCE; x++) { int surx = x - pchunk.x + VIEWDISTANCE; int surz = z - pchunk.z + VIEWDISTANCE; if (!surround[surz][surx]) { gen_chunk(ctx, ecs, (struct vec3i){.x = x, .y = 0, .z = z}); if (++chunkgen >= MAXCHUNKPERFRAME) { break; } } } } } // --------------------------------------------------- // The terrain ecs systems // --------------------------------------------------- // All initialization for a specific render context void sys_rendercontext(ecs_rendercontext *erc, ecs_placement *p, tecs **ecs) { render_context *rc = *erc; struct vec3 lookat; mfloat_t cammatrix[MAT4_SIZE]; mfloat_t perspective[MAT4_SIZE]; // Some initial clearing haloo3d_fb_cleardepth(&rc->window); if (rc->windowclear & 0xF000) { haloo3d_fb_clear(&rc->window, rc->windowclear); } // Precalc stuff for later object rendering rc->precalc_halfwidth = rc->window.width * H3DVF(0.5); rc->precalc_halfheight = rc->window.height * H3DVF(0.5); haloo3d_perspective(perspective, rc->fov, (mfloat_t)rc->window.width / rc->window.height, rc->nearclip, rc->farclip); vec3_add(lookat.v, p->pos.v, p->lookvec.v); haloo3d_my_lookat(cammatrix, p->pos.v, lookat.v, p->up.v); mat4_inverse(cammatrix, cammatrix); mat4_multiply(rc->precalc_screen, perspective, cammatrix); // Well, since we're here... might as well do the player-related stuff. // After all, we can be sure this is a player... right? player_chunkload(p, rc, *ecs); } void sys_towerlight(ecs_towerlight *tl, tecs **ecs, ecs_object *o) { tl->timer += (*ecs)->delta_s; float ang = fabs(sin(tl->timer * TOWERLIGHTRATE)); o->flatdither = TOWERLIGHTMINTRANS + (TOWERLIGHTMAXTRANS - TOWERLIGHTMINTRANS) * ang; for (int i = 0; i < 3; i++) { o->scale.v[i] = TOWERLIGHTMINSIZE + (TOWERLIGHTMAXSIZE - TOWERLIGHTMINSIZE) * ang; } } // Apply rotation to lookvec of placement, overrides lookvec void sys_rotation(ecs_placement *p, ecs_rotation *r) { YAWP2VEC(r->yaw, r->pitch, p->lookvec.v); } void sys_movement(ecs_placement *p, ecs_movement *m, tecs **ecs) { mfloat_t temp[VEC3_SIZE]; vec3_multiply_f(temp, m->lookvec.v, (*ecs)->delta_s); vec3_add(p->lookvec.v, p->lookvec.v, temp); vec3_multiply_f(temp, m->up.v, (*ecs)->delta_s); vec3_add(p->up.v, p->up.v, temp); vec3_multiply_f(temp, m->pos.v, (*ecs)->delta_s); vec3_add(p->pos.v, p->pos.v, temp); // vec3_multiply(p->pos.v, p->pos.v, p->pos.v); } // Perform the entire rendering of an object void sys_renderobject(ecs_placement *p, ecs_object *o, tecs **ecs) { // --**-- First, precalc all the vertices in the object --**-- mfloat_t tmp[VEC4_SIZE]; mfloat_t modelm[MAT4_SIZE]; mfloat_t finalmatrix[MAT4_SIZE]; struct vec4 precalc_verts[H3D_OBJ_MAXVERTICES]; vec3_add(tmp, p->pos.v, p->lookvec.v); haloo3d_my_lookat(modelm, p->pos.v, tmp, p->up.v); // Apply scale such that it looks like it was applied first (this prevents // scaling applying skew to a rotated object) haloo3d_mat4_prescalev(modelm, o->scale.v); haloo3d_trirender rsettings; // We might be rendering into multiple contexts in a multiplayer game (or // just different angles or something) for (int ctx = 0; ctx < o->contextcount; ctx++) { memcpy(&rsettings, &o->context[ctx]->rendersettings, sizeof(haloo3d_trirender)); rsettings.texture = o->texture; if (o->flatdither > 0) { rsettings.ditherflat = o->flatdither; } mat4_multiply(finalmatrix, o->context[ctx]->precalc_screen, modelm); haloo3d_precalc_verts(o->model, finalmatrix, precalc_verts); // --**-- Next, setup some rendering invariants --**-- struct vec3 lighting; // The compiler is complaining about lighting being used unitialized, even // though it's not. Just shut it up vec3(lighting.v, 0, 0, 0); if (o->lighting) { if (o->lighting->autolightfix) { // Lighting doesn't rotate with the model unless you do it yourself. // In the easy system, you can request the renderer to do it for you struct vec4 ltmp, lout; // Lighting is centered at 0 vec4(ltmp.v, 0, 0, 0, 1); // Calc the same lookat just without translation. THis should be the // same rotation matrix used on the model haloo3d_my_lookat(modelm, ltmp.v, p->lookvec.v, p->up.v); // We actually want the inverse. Apparently to speed things up, the // transpose works for rotation matrices(?) but I don't trust that // this lookat does that mat4_inverse(modelm, modelm); mat4_transpose(modelm, modelm); // We HAVE to have a vec4 (oof) vec4(ltmp.v, o->lighting->dir.x, o->lighting->dir.y, o->lighting->dir.z, 1); haloo3d_vec4_multmat_into(<mp, modelm, &lout); // No need to fix W, should all be good (no perspective divide). But // we DO need to pull out that result vec3(lighting.v, lout.x, lout.y, lout.z); vec3_normalize(lighting.v, lighting.v); } else { vec3_assign(lighting.v, o->lighting->dir.v); } } // --**-- Finally, actually render faces --**-- haloo3d_facef face, baseface; haloo3d_facef outfaces[H3D_FACEF_MAXCLIP]; for (int facei = 0; facei < o->model->numfaces; facei++) { // Copy face values out of precalc array and clip them haloo3d_make_facef(o->model->faces[facei], precalc_verts, o->model->vtexture, face); int tris = haloo3d_facef_clip(face, outfaces); if (tris > 0) { uint8_t oflags = rsettings.flags; if (o->lighting && facei < o->stoplighting) { haloo3d_obj_facef(o->model, o->model->faces[facei], baseface); rsettings.intensity = haloo3d_calc_light(lighting.v, o->lighting->minlight, baseface); } else { rsettings.intensity = H3DVF(1.0); } // if ((r->_objstate[object - r->objects] & H3D_EASYOBJSTATE_NOTRANS)) // { // r->rendersettings.flags &= ~H3DR_TRANSPARENCY; // } for (int ti = 0; ti < tris; ti++) { int backface = !haloo3d_facef_finalize(outfaces[ti]); if (o->cullbackface && facei < o->stoplighting && backface) { continue; } (*ecs)->totaldrawn++; // We still have to convert the points into the view haloo3d_facef_viewport_into_fast(outfaces[ti], o->context[ctx]->precalc_halfwidth, o->context[ctx]->precalc_halfheight); haloo3d_triangle(&o->context[ctx]->window, &rsettings, outfaces[ti]); } rsettings.flags = oflags; } } } } void sys_playerinput(ecs_input *in) { in->numevents = 0; unigi_type_event event; 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; } in->numevents++; } while (event.type != unigi_enum_event_none); } void sys_playergarbage(ecs_playergarbage *pg, ecs_object *ob, tecs **ecs) { ecs_eid id = tecs_eid(ecs); if (ob->contextcount <= 0) { eprintf("Deleting object %d\n", id); if (pg->dynmodel[0]) { haloo3d_easystore_deleteobj(&(*ecs)->storage, pg->dynmodel, haloo3d_obj_free); } tecs_deleteentity(*ecs, id); } } void sys_placement_lock(ecs_placement_lock *lock, ecs_placement *pl) { if (lock->options & TECS_PLOCK_LOCKUP) { pl->up = lock->link->up; } if (lock->options & TECS_PLOCK_LOCKPOSX) { pl->pos.x = lock->link->pos.x; } if (lock->options & TECS_PLOCK_LOCKPOSY) { pl->pos.y = lock->link->pos.y; } if (lock->options & TECS_PLOCK_LOCKPOSZ) { pl->pos.z = lock->link->pos.z; } if (lock->options & TECS_PLOCK_LOCKLOOKX) { pl->lookvec.x = lock->link->lookvec.x; } if (lock->options & TECS_PLOCK_LOCKLOOKY) { pl->lookvec.y = lock->link->lookvec.y; } if (lock->options & TECS_PLOCK_LOCKLOOKZ) { pl->lookvec.z = lock->link->lookvec.z; } } // void sys_chunk(ecs_chunk *ch, ecs_object *o, tecs **ecs) { // // Is it really ok to have this check every single time? Seems kinda // wasteful if (ch->generation == 1) { // } // } // --------------------------------------------------- // Setup functions // --------------------------------------------------- ecs_eid setup_player(tecs *ecs, render_context *context) { ecs_eid playerid = tecs_newentity(ecs, 0); ECS_SETCOMPONENT(ecs, playerid, ecs_placement){ .pos = {.x = 0, .y = INIT_PLAYERHEIGHT, .z = 0}, .up = DEFAULTUP}; ECS_SETCOMPONENT(ecs, playerid, ecs_movement){.pos = INIT_PLAYERSPEED}; ECS_SETCOMPONENT(ecs, playerid, ecs_rotation){.yaw = 0, .pitch = INIT_CAMPITCH}; ECS_SETCOMPONENT(ecs, playerid, ecs_rendercontext) context; ECS_SETCOMPONENT(ecs, playerid, ecs_input){}; return playerid; } ecs_eid setup_sea(tecs *ecs, render_context *ctx, ecs_placement *playerpos, haloo3d_easystore *storage) { ecs_eid seaid = tecs_newentity(ecs, 0); ECS_SETCOMPONENT(ecs, seaid, ecs_placement){ .pos = {.x = 0, .y = 0, .z = 0}, .lookvec = DEFAULTLOOK, .up = DEFAULTUP}; ECS_SETCOMPONENT(ecs, seaid, ecs_placement_lock){ .options = TECS_PLOCK_LOCKPOSX | TECS_PLOCK_LOCKPOSZ | TECS_PLOCK_LOCKUP, .link = playerpos, }; haloo3d_obj *model = haloo3d_easystore_addobj(storage, "sea"); haloo3d_obj_resetfixed(model, 2, 4); struct vec3 seacol = haloo3d_gen_paletteuv(SEACOL); // 0xF26F); int seauv = haloo3d_obj_addvtexture(model, seacol); // struct vec3 seacol2 = haloo3d_gen_paletteuv(0xF008); // haloo3d_obj_addvtexture(model, seacol2); // clang-format off // REMEMBER: NEGATIVE Z IS FORWARD int tl = haloo3d_obj_addvertex(model, (struct vec4){.x = -1, .y = 0, .z = -1, .w = 1}); int tr = haloo3d_obj_addvertex(model, (struct vec4){.x = 1, .y = 0, .z = -1, .w = 1}); int bl = haloo3d_obj_addvertex(model, (struct vec4){.x = -1, .y = 0, .z = 1, .w = 1}); int br = haloo3d_obj_addvertex(model, (struct vec4){.x = 1, .y = 0, .z = 1, .w = 1}); // clang-format on haloo3d_facei face; fastface(face, seauv, bl, br, tl); haloo3d_obj_addface(model, face); // model->faces[0][0].texi++; fastface(face, seauv, br, tr, tl); haloo3d_obj_addface(model, face); ECS_SETCOMPONENT(ecs, seaid, ecs_object){ .flatdither = SEATRANS, .texture = haloo3d_easystore_gettex(&ecs->storage, PALETTEKEY), .scale = {.x = VIEWDISTANCE * 1.5, .y = VIEWDISTANCE * 1.5, .z = VIEWDISTANCE * 1.5}, .lighting = NULL, .model = model, .cullbackface = 1, .context = {ctx}, .contextcount = 1}; return seaid; } // --------------------------------------------------- // MAIN FUNCTION // --------------------------------------------------- int main() { // int argc, char **argv) { srand(clock()); gen_terrain_seed = rand(); // Init unigi system. Can use anything here that can render to screen unigi_type_resolution res; res.width = SWIDTH; res.height = SHEIGHT; res.depth = 0; unigi_graphics_init(); unigi_window_create(res, "terrain.exe"); // render.printbuf); eprintf("Initialized unigi system\n"); haloo3d_fb screen; haloo3d_fb_init(&screen, SWIDTH, SHEIGHT); haloo3d_print_tracker pt; char printbuf[8192]; haloo3d_print_initdefault(&pt, printbuf, sizeof(printbuf)); pt.fb = &screen; pt.scale = 1; haloo3d_easytimer frametimer, drawtimer, sdltimer; haloo3d_easytimer_init(&frametimer, AVGWEIGHT); haloo3d_easytimer_init(&drawtimer, AVGWEIGHT); haloo3d_easytimer_init(&sdltimer, AVGWEIGHT); render_context context; context.windowclear = SKYCOL; context.nearclip = INIT_NEARCLIP; context.farclip = INIT_FARCLIP; context.fov = INIT_FOV; haloo3d_trirender_init(&context.rendersettings); // context.rendersettings.flags &= ~H3DR_PCT; // context.rendersettings.flags &= ~H3DR_DITHERPIX; // context.rendersettings.flags |= H3DR_DITHERTRI; context.rendersettings.ditherclose = INIT_DITHERSTART; context.rendersettings.ditherfar = INIT_DITHEREND; haloo3d_fb_init(&context.window, WIDTH, HEIGHT); eprintf("Initialized screen buffers, context, and timers\n"); tecs ecs; ecs.delta_s = 0; tecs_init(&ecs); int tecs_size = sizeof(tecs); YAWP2VEC(INIT_LIGHTYAW, INIT_LIGHTPITCH, ecs.globallighting.v); // An issue? Not a pointer? Eeehhh.... ecs.chunklight.dir = ecs.globallighting; ecs.chunklight.minlight = INIT_MINLIGHT; ecs.chunklight.autolightfix = 0; haloo3d_easystore_init(&ecs.storage); haloo3d_fb *palettetex = haloo3d_easystore_addtex(&ecs.storage, PALETTEKEY); haloo3d_gen_palettetex(palettetex); haloo3d_obj *treemodel = haloo3d_easystore_addobj(&ecs.storage, TREEKEY); gen_tree_model(treemodel); haloo3d_obj *cloudmodel = haloo3d_easystore_addobj(&ecs.storage, CLOUDKEY); gen_circle_model(cloudmodel, CLOUDCOL, 32); haloo3d_obj *lightmodel = haloo3d_easystore_addobj(&ecs.storage, LIGHTKEY); gen_circle_model(lightmodel, LIGHTCOL, 16); haloo3d_obj *redflower = haloo3d_easystore_addobj(&ecs.storage, REDFLOWERKEY); gen_flower_model(redflower, REDFLOWERCOL); haloo3d_obj *yellowflower = haloo3d_easystore_addobj(&ecs.storage, YELLOWFLOWERKEY); gen_flower_model(yellowflower, YELLOWFLOWERCOL); haloo3d_obj *tower = haloo3d_easystore_addobj(&ecs.storage, TOWERKEY); gen_tower_model(tower, TOWERCOL); eprintf("Setup ECS system + obj/tex storage (%d bytes)\n", tecs_size); ecs_eid playerid = setup_player(&ecs, &context); ecs_placement *playerpos = &ECS_GETCOMPONENT(&ecs, playerid, ecs_placement); ecs_eid seaid = setup_sea(&ecs, &context, playerpos, &ecs.storage); eprintf("Setup player(%d) + sea(%d)\n", playerid, seaid); // MAIN LOOP while (1) { haloo3d_easytimer_start(&frametimer); haloo3d_print_refresh(&pt); ecs.totaldrawn = 0; // ECS logic (which includes rendering) if (ecs.delta_s) { ECS_RUNSYSTEM1(&ecs, sys_playerinput, ecs_input); ECS_RUNSYSTEM2(&ecs, sys_rotation, ecs_placement, ecs_rotation); ECS_RUNSYSTEM3(&ecs, sys_movement, ecs_placement, ecs_movement, tecs); ECS_RUNSYSTEM2(&ecs, sys_placement_lock, ecs_placement_lock, ecs_placement); ECS_RUNSYSTEM3(&ecs, sys_rendercontext, ecs_rendercontext, ecs_placement, tecs); ECS_RUNSYSTEM3(&ecs, sys_playergarbage, ecs_playergarbage, ecs_object, tecs); ECS_RUNSYSTEM3(&ecs, sys_towerlight, ecs_towerlight, tecs, ecs_object); haloo3d_easytimer_start(&drawtimer); ECS_RUNSYSTEM3(&ecs, sys_renderobject, ecs_placement, ecs_object, tecs); haloo3d_easytimer_end(&drawtimer); } // Scale 3D into final buffer haloo3d_fb_fill(&screen, &context.window); // clang-format off haloo3d_print(&pt, "Pframe: %05.2f (%05.2f) DT: %0.3f\n" #ifndef LIMITEDPRINT "PSDLFl: %05.2f (%05.2f)\n" "Render: %05.2f (%05.2f)\n" "PlPos: %05.2f (%05.2f)\n" #endif "Tris: %d\n", frametimer.last * 1000, frametimer.sum * 1000, ecs.delta_s, #ifndef LIMITEDPRINT sdltimer.last * 1000, sdltimer.sum * 1000, drawtimer.last * 1000, drawtimer.sum * 1000, playerpos->pos.x, playerpos->pos.z, #endif ecs.totaldrawn); // clang-format on // Finally, actually put buffer onto screen 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); // Wait for next frame based on fps float waittime = (1.0 / fps) - frametimer.last; if (waittime > 0) { unigi_time_sleep(waittime * unigi_time_clocks_per_s); } ecs.delta_s = frametimer.last + MAX(waittime, 0); } haloo3d_fb_free(&screen); haloo3d_fb_free(&context.window); haloo3d_easystore_deleteallobj(&ecs.storage, haloo3d_obj_free); haloo3d_easystore_deletealltex(&ecs.storage, haloo3d_fb_free); }