diff --git a/include/light/ltface.hh b/include/light/ltface.hh index 63d3389b..9055642b 100644 --- a/include/light/ltface.hh +++ b/include/light/ltface.hh @@ -57,5 +57,21 @@ void FinishLightmapSurface(const mbsp_t *bsp, lightsurf_t *lightsurf); void SaveLightmapSurface(const mbsp_t *bsp, mface_t *face, facesup_t *facesup, bspx_decoupled_lm_perface *facesup_decoupled, lightsurf_t *lightsurf, const faceextents_t &extents, const faceextents_t &output_extents); -qvec3d CalcLightgridAtPoint(const mbsp_t *bsp, const qvec3d &world_point); + +struct lightgrid_sample_t { + bool used = false; + int style = 0; + qvec3d color {}; + + qvec3b round_to_int() const; +}; + +struct lightgrid_samples_t { + std::array samples_by_style; + + void add(const qvec3d &color, int style); + int used_styles() const; +}; + +lightgrid_samples_t CalcLightgridAtPoint(const mbsp_t *bsp, const qvec3d &world_point); void ResetLtFace(); diff --git a/light/light.cc b/light/light.cc index a9aceb55..dfb06452 100644 --- a/light/light.cc +++ b/light/light.cc @@ -1064,10 +1064,9 @@ static void LightGrid(bspdata_t *bspdata) // number of grid points on each axis const qvec3i grid_size = {GRIDSIZE, GRIDSIZE, GRIDSIZE}; const qvec3f grid_mins = bsp.dmodels[0].mins; - const uint8_t num_styles = 1; - std::vector grid_result; - grid_result.resize(GRIDSIZE * GRIDSIZE * GRIDSIZE * 3); + std::vector grid_result; + grid_result.resize(GRIDSIZE * GRIDSIZE * GRIDSIZE); std::vector occlusion; occlusion.resize(GRIDSIZE * GRIDSIZE * GRIDSIZE); @@ -1081,24 +1080,42 @@ static void LightGrid(bspdata_t *bspdata) for (int z = z_range.begin(); z < z_range.end(); ++z) { for (int y = y_range.begin(); y < y_range.end(); ++y) { for (int x = x_range.begin(); x < x_range.end(); ++x) { - qvec3d world_point = grid_mins + (qvec3d{x,y,z} * grid_dist); - qvec3d color = CalcLightgridAtPoint(&bsp, world_point); + lightgrid_samples_t samples = CalcLightgridAtPoint(&bsp, world_point); bool occluded = Light_PointInWorld(&bsp, world_point); int sample_index = (GRIDSIZE * GRIDSIZE * z) + (GRIDSIZE * y) + x; - grid_result[sample_index * 3] = clamp((int)color[0], 0, 255); - grid_result[sample_index * 3 + 1] = clamp((int)color[1], 0, 255); - grid_result[sample_index * 3 + 2] = clamp((int)color[2], 0, 255); - + grid_result[sample_index] = samples; occlusion[sample_index] = occluded; } } } }); + // num_styles == 1 is a flag for "all grid points use only style 0" + const uint8_t all_style_0 = [&](){ + for (auto &samples : grid_result) { + if (samples.used_styles() != 1) + return false; + if (samples.samples_by_style[0].style != 0) + return false; + } + return true; + }(); + + // otherwise, it gives the maximum used styles across the map. + const uint8_t num_styles = [&](){ + int result = 0; + for (auto &samples : grid_result) { + result = max(result, samples.used_styles()); + } + return result; + }(); + + // FIXME: technically num_styles == 1 could happen if all grid points are style 1 or something + // non-final, experimental lump std::ostringstream str(std::ios_base::out | std::ios_base::binary); str << endianness; @@ -1107,9 +1124,23 @@ static void LightGrid(bspdata_t *bspdata) str <= grid_mins; str <= num_styles; - // color data 3D array - for (uint8_t byte : grid_result) { - str <= byte; + // if the map only has 1 style, write a more compact form + if (num_styles == 1) { + // color data 3D array + for (const lightgrid_samples_t &samples : grid_result) { + str <= samples.samples_by_style[0].round_to_int(); + } + } else { + // general case + for (const lightgrid_samples_t &samples : grid_result) { + str <= static_cast(samples.used_styles()); + for (int i = 0; i < samples.used_styles(); ++i) { + str <= static_cast(samples.samples_by_style[i].style); + } + for (int i = 0; i < samples.used_styles(); ++i) { + str <= samples.samples_by_style[i].round_to_int(); + } + } } // occlusion 3D array diff --git a/light/ltface.cc b/light/ltface.cc index d7f26c34..f8132b5a 100644 --- a/light/ltface.cc +++ b/light/ltface.cc @@ -1253,11 +1253,8 @@ static void LightFace_Entity( * Calculates light at a given point from an entity */ static void LightPoint_Entity( - const mbsp_t *bsp, raystream_occlusion_t &rs, const light_t *entity, const qvec3d &surfpoint, qvec3d &result) + const mbsp_t *bsp, raystream_occlusion_t &rs, const light_t *entity, const qvec3d &surfpoint, lightgrid_samples_t &result) { - if (entity->style.value() != 0) - return; // not doing style lightgrid for now - rs.clearPushedRays(); qvec3d surfpointToLightDir; @@ -1297,7 +1294,7 @@ static void LightPoint_Entity( continue; } - result += rs.getPushedRayColor(j); + result.add(rs.getPushedRayColor(j), entity->style.value()); } } @@ -1415,11 +1412,8 @@ static void LightFace_Sky(const sun_t *sun, lightsurf_t *lightsurf, lightmapdict } static void LightPoint_Sky( - const mbsp_t *bsp, raystream_intersection_t &rs, const sun_t *sun, const qvec3d &surfpoint, qvec3d &result) + const mbsp_t *bsp, raystream_intersection_t &rs, const sun_t *sun, const qvec3d &surfpoint, lightgrid_samples_t &result) { - if (sun->style != 0) - return; // not doing style lightgrid for now - // FIXME: Normalized sun vector should be stored in the sun_t. Also clarify which way the vector points (towards or // away..) // FIXME: Much of this is copied/pasted from LightFace_Entity, should probably be merged @@ -1471,7 +1465,7 @@ static void LightPoint_Sky( continue; } - result += rs.getPushedRayColor(j); + result.add(rs.getPushedRayColor(j), sun->style); } } @@ -1923,7 +1917,7 @@ LightFace_SurfaceLight(const mbsp_t *bsp, lightsurf_t *lightsurf, lightmapdict_t static void // mxd LightPoint_SurfaceLight(const mbsp_t *bsp, raystream_occlusion_t &rs, const std::vector &surface_lights, - const vec_t &standard_scale, const vec_t &sky_scale, const float &hotspot_clamp, const qvec3d &surfpoint, qvec3d &result) + const vec_t &standard_scale, const vec_t &sky_scale, const float &hotspot_clamp, const qvec3d &surfpoint, lightgrid_samples_t &result) { const settings::worldspawn_keys &cfg = light_options; const float surflight_gate = 0.01f; @@ -1978,7 +1972,7 @@ LightPoint_SurfaceLight(const mbsp_t *bsp, raystream_occlusion_t &rs, const std: Q_assert(!std::isnan(indirect[0])); - result += indirect; + result.add(indirect, vpl.style); } } } @@ -2267,7 +2261,7 @@ inline void LightFace_ScaleAndClamp(lightsurf_t *lightsurf) * Same as above LightFace_ScaleAndClamp, but for lightgrid * TODO: share code with above */ -inline void LightPoint_ScaleAndClamp(qvec3d &color) +static void LightPoint_ScaleAndClamp(qvec3d &color) { const settings::worldspawn_keys &cfg = light_options; @@ -2303,6 +2297,15 @@ inline void LightPoint_ScaleAndClamp(qvec3d &color) } } +static void LightPoint_ScaleAndClamp(lightgrid_samples_t &result) +{ + for (auto &sample : result.samples_by_style) { + if (sample.used) { + LightPoint_ScaleAndClamp(sample.color); + } + } +} + void FinishLightmapSurface(const mbsp_t *bsp, lightsurf_t *lightsurf) { /* Apply gamma, rangescale, and clamp */ @@ -3244,7 +3247,56 @@ void IndirectLightFace(const mbsp_t *bsp, lightsurf_t &lightsurf, const settings // lightgrid -qvec3d CalcLightgridAtPoint(const mbsp_t *bsp, const qvec3d &world_point) +void lightgrid_samples_t::add(const qvec3d &color, int style) +{ + for (auto &sample : samples_by_style) { + if (!sample.used) { + // allocate new style + sample.used = true; + sample.style = style; + sample.color = color; + return; + } + + if (sample.style == style) { + // found matching style + sample.color += color; + return; + } + } + + // uncommon case: all slots are used, but we didn't find a matching style + // drop the style with the lowest brightness + + // FIXME: pick lowest brightness + logging::print("out of lightstyles\n"); + samples_by_style[0].style = style; + samples_by_style[0].color = color; +} + +qvec3b lightgrid_sample_t::round_to_int() const +{ + return qvec3b{ + clamp((int)round(color[0]), 0, 255), + clamp((int)round(color[1]), 0, 255), + clamp((int)round(color[2]), 0, 255) + }; +} + +int lightgrid_samples_t::used_styles() const +{ + int used = 0; + for (auto &sample : samples_by_style) { + if (sample.used) { + used++; + } else { + break; + } + } + return used; +} + +lightgrid_samples_t CalcLightgridAtPoint(const mbsp_t *bsp, const qvec3d &world_point) { // TODO: use more than 1 ray for better performance raystream_occlusion_t rs(1); @@ -3252,7 +3304,7 @@ qvec3d CalcLightgridAtPoint(const mbsp_t *bsp, const qvec3d &world_point) auto &cfg = light_options; - qvec3d result {0, 0, 0}; + lightgrid_samples_t result; // from DirectLightFace