1 /*
2  * Portions of this file are copyright Rebirth contributors and licensed as
3  * described in COPYING.txt.
4  * Portions of this file are copyright Parallax Software and licensed
5  * according to the Parallax license below.
6  * See COPYING.txt for license details.
7 
8 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
9 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
10 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
11 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
12 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
13 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
14 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
15 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
16 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
17 COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
18 */
19 
20 /*
21  *
22  * Editor lighting functions.
23  *
24  */
25 
26 #include <stdio.h>
27 #include "inferno.h"
28 #include "segment.h"
29 #include "editor.h"
30 #include "editor/esegment.h"
31 #include "seguvs.h"
32 #include "wall.h"
33 #include "textures.h"
34 #include "maths.h"
35 #include "dxxerror.h"
36 #include "kdefs.h"
37 #include "gameseg.h"
38 #include "texmap.h"
39 
40 #include "compiler-range_for.h"
41 #include "d_range.h"
42 
43 // -----------------------------------------------------------------------------
44 //	Return light intensity at an instance of a vertex on a side in a segment.
get_light_intensity(const unique_side & s,const uint_fast32_t vert)45 static fix get_light_intensity(const unique_side &s, const uint_fast32_t vert)
46 {
47 	Assert(vert <= 3);
48 	return s.uvls[vert].l;
49 }
50 
get_light_intensity(const unique_segment & segp,const uint_fast32_t sidenum,const uint_fast32_t vert)51 static fix get_light_intensity(const unique_segment &segp, const uint_fast32_t sidenum, const uint_fast32_t vert)
52 {
53 	Assert(sidenum <= MAX_SIDES_PER_SEGMENT);
54 	return get_light_intensity(segp.sides[sidenum], vert);
55 }
56 
clamp_light_intensity(const fix intensity)57 static fix clamp_light_intensity(const fix intensity)
58 {
59 	if (intensity < MIN_LIGHTING_VALUE)
60 		return MIN_LIGHTING_VALUE;
61 	if (intensity > MAX_LIGHTING_VALUE)
62 		return MAX_LIGHTING_VALUE;
63 	return intensity;
64 }
65 
66 // -----------------------------------------------------------------------------
67 //	Set light intensity at a vertex, saturating in .5 to 15.5
set_light_intensity(unique_side & s,const uint_fast32_t vert,const fix intensity)68 static void set_light_intensity(unique_side &s, const uint_fast32_t vert, const fix intensity)
69 {
70 	Assert(vert <= 3);
71 	s.uvls[vert].l = clamp_light_intensity(intensity);
72 	Update_flags |= UF_WORLD_CHANGED;
73 }
74 
set_light_intensity(unique_segment & segp,const uint_fast32_t sidenum,const uint_fast32_t vert,const fix intensity)75 static void set_light_intensity(unique_segment &segp, const uint_fast32_t sidenum, const uint_fast32_t vert, const fix intensity)
76 {
77 	Assert(sidenum <= MAX_SIDES_PER_SEGMENT);
78 	set_light_intensity(segp.sides[sidenum], vert, intensity);
79 }
80 
81 // -----------------------------------------------------------------------------
82 //	Add light intensity to a vertex, saturating in .5 to 15.5
add_light_intensity_all_verts(unique_side & s,const fix intensity)83 static void add_light_intensity_all_verts(unique_side &s, const fix intensity)
84 {
85 	range_for (auto &u, s.uvls)
86 		u.l = clamp_light_intensity(u.l + intensity);
87 	Update_flags |= UF_WORLD_CHANGED;
88 }
89 
90 // -----------------------------------------------------------------------------
91 //	Recursively apply light to segments.
92 //	If current side is a wall, apply light there.
93 //	If not a wall, apply light to child through that wall.
94 //	Notes:
95 //		It is possible to enter a segment twice by taking different paths.  It is easy
96 //		to prevent this by maintaining a list of visited segments, but it is important
97 //		to reach segments with the greatest light intensity.  This can be done by doing
98 //		a breadth-first-search, or by storing the applied intensity with a visited segment,
99 //		and if the current intensity is brighter, then apply the difference between it and
100 //		the previous intensity.
101 //		Note that it is also possible to visit the original light-casting segment, for example
102 //		going from segment 0 to 2, then from 2 to 0.  This is peculiar and probably not
103 //		desired, but not entirely invalid.  2 reflects some light back to 0.
apply_light_intensity(const csmusegment segp,const unsigned sidenum,fix intensity,const unsigned depth)104 static void apply_light_intensity(const csmusegment segp, const unsigned sidenum, fix intensity, const unsigned depth)
105 {
106 	if (intensity == 0)
107 		return;
108 
109 	auto &Walls = LevelUniqueWallSubsystemState.Walls;
110 	auto &vcwallptr = Walls.vcptr;
111 	const auto wid_result = WALL_IS_DOORWAY(GameBitmaps, Textures, vcwallptr, segp, sidenum);
112 	if (!(wid_result & WALL_IS_DOORWAY_FLAG::rendpast)) {
113 		add_light_intensity_all_verts(segp.u.sides[sidenum], intensity);
114 		return;										// we return because there is a wall here, and light does not shine through walls
115 	}
116 
117 	//	No wall here, so apply light recursively
118 	if (depth < 3) {
119 		intensity /= 3;
120 		if (!intensity)
121 			return;
122 		const csmusegment &&csegp = vmsegptr(segp.s.children[sidenum]);
123 		for (int s=0; s<MAX_SIDES_PER_SEGMENT; s++)
124 			apply_light_intensity(csegp, s, intensity, depth+1);
125 	}
126 
127 }
128 
129 // -----------------------------------------------------------------------------
130 //	Top level recursive function for applying light.
131 //	Calls apply_light_intensity.
132 //	Uses light value on segp:sidenum (tmap_num2 defines light value) and applies
133 //	the associated intensity to segp.  It calls apply_light_intensity to apply intensity/3
134 //	to all neighbors.  apply_light_intensity recursively calls itself to apply light to
135 //	subsequent neighbors (and forming loops, see above).
propagate_light_intensity(const csmusegment segp,const unsigned sidenum)136 static void propagate_light_intensity(const csmusegment segp, const unsigned sidenum)
137 {
138 	fix		intensity;
139 
140 	intensity = 0;
141 	auto &us = segp.u.sides[sidenum];
142 	auto &TmapInfo = LevelUniqueTmapInfoState.TmapInfo;
143 	const auto texmap = get_texture_index(us.tmap_num);
144 	intensity += TmapInfo[texmap].lighting;
145 	intensity += TmapInfo[get_texture_index(us.tmap_num2)].lighting;
146 
147 	if (intensity > 0) {
148 		add_light_intensity_all_verts(us, intensity);
149 
150 		//	Now, for all sides which are not the same as sidenum (the side casting the light),
151 		//	add a light value to them (if they have no children, ie, they have a wall there).
152 		for (int s=0; s<MAX_SIDES_PER_SEGMENT; s++)
153 			if (s != sidenum)
154 				apply_light_intensity(segp, s, intensity/2, 1);
155 	}
156 
157 }
158 
159 
160 // -----------------------------------------------------------------------------
161 //	Highest level function, bound to a key.  Apply ambient light to all segments based
162 //	on user-defined light sources.
LightAmbientLighting()163 int LightAmbientLighting()
164 {
165 	range_for (const auto &&segp, vmsegptr)
166 	{
167 		for (int side=0;side<MAX_SIDES_PER_SEGMENT;side++)
168 			propagate_light_intensity(segp, side);
169 	}
170 	return 0;
171 }
172 
173 
174 // -----------------------------------------------------------------------------
LightSelectNextVertex(void)175 int LightSelectNextVertex(void)
176 {
177 	Curvert++;
178 	if (Curvert >= 4)
179 		Curvert = 0;
180 
181 	Update_flags |= UF_WORLD_CHANGED;
182 
183 	return	1;
184 }
185 
186 // -----------------------------------------------------------------------------
LightSelectNextEdge(void)187 int LightSelectNextEdge(void)
188 {
189 	Curedge++;
190 	if (Curedge >= 4)
191 		Curedge = 0;
192 
193 	Update_flags |= UF_WORLD_CHANGED;
194 
195 	return	1;
196 }
197 
198 // -----------------------------------------------------------------------------
199 //	Copy intensity from current vertex to all other vertices on side.
LightCopyIntensity(void)200 int LightCopyIntensity(void)
201 {
202 	int	intensity;
203 
204 	unique_segment &segp = Cursegp;
205 	intensity = get_light_intensity(segp, Curside, Curvert);
206 
207 	range_for (const int v, xrange(4u))
208 		if (v != Curvert)
209 			set_light_intensity(segp, Curside, v, intensity);
210 
211 	return	1;
212 }
213 
214 // -----------------------------------------------------------------------------
215 //	Copy intensity from current vertex to all other vertices on side.
LightCopyIntensitySegment(void)216 int LightCopyIntensitySegment(void)
217 {
218 	int	intensity;
219 
220 	unique_segment &segp = Cursegp;
221 	intensity = get_light_intensity(segp, Curside, Curvert);
222 
223 	for (int s=0; s<MAX_SIDES_PER_SEGMENT; s++)
224 		range_for (const int v, xrange(4u))
225 			if ((s != Curside) || (v != Curvert))
226 				set_light_intensity(segp, s, v, intensity);
227 
228 	return	1;
229 }
230 
231 // -----------------------------------------------------------------------------
LightDecreaseLightVertex(void)232 int LightDecreaseLightVertex(void)
233 {
234 	unique_segment &segp = Cursegp;
235 	set_light_intensity(segp, Curside, Curvert, get_light_intensity(segp, Curside, Curvert) - F1_0 / NUM_LIGHTING_LEVELS);
236 
237 	return	1;
238 }
239 
240 // -----------------------------------------------------------------------------
LightIncreaseLightVertex(void)241 int LightIncreaseLightVertex(void)
242 {
243 	unique_segment &segp = Cursegp;
244 	set_light_intensity(segp, Curside, Curvert, get_light_intensity(segp, Curside, Curvert) + F1_0 / NUM_LIGHTING_LEVELS);
245 
246 	return	1;
247 }
248 
249 // -----------------------------------------------------------------------------
LightDecreaseLightSide(void)250 int LightDecreaseLightSide(void)
251 {
252 	unique_segment &segp = Cursegp;
253 	range_for (const int v, xrange(4u))
254 		set_light_intensity(segp, Curside, v, get_light_intensity(segp, Curside, v) - F1_0 / NUM_LIGHTING_LEVELS);
255 
256 	return	1;
257 }
258 
259 // -----------------------------------------------------------------------------
LightIncreaseLightSide(void)260 int LightIncreaseLightSide(void)
261 {
262 	unique_segment &segp = Cursegp;
263 	range_for (const int v, xrange(4u))
264 		set_light_intensity(segp, Curside, v, get_light_intensity(segp, Curside, v) + F1_0 / NUM_LIGHTING_LEVELS);
265 
266 	return	1;
267 }
268 
269 // -----------------------------------------------------------------------------
LightDecreaseLightSegment(void)270 int LightDecreaseLightSegment(void)
271 {
272 	unique_segment &segp = Cursegp;
273 	for (int s=0; s<MAX_SIDES_PER_SEGMENT; s++)
274 		range_for (const int v, xrange(4u))
275 			set_light_intensity(segp, s, v, get_light_intensity(segp, s, v) - F1_0 / NUM_LIGHTING_LEVELS);
276 
277 	return	1;
278 }
279 
280 // -----------------------------------------------------------------------------
LightIncreaseLightSegment(void)281 int LightIncreaseLightSegment(void)
282 {
283 	unique_segment &segp = Cursegp;
284 	for (int s=0; s<MAX_SIDES_PER_SEGMENT; s++)
285 		range_for (const int v, xrange(4u))
286 			set_light_intensity(segp, s, v, get_light_intensity(segp, s, v) + F1_0 / NUM_LIGHTING_LEVELS);
287 
288 	return	1;
289 }
290 
291 // -----------------------------------------------------------------------------
LightSetDefault(void)292 int LightSetDefault(void)
293 {
294 	unique_segment &segp = Cursegp;
295 	range_for (const int v, xrange(4u))
296 		set_light_intensity(segp, Curside, v, DEFAULT_LIGHTING);
297 
298 	return	1;
299 }
300 
301 
302 // -----------------------------------------------------------------------------
LightSetMaximum(void)303 int LightSetMaximum(void)
304 {
305 	unique_segment &segp = Cursegp;
306 	range_for (const int v, xrange(4u))
307 		set_light_intensity(segp, Curside, v, (NUM_LIGHTING_LEVELS - 1) * F1_0 / NUM_LIGHTING_LEVELS);
308 
309 	return	1;
310 }
311 
312 
313 // -----------------------------------------------------------------------------
LightSetDefaultAll(void)314 int LightSetDefaultAll(void)
315 {
316 
317 	assign_default_lighting_all();
318 
319 	Update_flags |= UF_WORLD_CHANGED;
320 
321 	return	1;
322 }
323 
324 
325 
326