diff options
| author | Lizzy Fleckenstein <lizzy@vlhl.dev> | 2026-04-12 20:57:06 +0200 |
|---|---|---|
| committer | Lizzy Fleckenstein <lizzy@vlhl.dev> | 2026-04-12 20:59:39 +0200 |
| commit | e5af28536bfb0f4c9131df56d2009ba5196f5e3a (patch) | |
| tree | 3ab928f961a1ccd8440b070d7b57f79146457e8c /src/render.c | |
| download | animtool-e5af28536bfb0f4c9131df56d2009ba5196f5e3a.tar.xz | |
init
Diffstat (limited to 'src/render.c')
| -rw-r--r-- | src/render.c | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/src/render.c b/src/render.c new file mode 100644 index 0000000..9c7d796 --- /dev/null +++ b/src/render.c @@ -0,0 +1,191 @@ +#include <math.h> +#include <time.h> +#include <unistd.h> +#include "lib/linmath.h" +#include "render.h" + +typedef arraybuf(struct draw_call) draw_calls; + +struct render_state { + size_t n_frame; + double time; + struct timespec start_ts; + draw_calls calls_buf; + rgba_data render_buffer; +}; + +static void render_surface(struct draw_surface *dsurf, struct surface *surf, double time) +{ + uint32_t color = round(expr_eval(&surf->color, time)); + for (size_t i = 0; i < 3; i++) + dsurf->color[i] = (float) ((color >> ((3-i-1)*8)) & 0xff) / 255.0; + if ((dsurf->texture.present = surf->texture.present)) + dsurf->texture.data = surf->texture.data.id; +} + +#define deg2rad(X) ((X)*M_PI/180.0) + +static void render_node(struct node *node, mat4x4 model, draw_calls *calls, double time) +{ + switch (node->type) { + case NODE_TRANSFORM: { + float vals[TRANSFORM_OPS_N][3] = { + [TR_TRANSLATE] = { 0.0, 0.0, 0.0 }, + [TR_SCALE] = { 1.0, 1.0, 1.0 }, + [TR_ROTATE] = { 0.0, 0.0, 0.0 }, + }; + for (enum transform_op op = 0; op < TRANSFORM_OPS_N; op++) + for (size_t i = 0; i < 3; i++) + if (node->transform.has_op[op][i]) + vals[op][i] = expr_eval(&node->transform.op[op][i], time); + + mat4x4 trans; + mat4x4_identity(trans); + mat4x4_translate(trans, vals[TR_TRANSLATE][0], vals[TR_TRANSLATE][1], vals[TR_TRANSLATE][2]); + mat4x4_scale_aniso(trans, trans, vals[TR_SCALE][0], vals[TR_SCALE][1], vals[TR_SCALE][2]); + mat4x4_rotate_X(trans, trans, deg2rad(vals[TR_ROTATE][0])); + mat4x4_rotate_Y(trans, trans, deg2rad(vals[TR_ROTATE][1])); + mat4x4_rotate_Z(trans, trans, deg2rad(vals[TR_ROTATE][2])); + + mat4x4 new_model; + mat4x4_mul(new_model, model, trans); + + for (size_t i = 0; i < node->transform.children.len; i++) + render_node(&node->transform.children.ptr[i], new_model, calls, time); + break; + } + case NODE_CAMERA: { + vec4 pos, up, origin = { 0.0, 0.0, 0.0, 1.0 }, origin_up = { 0.0, 1.0, 0.0, 1.0 }; + mat4x4_mul_vec4(pos, model, origin); + if (node->camera.roll) + mat4x4_mul_vec4(up, model, origin_up); + else + vec4_dup(up, origin_up); + vec3_dup(node->camera.pos, pos); + vec3_norm(node->camera.up, up); + break; + } + case NODE_OBJECT: { + arraybuf_grow(calls, 1); + struct draw_call *call = &calls->ptr[calls->len++]; + call->mesh = node->object.mesh.id; + render_surface(&call->surface, &node->object.surface, time); + mat4x4_dup(call->model, model); + break; + } + case NODE_BONE: { + vec4 pos, origin = { 0.0, 0.0, 0.0, 1.0 }; + mat4x4_mul_vec4(pos, model, origin); + vec3_dup(node->bone.pos, pos); + } + case NODE_LIGHT: + // TODO: light + break; + } +} + +static void eval_pos(struct pos *pos, float out[3], double time) +{ + switch (pos->type) { + case POS_ABSOLUTE: + for (size_t i = 0; i < 3; i++) + out[i] = expr_eval(&pos->absolute[i], time); + break; + case POS_ATTACHED: + vec3_dup(out, pos->attach->pos); + break; + } +} + +static void render_frame(struct draw_frame *frame, struct scene *scene, draw_calls *calls_buf, double time) +{ + mat4x4 model; + mat4x4_identity(model); + calls_buf->len = 0; + render_node(&scene->root, model, calls_buf, time); + + mat4x4 view; + vec3 target; + eval_pos(&scene->camera->target, target, time); + mat4x4_look_at(view, scene->camera->pos, target, scene->camera->up); + + mat4x4 proj; + switch (scene->proj.type) { + case PROJ_PERSPECTIVE: { + float fov = deg2rad(expr_eval(&scene->proj.perspective.fov, time)); + mat4x4_perspective(proj, fov, (float) scene->size[0] / scene->size[1], + scene->proj.perspective.planes[0], scene->proj.perspective.planes[1]); + break; + } + case PROJ_ORTHO: { + // TODO: aspect scaling? + float *planes = scene->proj.ortho.planes; + mat4x4_ortho(proj, planes[0], planes[1], planes[2], planes[3], planes[4], planes[5]); + break; + } + } + + array_assign(&frame->calls, *calls_buf); + if ((frame->bg.present = scene->bg.present)) + render_surface(&frame->bg.data, &scene->bg.data, time); + mat4x4_mul(frame->view_proj, proj, view); +} + +static void render_seq(size_t n_inst, struct seq_inst inst[n_inst], struct scene *scene, struct draw_backend *draw, struct nut_writer *out, struct render_state *state) +{ + for (size_t i = 0; i < n_inst; i++) { + switch (inst[i].type) { + case SEQ_TIME: { + double target = state->time + inst[i].time; + double time; + while ((time = (double) state->n_frame / scene->fps) < target) { + if (scene->live.present) { + struct timespec ts_now; + clock_gettime(CLOCK_REALTIME, &ts_now); + double e_time = (double) + (ts_now.tv_nsec - state->start_ts.tv_nsec) / 1000000000.0 + + (ts_now.tv_sec - state->start_ts.tv_sec); + double ahead = time - e_time - scene->live.data; + if (ahead > 0.0) + usleep(ahead * 1000000.0); + } + + struct draw_frame frame; + render_frame(&frame, scene, &state->calls_buf, time); + draw->draw_frame(&frame, state->render_buffer); + nut_write_frame(out, state->render_buffer); + state->n_frame++; + } + state->time = target; + break; + } + case SEQ_REP: + for (size_t n = 0; inst[i].rep.times == SEQ_TIMES_INF || n < inst[i].rep.times; n++) + render_seq(inst[i].rep.children.len, inst[i].rep.children.ptr, scene, draw, out, state); + break; + case SEQ_POV: + scene->camera = inst[i].pov; + break; + } + } +} + +void render_scene(struct scene *scene, struct draw_backend *draw, struct nut_writer *out) +{ + struct render_state state = {}; + state.time = 0.0; + state.render_buffer = malloc(4 * scene->size[0] * scene->size[1]); + clock_gettime(CLOCK_REALTIME, &state.start_ts); + + if (scene->seq.len) { + render_seq(scene->seq.len, scene->seq.ptr, scene, draw, out, &state); + } else { + struct seq_inst seq; + seq.type = SEQ_TIME; + seq.time = 1.0; + render_seq(1, &seq, scene, draw, out, &state); + } + + free(state.render_buffer); + free(state.calls_buf.ptr); +} |
