From 0ddfe3c3dff0b95718cdaf8cb5684f09023feb1d Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 29 Oct 2024 19:48:50 +0200 Subject: [PATCH] WIP: shadow mapping Signed-off-by: Alexander Shishkin --- core/CMakeLists.txt | 2 +- core/camera.c | 75 --------------- core/camera.h | 8 +- core/light.c | 33 +++++++ core/light.h | 26 ++++++ core/model.c | 120 ++++++++++++++---------- core/model.h | 22 ++--- core/pipeline.c | 70 +++++++++++--- core/pipeline.h | 5 +- core/render-gl.c | 21 ++++- core/render.h | 3 + core/scene.c | 78 ++++++---------- core/scene.h | 5 +- core/shader.c | 6 +- core/shader.h | 3 +- core/ui.c | 28 ++++-- core/ui.h | 3 +- core/view.c | 178 ++++++++++++++++++++++++++++++++++++ core/view.h | 24 +++++ demo/ldjam56/CMakeLists.txt | 2 + demo/ldjam56/onehandclap.c | 56 +++++++----- shaders/model.frag | 32 ++++++- shaders/model.vert | 5 + shaders/passthrough.frag | 12 +++ shaders/passthrough.vert | 14 +++ shaders/shadow.frag | 10 ++ shaders/shadow.vert | 32 +++++++ 27 files changed, 616 insertions(+), 257 deletions(-) create mode 100644 core/light.c create mode 100644 core/light.h create mode 100644 core/view.c create mode 100644 core/view.h create mode 100644 shaders/passthrough.frag create mode 100644 shaders/passthrough.vert create mode 100644 shaders/shadow.frag create mode 100644 shaders/shadow.vert diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index e7b67ad..92e0251 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -82,7 +82,7 @@ set(ENGINE_LIB libonehandclap) set(ENGINE_SRC ${SERVER_SRC} ) set(ENGINE_SRC object.c ref.c util.c logger.c graphics.c input.c messagebus.c - matrix.c model.c shader.c librarian.c json.c clap.c + matrix.c model.c shader.c librarian.c json.c clap.c view.c light.c terrain.c ui.c scene.c font.c sound.c networking.c pngloader.c physics.c ui-animations.c input-fuzzer.c character.c settings.c gltf.c input-joystick.c render-gl.c mesh.c pipeline.c input-keyboard.c diff --git a/core/camera.c b/core/camera.c index df3989e..bc01450 100644 --- a/core/camera.c +++ b/core/camera.c @@ -3,81 +3,6 @@ #include "character.h" #include "ui-debug.h" -void camera_calc_frustum(struct camera *c, mat4x4 projmx) -{ - mat4x4 mvp, trans, invmvp; - vec4 corners[] = { - { -1, -1, -1, 1 }, { 1, -1, -1, 1 }, - { 1, 1, -1, 1 }, { -1, 1, -1, 1 }, - { -1, -1, 1, 1 }, { 1, -1, 1, 1 }, - { 1, 1, 1, 1 }, { -1, 1, 1, 1 } - }; - int i; - - mat4x4_mul(mvp, projmx, c->view_mx->m); - mat4x4_transpose(trans, mvp); - mat4x4_invert(invmvp, mvp); - - /* frustum planes */ - vec4_add(c->frustum_planes[0], trans[3], trans[0]); - vec4_sub(c->frustum_planes[1], trans[3], trans[0]); - vec4_add(c->frustum_planes[2], trans[3], trans[1]); - vec4_sub(c->frustum_planes[3], trans[3], trans[1]); - vec4_add(c->frustum_planes[4], trans[3], trans[2]); - vec4_sub(c->frustum_planes[5], trans[3], trans[2]); - - /* frustum corners */ - for (i = 0; i < 8; i++) { - vec4 q; - - mat4x4_mul_vec4(q, invmvp, corners[i]); - vec4_scale(c->frustum_corners[i], q, 1.f / q[3]); - } -} - -bool camera_entity_in_frustum(struct camera *c, struct entity3d *e) -{ - vec3 min, max; - int i; - - entity3d_aabb_min(e, min); - entity3d_aabb_max(e, max); - - for (i = 0; i < 6; i++) { - int r = 0; - vec4 v; - - vec4_setup(v, min[0], min[1], min[2], 1.0); - r += (vec4_mul_inner(c->frustum_planes[i], v) < 0.0) ? 1 : 0; - vec4_setup(v, max[0], min[1], min[2], 1.0); - r += (vec4_mul_inner(c->frustum_planes[i], v) < 0.0) ? 1 : 0; - vec4_setup(v, min[0], max[1], min[2], 1.0); - r += (vec4_mul_inner(c->frustum_planes[i], v) < 0.0) ? 1 : 0; - vec4_setup(v, max[0], max[1], min[2], 1.0); - r += (vec4_mul_inner(c->frustum_planes[i], v) < 0.0) ? 1 : 0; - vec4_setup(v, min[0], min[1], max[2], 1.0); - r += (vec4_mul_inner(c->frustum_planes[i], v) < 0.0) ? 1 : 0; - vec4_setup(v, max[0], min[1], max[2], 1.0); - r += (vec4_mul_inner(c->frustum_planes[i], v) < 0.0) ? 1 : 0; - vec4_setup(v, min[0], max[1], max[2], 1.0); - r += (vec4_mul_inner(c->frustum_planes[i], v) < 0.0) ? 1 : 0; - vec4_setup(v, max[0], max[1], max[2], 1.0); - r += (vec4_mul_inner(c->frustum_planes[i], v) < 0.0) ? 1 : 0; - if (r == 8) - return false; - } - - int r = 0; - for (r = 0, i = 0; i < 8; i++) r += c->frustum_corners[i][0] > max[0] ? 1 : 0; if (r == 8) return false; - for (r = 0, i = 0; i < 8; i++) r += c->frustum_corners[i][0] < min[0] ? 1 : 0; if (r == 8) return false; - for (r = 0, i = 0; i < 8; i++) r += c->frustum_corners[i][1] > max[1] ? 1 : 0; if (r == 8) return false; - for (r = 0, i = 0; i < 8; i++) r += c->frustum_corners[i][1] < min[1] ? 1 : 0; if (r == 8) return false; - for (r = 0, i = 0; i < 8; i++) r += c->frustum_corners[i][2] > max[2] ? 1 : 0; if (r == 8) return false; - for (r = 0, i = 0; i < 8; i++) r += c->frustum_corners[i][2] < min[2] ? 1 : 0; if (r == 8) return false; - - return true; -} - void camera_setup(struct camera *c) { // starting yaw values diff --git a/core/camera.h b/core/camera.h index 771202a..8c137cc 100644 --- a/core/camera.h +++ b/core/camera.h @@ -7,12 +7,14 @@ //#include "matrix.h" //#include "model.h" #include "model.h" +#include "view.h" //#include "physics.h" #define NUMBER_OF_DEBUG_LINES 4 struct camera { struct character *ch; + struct view view; /* GLfloat pitch; /\* left/right *\/ */ /* GLfloat yaw; /\* sideways *\/ */ /* GLfloat roll; /\* up/down *\/ */ @@ -26,10 +28,6 @@ struct camera { float dist; float yaw_delta; float pitch_delta; - struct matrix4f *view_mx; - struct matrix4f *inv_view_mx; - vec4 frustum_planes[6]; - vec4 frustum_corners[8]; float tmp_debug_line_start[3]; float tmp_debug_line_end[3 * NUMBER_OF_DEBUG_LINES]; float debug_line_start[3]; @@ -37,8 +35,6 @@ struct camera { }; void camera_setup(struct camera *c); -void camera_calc_frustum(struct camera *c, mat4x4 projmx); -bool camera_entity_in_frustum(struct camera *c, struct entity3d *e); void camera_move(struct camera *c, unsigned long fps); void camera_position(struct camera *c, float x, float y, float z, GLfloat *pos); void camera_reset_movement(struct camera *c); diff --git a/core/light.c b/core/light.c new file mode 100644 index 0000000..c44a336 --- /dev/null +++ b/core/light.c @@ -0,0 +1,33 @@ +#include "light.h" + +void light_update(struct light *light, int idx) +{ + // view_update(&light->view[idx], &light->pos[idx * 3], -90, 0, 0); + // mat4x4_look_at(light->view->view_mx.m, ) +} + +void light_set_pos(struct light *light, int idx, float pos[3]) +{ + int i; + + for (i = 0; i < 3; i++) + light->pos[idx * 3 + i] = pos[i]; + + light_update(light, idx); +} + +void light_set_color(struct light *light, int idx, float color[3]) +{ + int i; + + for (i = 0; i < 3; i++) + light->color[idx * 3 + i] = color[i]; +} + +void light_set_attenuation(struct light *light, int idx, float attenuation[3]) +{ + int i; + + for (i = 0; i < 3; i++) + light->attenuation[idx * 3 + i] = attenuation[i]; +} diff --git a/core/light.h b/core/light.h new file mode 100644 index 0000000..a3e945c --- /dev/null +++ b/core/light.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +#ifndef __CLAP_LIGHT_H__ +#define __CLAP_LIGHT_H__ + +#include "common.h" +#include "render.h" +#include "display.h" +#include "view.h" + +#define LIGHTS_MAX 4 + +struct light { + GLfloat pos[3 * LIGHTS_MAX]; + GLfloat color[3 * LIGHTS_MAX]; + GLfloat attenuation[3 * LIGHTS_MAX]; + GLfloat dir[3 * LIGHTS_MAX]; + struct view view[LIGHTS_MAX]; + texture_t *shadow[LIGHTS_MAX]; +}; + +void light_update(struct light *light, int idx); +void light_set_pos(struct light *light, int idx, float pos[3]); +void light_set_color(struct light *light, int idx, float color[3]); +void light_set_attenuation(struct light *light, int idx, float attenuation[3]); + +#endif /* __CLAP_LIGHT_H__ */ diff --git a/core/model.c b/core/model.c index 9450a1f..43026ca 100644 --- a/core/model.c +++ b/core/model.c @@ -321,8 +321,8 @@ struct model3dtx *model3dtx_new_texture(struct model3d *model, texture_t *tex) void model3dtx_set_texture(struct model3dtx *txm, int target, texture_t *tex) { struct shader_prog *prog = txm->model->prog; - texture_t **targets[] = { &txm->texture, &txm->normals, &txm->emission, &txm->sobel }; - GLint locs[] = { prog->texture_map, prog->normal_map, prog->emission_map, prog->sobel_tex }; + texture_t **targets[] = { &txm->texture, &txm->normals, &txm->emission, &txm->sobel, &txm->shadow }; + GLint locs[] = { prog->texture_map, prog->normal_map, prog->emission_map, prog->sobel_tex, prog->shadow_map }; if (target >= array_size(targets)) return; @@ -337,7 +337,7 @@ void model3dtx_set_texture(struct model3dtx *txm, int target, texture_t *tex) void model3dtx_set_texture_from(struct model3dtx *txm, int to, struct model3dtx *src, int from) { - texture_t *targets[] = { src->texture, src->normals, src->emission, src->sobel }; + texture_t *targets[] = { src->texture, src->normals, src->emission, src->sobel, src->shadow }; model3dtx_set_texture(txm, to, targets[from]); } @@ -420,8 +420,8 @@ void model3d_aabb_center(struct model3d *m, vec3 center) vec3_scale(center, center, 0.5); } -static void model3d_prepare(struct model3d *m); -static void model3d_done(struct model3d *m); +static void model3d_prepare(struct model3d *m, struct shader_prog *p); +static void model3d_done(struct model3d *m, struct shader_prog *p); void model3d_add_tangents(struct model3d *m, float *tg, size_t tgsz) { @@ -431,9 +431,9 @@ void model3d_add_tangents(struct model3d *m, float *tg, size_t tgsz) } shader_prog_use(m->prog); - model3d_prepare(m); + model3d_prepare(m, m->prog); load_gl_buffer(m->prog->tangent, tg, GL_FLOAT, tgsz, &m->tangent_obj, 4, GL_ARRAY_BUFFER); - model3d_done(m); + model3d_done(m, m->prog); shader_prog_done(m->prog); } @@ -574,10 +574,8 @@ static void model3d_set_lod(struct model3d *m, unsigned int lod) m->cur_lod = lod; } -static void model3d_prepare(struct model3d *m) +static void model3d_prepare(struct model3d *m, struct shader_prog *p) { - struct shader_prog *p = m->prog; - if (gl_does_vao()) GL(glBindVertexArray(m->vao)); if (m->cur_lod >= 0) @@ -611,12 +609,11 @@ static void model3d_prepare(struct model3d *m) /* Cube and quad */ #include "primitives.c" -void model3dtx_prepare(struct model3dtx *txm) +void model3dtx_prepare(struct model3dtx *txm, struct shader_prog *p) { - struct shader_prog *p = txm->model->prog; - struct model3d * m = txm->model; + struct model3d *m = txm->model; - model3d_prepare(txm->model); + model3d_prepare(txm->model, p); if (p->tex >= 0 && m->tex_obj && texture_loaded(txm->texture)) { GL(glBindBuffer(GL_ARRAY_BUFFER, m->tex_obj)); @@ -654,10 +651,8 @@ void model3dtx_draw(struct model3dtx *txm) GL(glDrawElements(m->draw_type, m->nr_faces[m->cur_lod], GL_UNSIGNED_SHORT, 0)); } -static void model3d_done(struct model3d *m) +static void model3d_done(struct model3d *m, struct shader_prog *p) { - struct shader_prog *p = m->prog; - GL(glDisableVertexAttribArray(p->pos)); if (m->norm_obj && p->norm >= 0) GL(glDisableVertexAttribArray(p->norm)); @@ -676,10 +671,8 @@ static void model3d_done(struct model3d *m) m->cur_lod = -1; } -void model3dtx_done(struct model3dtx *txm) +void model3dtx_done(struct model3dtx *txm, struct shader_prog *p) { - struct shader_prog *p = txm->model->prog; - if (p->tex >= 0 && txm->model->tex_obj) { GL(glDisableVertexAttribArray(p->tex)); GL(glActiveTexture(GL_TEXTURE0)); @@ -690,7 +683,7 @@ void model3dtx_done(struct model3dtx *txm) GL(glBindTexture(GL_TEXTURE_2D, 0)); } - model3d_done(txm->model); + model3d_done(txm->model, p); } static int fbo_create(void) @@ -709,15 +702,15 @@ static void fbo_texture_init(struct fbo *fbo) texture_fbo(&fbo->tex, GL_COLOR_ATTACHMENT0, GL_RGBA, fbo->width, fbo->height); } -static int fbo_depth_texture(struct fbo *fbo) +static void fbo_depth_texture_init(struct fbo *fbo) { - int tex; - - texture_init(&fbo->depth); - texture_filters(&fbo->depth, GL_CLAMP_TO_EDGE, GL_LINEAR); - texture_fbo(&fbo->depth, GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT, fbo->width, fbo->height); + texture_init(&fbo->tex); + texture_filters(&fbo->tex, GL_REPEAT, GL_NEAREST); + texture_fbo(&fbo->tex, GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT, fbo->width, fbo->height); - return tex; + GLenum buffers[1] = { GL_NONE }; + GL(glDrawBuffers(1, buffers)); + GL(glReadBuffer(GL_NONE)); } static int fbo_color_buffer(struct fbo *fbo, int output) @@ -760,7 +753,6 @@ void fbo_resize(struct fbo *fbo, int width, int height) fbo->height = height; GL(glFinish()); texture_resize(&fbo->tex, width, height); - texture_resize(&fbo->depth, width, height); if (fbo->depth_buf) { GL(glBindRenderbuffer(GL_RENDERBUFFER, fbo->depth_buf)); @@ -769,7 +761,7 @@ void fbo_resize(struct fbo *fbo, int width, int height) } } -#define NR_TARGETS 4 +#define NR_TARGETS 5 void fbo_prepare(struct fbo *fbo) { GL(glBindFramebuffer(GL_FRAMEBUFFER, fbo->fbo)); @@ -824,8 +816,14 @@ DECLARE_REFCLASS2(fbo); static void fbo_init(struct fbo *fbo, int nr_targets) { + bool depth_texture = false; int err; + if (nr_targets < 0) { + nr_targets = 0; + depth_texture = true; + } + fbo->fbo = fbo_create(); if (fbo->ms) { int target; @@ -837,11 +835,14 @@ static void fbo_init(struct fbo *fbo, int nr_targets) } else { fbo_texture_init(fbo); } - //fbo->depth_tex = fbo_depth_texture(fbo); - fbo->depth_buf = fbo_depth_buffer(fbo); + + if (depth_texture) + fbo_depth_texture_init(fbo); + else + fbo->depth_buf = fbo_depth_buffer(fbo); err = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (err != GL_FRAMEBUFFER_COMPLETE) - dbg("## framebuffer status: %d\n", err); + warn("## framebuffer status: %d\n", err); GL(glBindFramebuffer(GL_FRAMEBUFFER, 0)); } @@ -902,26 +903,30 @@ void animation_add_channel(struct animation *an, size_t frames, float *time, flo an->time_end = max(an->time_end, time[frames - 1]/* + time[1] - time[0]*/); } -void models_render(struct mq *mq, struct light *light, struct camera *camera, - struct matrix4f *proj_mx, struct entity3d *focus, int width, int height, - unsigned long *count) +void models_render(struct mq *mq, struct shader_prog *shader_override, struct light *light, + struct camera *camera, struct matrix4f *proj_mx, struct entity3d *focus, + int width, int height, unsigned long *count) { struct entity3d *e; struct shader_prog *prog = NULL; struct model3d *model; struct model3dtx *txmodel; - struct matrix4f *view_mx = NULL, *inv_view_mx = NULL; + struct view *view = NULL; unsigned long nr_txms = 0, nr_ents = 0, culled = 0; - if (camera) { - view_mx = camera->view_mx; - inv_view_mx = camera->inv_view_mx; + if (camera) + view = &camera->view; + else if (light) { + view = &light->view[0]; + proj_mx = light->view[0].proj_mx; } list_for_each_entry(txmodel, &mq->txmodels, entry) { // err_on(list_empty(&txmodel->entities), "txm '%s' has no entities\n", // txmodel_name(txmodel)); model = txmodel->model; + struct shader_prog *model_prog = shader_override ? shader_override : model->prog; + model->cur_lod = 0; /* XXX: model-specific draw method */ if (model->cull_face) { @@ -943,11 +948,11 @@ void models_render(struct mq *mq, struct light *light, struct camera *camera, GL(glEnable(GL_DEPTH_TEST)); //dbg("rendering model '%s'\n", model->name); - if (model->prog != prog) { + if (model_prog != prog) { if (prog) shader_prog_done(prog); - prog = model->prog; + prog = model_prog; shader_prog_use(prog); trace("rendering model '%s' using '%s'\n", model->name, prog->name); @@ -964,18 +969,35 @@ void models_render(struct mq *mq, struct light *light, struct camera *camera, if (light && prog->data.attenuation >= 0) GL(glUniform3fv(prog->data.attenuation, LIGHTS_MAX, light->attenuation)); - if (view_mx && prog->data.viewmx >= 0) + if (view && prog->data.viewmx >= 0) /* View matrix is the same for all entities and models */ - GL(glUniformMatrix4fv(prog->data.viewmx, 1, GL_FALSE, view_mx->cell)); - if (inv_view_mx && prog->data.inv_viewmx >= 0) - GL(glUniformMatrix4fv(prog->data.inv_viewmx, 1, GL_FALSE, inv_view_mx->cell)); + GL(glUniformMatrix4fv(prog->data.viewmx, 1, GL_FALSE, view->view_mx.cell)); + if (view && prog->data.inv_viewmx >= 0) + GL(glUniformMatrix4fv(prog->data.inv_viewmx, 1, GL_FALSE, view->inv_view_mx.cell)); /* Projection matrix is the same for everything, but changes on resize */ if (proj_mx && prog->data.projmx >= 0) GL(glUniformMatrix4fv(prog->data.projmx, 1, GL_FALSE, proj_mx->cell)); + + if (prog->data.shadow_mvp >= 0) { + mat4x4 mvp, off, bias; + mat4x4_identity(off); + mat4x4_translate(bias, 0.5, 0.5, 0.5); + mat4x4_scale_aniso(off, bias, 0.5, 0.5, 0.5); + mat4x4_mul(mvp, light->view[0].proj_mx->m, light->view[0].view_mx.m); + mat4x4_mul(mvp, off, mvp); + GL(glUniformMatrix4fv(prog->data.shadow_mvp, 1, GL_FALSE, (GLfloat *)mvp)); + } + + if (prog->shadow_map >= 0 && light->shadow[0] && texture_loaded(light->shadow[0])) { + GL(glActiveTexture(GL_TEXTURE4)); + GL(glBindTexture(GL_TEXTURE_2D, texture_id(light->shadow[0]))); + GL(glUniform1i(prog->shadow_map, 4)); + dbg_once("loading shadow map id %d\n", texture_id(light->shadow[0])); + } } - model3dtx_prepare(txmodel); + model3dtx_prepare(txmodel, prog); if (prog->data.use_normals >= 0 && txmodel->normals) GL(glUniform1f(prog->data.use_normals, texture_id(txmodel->normals) ? 1.0 : 0.0)); @@ -994,7 +1016,7 @@ void models_render(struct mq *mq, struct light *light, struct camera *camera, dbg_on(!e->visible, "rendering an invisible entity!\n"); if (!e->skip_culling && - camera && !camera_entity_in_frustum(camera, e)) { + camera && !view_entity_in_frustum(view, e)) { culled++; continue; } @@ -1049,7 +1071,7 @@ void models_render(struct mq *mq, struct light *light, struct camera *camera, model3dtx_draw(txmodel); nr_ents++; } - model3dtx_done(txmodel); + model3dtx_done(txmodel, prog); nr_txms++; //dbg("RENDERED model '%s': %lu\n", txmodel->model->name, nr_ents); } diff --git a/core/model.h b/core/model.h index 44ae001..278bf9c 100644 --- a/core/model.h +++ b/core/model.h @@ -13,17 +13,10 @@ #include "render.h" struct scene; +struct light; struct camera; struct shader_prog; -#define LIGHTS_MAX 4 - -struct light { - GLfloat pos[3 * LIGHTS_MAX]; - GLfloat color[3 * LIGHTS_MAX]; - GLfloat attenuation[3 * LIGHTS_MAX]; -}; - struct model_joint { darray(int, children); char *name; @@ -116,10 +109,12 @@ struct model3dtx { texture_t _normals; texture_t _emission; texture_t _sobel; + texture_t _shadow; texture_t *texture; texture_t *normals; texture_t *emission; texture_t *sobel; + texture_t *shadow; // GLuint normals_id; float metallic; float roughness; @@ -153,8 +148,8 @@ void model3dtx_set_texture_from(struct model3dtx *txm, int to, struct model3dtx struct model3d *model3d_new_cube(struct shader_prog *p); struct model3d *model3d_new_quad(struct shader_prog *p, float x, float y, float z, float w, float h); struct model3d *model3d_new_frame(struct shader_prog *p, float x, float y, float z, float w, float h, float t); -void model3dtx_prepare(struct model3dtx *m); -void model3dtx_done(struct model3dtx *m); +void model3dtx_prepare(struct model3dtx *m, struct shader_prog *p); +void model3dtx_done(struct model3dtx *m, struct shader_prog *p); void model3dtx_draw(struct model3dtx *m); static inline const char *txmodel_name(struct model3dtx *txm) @@ -201,7 +196,6 @@ struct fbo { int depth_buf; darray(int, color_buf); texture_t tex; - texture_t depth; bool ms; int retain_tex; }; @@ -261,9 +255,9 @@ struct entity3d { }; void model3dtx_add_entity(struct model3dtx *txm, struct entity3d *e); -void models_render(struct mq *mq, struct light *light, struct camera *camera, - struct matrix4f *proj_mx, struct entity3d *focus, int width, int height, - unsigned long *count); +void models_render(struct mq *mq, struct shader_prog *shader_override, struct light *light, + struct camera *camera, struct matrix4f *proj_mx, struct entity3d *focus, + int width, int height, unsigned long *count); static inline const char *entity_name(struct entity3d *e) { diff --git a/core/pipeline.c b/core/pipeline.c index a8e8f5f..ad67e94 100644 --- a/core/pipeline.c +++ b/core/pipeline.c @@ -11,11 +11,20 @@ struct render_pass { struct mq mq; struct list entry; struct render_pass *repeat; + struct shader_prog *prog_override; + texture_t *shadow; int rep_total; int rep_count; bool blit; }; +texture_t *pipeline_pass_get_texture(struct render_pass *pass, unsigned int idx) +{ + if (idx >= darray_count(pass->fbo)) + return NULL; + return &pass->fbo.x[idx]->tex; +} + static void pipeline_drop(struct ref *ref) { struct pipeline *pl = container_of(ref, struct pipeline, ref); @@ -52,6 +61,9 @@ static void pipeline_drop(struct ref *ref) ref_put(*pfbo); darray_clearout(&pass->fbo.da); + if (pass->prog_override) + ref_put(pass->prog_override); + free(pass); } } @@ -68,8 +80,8 @@ struct pipeline *pipeline_new(struct scene *s) return pl; } -struct render_pass *pipeline_add_pass(struct pipeline *pl, struct render_pass *src, const char *prog_name, - bool ms, int nr_targets, int target) +struct render_pass *pipeline_add_pass(struct pipeline *pl, struct render_pass *src, const char *shader, + const char *shader_override, bool ms, int nr_targets, int target) { struct render_pass *pass; struct shader_prog *p; @@ -92,7 +104,20 @@ struct render_pass *pipeline_add_pass(struct pipeline *pl, struct render_pass *s if (!pfbo) return NULL; - *pfbo = fbo_new_ms(pl->scene->width, pl->scene->height, ms, nr_targets); + int width = pl->scene->width, height = pl->scene->height; + + /* + * XXX: better way of determining shadow pass + * XXX^2: obviously, don't hardcode the map size + */ + if (shader_override) + width = height = 8192; + else if (src) { + width = src->fbo.x[0]->width; + height = src->fbo.x[0]->height; + } + + *pfbo = fbo_new_ms(width, height, ms, nr_targets); if (!*pfbo) goto err_fbo_array; @@ -102,14 +127,14 @@ struct render_pass *pipeline_add_pass(struct pipeline *pl, struct render_pass *s *psrc = src; - pass->blit = ms; + pass->blit = ms && nr_targets >= 0; mq_init(&pass->mq, NULL); /* - * XXX: any number of things mean the same thing: !prog_name, ms, nr_targets, + * XXX: any number of things mean the same thing: !shader, ms, nr_targets, * !src. Streamline the parameters of this function, it's a mess. */ - if (!prog_name) + if (!shader) return pass; int *pblit_src = darray_add(&pass->blit_src.da); @@ -118,9 +143,15 @@ struct render_pass *pipeline_add_pass(struct pipeline *pl, struct render_pass *s *pblit_src = target; - p = shader_prog_find(&pl->scene->shaders, prog_name); + if (shader_override) { + pass->prog_override = shader_prog_find(&pl->scene->shaders, shader_override); + if (!pass->prog_override) + goto err_blit_src; + } + + p = shader_prog_find(&pl->scene->shaders, shader); if (!p) - goto err_blit_src; + goto err_override; m = model3d_new_quad(p, -1, 1, 0.1, 2, -2); m->cull_face = false; @@ -135,6 +166,9 @@ struct render_pass *pipeline_add_pass(struct pipeline *pl, struct render_pass *s return pass; +err_override: + if (shader_override) + ref_put(pass->prog_override); err_blit_src: darray_clearout(&pass->blit_src.da); err_src: @@ -143,6 +177,7 @@ struct render_pass *pipeline_add_pass(struct pipeline *pl, struct render_pass *s ref_put_last(*pfbo); err_fbo_array: darray_clearout(&pass->fbo.da); + list_del(&pass->entry); free(pass); return NULL; } @@ -155,6 +190,11 @@ void pipeline_pass_repeat(struct render_pass *pass, struct render_pass *repeat, void pipeline_pass_add_source(struct pipeline *pl, struct render_pass *pass, int to, struct render_pass *src, int blit_src) { + if (to < 0) { + pl->scene->light.shadow[0] = &src->fbo.x[0]->tex; + return; + } + struct model3dtx *txmsrc = list_first_entry(&src->mq.txmodels, struct model3dtx, entry); struct model3dtx *txm = list_first_entry(&pass->mq.txmodels, struct model3dtx, entry); struct render_pass **psrc = darray_add(&pass->src.da); @@ -229,13 +269,19 @@ void pipeline_render(struct pipeline *pl) fbo_done(fbo, s->width, s->height); } else { fbo_prepare(fbo); - GL(glDisable(GL_DEPTH_TEST)); + bool shadow = !darray_count(fbo->color_buf); GL(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); GL(glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT)); + + unsigned int width, height; + texture_get_dimesnions(&fbo->tex, &width, &height); + if (!src) - models_render(&s->mq, &s->light, &s->cameras[0], s->proj_mx, s->focus, s->width, s->height, NULL); + models_render(&s->mq, pass->prog_override, &s->light, + shadow ? NULL : &s->cameras[0], + s->proj_mx, s->focus, width, height, NULL); else - models_render(&src->mq, NULL, NULL, NULL, NULL, s->width, s->height, NULL); + models_render(&src->mq, NULL, NULL, NULL, NULL, NULL, fbo->width, fbo->height, NULL); fbo_done(fbo, s->width, s->height); } @@ -264,5 +310,5 @@ void pipeline_render(struct pipeline *pl) GL(glEnable(GL_DEPTH_TEST)); GL(glClearColor(0.2f, 0.2f, 0.6f, 1.0f)); GL(glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT)); - models_render(&last_pass->mq, NULL, NULL, NULL, NULL, s->width, s->height, NULL); + models_render(&last_pass->mq, NULL, NULL, NULL, NULL, NULL, s->width, s->height, NULL); } diff --git a/core/pipeline.h b/core/pipeline.h index 3af742e..ea4deb9 100644 --- a/core/pipeline.h +++ b/core/pipeline.h @@ -12,10 +12,11 @@ struct pipeline { }; struct pipeline *pipeline_new(struct scene *s); -struct render_pass *pipeline_add_pass(struct pipeline *pl, struct render_pass *src, const char *prog_name, - bool ms, int nr_targets, int target); +struct render_pass *pipeline_add_pass(struct pipeline *pl, struct render_pass *src, const char *shader, + const char *shader_override, bool ms, int nr_targets, int target); void pipeline_pass_add_source(struct pipeline *pl, struct render_pass *pass, int to, struct render_pass *src, int blit_src); void pipeline_pass_repeat(struct render_pass *pass, struct render_pass *repeat, int count); void pipeline_render(struct pipeline *pl); +texture_t *pipeline_pass_get_texture(struct render_pass *pass, unsigned int idx); #endif /* __CLAP_PIPELINE_H__ */ diff --git a/core/render-gl.c b/core/render-gl.c index 8d60d49..119f402 100644 --- a/core/render-gl.c +++ b/core/render-gl.c @@ -78,17 +78,20 @@ void texture_filters(texture_t *tex, GLint wrap, GLint filter) tex->filter = filter; } +void texture_type(texture_t *tex, GLenum type) +{ + tex->type = type; +} + static void texture_setup_begin(texture_t *tex, void *buf) { - if (tex->format == GL_DEPTH_COMPONENT) - tex->type = GL_FLOAT; GL(glActiveTexture(tex->target)); GL(glBindTexture(GL_TEXTURE_2D, tex->id)); GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, tex->wrap)); GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, tex->wrap)); GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, tex->filter)); GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, tex->filter)); - GL(glTexImage2D(GL_TEXTURE_2D, 0, tex->format, tex->width, tex->height, + GL(glTexImage2D(GL_TEXTURE_2D, 0, tex->internal_format, tex->width, tex->height, 0, tex->format, tex->type, buf)); } @@ -101,6 +104,7 @@ void texture_load(texture_t *tex, GLenum format, unsigned int width, unsigned in void *buf) { tex->format = format; + tex->internal_format = format; tex->width = width; tex->height = height; texture_setup_begin(tex, buf); @@ -112,14 +116,25 @@ void texture_fbo(texture_t *tex, GLuint attachment, GLenum format, unsigned int unsigned int height) { tex->format = format; + tex->internal_format = format; tex->width = width; tex->height = height; + if (attachment == GL_DEPTH_ATTACHMENT) { + tex->type = GL_UNSIGNED_SHORT; + tex->internal_format = GL_DEPTH_COMPONENT16; + } texture_setup_begin(tex, NULL); GL(glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex->id, 0)); texture_setup_end(tex); tex->loaded = true; } +void texture_get_dimesnions(texture_t *tex, unsigned int *pwidth, unsigned int *pheight) +{ + *pwidth = tex->width; + *pheight = tex->height; +} + void texture_done(struct texture *tex) { if (!ref_is_static(&tex->ref)) diff --git a/core/render.h b/core/render.h index 4cd2359..4f3a5b1 100644 --- a/core/render.h +++ b/core/render.h @@ -12,6 +12,7 @@ TYPE(texture, struct ref ref; GLuint id; GLenum format; + GLenum internal_format; GLenum type; GLint wrap; GLint filter; @@ -48,6 +49,7 @@ int texture_init(texture_t *tex); int texture_init_target(texture_t *tex, GLuint target); void texture_deinit(texture_t *tex); void texture_filters(texture_t *tex, GLint wrap, GLint filter); +void texture_type(texture_t *tex, GLenum type); void texture_done(texture_t *tex); void texture_load(texture_t *tex, GLenum format, unsigned int width, unsigned int height, void *buf); @@ -55,6 +57,7 @@ void texture_fbo(texture_t *tex, GLuint attachment, GLenum format, unsigned int unsigned int height); void texture_resize(texture_t *tex, unsigned int width, unsigned int height); GLuint texture_id(texture_t *tex); +void texture_get_dimesnions(texture_t *tex, unsigned int *pwidth, unsigned int *pheight); bool texture_loaded(texture_t *tex); texture_t *texture_clone(texture_t *tex); diff --git a/core/scene.c b/core/scene.c index 8af5529..6620002 100644 --- a/core/scene.c +++ b/core/scene.c @@ -127,8 +127,9 @@ int scene_camera_add(struct scene *s) scene_add_model(s, entity->txmodel); ref_put(entity->txmodel); - s->camera->view_mx = mx_new(); - s->camera->inv_view_mx = mx_new(); + mx_set_identity(&s->camera->view.view_mx); + mx_set_identity(&s->camera->view.inv_view_mx); + s->camera->view.proj_mx = s->proj_mx; s->camera->ch->pos[0] = 0.0; @@ -143,6 +144,7 @@ int scene_camera_add(struct scene *s) static void scene_camera_calc(struct scene *s, int camera) { + struct camera *cam = &s->cameras[camera]; float scalev[3]; int i; @@ -150,46 +152,39 @@ static void scene_camera_calc(struct scene *s, int camera) return; if (s->autopilot) scene_camera_autopilot(s); - if (!s->cameras[camera].ch->moved && s->control == s->cameras[camera].ch) + if (!cam->ch->moved && s->control == cam->ch) return; for (i = 0; i < 3; i++) - scalev[i] = s->cameras[camera].zoom ? 3.0 : 1.0; - - struct camera *cam = &s->cameras[camera]; + scalev[i] = cam->zoom ? 3.0 : 1.0; /* circle the character s->control */ - if (s->control != s->cameras[camera].ch && + if (s->control != cam->ch && (camera_has_moved(cam) || s->control->moved)) { - // float dist = s->cameras[camera].zoom ? 1 : 10; + // float dist = cam->zoom ? 1 : 10; float x = s->control->pos[0]; float y = s->control->pos[1] + entity3d_aabb_Y(s->control->entity) / 4 * 3; float z = s->control->pos[2]; - camera_position(cam, x, y, z, s->cameras[camera].ch->pos); + camera_position(cam, x, y, z, cam->ch->pos); //s->control->moved = 0; /* XXX */ } camera_reset_movement(cam); - s->cameras[camera].ch->moved = 0; - trace("camera: %f/%f/%f zoom: %d\n", s->cameras[camera].ch->pos[0], s->cameras[camera].ch->pos[1], s->cameras[camera].ch->pos[2], s->cameras[camera].zoom); - - //free(s->view_mx); - //s->view_mx = transmx_new(negpos, 0.0, 0.0, 0.0, 1.0); - mat4x4_identity(s->cameras[camera].view_mx->m); - mat4x4_rotate_X(s->cameras[camera].view_mx->m, s->cameras[camera].view_mx->m, to_radians(s->cameras[camera].current_pitch)); - mat4x4_rotate_Y(s->cameras[camera].view_mx->m, s->cameras[camera].view_mx->m, to_radians(s->cameras[camera].current_yaw)); - mat4x4_scale_aniso(s->cameras[camera].view_mx->m, s->cameras[camera].view_mx->m, scalev[0], scalev[1], scalev[2]); - //mat4x4_scale(s->cameras[camera].view_mx->m, scalev, 1.0); - mat4x4_translate_in_place(s->cameras[camera].view_mx->m, -s->cameras[camera].ch->pos[0], -s->cameras[camera].ch->pos[1], -s->cameras[camera].ch->pos[2]); - - mat4x4_invert(s->cameras[camera].inv_view_mx->m, s->cameras[camera].view_mx->m); - camera_calc_frustum(cam, s->proj_mx->m); + cam->ch->moved = 0; + trace("camera: %f/%f/%f zoom: %d\n", cam->ch->pos[0], cam->ch->pos[1], cam->ch->pos[2], cam->zoom); + + view_update_from_angles(&cam->view, cam->ch->pos, cam->current_pitch, cam->current_yaw, cam->current_roll); + view_calc_frustum(&cam->view, s->proj_mx->m); + + view_update_from_frustum(&s->light.view[0], &s->light.dir[0 * 3], &cam->view); + view_projection_update(&s->light.view[0], &cam->view); + view_calc_frustum(&s->light.view[0], NULL); #ifndef CONFIG_FINAL if (!(s->frames_total & 0xf) && camera == 0) gl_title("One Hand Clap @%d FPS camera0 [%f,%f,%f] [%f/%f]", s->fps.fps_coarse, - s->cameras[camera].ch->pos[0], s->cameras[camera].ch->pos[1], s->cameras[camera].ch->pos[2], - s->cameras[camera].current_pitch, s->cameras[camera].current_yaw); + cam->ch->pos[0], cam->ch->pos[1], cam->ch->pos[2], + cam->current_pitch, cam->current_yaw); #endif } @@ -298,30 +293,6 @@ int scene_add_model(struct scene *s, struct model3dtx *txm) return 0; } -void light_set_pos(struct light *light, int idx, float pos[3]) -{ - int i; - - for (i = 0; i < 3; i++) - light->pos[idx * 3 + i] = pos[i]; -} - -void light_set_color(struct light *light, int idx, float color[3]) -{ - int i; - - for (i = 0; i < 3; i++) - light->color[idx * 3 + i] = color[i]; -} - -void light_set_attenuation(struct light *light, int idx, float attenuation[3]) -{ - int i; - - for (i = 0; i < 3; i++) - light->attenuation[idx * 3 + i] = attenuation[i]; -} - int scene_get_light(struct scene *scene) { if (scene->nr_lights == LIGHTS_MAX) @@ -343,8 +314,8 @@ int scene_init(struct scene *scene) memset(scene, 0, sizeof(*scene)); scene->proj_mx = mx_new(); scene->auto_yoffset = 4.0; - scene->near_plane = 0.1; - scene->far_plane = 1000.0; + scene->near_plane = 1.0; + scene->far_plane = 300.0; mq_init(&scene->mq, scene); list_init(&scene->characters); list_init(&scene->instor); @@ -682,6 +653,11 @@ static int scene_add_light_from_json(struct scene *s, JsonNode *light) float fcolor[3] = { color[0], color[1], color[2] }; light_set_pos(&s->light, idx, fpos); light_set_color(&s->light, idx, fcolor); + vec3 center = { 0.0, 0.001, 0.0 }; + vec3_sub(&s->light.dir[idx * 3], center, &s->light.pos[idx * 3]); + view_update_from_target(&s->light.view[idx], &s->light.pos[idx * 3], center); + view_projection_update(&s->light.view[idx], &s->camera[0].view); + view_calc_frustum(&s->light.view[idx], NULL); return 0; } diff --git a/core/scene.h b/core/scene.h index 9d48a41..57f9029 100644 --- a/core/scene.h +++ b/core/scene.h @@ -7,6 +7,7 @@ #include "character.h" #include "matrix.h" #include "model.h" +#include "light.h" #include "physics.h" #include "camera.h" @@ -57,10 +58,6 @@ struct scene { bool debug_draws_enabled; }; -void light_set_pos(struct light *light, int idx, float pos[3]); -void light_set_color(struct light *light, int idx, float color[3]); -void light_set_attenuation(struct light *light, int idx, float attenuation[3]); - int scene_get_light(struct scene *scene); int scene_camera_add(struct scene *s); void scene_cameras_calc(struct scene *s); diff --git a/core/shader.c b/core/shader.c index fc011d5..5b43d4b 100644 --- a/core/shader.c +++ b/core/shader.c @@ -179,6 +179,7 @@ static void shader_prog_link(struct shader_prog *p) p->data.ray = shader_prog_find_var(p, "ray"); p->data.color = shader_prog_find_var(p, "in_color"); p->data.colorpt = shader_prog_find_var(p, "color_passthrough"); + p->data.shadow_mvp = shader_prog_find_var(p, "shadow_mvp"); p->data.entity_hash = shader_prog_find_var(p, "entity_hash"); p->data.use_normals = shader_prog_find_var(p, "use_normals"); p->data.use_skinning = shader_prog_find_var(p, "use_skinning"); @@ -215,12 +216,13 @@ shader_prog_from_strings(const char *name, const char *vsh, const char *fsh) p->texture_map = shader_prog_find_var(p, "model_tex"); p->normal_map = shader_prog_find_var(p, "normal_map"); p->sobel_tex = shader_prog_find_var(p, "sobel_tex"); + p->shadow_map = shader_prog_find_var(p, "shadow_map"); p->joints = shader_prog_find_var(p, "joints"); p->weights = shader_prog_find_var(p, "weights"); p->emission_map = shader_prog_find_var(p, "emission_map"); - dbg("model '%s' %d/%d/%d/%d/%d/%d/%d/%d/%d/%d\n", + dbg("model '%s' %d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d\n", p->name, p->pos, p->norm, p->tex, p->tangent, - p->texture_map, p->normal_map, p->emission_map, p->sobel_tex, p->joints, p->weights); + p->texture_map, p->normal_map, p->emission_map, p->sobel_tex, p->shadow_map, p->joints, p->weights); if (p->pos < 0) { shader_prog_done(p); return NULL; diff --git a/core/shader.h b/core/shader.h index 447fde0..63458b2 100644 --- a/core/shader.h +++ b/core/shader.h @@ -11,7 +11,7 @@ struct shader_data { GLint inv_viewmx, shine_damper, reflectivity; GLint highlight, color, ray, colorpt, use_normals; GLint use_skinning, joint_transforms, width, height; - GLint attenuation, albedo_texture, entity_hash; + GLint attenuation, albedo_texture, entity_hash, shadow_mvp; }; struct shader_var; @@ -25,6 +25,7 @@ struct shader_prog { GLint normal_map; GLint emission_map; GLint sobel_tex; + GLint shadow_map; GLint joints; GLint weights; GLint tex; diff --git a/core/ui.c b/core/ui.c index 7e7ea7c..c181f2e 100644 --- a/core/ui.c +++ b/core/ui.c @@ -459,7 +459,7 @@ ui_render_string(struct ui *ui, struct font *font, struct ui_element *parent, glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); // dbg("rendering '%s' uit(%dx%d) to FBO %d (%dx%d)\n", str, uit.width, uit.height, // fbo->fbo, fbo->width, fbo->height); - models_render(&fbo_ui.mq, NULL, NULL, NULL, NULL, 0, 0, NULL); + models_render(&fbo_ui.mq, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL); mq_release(&fbo_ui.mq); fbo_done(fbo, ui->width, ui->height); @@ -1377,12 +1377,12 @@ struct ui_element *uie0, *uie1, *health, *pocket, **pocket_text; static int pocket_buckets, *pocket_count, *pocket_total; static float health_bar_width; -void ui_pip_update(struct ui *ui, struct fbo *fbo) +void ui_pip_update(struct ui *ui, texture_t *tex) { struct model3d *m; struct shader_prog *prog; - ui_fbo_tex = &fbo->tex; + ui_fbo_tex = tex; if (ui_pip) ref_put(ui_pip); @@ -1396,14 +1396,26 @@ void ui_pip_update(struct ui *ui, struct fbo *fbo) ui_pip = model3dtx_new_texture(ref_pass(m), ui_fbo_tex); ui_add_model_tail(ui, ui_pip); ref_put(prog); - dbg("### ui_pip tex: %d width: %d height: %d\n", texture_id(ui_fbo_tex), fbo->width, fbo->height); - if (fbo->width < fbo->height) - uie0 = ui_element_new(ui, NULL, ui_pip, UI_AF_VCENTER | UI_AF_LEFT, 0, 0, fbo->width, fbo->height); - else - uie0 = ui_element_new(ui, NULL, ui_pip, UI_AF_TOP | UI_AF_HCENTER, 0, 0, fbo->width, fbo->height); + + unsigned int width, height; + texture_get_dimesnions(ui_fbo_tex, &width, &height); + /* XXX */ + for (; width > 512; width >>= 1, height >>= 1) + ; + dbg("### ui_pip tex: %d width: %d height: %d\n", texture_id(ui_fbo_tex), width, height); + // if (width < height) + // uie0 = ui_element_new(ui, NULL, ui_pip, UI_AF_VCENTER | UI_AF_LEFT, 0, 0, width, height); + // else + // uie0 = ui_element_new(ui, NULL, ui_pip, UI_AF_TOP | UI_AF_HCENTER, 0, 0, width, height); + uie0 = ui_element_new(ui, NULL, ui_pip, UI_AF_TOP | UI_AF_RIGHT, 0, 0, width, height); uie0->entity->color_pt = COLOR_PT_NONE; } +void ui_pip_done(struct ui *ui) +{ + ui_pip->texture = &ui_pip->_texture; +} + struct ui_element *ui_pocket_new(struct ui *ui, const char **tex, int nr) { struct model3d *model; diff --git a/core/ui.h b/core/ui.h index cbe6ed1..209cbee 100644 --- a/core/ui.h +++ b/core/ui.h @@ -108,7 +108,8 @@ struct ui { float mod_x, mod_y; }; -void ui_pip_update(struct ui *ui, struct fbo *fbo); +void ui_pip_update(struct ui *ui, texture_t *tex); +void ui_pip_done(struct ui *ui); struct ui_element *ui_element_new(struct ui *ui, struct ui_element *parent, struct model3dtx *txmodel, unsigned long affinity, float x_off, float y_off, float w, float h); diff --git a/core/view.c b/core/view.c new file mode 100644 index 0000000..f387bfe --- /dev/null +++ b/core/view.c @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: Apache-2.0 +#include "model.h" +#include "view.h" +#include "shader.h" +#include "ui-debug.h" + +static bool frustum_ui, light_ui; +static float near_factor = 1.0, far_factor = 1.0; + +#define FRUSTUM_EXTRA 10.0 +void view_projection_update(struct view *view, struct view *src) +{ + vec4 aabb_min = { INFINITY, INFINITY, INFINITY, 1 }, aabb_max = { -INFINITY, -INFINITY, -INFINITY, 1 }; + int i, j; + + for (i = 0; i < 8; i++) { + vec4 corner; + mat4x4_mul_vec4(corner, view->view_mx.m, src->frustum_corners[i]); + + for (j = 0; j < 3; j++) { + aabb_min[j] = fminf(aabb_min[j], corner[j]); + aabb_max[j] = fmaxf(aabb_max[j], corner[j]); + } + } + + // if (aabb_min[2] < 0) + // aabb_min[2] *= FRUSTUM_EXTRA; + // else + // aabb_min[2] /= FRUSTUM_EXTRA; + // if (aabb_max[2] < 0) + // aabb_max[2] /= FRUSTUM_EXTRA; + // else + // aabb_max[2] *= FRUSTUM_EXTRA; + + view->proj_mx = &view->_proj_mx; + mat4x4_ortho(view->proj_mx->m, aabb_min[0], aabb_max[0], aabb_min[1], aabb_max[1], + aabb_min[2] * near_factor, aabb_max[2] * far_factor); + view_calc_frustum(view, NULL); + + if (igBegin("Camera frustum", &frustum_ui, ImGuiWindowFlags_AlwaysAutoResize)) { + ui_igVec3TableHeader("in light space"); + for (i = 0; i < 8; i++) + ui_igVec3Row(view->frustum_corners[i], "light corner %d", i); + for (i = 0; i < 8; i++) + ui_igVec3Row(src->frustum_corners[i], "camera corner %d", i); + igEndTable(); + igSliderFloat("near plane", &near_factor, 0.1, 10.0, "%.1f", ImGuiSliderFlags_ClampOnInput); + igSliderFloat("far plane", &far_factor, 0.1, 10.0, "%.1f", ImGuiSliderFlags_ClampOnInput); + igEnd(); + } else { + igEnd(); + } +} + +void view_update_from_angles(struct view *view, vec3 eye, float pitch, float yaw, float roll) +{ + mat4x4_identity(view->view_mx.m); + mat4x4_rotate_X(view->view_mx.m, view->view_mx.m, to_radians(pitch)); + mat4x4_rotate_Y(view->view_mx.m, view->view_mx.m, to_radians(yaw)); + mat4x4_rotate_Z(view->view_mx.m, view->view_mx.m, to_radians(roll)); + // mat4x4_scale_aniso(view->view_mx.m, view->view_mx.m, scalev[0], scalev[1], scalev[2]); + //mat4x4_scale(view->view_mx.m, scalev, 1.0); + mat4x4_translate_in_place(view->view_mx.m, -eye[0], -eye[1], -eye[2]); + + mat4x4_invert(view->inv_view_mx.m, view->view_mx.m); +} + +void view_update_from_target(struct view *view, vec3 eye, vec3 target) +{ + vec3 up = { 0.0, 1.0, 0.0 }; + + mat4x4_look_at(view->view_mx.m, eye, target, up); + mat4x4_invert(view->inv_view_mx.m, view->view_mx.m); +} + +void view_update_from_frustum(struct view *view, vec3 dir, struct view *src) +{ + vec3 up = { 0.0, 1.0, 0.0 }, center = { 0.0, 0.0, 0.0 }; + int i; + + for (i = 0; i < 8; i++) + vec3_add(center, center, src->frustum_corners[i]); + vec3_scale(center, center, 1.0 / 8.0); + + vec3 eye; + vec3_sub(eye, center, dir); + view_update_from_target(view, eye, center); + + if (igBegin("Light position", &light_ui, ImGuiWindowFlags_AlwaysAutoResize)) { + igSetNextItemWidth(400); + igSliderFloat3("light", dir, -100, 100, "%f", 0); + if (ui_igVec3TableHeader("view positions")) { + ui_igVec3Row(eye, "eye"); + ui_igVec3Row(dir, "direction"); + ui_igVec3Row(center, "center"); + igEndTable(); + } + igEnd(); + } else { + igEnd(); + } +} + +void view_calc_frustum(struct view *view, mat4x4 projmx) +{ + /* They're not really MVPs, since there's no M matrices involved */ + mat4x4 mvp, trans, invmvp; + vec4 corners[] = { + { -1, -1, -1, 1 }, { 1, -1, -1, 1 }, + { 1, 1, -1, 1 }, { -1, 1, -1, 1 }, + { -1, -1, 1, 1 }, { 1, -1, 1, 1 }, + { 1, 1, 1, 1 }, { -1, 1, 1, 1 } + }; + int i = 0; + + mat4x4_mul(mvp, view->proj_mx->m, view->view_mx.m); + mat4x4_transpose(trans, mvp); + mat4x4_invert(invmvp, mvp); + + /* frustum planes */ + vec4_add(view->frustum_planes[0], trans[3], trans[0]); + vec4_sub(view->frustum_planes[1], trans[3], trans[0]); + vec4_add(view->frustum_planes[2], trans[3], trans[1]); + vec4_sub(view->frustum_planes[3], trans[3], trans[1]); + vec4_add(view->frustum_planes[4], trans[3], trans[2]); + vec4_sub(view->frustum_planes[5], trans[3], trans[2]); + + /* frustum corners */ + for (i = 0; i < 8; i++) { + vec4 q; + + mat4x4_mul_vec4(q, invmvp, corners[i]); + vec4_scale(view->frustum_corners[i], q, 1.f / q[3]); + } +} + +bool view_entity_in_frustum(struct view *view, struct entity3d *e) +{ + vec3 min, max; + int i; + + entity3d_aabb_min(e, min); + entity3d_aabb_max(e, max); + + for (i = 0; i < 6; i++) { + int r = 0; + vec4 v; + + vec4_setup(v, min[0], min[1], min[2], 1.0); + r += (vec4_mul_inner(view->frustum_planes[i], v) < 0.0) ? 1 : 0; + vec4_setup(v, max[0], min[1], min[2], 1.0); + r += (vec4_mul_inner(view->frustum_planes[i], v) < 0.0) ? 1 : 0; + vec4_setup(v, min[0], max[1], min[2], 1.0); + r += (vec4_mul_inner(view->frustum_planes[i], v) < 0.0) ? 1 : 0; + vec4_setup(v, max[0], max[1], min[2], 1.0); + r += (vec4_mul_inner(view->frustum_planes[i], v) < 0.0) ? 1 : 0; + vec4_setup(v, min[0], min[1], max[2], 1.0); + r += (vec4_mul_inner(view->frustum_planes[i], v) < 0.0) ? 1 : 0; + vec4_setup(v, max[0], min[1], max[2], 1.0); + r += (vec4_mul_inner(view->frustum_planes[i], v) < 0.0) ? 1 : 0; + vec4_setup(v, min[0], max[1], max[2], 1.0); + r += (vec4_mul_inner(view->frustum_planes[i], v) < 0.0) ? 1 : 0; + vec4_setup(v, max[0], max[1], max[2], 1.0); + r += (vec4_mul_inner(view->frustum_planes[i], v) < 0.0) ? 1 : 0; + if (r == 8) + return false; + } + + int r = 0; + for (r = 0, i = 0; i < 8; i++) r += view->frustum_corners[i][0] > max[0] ? 1 : 0; if (r == 8) return false; + for (r = 0, i = 0; i < 8; i++) r += view->frustum_corners[i][0] < min[0] ? 1 : 0; if (r == 8) return false; + for (r = 0, i = 0; i < 8; i++) r += view->frustum_corners[i][1] > max[1] ? 1 : 0; if (r == 8) return false; + for (r = 0, i = 0; i < 8; i++) r += view->frustum_corners[i][1] < min[1] ? 1 : 0; if (r == 8) return false; + for (r = 0, i = 0; i < 8; i++) r += view->frustum_corners[i][2] > max[2] ? 1 : 0; if (r == 8) return false; + for (r = 0, i = 0; i < 8; i++) r += view->frustum_corners[i][2] < min[2] ? 1 : 0; if (r == 8) return false; + + return true; +} diff --git a/core/view.h b/core/view.h new file mode 100644 index 0000000..af01ab7 --- /dev/null +++ b/core/view.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +#ifndef __CLAP_VIEW_H__ +#define __CLAP_VIEW_H__ + +#include "matrix.h" + +struct view { + struct matrix4f view_mx; + struct matrix4f inv_view_mx; + struct matrix4f _proj_mx; + struct matrix4f *proj_mx; + vec4 frustum_planes[6]; + vec4 frustum_corners[8]; +}; + +struct entity3d; +void view_projection_update(struct view *view, struct view *src); +void view_update_from_angles(struct view *view, vec3 eye, float pitch, float yaw, float roll); +void view_update_from_target(struct view *view, vec3 eye, vec3 target); +void view_update_from_frustum(struct view *view, vec3 dir, struct view *src); +void view_calc_frustum(struct view *view, mat4x4 projmx); +bool view_entity_in_frustum(struct view *view, struct entity3d *e); + +#endif /* __CLAP_VIEW_H__ */ diff --git a/demo/ldjam56/CMakeLists.txt b/demo/ldjam56/CMakeLists.txt index d96aa1f..22d0ad6 100644 --- a/demo/ldjam56/CMakeLists.txt +++ b/demo/ldjam56/CMakeLists.txt @@ -28,6 +28,8 @@ set(SHADERS "ui" "contrast" "sobel" "combine" + "passthrough" + "shadow" "debug" "hblur" "vblur" diff --git a/demo/ldjam56/onehandclap.c b/demo/ldjam56/onehandclap.c index b710bdc..0cb1f56 100644 --- a/demo/ldjam56/onehandclap.c +++ b/demo/ldjam56/onehandclap.c @@ -77,6 +77,8 @@ DECLARE_PROF(end); struct clap_context *clap_ctx; struct pipeline *main_pl, *blur_pl; +static texture_t *depth_map; +static bool depth_map_ui; EMSCRIPTEN_KEEPALIVE void render_frame(void *data) { @@ -159,7 +161,18 @@ EMSCRIPTEN_KEEPALIVE void render_frame(void *data) PROF_STEP(models, updates); s->proj_updated = 0; - models_render(&ui.mq, NULL, NULL, NULL, NULL, 0, 0, &count); + models_render(&ui.mq, NULL, NULL, NULL, NULL, NULL, 0, 0, &count); + + if (igBegin("Depth map", &depth_map_ui, 0)) { + unsigned int depth_width, depth_height; + texture_get_dimesnions(depth_map, &depth_width, &depth_height); + igPushItemWidth(512); + igImage((ImTextureID)texture_id(depth_map), (ImVec2){depth_width / 16, depth_height / 16}, + (ImVec2){1,1}, (ImVec2){0,0}, (ImVec4){1,1,1,1}, (ImVec4){1,1,1,1}); + igEnd(); + } else { + igEnd(); + } imgui_render(); PROF_STEP(ui, models); @@ -192,17 +205,7 @@ EMSCRIPTEN_KEEPALIVE void render_frame(void *data) static void projmx_update(struct scene *s) { - struct matrix4f *m = s->proj_mx; - float y_scale = (1 / tan(FOV / 2)); - float x_scale = y_scale / s->aspect; - float frustum_length = s->far_plane - s->near_plane; - - m->cell[0] = x_scale; - m->cell[5] = y_scale; - m->cell[10] = -((s->far_plane + s->near_plane) / frustum_length); - m->cell[11] = -1; - m->cell[14] = -((2 * s->near_plane * s->far_plane) / frustum_length); - m->cell[15] = 0; + mat4x4_perspective(s->proj_mx->m, FOV, s->aspect, s->near_plane, s->far_plane); s->proj_updated++; } @@ -389,6 +392,8 @@ int main(int argc, char **argv, char **envp) lib_request_shaders("vblur", &scene.shaders); lib_request_shaders("sobel", &scene.shaders); lib_request_shaders("combine", &scene.shaders); + lib_request_shaders("passthrough", &scene.shaders); + lib_request_shaders("shadow", &scene.shaders); lib_request_shaders("debug", &scene.shaders); // lib_request_shaders("terrain", &scene.shaders); lib_request_shaders("model", &scene.shaders); @@ -417,22 +422,27 @@ int main(int argc, char **argv, char **envp) goto exit_ui; blur_pl = pipeline_new(&scene); - struct render_pass *model_pass = pipeline_add_pass(blur_pl, NULL, NULL, true, 3, 0); - pass = pipeline_add_pass(blur_pl, model_pass, "contrast", false, 0, 0); - struct render_pass *rep_pass = pipeline_add_pass(blur_pl, pass, "vblur", false, 0, 0); - pass = pipeline_add_pass(blur_pl, rep_pass, "hblur", false, 0, 0); + struct render_pass *model_pass = pipeline_add_pass(blur_pl, NULL, NULL, NULL, true, 3, 0); + pass = pipeline_add_pass(blur_pl, model_pass, "contrast", NULL, false, 0, 0); + struct render_pass *rep_pass = pipeline_add_pass(blur_pl, pass, "vblur", NULL, false, 0, 0); + pass = pipeline_add_pass(blur_pl, rep_pass, "hblur", NULL, false, 0, 0); pipeline_pass_repeat(pass, rep_pass, 5); main_pl = pipeline_new(&scene); - model_pass = pipeline_add_pass(main_pl, NULL, NULL, true, 3, -1); - pass = pipeline_add_pass(main_pl, model_pass, "contrast", false, 0, 1); - pass = pipeline_add_pass(main_pl, pass, "vblur", false, 0, -1); - struct render_pass *bloom_pass = pipeline_add_pass(main_pl, pass, "hblur", false, 0, -1); + struct render_pass *shadow_pass = pipeline_add_pass(main_pl, NULL, "passthrough", "shadow", true, -1, -1); + pass = pipeline_add_pass(main_pl, shadow_pass, "contrast", NULL, false, 0, 0); + depth_map = pipeline_pass_get_texture(pass, 0); + + model_pass = pipeline_add_pass(main_pl, NULL, NULL, NULL, true, 3, -1); + pipeline_pass_add_source(main_pl, model_pass, -1, shadow_pass, 0); + pass = pipeline_add_pass(main_pl, model_pass, "contrast", NULL, false, 0, 1); + pass = pipeline_add_pass(main_pl, pass, "vblur", NULL, false, 0, -1); + struct render_pass *bloom_pass = pipeline_add_pass(main_pl, pass, "hblur", NULL, false, 0, -1); pipeline_pass_repeat(bloom_pass, pass, 5); - // struct render_pass *contrast_pass = pipeline_add_pass(main_pl, model_pass, "contrast", false, 0, 2); - struct render_pass *sobel_pass = pipeline_add_pass(main_pl, model_pass, "sobel", false, 0, 2); - pass = pipeline_add_pass(main_pl, model_pass, "combine", false, 0, 0); + // // struct render_pass *contrast_pass = pipeline_add_pass(main_pl, model_pass, "contrast", NULL, false, 0, 2); + struct render_pass *sobel_pass = pipeline_add_pass(main_pl, model_pass, "sobel", NULL, false, 0, 2); + pass = pipeline_add_pass(main_pl, model_pass, "combine", NULL, false, 0, 0); pipeline_pass_add_source(main_pl, pass, 2, bloom_pass, -1); pipeline_pass_add_source(main_pl, pass, 3, sobel_pass, -1); diff --git a/shaders/model.frag b/shaders/model.frag index fc25742..1e91b51 100644 --- a/shaders/model.frag +++ b/shaders/model.frag @@ -8,10 +8,12 @@ in vec3 to_camera_vector; in float color_override; in float do_use_normals; in vec4 pass_tangent; +in vec4 shadow_pos; uniform sampler2D model_tex; uniform sampler2D normal_map; uniform sampler2D emission_map; +uniform sampler2D shadow_map; uniform vec3 light_color[4]; uniform vec3 attenuation[4]; uniform float shine_damper; @@ -24,6 +26,9 @@ layout (location=0) out vec4 FragColor; layout (location=1) out vec4 EmissiveColor; layout (location=2) out vec4 Albedo; +const int pcf_count = 2; +const float pcf_total_texels = pow(float(pcf_count) * 2.0 + 1.0, 2.0); + void main() { if (highlight_color.w != 0.0) { @@ -40,8 +45,6 @@ void main() if (do_use_normals > 0.5) { vec4 normal_vec = texture(normal_map, pass_tex) * 2.0 - 1.0; unit_normal = normalize(normal_vec.xyz); - // /*gl_*/FragColor = normal_vec * pass_tangent; - // return; } else { unit_normal = normalize(surface_normal); } @@ -50,6 +53,23 @@ void main() vec4 texture_sample = texture(model_tex, pass_tex); EmissiveColor = texture(emission_map, pass_tex); + float shadow_factor = 1.0; + + float light_dot = dot(unit_normal, normalize(to_light_vector[0])); + if (light_dot > 0.0) { + float texel_size = 1.0 / float(textureSize(shadow_map, 0).x); + float total = 0.0; + + float bias = max(0.05 * (1.0 - light_dot), 0.0008); + for (int x = -pcf_count; x < pcf_count; x++) + for (int y = -pcf_count; y < pcf_count; y++) { + float shadow_distance = texture(shadow_map, shadow_pos.xy + vec2(float(x), float(y)) * texel_size).r; + if (shadow_pos.z - bias > shadow_distance) + total += 1.0; + } + shadow_factor = 1.0 - (total / pcf_total_texels * shadow_pos.w); + } + vec3 total_diffuse = vec3(0.0); vec3 total_specular = vec3(0.0); @@ -68,16 +88,18 @@ void main() total_diffuse = total_diffuse + brightness * light_color[i] / att_fac; } - FragColor = vec4(max(total_diffuse, 0.2), 1.0) * texture_sample + vec4(total_specular, 1.0); + total_diffuse = max(total_diffuse, 0.2) * shadow_factor; + FragColor = vec4(total_diffuse, 1.0) * texture_sample + vec4(total_specular, 1.0); + // FragColor = vec4(vec3(pow(shadow_distance - shadow_pos.z, 0.5)), 1.0); if (albedo_texture) { Albedo = vec4((normalize(cross(texture_sample.rgb, vec3( abs(fract(cos(mod(float(entity_hash), 3.1415926)))), abs(fract(sin(mod(float(entity_hash), 3.1415926)))), abs(fract(log(float(entity_hash)))) - ))) + vec3(1.0, 1.0, 1.0) / 2.0), 1.0); + ))) + vec3(1.0, 1.0, 1.0) / 2.0) * shadow_factor, 1.0); } else { vec3 pos_normal = (normalize(orig_normal) + vec3(1.0, 1.0, 1.0)) / 2.0; - Albedo = vec4(pos_normal, 1.0); + Albedo = vec4(pos_normal/* * shadow_factor*/, 1.0); } } diff --git a/shaders/model.vert b/shaders/model.vert index 57ba729..97ded97 100644 --- a/shaders/model.vert +++ b/shaders/model.vert @@ -13,6 +13,7 @@ uniform mat4 proj; uniform mat4 view; uniform mat4 inverse_view; uniform mat4 trans; +uniform mat4 shadow_mvp; uniform float use_normals; uniform float use_skinning; uniform mat4 joint_transforms[100]; @@ -24,10 +25,14 @@ out vec3 orig_normal; out vec3 to_light_vector[4]; out vec3 to_camera_vector; out float color_override; +out vec4 shadow_pos; void main() { vec4 world_pos = trans * vec4(position, 1.0); + + shadow_pos = shadow_mvp * world_pos; + color_override = 0.0; if (ray.z > 0.5) { if (pow(world_pos.x - ray.x, 2.0) + pow(world_pos.z - ray.y, 2.0) <= 64.0) diff --git a/shaders/passthrough.frag b/shaders/passthrough.frag new file mode 100644 index 0000000..0021e70 --- /dev/null +++ b/shaders/passthrough.frag @@ -0,0 +1,12 @@ +#version 330 + +in vec2 pass_tex; +layout (location=0) out vec4 FragColor; + +uniform sampler2D model_tex; + +void main() +{ + float depth = texture(model_tex, pass_tex).r; + FragColor = vec4(vec3(depth), 1.0); +} diff --git a/shaders/passthrough.vert b/shaders/passthrough.vert new file mode 100644 index 0000000..3a8b2e3 --- /dev/null +++ b/shaders/passthrough.vert @@ -0,0 +1,14 @@ +#version 330 + +in vec3 position; +in vec2 tex; + +out vec2 pass_tex; + +uniform mat4 trans; + +void main() +{ + gl_Position = vec4(position, 1.0); + pass_tex = tex; +} diff --git a/shaders/shadow.frag b/shaders/shadow.frag new file mode 100644 index 0000000..555e6d6 --- /dev/null +++ b/shaders/shadow.frag @@ -0,0 +1,10 @@ +#version 330 + +in vec2 pass_tex; +layout (location=0) out vec4 FragColor; +uniform sampler2D model_tex; + +void main() +{ + // FragColor = texture(model_tex, pass_tex); +} diff --git a/shaders/shadow.vert b/shaders/shadow.vert new file mode 100644 index 0000000..ebfb79d --- /dev/null +++ b/shaders/shadow.vert @@ -0,0 +1,32 @@ +#version 330 + +in vec3 position; +in vec2 tex; +in vec4 joints; +in vec4 weights; + +out vec2 pass_tex; + +uniform mat4 trans; +uniform mat4 view; +uniform mat4 proj; +uniform float use_skinning; +uniform mat4 joint_transforms[100]; + +void main() +{ + vec4 total_local_pos = vec4(0.0); + if (use_skinning > 0.5) { + for (int i = 0; i < 4; i++) { + mat4 joint_transform = joint_transforms[int(joints[i])]; + vec4 local_pos = joint_transform * vec4(position, 1.0); + total_local_pos += local_pos * weights[i]; + } + + gl_Position = proj * view * trans * total_local_pos; + } else { + gl_Position = proj * view * trans * vec4(position, 1.0); + } + + pass_tex = tex; +}