#include "lighting.hpp" #include "model.hpp" #include "renderer.hpp" #include "world.hpp" extern "C" { #include "memory.h" #include "plat.h" } /* needs to match surface shader */ struct GPU_Light { v3f dir; float brightness; v3f colour; int caster_id; }; struct GPU_Caster { m4f projection; }; void Lighting::init(Device* dev, int w, int h) { int i; Sampler_State ss{}; lights.init( dev, "Light buffer", max_lights * sizeof(GPU_Light), Buffer_Flags::storage_buffer ); casters.init( dev, "Caster buffer", max_shadows * sizeof(GPU_Caster), Buffer_Flags::storage_buffer ); shadows = dev->create_texture( "Shadowmap Array", texture_format_d16, Texture_Flags::sampleable | Texture_Flags::depth_stencil_target, shadow_res, shadow_res, 1, 1, max_shadows, 0 ); zero(ss_shadows, sizeof ss_shadows); zero(ss_shadow_slices, sizeof ss_shadow_slices); recreate(dev, w, h); for (i = 0; i < max_shadows; i++) { cameras[i] = 0; shadow_slices[i] = dev->alias_texture( shadows, "Shadowmap Slice", texture_format_d16, Texture_Flags::depth_stencil_target, shadow_res, shadow_res, 1, 1, 1, 0, i ); } ss.min = Filter_Mode::linear; ss.mag = Filter_Mode::linear; ss.mip = Filter_Mode::linear; ss.address_u = Address_Mode::border; ss.address_v = Address_Mode::border; ss.border[0] = 1.0f; ss.border[1] = 1.0f; ss.border[2] = 1.0f; ss.border[3] = 1.0f; ss.compare = Depth_Mode::less; shadow_sampler = dev->create_sampler("shadow sampler", ss); } void Lighting::destroy(Device* dev, Renderer& r) { int i; lights.destroy(dev); casters.destroy(dev); dev->destroy_sampler(shadow_sampler); for (i = 0; i < max_shadows; i++) { dev->destroy_texture(shadow_slices[i]); r.destroy_camera(cameras[i]); } dev->destroy_texture(shadows); destroy_ss(dev); } void Lighting::destroy_ss(Device* dev) { int i, j; for (i = 0; i < 2; i++) { for (j = 0; j < max_shadows; j++) if (ss_shadow_slices[i][j]) dev->destroy_texture(ss_shadow_slices[i][j]); if (ss_shadows[i]) dev->destroy_texture(ss_shadows[i]); } } void Lighting::recreate(Device* dev, int w, int h) { int i, j; destroy_ss(dev); for (i = 0; i < 2; i++) { ss_shadows[i] = dev->create_texture( "Shadow accumulation buffer", texture_format_r8i, Texture_Flags::sampleable | Texture_Flags::colour_target, w, h, 1, 1, max_shadows, 0 ); for (j = 0; j < max_shadows; j++) ss_shadow_slices[i][j] = dev->alias_texture( ss_shadows[i], "Shadow accumulation buffer slice", texture_format_r8i, Texture_Flags::colour_target, shadow_res, shadow_res, 1, 1, 1, 0, j ); } } void Lighting::write_bufs( void* lptr, void* cptr, World& w, Renderer& r, Model_Scene& s ) { GPU_Light* ldst = (GPU_Light*)lptr; GPU_Caster* cdst = (GPU_Caster*)cptr; int count = 0, ccount = 0; for (auto v : w.view()) { GPU_Light gl; Sun_Light& l = v.get(); if (count >= max_lights) { print_war("Over light limit.\n"); return; } gl.brightness = l.brightness; gl.colour = l.colour; gl.dir = l.dir; if (l.caster && ccount < max_shadows) { int cid = ccount++; GPU_Caster& c = cdst[cid]; Camera_Id camid = cameras[cid]; if (!camid) camid = r.create_camera(); Camera& cam = r.get_camera(camid); cam.init_shadow(l.dir, s.bound.min, s.bound.max); c.projection = cam.get_proj() * cam.get_view(); cameras[cid] = camid; gl.caster_id = cid; } else gl.caster_id = -1; ldst[count++] = gl; } light_count = count; caster_count = ccount; } void Lighting::update( Device* dev, Context& ctx, World& w, Renderer& r, Model_Scene& s ) { light_count = 0; write_bufs(lights.map(dev), casters.map(dev), w, r, s); casters.unmap(dev); lights.unmap(dev); lights.update(ctx); casters.update(ctx); }