dxx-rebirth/similar/editor/elight.cpp
Kp 6d3dce4e16 Use enum class for tmap_num2
Define separate enum values for rotation data in both the high bits,
where it is usually kept, and the low bits, where it is sometimes used
for math or comparisons.

Define an enum value to represent the composite of the index and the
rotation, since the composite is not suitable for use as an array
subscript.  Add helper functions to extract the component pieces.
2020-08-24 01:31:28 +00:00

327 lines
10 KiB
C++

/*
* Portions of this file are copyright Rebirth contributors and licensed as
* described in COPYING.txt.
* Portions of this file are copyright Parallax Software and licensed
* according to the Parallax license below.
* See COPYING.txt for license details.
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
*/
/*
*
* Editor lighting functions.
*
*/
#include <stdio.h>
#include "inferno.h"
#include "segment.h"
#include "editor.h"
#include "editor/esegment.h"
#include "seguvs.h"
#include "wall.h"
#include "textures.h"
#include "maths.h"
#include "dxxerror.h"
#include "kdefs.h"
#include "gameseg.h"
#include "texmap.h"
#include "compiler-range_for.h"
#include "d_range.h"
// -----------------------------------------------------------------------------
// Return light intensity at an instance of a vertex on a side in a segment.
static fix get_light_intensity(const unique_side &s, const uint_fast32_t vert)
{
Assert(vert <= 3);
return s.uvls[vert].l;
}
static fix get_light_intensity(const unique_segment &segp, const uint_fast32_t sidenum, const uint_fast32_t vert)
{
Assert(sidenum <= MAX_SIDES_PER_SEGMENT);
return get_light_intensity(segp.sides[sidenum], vert);
}
static fix clamp_light_intensity(const fix intensity)
{
if (intensity < MIN_LIGHTING_VALUE)
return MIN_LIGHTING_VALUE;
if (intensity > MAX_LIGHTING_VALUE)
return MAX_LIGHTING_VALUE;
return intensity;
}
// -----------------------------------------------------------------------------
// Set light intensity at a vertex, saturating in .5 to 15.5
static void set_light_intensity(unique_side &s, const uint_fast32_t vert, const fix intensity)
{
Assert(vert <= 3);
s.uvls[vert].l = clamp_light_intensity(intensity);
Update_flags |= UF_WORLD_CHANGED;
}
static void set_light_intensity(unique_segment &segp, const uint_fast32_t sidenum, const uint_fast32_t vert, const fix intensity)
{
Assert(sidenum <= MAX_SIDES_PER_SEGMENT);
set_light_intensity(segp.sides[sidenum], vert, intensity);
}
// -----------------------------------------------------------------------------
// Add light intensity to a vertex, saturating in .5 to 15.5
static void add_light_intensity_all_verts(unique_side &s, const fix intensity)
{
range_for (auto &u, s.uvls)
u.l = clamp_light_intensity(u.l + intensity);
Update_flags |= UF_WORLD_CHANGED;
}
// -----------------------------------------------------------------------------
// Recursively apply light to segments.
// If current side is a wall, apply light there.
// If not a wall, apply light to child through that wall.
// Notes:
// It is possible to enter a segment twice by taking different paths. It is easy
// to prevent this by maintaining a list of visited segments, but it is important
// to reach segments with the greatest light intensity. This can be done by doing
// a breadth-first-search, or by storing the applied intensity with a visited segment,
// and if the current intensity is brighter, then apply the difference between it and
// the previous intensity.
// Note that it is also possible to visit the original light-casting segment, for example
// going from segment 0 to 2, then from 2 to 0. This is peculiar and probably not
// desired, but not entirely invalid. 2 reflects some light back to 0.
static void apply_light_intensity(const csmusegment segp, const unsigned sidenum, fix intensity, const unsigned depth)
{
if (intensity == 0)
return;
auto &Walls = LevelUniqueWallSubsystemState.Walls;
auto &vcwallptr = Walls.vcptr;
const auto wid_result = WALL_IS_DOORWAY(GameBitmaps, Textures, vcwallptr, segp, sidenum);
if (!(wid_result & WID_RENDPAST_FLAG)) {
add_light_intensity_all_verts(segp.u.sides[sidenum], intensity);
return; // we return because there is a wall here, and light does not shine through walls
}
// No wall here, so apply light recursively
if (depth < 3) {
intensity /= 3;
if (!intensity)
return;
const csmusegment &&csegp = vmsegptr(segp.s.children[sidenum]);
for (int s=0; s<MAX_SIDES_PER_SEGMENT; s++)
apply_light_intensity(csegp, s, intensity, depth+1);
}
}
// -----------------------------------------------------------------------------
// Top level recursive function for applying light.
// Calls apply_light_intensity.
// Uses light value on segp:sidenum (tmap_num2 defines light value) and applies
// the associated intensity to segp. It calls apply_light_intensity to apply intensity/3
// to all neighbors. apply_light_intensity recursively calls itself to apply light to
// subsequent neighbors (and forming loops, see above).
static void propagate_light_intensity(const csmusegment segp, const unsigned sidenum)
{
fix intensity;
short texmap;
intensity = 0;
auto &us = segp.u.sides[sidenum];
auto &TmapInfo = LevelUniqueTmapInfoState.TmapInfo;
texmap = us.tmap_num;
intensity += TmapInfo[texmap].lighting;
intensity += TmapInfo[get_texture_index(us.tmap_num2)].lighting;
if (intensity > 0) {
add_light_intensity_all_verts(us, intensity);
// Now, for all sides which are not the same as sidenum (the side casting the light),
// add a light value to them (if they have no children, ie, they have a wall there).
for (int s=0; s<MAX_SIDES_PER_SEGMENT; s++)
if (s != sidenum)
apply_light_intensity(segp, s, intensity/2, 1);
}
}
// -----------------------------------------------------------------------------
// Highest level function, bound to a key. Apply ambient light to all segments based
// on user-defined light sources.
int LightAmbientLighting()
{
range_for (const auto &&segp, vmsegptr)
{
for (int side=0;side<MAX_SIDES_PER_SEGMENT;side++)
propagate_light_intensity(segp, side);
}
return 0;
}
// -----------------------------------------------------------------------------
int LightSelectNextVertex(void)
{
Curvert++;
if (Curvert >= 4)
Curvert = 0;
Update_flags |= UF_WORLD_CHANGED;
return 1;
}
// -----------------------------------------------------------------------------
int LightSelectNextEdge(void)
{
Curedge++;
if (Curedge >= 4)
Curedge = 0;
Update_flags |= UF_WORLD_CHANGED;
return 1;
}
// -----------------------------------------------------------------------------
// Copy intensity from current vertex to all other vertices on side.
int LightCopyIntensity(void)
{
int intensity;
unique_segment &segp = Cursegp;
intensity = get_light_intensity(segp, Curside, Curvert);
range_for (const int v, xrange(4u))
if (v != Curvert)
set_light_intensity(segp, Curside, v, intensity);
return 1;
}
// -----------------------------------------------------------------------------
// Copy intensity from current vertex to all other vertices on side.
int LightCopyIntensitySegment(void)
{
int intensity;
unique_segment &segp = Cursegp;
intensity = get_light_intensity(segp, Curside, Curvert);
for (int s=0; s<MAX_SIDES_PER_SEGMENT; s++)
range_for (const int v, xrange(4u))
if ((s != Curside) || (v != Curvert))
set_light_intensity(segp, s, v, intensity);
return 1;
}
// -----------------------------------------------------------------------------
int LightDecreaseLightVertex(void)
{
unique_segment &segp = Cursegp;
set_light_intensity(segp, Curside, Curvert, get_light_intensity(segp, Curside, Curvert) - F1_0 / NUM_LIGHTING_LEVELS);
return 1;
}
// -----------------------------------------------------------------------------
int LightIncreaseLightVertex(void)
{
unique_segment &segp = Cursegp;
set_light_intensity(segp, Curside, Curvert, get_light_intensity(segp, Curside, Curvert) + F1_0 / NUM_LIGHTING_LEVELS);
return 1;
}
// -----------------------------------------------------------------------------
int LightDecreaseLightSide(void)
{
unique_segment &segp = Cursegp;
range_for (const int v, xrange(4u))
set_light_intensity(segp, Curside, v, get_light_intensity(segp, Curside, v) - F1_0 / NUM_LIGHTING_LEVELS);
return 1;
}
// -----------------------------------------------------------------------------
int LightIncreaseLightSide(void)
{
unique_segment &segp = Cursegp;
range_for (const int v, xrange(4u))
set_light_intensity(segp, Curside, v, get_light_intensity(segp, Curside, v) + F1_0 / NUM_LIGHTING_LEVELS);
return 1;
}
// -----------------------------------------------------------------------------
int LightDecreaseLightSegment(void)
{
unique_segment &segp = Cursegp;
for (int s=0; s<MAX_SIDES_PER_SEGMENT; s++)
range_for (const int v, xrange(4u))
set_light_intensity(segp, s, v, get_light_intensity(segp, s, v) - F1_0 / NUM_LIGHTING_LEVELS);
return 1;
}
// -----------------------------------------------------------------------------
int LightIncreaseLightSegment(void)
{
unique_segment &segp = Cursegp;
for (int s=0; s<MAX_SIDES_PER_SEGMENT; s++)
range_for (const int v, xrange(4u))
set_light_intensity(segp, s, v, get_light_intensity(segp, s, v) + F1_0 / NUM_LIGHTING_LEVELS);
return 1;
}
// -----------------------------------------------------------------------------
int LightSetDefault(void)
{
unique_segment &segp = Cursegp;
range_for (const int v, xrange(4u))
set_light_intensity(segp, Curside, v, DEFAULT_LIGHTING);
return 1;
}
// -----------------------------------------------------------------------------
int LightSetMaximum(void)
{
unique_segment &segp = Cursegp;
range_for (const int v, xrange(4u))
set_light_intensity(segp, Curside, v, (NUM_LIGHTING_LEVELS - 1) * F1_0 / NUM_LIGHTING_LEVELS);
return 1;
}
// -----------------------------------------------------------------------------
int LightSetDefaultAll(void)
{
assign_default_lighting_all();
Update_flags |= UF_WORLD_CHANGED;
return 1;
}