#include #include #include #include "lib/linmath.h" #include "render.h" #define deg2rad(X) ((X)*M_PI/180.0) 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; } 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) { uint64_t nanos = ahead * 1000000000.0; struct timespec ts_sleep; ts_sleep.tv_nsec = nanos % 1000000000; ts_sleep.tv_sec = nanos / 1000000000; nanosleep(&ts_sleep, nullptr); } } 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); }