From 8b227bf9933431cc594f4426e78f1ee3e04eea38 Mon Sep 17 00:00:00 2001 From: Carlos Sanchez Date: Sun, 8 Sep 2024 23:04:57 -0400 Subject: [PATCH] Setting up basic components and systems --- ecs2.h | 74 +++++++++++++++-- terrain.c | 199 +++++++++++++++++++++++++++++++++++++++++++-- terrain_ecstypes.h | 54 +++++++++--- 3 files changed, 303 insertions(+), 24 deletions(-) diff --git a/ecs2.h b/ecs2.h index 58f5b1d..e16e1ce 100644 --- a/ecs2.h +++ b/ecs2.h @@ -93,19 +93,77 @@ typedef int ecs_eid; (_ecs)->entities[eid] |= type##_fl; \ (_ecs)->c_##type[eid] = (type) +// Internal-use loop for ECS_RUNSYSTEM +#define _ECS_RSLOOP(_i, _ecs, _comps) \ + for (int _i = 0; _i < ECS_MAXENTITIES; _i++) \ + if (((_ecs)->entities[_i] & _comps) == _comps) + // A shortcut to run a system against all matching components. You could also -// use the query function for your ecs type if you don't want to use this macro. -// Make sure you pass int the name of YOUR system, not the "run" function. This -// function bypasses -#define ECS_RUNSYSTEM(_ecs, _comps, _system) \ +// define systems using ECS_SYSTEM1 etc. and call them per entity. +// Make sure you pass int the name of YOUR system, not the "run" function (you +// don't need the run function when using these macros) +#define ECS_RUNSYSTEM1(_ecs, _system, type1) \ { \ - ecs_cid _realcomps = ECS_SELFFLAG | _comps; \ - for (int __i = 0; __i < ECS_MAXENTITIES; __i++) { \ - if ((_ecs->entities[__i] & _realcomps) == _realcomps) \ - _system##_run(_ecs, __i); \ + ecs_cid _ecsflags = ECS_SELFFLAG | type1##_fl; \ + _ECS_RSLOOP(eid, _ecs, _ecsflags) { _system((_ecs)->c_##type1 + eid); } \ + } + +// A shortcut to run a system against all matching components. You could also +// define systems using ECS_SYSTEM2 etc. and call them per entity. +// Make sure you pass int the name of YOUR system, not the "run" function (you +// don't need the run function when using these macros) +#define ECS_RUNSYSTEM2(_ecs, _system, t1, t2) \ + { \ + ecs_cid _ecsflags = ECS_SELFFLAG | t1##_fl | t2##_fl; \ + _ECS_RSLOOP(eid, _ecs, _ecsflags) { \ + _system((_ecs)->c_##t1 + eid, (_ecs)->c_##t2 + eid); \ } \ } +// A shortcut to run a system against all matching components. You could also +// define systems using ECS_SYSTEM3 etc. and call them per entity. +// Make sure you pass int the name of YOUR system, not the "run" function (you +// don't need the run function when using these macros) +#define ECS_RUNSYSTEM3(_ecs, _system, t1, t2, t3) \ + { \ + ecs_cid _ecsflags = ECS_SELFFLAG | t1##_fl | t2##_fl | t3##_fl; \ + _ECS_RSLOOP(eid, _ecs, _ecsflags) { \ + _system((_ecs)->c_##t1 + eid, (_ecs)->c_##t2 + eid, \ + (_ecs)->c_##t3 + eid); \ + } \ + } + +// A shortcut to run a system against all matching components. You could also +// define systems using ECS_SYSTEM4 etc. and call them per entity. +// Make sure you pass int the name of YOUR system, not the "run" function (you +// don't need the run function when using these macros) +#define ECS_RUNSYSTEM4(_ecs, _system, t1, t2, t3, t4) \ + { \ + ecs_cid _ecsflags = ECS_SELFFLAG | t1##_fl | t2##_fl | t3##_fl | t4##_fl; \ + _ECS_RSLOOP(eid, _ecs, _ecsflags) { \ + _system((_ecs)->c_##t1 + eid, (_ecs)->c_##t2 + eid, \ + (_ecs)->c_##t3 + eid, (_ecs)->c_##t4 + eid); \ + } \ + } + +// A shortcut to run a system against all matching components. You could also +// define systems using ECS_SYSTEM5 etc. and call them per entity. +// Make sure you pass int the name of YOUR system, not the "run" function (you +// don't need the run function when using these macros) +#define ECS_RUNSYSTEM5(_ecs, _system, t1, t2, t3, t4, t5) \ + { \ + ecs_cid _ecsflags = \ + ECS_SELFFLAG | t1##_fl | t2##_fl | t3##_fl | t4##_fl | t5##_fl; \ + _ECS_RSLOOP(eid, _ecs, _ecsflags) { \ + _system(_ecs->c_##t1 + eid, _ecs->c_##t2 + eid, _ecs->c_##t3 + eid, \ + _ecs->c_##t4 + eid, _ecs->c_##t5 + eid); \ + } \ + } + +// --------------------------------------------------------- +// YOU DON'T NEED THIS STUFF IF YOU USE ECS_RUNSYSTEM +// --------------------------------------------------------- + // Add a system function which automatically calls your given function with // pre-pulled items from the entity component arrays. The new function is // named the same as the old one, just with _run appeneded diff --git a/terrain.c b/terrain.c index 8fa0077..44e8b8d 100644 --- a/terrain.c +++ b/terrain.c @@ -3,29 +3,146 @@ #include "haloo3d/haloo3dex_gen.h" #include "haloo3d/haloo3dex_obj.h" +#include "haloo3d/lib/mathc.h" #include "unigi/unigi.headers/src/main.h" #include "unigi/unigi.platform.sdl1/src/main.c" #include "ecs2.h" +// #include "keys.h" #include "terrain_ecstypes.h" #include #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 AVGWEIGHT 0.85 +// These are initial values but there may be ways to change it #define CAM_INITPITCH MPI_2 +#define INIT_NEARCLIP 0.01 +#define INIT_FARCLIP 100.0 +#define INIT_DITHERSTART 10000 +#define INIT_DITHEREND 10000 -// Try 0.5 and 3.5 or something -#define DITHERSTART 10000 -#define DITHEREND 10000 +// Some globals you can mess around with potentially +int fps = 30; + +// The terrain ecs systems + +// All initialization for a specific render context +void sys_rendercontext(ecs_rendercontext *erc, ecs_placement *p) { + render_context *rc = *erc; + rc->precalc_halfwidth = rc->window.width * H3DVF(0.5); + rc->precalc_halfheight = rc->window.height * H3DVF(0.5); + haloo3d_perspective(rc->precalc_perspective, rc->fov, + (mfloat_t)rc->window.width / rc->window.height, + rc->nearclip, rc->farclip); + haloo3d_fb_cleardepth(&rc->window); + if (rc->windowclear & 0xF000) { + haloo3d_fb_clear(&rc->window, rc->windowclear); + } +} + +// Apply rotation to lookvec of placement +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) { + vec3_add(p->lookvec.v, p->lookvec.v, m->lookvec.v); + vec3_add(p->up.v, p->up.v, m->up.v); + vec3_add(p->pos.v, p->pos.v, m->pos.v); +} + +// Perform the entire rendering of an object +void sys_renderobject(ecs_placement *p, ecs_object *o) { + struct vec3 lighting; + struct vec4 precalc_verts[H3D_OBJ_MAXVERTICES]; + // First, precalc all the vertices in the object + // ------------------------------------------------------------------ + mfloat_t tmp[VEC4_SIZE]; + mfloat_t modelm[MAT4_SIZE]; + mfloat_t finalmatrix[MAT4_SIZE]; + 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); + mat4_multiply(finalmatrix, o->context->precalc_screen, modelm); + haloo3d_precalc_verts(o->model, finalmatrix, precalc_verts); + // Next, setup some rendering invariants + // ------------------------------------------------------------------ + o->context->rendersettings.texture = o->texture; + 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 + // ------------------------------------------------------------------ + int totaldrawn = 0; + 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 = o->context->rendersettings.flags; + if (o->lighting) { + haloo3d_obj_facef(o->model, o->model->faces[facei], baseface); + o->context->rendersettings.intensity = + haloo3d_calc_light(lighting.v, o->lighting->minlight, baseface); + } else { + o->context->rendersettings.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 && backface) { + continue; + } + totaldrawn++; + // We still have to convert the points into the view + haloo3d_facef_viewport_into_fast(outfaces[ti], + o->context->precalc_halfwidth, + o->context->precalc_halfheight); + haloo3d_triangle(&o->context->window, &o->context->rendersettings, + outfaces[ti]); + } + o->context->rendersettings.flags = oflags; + } + } + // return totaldrawn; +} int main() { // int argc, char **argv) { srand(clock()); @@ -49,6 +166,8 @@ int main() { // int argc, char **argv) { eprintf("Initialized storage and default textures/etc\n"); + haloo3d_fb screen3d; + haloo3d_fb_init(&screen3d, WIDTH, HEIGHT); haloo3d_fb screen; haloo3d_fb_init(&screen, SWIDTH, SHEIGHT); @@ -59,6 +178,74 @@ int main() { // int argc, char **argv) { haloo3d_easytimer_init(&filltimer, AVGWEIGHT); haloo3d_easytimer_init(&logictimer, AVGWEIGHT); + eprintf("Initialized screen buffers and timers\n"); + + tecs ecs; + tecs_init(&ecs); + + // MAIN LOOP + while (1) { + haloo3d_easytimer_start(&frametimer); + + 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); + + // ECS logic (which includes rendering) + ECS_RUNSYSTEM2(&ecs, sys_rotation, ecs_placement, ecs_rotation); + ECS_RUNSYSTEM2(&ecs, sys_movement, ecs_placement, ecs_movement); + ECS_RUNSYSTEM2(&ecs, sys_rendercontext, ecs_rendercontext, ecs_placement); + ECS_RUNSYSTEM2(&ecs, sys_renderobject, ecs_placement, ecs_object); + + // Scale 3D into final buffer + haloo3d_easytimer_start(&filltimer); + haloo3d_fb_fill(&screen, &screen3d); + haloo3d_easytimer_end(&filltimer); + + /* + haloo3d_print(&render.tprint, + "Pframe: %05.2f (%05.2f)\nPSDLFl: %05.2f " + "(%05.2f)\nFill: %05.2f " + "(%05.2f)\nLogic: %05.2f (%05.2f)\nTris: %d / %d\nVerts: " + "%d\nWState: %d", + frametimer.last * 1000, frametimer.sum * 1000, + sdltimer.last * 1000, sdltimer.sum * 1000, + filltimer.last * 1000, filltimer.sum * 1000, + logictimer.last * 1000, logictimer.sum * 1000, totaldrawn, + render.totalfaces, render.totalverts, wstate.state); + */ + + // 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); + } + } + + haloo3d_fb_free(&screen); + haloo3d_fb_free(&screen3d); haloo3d_easystore_deleteallobj(&storage, haloo3d_obj_free); haloo3d_easystore_deletealltex(&storage, haloo3d_fb_free); } diff --git a/terrain_ecstypes.h b/terrain_ecstypes.h index e12c386..6ed270b 100644 --- a/terrain_ecstypes.h +++ b/terrain_ecstypes.h @@ -11,31 +11,47 @@ typedef struct { // struct vec4 precalcs[H3D_OBJ_MAXVERTICES]; // haloo3d_facef outfaces[H3D_FACEF_MAXCLIP]; - mfloat_t perspective[MAT4_SIZE]; + // haloo3d_perspective(render.perspective, fov, ASPECT, NEARCLIP, FARCLIP); + mfloat_t precalc_perspective[MAT4_SIZE]; mfloat_t precalc_screen[MAT4_SIZE]; + mfloat_t precalc_halfwidth; // Optimization for reducing calcs per tri + mfloat_t precalc_halfheight; // Optimization for reducing calcs per tri + mfloat_t fov; + mfloat_t nearclip; + mfloat_t farclip; + // NOTE: aspect ratio calculated from window haloo3d_fb window; // Baseline render settings. Some of these settings will be applied to // each object associated with this context haloo3d_trirender rendersettings; - mfloat_t precalc_halfwidth; // Optimization for reducing calcs per tri - mfloat_t precalc_halfheight; // Optimization for reducing calcs per tri + // If alpha is 0, screen is not cleared + uint16_t windowclear; } render_context; // Associate this to some entity along with a placement and it will be the // main "renderer" for a viewpoint typedef render_context *ecs_rendercontext; -// All values which allow rendering of a 3d object +// Associate lighting with some object. Since this is part of the renderer, +// this isn't a component you can assign (it also doesn't mean anything on its +// own) typedef struct { - struct vec3 scale; // how big the thing should be in world - struct vec3 *lighting; // a pointer to lighting, null for none - render_context *context; // What to render into - haloo3d_obj *model; - haloo3d_fb *texture; - uint8_t cullbackface; // Whether to cull backfaces (you probably should) + struct vec3 dir; + mfloat_t minlight; // Whether to automatically move lighting when models have a // lookvec. This also normalizes the light uint8_t autolightfix; +} object_lighting; + +// All values which allow rendering of a 3d object +typedef struct { + // haloo3d_trirender rendersettings; // baseline settings for THIS object. + struct vec3 scale; // how big the thing should be in world + object_lighting *lighting; // a pointer to lighting, null for none + render_context *context; // What to render into + haloo3d_obj *model; + haloo3d_fb *texture; + uint8_t cullbackface; // Whether to cull backfaces (you probably should) } ecs_object; // Some placement within the world. Doesn't imply anything other than a @@ -46,6 +62,20 @@ typedef struct { struct vec3 lookvec; } ecs_placement; +// Movement is applied to placement and may also be used to check for +// collisions and whatever +typedef struct { + struct vec3 pos; + struct vec3 up; + struct vec3 lookvec; +} ecs_movement; + +// Use rotation to override the lookvec of an ecs_placement +typedef struct { + mfloat_t yaw; + mfloat_t pitch; +} ecs_rotation; + // typedef haloo3d_camera *ecs_camera; // typedef haloo3d_obj_instance *ecs_object; @@ -73,11 +103,15 @@ ECS_START(tecs) ECS_COMPONENT(ecs_rendercontext); ECS_COMPONENT(ecs_object); ECS_COMPONENT(ecs_placement); +ECS_COMPONENT(ecs_rotation); +ECS_COMPONENT(ecs_movement); ECS_END(tecs) // And then a copy of the components here... that sucksssss ECS_CID(ecs_rendercontext, 0); ECS_CID(ecs_object, 1); ECS_CID(ecs_placement, 2); +ECS_CID(ecs_rotation, 3); +ECS_CID(ecs_movement, 4); #endif