From 0beb264e2b9778b0fe43bdfe92359726a229cf19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Madar=C3=A1sz?= Date: Sat, 23 Sep 2023 17:22:48 +0200 Subject: [PATCH] scene: WIP lighting pass --- MAKE.bat | 9 ++ demos/99-material.c | 9 +- engine/art/shaders/fs_32_4_model.glsl | 136 +++++++++++++++++--------- engine/bind/v4k.lua | 39 ++++++++ engine/joint/v4k.h | 124 +++++++++++++++++++++++ engine/split/v4k_render.c | 1 + engine/split/v4k_scene.c | 85 ++++++++++++++++ engine/split/v4k_scene.h | 38 +++++++ engine/v4k.c | 86 ++++++++++++++++ engine/v4k.h | 38 +++++++ tools/cook.ini | 2 +- workbench/workbench.c | 6 +- 12 files changed, 524 insertions(+), 49 deletions(-) diff --git a/MAKE.bat b/MAKE.bat index f3172b5..520a579 100644 --- a/MAKE.bat +++ b/MAKE.bat @@ -13,6 +13,7 @@ if "%1"=="help" ( echo %0 [cook] ; cook .zipfiles with tools/cook.ini cookbook echo %0 [sync] ; sync repo to latest echo %0 [fwk] ; prepare files for fwk PR + echo %0 [fwk_sync] ; update fwk-mirror fork echo %0 [lua] ; execute lua script with v4k echo %0 [html5] ; build HTML5 demo echo %0 [web] ; run Python webserver in html5 dir @@ -214,6 +215,14 @@ if "%1"=="vps" ( exit /b ) +if "%1"=="fwk_sync" ( + pushd ..\fwk-mirror + call MAKE.bat sync + popd + call MAKE.bat fwk + exit /b +) + if "%1"=="fwk" ( if not exist "_fwk" mkdir "_fwk" if not exist "_fwk\demos" mkdir "_fwk\demos" diff --git a/demos/99-material.c b/demos/99-material.c index f6bdde4..74c08ba 100644 --- a/demos/99-material.c +++ b/demos/99-material.c @@ -39,6 +39,10 @@ int main() { object_move(obj3, vec3(-10+5*1,0,-10)); object_pivot(obj3, vec3(0,90,0)); + // create point light + light_t* l = scene_spawn_light(); + light_type(l, LIGHT_POINT); + while(window_swap() && !input(KEY_ESC)) { // draw environment viewport_color( RGB3(22,22,32) ); @@ -47,8 +51,11 @@ int main() { // update video video_decode( v ); + // update light position + light_teleport(l, cam.position); + // draw scene - scene_render(SCENE_FOREGROUND|SCENE_BACKGROUND); + scene_render(SCENE_FOREGROUND|SCENE_BACKGROUND|SCENE_UPDATE_SH_COEF); // fps camera bool active = ui_active() || ui_hover() || gizmo_active() ? false : input(MOUSE_L) || input(MOUSE_M) || input(MOUSE_R); diff --git a/engine/art/shaders/fs_32_4_model.glsl b/engine/art/shaders/fs_32_4_model.glsl index 1cb5843..8efc136 100644 --- a/engine/art/shaders/fs_32_4_model.glsl +++ b/engine/art/shaders/fs_32_4_model.glsl @@ -6,8 +6,8 @@ uniform bool u_lit = false; uniform bool u_matcaps = false; uniform vec4 u_diffuse = vec4(1.0,1.0,1.0,1.0); -#ifdef RIM in vec3 v_position; +#ifdef RIM uniform mat4 M; // RIM uniform vec3 u_rimcolor = vec3(0.2,0.2,0.2); uniform vec3 u_rimrange = vec3(0.11,0.98,0.5); @@ -28,54 +28,102 @@ vec4 shadowing() { return shadowmap(vpeye, vneye, v_texcoord, sc); } +uniform int u_num_lights; + +struct light_t { + int type; + vec3 color; + vec3 pos; + vec3 dir; + float radius; +}; + +#define MAX_LIGHTS 16 +const int LIGHT_DIRECTIONAL = 0; +const int LIGHT_POINT = 1; +const int LIGHT_SPOT = 2; + +uniform light_t u_lights[MAX_LIGHTS]; + +vec3 calculate_light(light_t l, vec3 normal, vec3 fragPos, vec3 viewDir) { + vec3 lightColor = l.color; + + vec3 lightDir; + float attenuation = 1.0; + + if (l.type == LIGHT_DIRECTIONAL) { + lightDir = normalize(-l.dir); + } else if (l.type == LIGHT_POINT) { + vec3 toLight = fragPos - l.pos; + lightDir = normalize(toLight); + float distance = length(toLight); + float factor = distance / l.radius; + attenuation = clamp(1.0 - factor, 0.0, 1.0); + } + + float diff = max(dot(normal, lightDir), 0.0); + vec3 diffuse = diff * lightColor; + + // vec3 reflectDir = reflect(-lightDir, normal); + // float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32); + // vec3 specular = spec * lightColor; + + return (diffuse /* + specular */) * l.color; +} void main() { -vec3 n = /*normalize*/(v_normal); + vec3 n = /*normalize*/(v_normal); + vec4 lit = vec4(1.0, 1.0, 1.0, 1.0); + // SH lighting + { + vec3 SHLightResult[9]; + SHLightResult[0] = 0.282095f * u_coefficients_sh[0]; + SHLightResult[1] = -0.488603f * u_coefficients_sh[1] * n.y; + SHLightResult[2] = 0.488603f * u_coefficients_sh[2] * n.z; + SHLightResult[3] = -0.488603f * u_coefficients_sh[3] * n.x; + SHLightResult[4] = 1.092548f * u_coefficients_sh[4] * n.x * n.y; + SHLightResult[5] = -1.092548f * u_coefficients_sh[5] * n.y * n.z; + SHLightResult[6] = 0.315392f * u_coefficients_sh[6] * (3.0f * n.z * n.z - 1.0f); + SHLightResult[7] = -1.092548f * u_coefficients_sh[7] * n.x * n.z; + SHLightResult[8] = 0.546274f * u_coefficients_sh[8] * (n.x * n.x - n.y * n.y); + vec3 result = vec3(0.0); + for (int i = 0; i < 9; ++i) + result += SHLightResult[i]; + if( (result.x*result.x+result.y*result.y+result.z*result.z) > 0.0 ) lit = vec4(result, 1.0); + } -// SH lighting -vec4 lit = vec4(1.0, 1.0, 1.0, 1.0); -vec3 SHLightResult[9]; -SHLightResult[0] = 0.282095f * u_coefficients_sh[0]; -SHLightResult[1] = -0.488603f * u_coefficients_sh[1] * n.y; -SHLightResult[2] = 0.488603f * u_coefficients_sh[2] * n.z; -SHLightResult[3] = -0.488603f * u_coefficients_sh[3] * n.x; -SHLightResult[4] = 1.092548f * u_coefficients_sh[4] * n.x * n.y; -SHLightResult[5] = -1.092548f * u_coefficients_sh[5] * n.y * n.z; -SHLightResult[6] = 0.315392f * u_coefficients_sh[6] * (3.0f * n.z * n.z - 1.0f); -SHLightResult[7] = -1.092548f * u_coefficients_sh[7] * n.x * n.z; -SHLightResult[8] = 0.546274f * u_coefficients_sh[8] * (n.x * n.x - n.y * n.y); -vec3 result = vec3(0.0); -for (int i = 0; i < 9; ++i) -result += SHLightResult[i]; + // analytical lights (phong shading) + // @todo: support more shading models (blinn-phong, ue4 brdf, ...) + // for (int i=0; i 0.0 ) lit = vec4(result, 1.0); - - -// base -vec4 diffuse; -if(u_matcaps) { - vec2 muv = vec2(view * vec4(v_normal_ws, 0))*0.5+vec2(0.5,0.5); // normal (model space) to view space - diffuse = texture(u_texture2d, vec2(muv.x, 1.0-muv.y)); + // base + vec4 diffuse; + + if(u_matcaps) { + vec2 muv = vec2(view * vec4(v_normal_ws, 0))*0.5+vec2(0.5,0.5); // normal (model space) to view space + diffuse = texture(u_texture2d, vec2(muv.x, 1.0-muv.y)); } else if(u_textured) { diffuse = texture(u_texture2d, v_texcoord); } else { - diffuse = u_diffuse; // * v_color; - } - - // lighting mix - fragcolor = diffuse * lit * shadowing(); - - // rimlight - #ifdef RIM - {vec3 n = normalize(mat3(M) * v_normal); // convert normal to view space - vec3 p = (M * vec4(v_position,1.0)).xyz; // convert position to view space - vec3 v = vec3(0,-1,0); - if (!u_rimambient) { - v = normalize(u_rimpivot-p); - } - float rim = 1.0 - max(dot(v,n), 0.0); - vec3 col = u_rimcolor*(pow(smoothstep(1.0-u_rimrange.x,u_rimrange.y,rim), u_rimrange.z)); - fragcolor += vec4(col, 1.0);} - #endif - } \ No newline at end of file + diffuse = u_diffuse; // * v_color; + } + + // lighting mix + fragcolor = diffuse * lit * shadowing(); + + // rimlight + #ifdef RIM + {vec3 n = normalize(mat3(M) * v_normal); // convert normal to view space + vec3 p = (M * vec4(v_position,1.0)).xyz; // convert position to view space + vec3 v = vec3(0,-1,0); + if (!u_rimambient) { + v = normalize(u_rimpivot-p); + } + float rim = 1.0 - max(dot(v,n), 0.0); + vec3 col = u_rimcolor*(pow(smoothstep(1.0-u_rimrange.x,u_rimrange.y,rim), u_rimrange.z)); + fragcolor += vec4(col, 1.0);} + #endif +} \ No newline at end of file diff --git a/engine/bind/v4k.lua b/engine/bind/v4k.lua index 67f7860..80248ed 100644 --- a/engine/bind/v4k.lua +++ b/engine/bind/v4k.lua @@ -1392,6 +1392,17 @@ ffi.cdef([[ //lcpp INF [0000] vec3: macro name but used as C declaration in:API void object_scale(object_t *obj, vec3 sca); //lcpp INF [0000] vec3: macro name but used as C declaration in:STATIC void object_scale(object_t *obj, vec3 sca); //lcpp INF [0000] vec3: macro name but used as C declaration in: void object_scale(object_t *obj, vec3 sca); +//lcpp INF [0000] vec3: macro name but used as C declaration in:vec3 color; +//lcpp INF [0000] vec3: macro name but used as C declaration in:vec3 pos, dir; +//lcpp INF [0000] vec3: macro name but used as C declaration in:API void light_color(light_t* l, vec3 color); +//lcpp INF [0000] vec3: macro name but used as C declaration in:STATIC void light_color(light_t* l, vec3 color); +//lcpp INF [0000] vec3: macro name but used as C declaration in: void light_color(light_t* l, vec3 color); +//lcpp INF [0000] vec3: macro name but used as C declaration in:API void light_teleport(light_t* l, vec3 pos); +//lcpp INF [0000] vec3: macro name but used as C declaration in:STATIC void light_teleport(light_t* l, vec3 pos); +//lcpp INF [0000] vec3: macro name but used as C declaration in: void light_teleport(light_t* l, vec3 pos); +//lcpp INF [0000] vec3: macro name but used as C declaration in:API void light_dir(light_t* l, vec3 dir); +//lcpp INF [0000] vec3: macro name but used as C declaration in:STATIC void light_dir(light_t* l, vec3 dir); +//lcpp INF [0000] vec3: macro name but used as C declaration in: void light_dir(light_t* l, vec3 dir); //lcpp INF [0000] vec2: macro name but used as C declaration in:API vec2 ui_get_dims(); //lcpp INF [0000] vec2: macro name but used as C declaration in:STATIC vec2 ui_get_dims(); //lcpp INF [0000] vec2: macro name but used as C declaration in: vec2 ui_get_dims(); @@ -2660,6 +2671,7 @@ handle* textures; model_t model; aabb bounds; unsigned billboard; +bool light_cached; } object_t; object_t object(); void object_rotate(object_t *obj, vec3 euler); @@ -2673,15 +2685,39 @@ unsigned billboard; void object_diffuse_push(object_t *obj, texture_t tex); void object_diffuse_pop(object_t *obj); void object_billboard(object_t *obj, unsigned mode); +enum LIGHT_TYPE { +LIGHT_DIRECTIONAL, +LIGHT_POINT, +LIGHT_SPOT, +}; +enum LIGHT_FLAGS { +LIGHT_CAST_SHADOWS = 1, +}; +typedef struct light_t { +char type; +vec3 color; +vec3 pos, dir; +float radius; +bool cached; +} light_t; + light_t light(); + void light_type(light_t* l, char type); + void light_color(light_t* l, vec3 color); + void light_teleport(light_t* l, vec3 pos); + void light_dir(light_t* l, vec3 dir); + void light_radius(light_t* l, float radius); + void light_update(unsigned num_lights, light_t *lv); enum SCENE_FLAGS { SCENE_WIREFRAME = 1, SCENE_CULLFACE = 2, SCENE_BACKGROUND = 4, SCENE_FOREGROUND = 8, +SCENE_UPDATE_SH_COEF = 16, }; typedef struct scene_t { handle program; object_t* objs; +light_t* lights; skybox_t skybox; int u_coefficients_sh; } scene_t; @@ -2693,6 +2729,9 @@ int u_coefficients_sh; object_t* scene_spawn(); unsigned scene_count(); object_t* scene_index(unsigned index); + light_t* scene_spawn_light(); + unsigned scene_count_light(); + light_t* scene_index_light(unsigned index); void script_init(); void script_run(const char *script); void script_runfile(const char *pathfile); diff --git a/engine/joint/v4k.h b/engine/joint/v4k.h index 27c5095..84ae8dd 100644 --- a/engine/joint/v4k.h +++ b/engine/joint/v4k.h @@ -17131,6 +17131,7 @@ typedef struct object_t { model_t model; aabb bounds; unsigned billboard; // [0..7] x(4),y(2),z(1) masks + bool light_cached; //< used by scene to update light data } object_t; API object_t object(); @@ -17149,6 +17150,37 @@ API void object_billboard(object_t *obj, unsigned mode); // object_pose(transform); // @todo + +// light +enum LIGHT_TYPE { + LIGHT_DIRECTIONAL, + LIGHT_POINT, + LIGHT_SPOT, +}; + +enum LIGHT_FLAGS { + LIGHT_CAST_SHADOWS = 1, +}; + +typedef struct light_t { + char type; + vec3 color; + vec3 pos, dir; + float radius; + bool cached; //< used by scene to invalidate cached light data + //@todo: inner/outer cone, flags, cookie, flare +} light_t; + +API light_t light(); +// API void light_flags(int flags); +API void light_type(light_t* l, char type); +API void light_color(light_t* l, vec3 color); +API void light_teleport(light_t* l, vec3 pos); +API void light_dir(light_t* l, vec3 dir); +API void light_radius(light_t* l, float radius); +// API void light_cone(light_t* l, float inner, float outer); +API void light_update(unsigned num_lights, light_t *lv); + // scene enum SCENE_FLAGS { @@ -17156,12 +17188,14 @@ enum SCENE_FLAGS { SCENE_CULLFACE = 2, SCENE_BACKGROUND = 4, SCENE_FOREGROUND = 8, + SCENE_UPDATE_SH_COEF = 16, }; typedef struct scene_t { handle program; array(object_t) objs; + array(light_t) lights; // special objects below: skybox_t skybox; @@ -17178,6 +17212,10 @@ API void scene_render(int flags); API object_t* scene_spawn(); API unsigned scene_count(); API object_t* scene_index(unsigned index); + +API light_t* scene_spawn_light(); +API unsigned scene_count_light(); +API light_t* scene_index_light(unsigned index); #line 0 #line 1 "v4k_script.h" @@ -342062,6 +342100,7 @@ skybox_t skybox(const char *asset, int flags) { shader_float("uRayleighScaleHeight", 8000.0); shader_float("uMieScaleHeight", 1200.0); shader_float("uMiePreferredDirection", 0.758); + skybox_mie_calc_sh(&sky, 1.2); } return sky; @@ -345154,6 +345193,54 @@ void object_billboard(object_t *obj, unsigned mode) { // ----------------------------------------------------------------------------- +light_t light() { + light_t l = {0}; + l.color = vec3(1,1,1); + l.radius = 2.5f; + l.dir = vec3(1,-1,-1); + + return l; +} + +void light_type(light_t* l, char type) { + l->cached = 0; + l->type = type; +} + +void light_color(light_t* l, vec3 color) { + l->cached = 0; + l->color = color; +} + +void light_teleport(light_t* l, vec3 pos) { + l->cached = 0; + l->pos = pos; +} + +void light_dir(light_t* l, vec3 dir) { + l->cached = 0; + l->dir = dir; +} + +void light_radius(light_t* l, float radius) { + l->cached = 0; + l->radius = radius; +} + +void light_update(unsigned num_lights, light_t *lv) { + shader_int("u_num_lights", num_lights); + + for (unsigned i=0; i < num_lights; ++i) { + lv[i].cached = 1; + shader_int(va("u_lights[%d].type", i), lv[i].type); + shader_vec3(va("u_lights[%d].color", i), lv[i].color); + shader_vec3(va("u_lights[%d].pos", i), lv[i].pos); + shader_vec3(va("u_lights[%d].dir", i), lv[i].dir); + } +} + +// ----------------------------------------------------------------------------- + array(scene_t*) scenes; scene_t* last_scene; @@ -345257,6 +345344,24 @@ object_t* scene_index(unsigned obj_index) { return &last_scene->objs[obj_index]; } +light_t* scene_spawn_light() { + light_t l = light(); + array_push(last_scene->lights, l); + + return array_back(last_scene->lights); +} + +unsigned scene_count_light() { + return array_count(last_scene->lights); +} + +light_t* scene_index_light(unsigned light_index) { + unsigned light_count = scene_count_light(); + ASSERT(light_index < light_count, "Light index %d exceeds number (%d) of spawned lights", light_index, light_count); + return &last_scene->lights[light_index]; +} + + void scene_render(int flags) { camera_t *cam = camera_get_active(); @@ -345293,6 +345398,14 @@ void scene_render(int flags) { // @todo texture mode if( flags & SCENE_FOREGROUND ) { + bool do_relighting = 0; + for (unsigned j = 0; j < array_count(last_scene->lights); ++j) { + if (!last_scene->lights[j].cached) { + do_relighting = 1; + break; + } + } + for(unsigned j = 0, obj_count = scene_count(); j < obj_count; ++j ) { object_t *obj = scene_index(j); model_t *model = &obj->model; @@ -345309,6 +345422,17 @@ void scene_render(int flags) { } } + if ( do_relighting || !obj->light_cached ) { + obj->light_cached = 1; + shader_bind(model->program); + light_update(array_count(last_scene->lights), last_scene->lights); + } + + if ( flags&SCENE_UPDATE_SH_COEF ) { + shader_bind(model->program); + shader_vec3v("u_coefficients_sh", 9, last_scene->skybox.cubemap.sh); + } + model->billboard = obj->billboard; model_render(*model, cam->proj, cam->view, obj->transform, 0); diff --git a/engine/split/v4k_render.c b/engine/split/v4k_render.c index 324372b..96d322a 100644 --- a/engine/split/v4k_render.c +++ b/engine/split/v4k_render.c @@ -2490,6 +2490,7 @@ skybox_t skybox(const char *asset, int flags) { shader_float("uRayleighScaleHeight", 8000.0); shader_float("uMieScaleHeight", 1200.0); shader_float("uMiePreferredDirection", 0.758); + skybox_mie_calc_sh(&sky, 1.2); } return sky; diff --git a/engine/split/v4k_scene.c b/engine/split/v4k_scene.c index 45c6430..a272545 100644 --- a/engine/split/v4k_scene.c +++ b/engine/split/v4k_scene.c @@ -241,6 +241,54 @@ void object_billboard(object_t *obj, unsigned mode) { // ----------------------------------------------------------------------------- +light_t light() { + light_t l = {0}; + l.color = vec3(1,1,1); + l.radius = 2.5f; + l.dir = vec3(1,-1,-1); + + return l; +} + +void light_type(light_t* l, char type) { + l->cached = 0; + l->type = type; +} + +void light_color(light_t* l, vec3 color) { + l->cached = 0; + l->color = color; +} + +void light_teleport(light_t* l, vec3 pos) { + l->cached = 0; + l->pos = pos; +} + +void light_dir(light_t* l, vec3 dir) { + l->cached = 0; + l->dir = dir; +} + +void light_radius(light_t* l, float radius) { + l->cached = 0; + l->radius = radius; +} + +void light_update(unsigned num_lights, light_t *lv) { + shader_int("u_num_lights", num_lights); + + for (unsigned i=0; i < num_lights; ++i) { + lv[i].cached = 1; + shader_int(va("u_lights[%d].type", i), lv[i].type); + shader_vec3(va("u_lights[%d].color", i), lv[i].color); + shader_vec3(va("u_lights[%d].pos", i), lv[i].pos); + shader_vec3(va("u_lights[%d].dir", i), lv[i].dir); + } +} + +// ----------------------------------------------------------------------------- + array(scene_t*) scenes; scene_t* last_scene; @@ -344,6 +392,24 @@ object_t* scene_index(unsigned obj_index) { return &last_scene->objs[obj_index]; } +light_t* scene_spawn_light() { + light_t l = light(); + array_push(last_scene->lights, l); + + return array_back(last_scene->lights); +} + +unsigned scene_count_light() { + return array_count(last_scene->lights); +} + +light_t* scene_index_light(unsigned light_index) { + unsigned light_count = scene_count_light(); + ASSERT(light_index < light_count, "Light index %d exceeds number (%d) of spawned lights", light_index, light_count); + return &last_scene->lights[light_index]; +} + + void scene_render(int flags) { camera_t *cam = camera_get_active(); @@ -380,6 +446,14 @@ void scene_render(int flags) { // @todo texture mode if( flags & SCENE_FOREGROUND ) { + bool do_relighting = 0; + for (unsigned j = 0; j < array_count(last_scene->lights); ++j) { + if (!last_scene->lights[j].cached) { + do_relighting = 1; + break; + } + } + for(unsigned j = 0, obj_count = scene_count(); j < obj_count; ++j ) { object_t *obj = scene_index(j); model_t *model = &obj->model; @@ -396,6 +470,17 @@ void scene_render(int flags) { } } + if ( do_relighting || !obj->light_cached ) { + obj->light_cached = 1; + shader_bind(model->program); + light_update(array_count(last_scene->lights), last_scene->lights); + } + + if ( flags&SCENE_UPDATE_SH_COEF ) { + shader_bind(model->program); + shader_vec3v("u_coefficients_sh", 9, last_scene->skybox.cubemap.sh); + } + model->billboard = obj->billboard; model_render(*model, cam->proj, cam->view, obj->transform, 0); diff --git a/engine/split/v4k_scene.h b/engine/split/v4k_scene.h index 9c5a84b..b3962b1 100644 --- a/engine/split/v4k_scene.h +++ b/engine/split/v4k_scene.h @@ -33,6 +33,7 @@ typedef struct object_t { model_t model; aabb bounds; unsigned billboard; // [0..7] x(4),y(2),z(1) masks + bool light_cached; //< used by scene to update light data } object_t; API object_t object(); @@ -51,6 +52,37 @@ API void object_billboard(object_t *obj, unsigned mode); // object_pose(transform); // @todo + +// light +enum LIGHT_TYPE { + LIGHT_DIRECTIONAL, + LIGHT_POINT, + LIGHT_SPOT, +}; + +enum LIGHT_FLAGS { + LIGHT_CAST_SHADOWS = 1, +}; + +typedef struct light_t { + char type; + vec3 color; + vec3 pos, dir; + float radius; + bool cached; //< used by scene to invalidate cached light data + //@todo: inner/outer cone, flags, cookie, flare +} light_t; + +API light_t light(); +// API void light_flags(int flags); +API void light_type(light_t* l, char type); +API void light_color(light_t* l, vec3 color); +API void light_teleport(light_t* l, vec3 pos); +API void light_dir(light_t* l, vec3 dir); +API void light_radius(light_t* l, float radius); +// API void light_cone(light_t* l, float inner, float outer); +API void light_update(unsigned num_lights, light_t *lv); + // scene enum SCENE_FLAGS { @@ -58,12 +90,14 @@ enum SCENE_FLAGS { SCENE_CULLFACE = 2, SCENE_BACKGROUND = 4, SCENE_FOREGROUND = 8, + SCENE_UPDATE_SH_COEF = 16, }; typedef struct scene_t { handle program; array(object_t) objs; + array(light_t) lights; // special objects below: skybox_t skybox; @@ -80,3 +114,7 @@ API void scene_render(int flags); API object_t* scene_spawn(); API unsigned scene_count(); API object_t* scene_index(unsigned index); + +API light_t* scene_spawn_light(); +API unsigned scene_count_light(); +API light_t* scene_index_light(unsigned index); diff --git a/engine/v4k.c b/engine/v4k.c index 082165e..be927d9 100644 --- a/engine/v4k.c +++ b/engine/v4k.c @@ -12754,6 +12754,7 @@ skybox_t skybox(const char *asset, int flags) { shader_float("uRayleighScaleHeight", 8000.0); shader_float("uMieScaleHeight", 1200.0); shader_float("uMiePreferredDirection", 0.758); + skybox_mie_calc_sh(&sky, 1.2); } return sky; @@ -15846,6 +15847,54 @@ void object_billboard(object_t *obj, unsigned mode) { // ----------------------------------------------------------------------------- +light_t light() { + light_t l = {0}; + l.color = vec3(1,1,1); + l.radius = 2.5f; + l.dir = vec3(1,-1,-1); + + return l; +} + +void light_type(light_t* l, char type) { + l->cached = 0; + l->type = type; +} + +void light_color(light_t* l, vec3 color) { + l->cached = 0; + l->color = color; +} + +void light_teleport(light_t* l, vec3 pos) { + l->cached = 0; + l->pos = pos; +} + +void light_dir(light_t* l, vec3 dir) { + l->cached = 0; + l->dir = dir; +} + +void light_radius(light_t* l, float radius) { + l->cached = 0; + l->radius = radius; +} + +void light_update(unsigned num_lights, light_t *lv) { + shader_int("u_num_lights", num_lights); + + for (unsigned i=0; i < num_lights; ++i) { + lv[i].cached = 1; + shader_int(va("u_lights[%d].type", i), lv[i].type); + shader_vec3(va("u_lights[%d].color", i), lv[i].color); + shader_vec3(va("u_lights[%d].pos", i), lv[i].pos); + shader_vec3(va("u_lights[%d].dir", i), lv[i].dir); + } +} + +// ----------------------------------------------------------------------------- + array(scene_t*) scenes; scene_t* last_scene; @@ -15949,6 +15998,24 @@ object_t* scene_index(unsigned obj_index) { return &last_scene->objs[obj_index]; } +light_t* scene_spawn_light() { + light_t l = light(); + array_push(last_scene->lights, l); + + return array_back(last_scene->lights); +} + +unsigned scene_count_light() { + return array_count(last_scene->lights); +} + +light_t* scene_index_light(unsigned light_index) { + unsigned light_count = scene_count_light(); + ASSERT(light_index < light_count, "Light index %d exceeds number (%d) of spawned lights", light_index, light_count); + return &last_scene->lights[light_index]; +} + + void scene_render(int flags) { camera_t *cam = camera_get_active(); @@ -15985,6 +16052,14 @@ void scene_render(int flags) { // @todo texture mode if( flags & SCENE_FOREGROUND ) { + bool do_relighting = 0; + for (unsigned j = 0; j < array_count(last_scene->lights); ++j) { + if (!last_scene->lights[j].cached) { + do_relighting = 1; + break; + } + } + for(unsigned j = 0, obj_count = scene_count(); j < obj_count; ++j ) { object_t *obj = scene_index(j); model_t *model = &obj->model; @@ -16001,6 +16076,17 @@ void scene_render(int flags) { } } + if ( do_relighting || !obj->light_cached ) { + obj->light_cached = 1; + shader_bind(model->program); + light_update(array_count(last_scene->lights), last_scene->lights); + } + + if ( flags&SCENE_UPDATE_SH_COEF ) { + shader_bind(model->program); + shader_vec3v("u_coefficients_sh", 9, last_scene->skybox.cubemap.sh); + } + model->billboard = obj->billboard; model_render(*model, cam->proj, cam->view, obj->transform, 0); diff --git a/engine/v4k.h b/engine/v4k.h index c818eb9..af12e0f 100644 --- a/engine/v4k.h +++ b/engine/v4k.h @@ -3214,6 +3214,7 @@ typedef struct object_t { model_t model; aabb bounds; unsigned billboard; // [0..7] x(4),y(2),z(1) masks + bool light_cached; //< used by scene to update light data } object_t; API object_t object(); @@ -3232,6 +3233,37 @@ API void object_billboard(object_t *obj, unsigned mode); // object_pose(transform); // @todo + +// light +enum LIGHT_TYPE { + LIGHT_DIRECTIONAL, + LIGHT_POINT, + LIGHT_SPOT, +}; + +enum LIGHT_FLAGS { + LIGHT_CAST_SHADOWS = 1, +}; + +typedef struct light_t { + char type; + vec3 color; + vec3 pos, dir; + float radius; + bool cached; //< used by scene to invalidate cached light data + //@todo: inner/outer cone, flags, cookie, flare +} light_t; + +API light_t light(); +// API void light_flags(int flags); +API void light_type(light_t* l, char type); +API void light_color(light_t* l, vec3 color); +API void light_teleport(light_t* l, vec3 pos); +API void light_dir(light_t* l, vec3 dir); +API void light_radius(light_t* l, float radius); +// API void light_cone(light_t* l, float inner, float outer); +API void light_update(unsigned num_lights, light_t *lv); + // scene enum SCENE_FLAGS { @@ -3239,12 +3271,14 @@ enum SCENE_FLAGS { SCENE_CULLFACE = 2, SCENE_BACKGROUND = 4, SCENE_FOREGROUND = 8, + SCENE_UPDATE_SH_COEF = 16, }; typedef struct scene_t { handle program; array(object_t) objs; + array(light_t) lights; // special objects below: skybox_t skybox; @@ -3261,6 +3295,10 @@ API void scene_render(int flags); API object_t* scene_spawn(); API unsigned scene_count(); API object_t* scene_index(unsigned index); + +API light_t* scene_spawn_light(); +API unsigned scene_count_light(); +API light_t* scene_index_light(unsigned index); #line 0 #line 1 "v4k_script.h" diff --git a/tools/cook.ini b/tools/cook.ini index 501682a..969fb7d 100644 --- a/tools/cook.ini +++ b/tools/cook.ini @@ -1,4 +1,4 @@ -; this is where you specify and configure the V4K pipeline. +; this is where you specify and configure the FWK pipeline. ; tweak the pipeline and add new importers just by editing this file. ; there is no flow control in this script file: lines are parsed and evaluated, from top to bottom. diff --git a/workbench/workbench.c b/workbench/workbench.c index dba88cf..fa78035 100644 --- a/workbench/workbench.c +++ b/workbench/workbench.c @@ -112,14 +112,14 @@ int main() { load_asset(file); show_browser = 1; } - ui_panel_end(); } + ui_panel_end(); static bool show_main_window = 1; if ( ui_window("Workbench", &show_main_window) ) { ui_label("v4.games"); - ui_window_end(); } + ui_window_end(); for (int i=0; i