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  * u,v coordinate computation for segment faces
23  *
24  */
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <math.h>
30 #include <string.h>
31 #include "inferno.h"
32 #include "segment.h"
33 #include "editor/editor.h"
34 #include "editor/esegment.h"
35 #include "gameseg.h"
36 #include "maths.h"
37 #include "dxxerror.h"
38 #include "wall.h"
39 #include "editor/kdefs.h"
40 #include "bm.h"		//	Needed for TmapInfo
41 #include "fvi.h"
42 #include "seguvs.h"
43 
44 #include "compiler-range_for.h"
45 #include "d_enumerate.h"
46 #include "d_range.h"
47 #include "d_zip.h"
48 
49 namespace dcx {
50 namespace {
51 static void cast_all_light_in_mine(int quick_flag);
52 }
53 }
54 //--rotate_uvs-- vms_vector Rightvec;
55 
56 #define	MAX_LIGHT_SEGS 16
57 
58 namespace {
59 
60 //	---------------------------------------------------------------------------------------------
61 //	Scan all polys in all segments, return average light value for vnum.
62 //	segs = output array for segments containing vertex, terminated by -1.
get_average_light_at_vertex(const vertnum_t vnum,segnum_t * segs)63 static fix get_average_light_at_vertex(const vertnum_t vnum, segnum_t *segs)
64 {
65 	fix	total_light;
66 	int	num_occurrences;
67 //	#ifndef NDEBUG //Removed this ifdef because the version of Assert that I used to get it to compile doesn't work without this symbol. -KRB
68         segnum_t   *original_segs;
69 
70         original_segs = segs;
71 //	#endif
72 
73 
74 	num_occurrences = 0;
75 	total_light = 0;
76 
77 	range_for (const auto &&segp, vcsegptridx)
78 	{
79 		auto e = end(segp->verts);
80 		auto relvnum = std::distance(std::find(begin(segp->verts), e, vnum), e);
81 		if (relvnum < MAX_VERTICES_PER_SEGMENT) {
82 
83 			*segs++ = segp;
84 			Assert(segs - original_segs < MAX_LIGHT_SEGS);
85 			(void)original_segs;
86 
87 			for (const auto &&[child_segnum, uside, vp] : zip(segp->children, segp->unique_segment::sides, Side_to_verts))
88 			{
89 				if (!IS_CHILD(child_segnum))
90 				{
91 					const auto vb = begin(vp);
92 					const auto ve = end(vp);
93 					const auto vi = std::find(vb, ve, relvnum);
94 					if (vi != ve)
95 					{
96 						const auto v = std::distance(vb, vi);
97 						total_light += uside.uvls[v].l;
98 							num_occurrences++;
99 						}
100 				}	// end if
101 			}	// end sidenum
102 		}
103 	}	// end segnum
104 
105 	*segs = segment_none;
106 
107 	if (num_occurrences)
108 		return total_light/num_occurrences;
109 	else
110 		return 0;
111 
112 }
113 
set_average_light_at_vertex(vertnum_t vnum)114 static void set_average_light_at_vertex(vertnum_t vnum)
115 {
116 	segnum_t	Segment_indices[MAX_LIGHT_SEGS];
117 	int	segind;
118 
119 	fix average_light;
120 
121 	average_light = get_average_light_at_vertex(vnum, Segment_indices);
122 
123 	if (!average_light)
124 		return;
125 
126 	segind = 0;
127 	while (Segment_indices[segind] != segment_none) {
128 		auto segnum = Segment_indices[segind++];
129 
130 		auto &ssegp = *vcsegptr(segnum);
131 		unique_segment &usegp = *vmsegptr(segnum);
132 
133 		for (const auto &&[relvnum, vert] : enumerate(ssegp.verts))
134 			if (vert == vnum)
135 			{
136 			for (const auto &&[child_segnum, sidep, vp] : zip(ssegp.children, usegp.sides, Side_to_verts))
137 			{
138 				if (!IS_CHILD(child_segnum))
139 				{
140 					const auto vb = begin(vp);
141 					const auto ve = end(vp);
142 					const auto vi = std::find(vb, ve, relvnum);
143 					if (vi != ve)
144 					{
145 						const auto v = std::distance(vb, vi);
146 						sidep.uvls[v].l = average_light;
147 					}
148 				}	// end if
149 			}	// end sidenum
150 				break;
151 			}	// end if
152 	}	// end while
153 
154 	Update_flags |= UF_WORLD_CHANGED;
155 }
156 
set_average_light_on_side(const shared_segment & segp,const unsigned sidenum)157 static void set_average_light_on_side(const shared_segment &segp, const unsigned sidenum)
158 {
159 	if (!IS_CHILD(segp.children[sidenum]))
160 		range_for (const auto v, Side_to_verts[sidenum])
161 		{
162 			set_average_light_at_vertex(segp.verts[v]);
163 		}
164 }
165 
166 }
167 
set_average_light_on_curside(void)168 int set_average_light_on_curside(void)
169 {
170 	set_average_light_on_side(Cursegp, Curside);
171 	return 0;
172 }
173 
set_average_light_on_all(void)174 int set_average_light_on_all(void)
175 {
176 	Doing_lighting_hack_flag = 1;
177 	cast_all_light_in_mine(0);
178 	Doing_lighting_hack_flag = 0;
179 	Update_flags |= UF_WORLD_CHANGED;
180 
181 //	int seg, side;
182 
183 //	for (seg=0; seg<=Highest_segment_index; seg++)
184 //		for (side=0; side<MAX_SIDES_PER_SEGMENT; side++)
185 //			if (Segments[seg].segnum != -1)
186 //				set_average_light_on_side(&Segments[seg], side);
187 	return 0;
188 }
189 
set_average_light_on_all_quick(void)190 int set_average_light_on_all_quick(void)
191 {
192 	cast_all_light_in_mine(1);
193 	Update_flags |= UF_WORLD_CHANGED;
194 
195 	return 0;
196 }
197 
198 namespace {
199 
200 //	---------------------------------------------------------------------------------------------
201 //	Given a polygon, compress the uv coordinates so that they are as close to 0 as possible.
202 //	Do this by adding a constant u and v to each uv pair.
compress_uv_coordinates(std::array<uvl,4> & uvls)203 static void compress_uv_coordinates(std::array<uvl, 4> &uvls)
204 {
205 	fix	uc, vc;
206 
207 	uc = 0;
208 	vc = 0;
209 
210 	range_for (auto &uvl, uvls)
211 	{
212 		uc += uvl.u;
213 		vc += uvl.v;
214 	}
215 
216 	uc /= 4;
217 	vc /= 4;
218 	uc = uc & 0xffff0000;
219 	vc = vc & 0xffff0000;
220 
221 	range_for (auto &uvl, uvls)
222 	{
223 		uvl.u -= uc;
224 		uvl.v -= vc;
225 	}
226 }
227 
assign_default_lighting_on_side(std::array<uvl,4> & uvls)228 static void assign_default_lighting_on_side(std::array<uvl, 4> &uvls)
229 {
230 	range_for (auto &uvl, uvls)
231 		uvl.l = DEFAULT_LIGHTING;
232 }
233 
assign_default_lighting(unique_segment & segp)234 static void assign_default_lighting(unique_segment &segp)
235 {
236 	range_for (auto &side, segp.sides)
237 		assign_default_lighting_on_side(side.uvls);
238 }
239 
240 }
241 
assign_default_lighting_all(void)242 void assign_default_lighting_all(void)
243 {
244 	range_for (const auto &&segp, vmsegptr)
245 	{
246 		if (segp->segnum != segment_none)
247 			assign_default_lighting(segp);
248 	}
249 }
250 
251 namespace {
252 
253 //	---------------------------------------------------------------------------------------------
validate_uv_coordinates(unique_segment & segp)254 static void validate_uv_coordinates(unique_segment &segp)
255 {
256 	range_for (auto &side, segp.sides)
257 	{
258 		compress_uv_coordinates(side.uvls);
259 	}
260 
261 }
262 
263 #ifdef __WATCOMC__
264 fix zhypot(fix a,fix b);
265 #pragma aux zhypot parm [eax] [ebx] value [eax] modify [eax ebx ecx edx] = \
266 	"imul	eax" \
267 	"xchg eax,ebx" \
268 	"mov	ecx,edx" \
269 	"imul eax" \
270 	"add	eax,ebx" \
271 	"adc	edx,ecx" \
272 	"call	quad_sqrt";
273 #else
zhypot(fix a,fix b)274 static fix zhypot(fix a,fix b) {
275 	double x = static_cast<double>(a) / 65536;
276 	double y = static_cast<double>(b) / 65536;
277 	return static_cast<long>(sqrt(x * x + y * y) * 65536);
278 }
279 #endif
280 
281 }
282 
283 //	---------------------------------------------------------------------------------------------
284 //	Assign lighting value to side, a function of the normal vector.
assign_light_to_side(unique_side & s)285 void assign_light_to_side(unique_side &s)
286 {
287 	range_for (auto &v, s.uvls)
288 		v.l = DEFAULT_LIGHTING;
289 }
290 
291 fix	Stretch_scale_x = F1_0;
292 fix	Stretch_scale_y = F1_0;
293 
294 namespace {
295 
296 //	---------------------------------------------------------------------------------------------
297 //	Given u,v coordinates at two vertices, assign u,v coordinates to other two vertices on a side.
298 //	(Actually, assign them to the coordinates in the faces.)
299 //	va, vb = face-relative vertex indices corresponding to uva, uvb.  Ie, they are always in 0..3 and should be looked up in
300 //	Side_to_verts[side] to get the segment relative index.
assign_uvs_to_side(fvcvertptr & vcvertptr,const vmsegptridx_t segp,int sidenum,const uvl & uva,const uvl & uvb,int va,int vb)301 static void assign_uvs_to_side(fvcvertptr &vcvertptr, const vmsegptridx_t segp, int sidenum, const uvl &uva, const uvl &uvb, int va, int vb)
302 {
303 	int			vlo,vhi;
304 	std::array<uvl, 4> uvls;
305 	uvl ruvmag,fuvmag;
306 	const uvl *uvlo, *uvhi;
307 	fix			fmag,mag01;
308 	Assert( (va<4) && (vb<4) );
309 	Assert((abs(va - vb) == 1) || (abs(va - vb) == 3));		// make sure the verticies specify an edge
310 
311 	auto &vp = Side_to_verts[sidenum];
312 
313 	// We want vlo precedes vhi, ie vlo < vhi, or vlo = 3, vhi = 0
314 	if (va == ((vb + 1) % 4)) {		// va = vb + 1
315 		vlo = vb;
316 		vhi = va;
317 		uvlo = &uvb;
318 		uvhi = &uva;
319 	} else {
320 		vlo = va;
321 		vhi = vb;
322 		uvlo = &uva;
323 		uvhi = &uvb;
324 	}
325 
326 	Assert(((vlo+1) % 4) == vhi);	// If we are on an edge, then uvhi is one more than uvlo (mod 4)
327 	uvls[vlo] = *uvlo;
328 	uvls[vhi] = *uvhi;
329 
330 	// Now we have vlo precedes vhi, compute vertices ((vhi+1) % 4) and ((vhi+2) % 4)
331 
332 	// Assign u,v scale to a unit length right vector.
333 	fmag = zhypot(uvhi->v - uvlo->v, uvhi->u - uvlo->u);
334 	if (fmag < 64) {		// this is a fix, so 64 = 1/1024
335 		ruvmag.u = F1_0*256;
336 		ruvmag.v = F1_0*256;
337 		fuvmag.u = F1_0*256;
338 		fuvmag.v = F1_0*256;
339 	} else {
340 		ruvmag.u = uvhi->v - uvlo->v;
341 		ruvmag.v = uvlo->u - uvhi->u;
342 
343 		fuvmag.u = uvhi->u - uvlo->u;
344 		fuvmag.v = uvhi->v - uvlo->v;
345 	}
346 
347 	const auto v0 = segp->verts[vp[vlo]];
348 	const auto v1 = segp->verts[vp[vhi]];
349 	const auto v2 = segp->verts[vp[(vhi+1)%4]];
350 	const auto v3 = segp->verts[vp[(vhi+2)%4]];
351 
352 	//	Compute right vector by computing orientation matrix from:
353 	//		forward vector = vlo:vhi
354 	//		  right vector = vlo:(vhi+2) % 4
355 	const auto &&vp0 = vcvertptr(v0);
356 	const auto &vv1v0 = vm_vec_sub(vcvertptr(v1), vp0);
357 	mag01 = vm_vec_mag(vv1v0);
358 	mag01 = fixmul(mag01, (va == 0 || va == 2) ? Stretch_scale_x : Stretch_scale_y);
359 
360 	if (unlikely(mag01 < F1_0/1024))
361 		editor_status_fmt("U, V bogosity in segment #%hu, probably on side #%i.  CLEAN UP YOUR MESS!", static_cast<uint16_t>(segp), sidenum);
362 	else {
363 		struct frvec {
364 			vms_vector fvec, rvec;
365 			frvec(const vms_vector &tfvec, const vms_vector &trvec) {
366 				if ((tfvec.x == 0 && tfvec.y == 0 && tfvec.z == 0) ||
367 					(trvec.x == 0 && trvec.y == 0 && trvec.z == 0))
368 				{
369 					fvec = vmd_identity_matrix.fvec;
370 					rvec = vmd_identity_matrix.rvec;
371 				}
372 				else
373 				{
374 					const auto &m = vm_vector_2_matrix(tfvec, nullptr, &trvec);
375 					fvec = m.fvec;
376 					rvec = m.rvec;
377 				}
378 				vm_vec_negate(rvec);
379 			}
380 		};
381 		const auto &vv3v0 = vm_vec_sub(vcvertptr(v3), vp0);
382 		const frvec fr{
383 			vv1v0,
384 			vv3v0
385 		};
386 		const auto build_uvl = [&](const vms_vector &tvec, const uvl &uvi) {
387 			const auto drt = vm_vec_dot(fr.rvec, tvec);
388 			const auto dft = vm_vec_dot(fr.fvec, tvec);
389 			return uvl{
390 				uvi.u +
391 					fixdiv(fixmul(ruvmag.u, drt), mag01) +
392 					fixdiv(fixmul(fuvmag.u, dft), mag01),
393 				uvi.v +
394 					fixdiv(fixmul(ruvmag.v, drt), mag01) +
395 					fixdiv(fixmul(fuvmag.v, dft), mag01),
396 				uvi.l
397 			};
398 		};
399 		uvls[(vhi + 1) % 4] = build_uvl(vm_vec_sub(vcvertptr(v2), vcvertptr(v1)), *uvhi);
400 		uvls[(vhi + 2) % 4] = build_uvl(vv3v0, *uvlo);
401 		//	For all faces in side, copy uv coordinates from uvs array to face.
402 		segp->unique_segment::sides[sidenum].uvls = uvls;
403 	}
404 }
405 
406 }
407 
408 
409 int Vmag = VMAG;
410 
411 namespace dsx {
412 
413 // -----------------------------------------------------------------------------------------------------------
414 //	Assign default uvs to side.
415 //	This means:
416 //		v0 = 0,0
417 //		v1 = k,0 where k is 3d size dependent
418 //	v2, v3 assigned by assign_uvs_to_side
assign_default_uvs_to_side(const vmsegptridx_t segp,const unsigned side)419 void assign_default_uvs_to_side(const vmsegptridx_t segp, const unsigned side)
420 {
421 	auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
422 	auto &Vertices = LevelSharedVertexState.get_vertices();
423 	uvl			uv0,uv1;
424 	uv0.u = 0;
425 	uv0.v = 0;
426 	auto &vp = Side_to_verts[side];
427 	uv1.u = 0;
428 	auto &vcvertptr = Vertices.vcptr;
429 	uv1.v = Num_tilings * fixmul(Vmag, vm_vec_dist(vcvertptr(segp->verts[vp[1]]), vcvertptr(segp->verts[vp[0]])));
430 
431 	assign_uvs_to_side(vcvertptr, segp, side, uv0, uv1, 0, 1);
432 }
433 
434 // -----------------------------------------------------------------------------------------------------------
435 //	Assign default uvs to side.
436 //	This means:
437 //		v0 = 0,0
438 //		v1 = k,0 where k is 3d size dependent
439 //	v2, v3 assigned by assign_uvs_to_side
stretch_uvs_from_curedge(const vmsegptridx_t segp,int side)440 void stretch_uvs_from_curedge(const vmsegptridx_t segp, int side)
441 {
442 	auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
443 	auto &Vertices = LevelSharedVertexState.get_vertices();
444 	uvl			uv0,uv1;
445 	int			v0, v1;
446 
447 	v0 = Curedge;
448 	v1 = (v0 + 1) % 4;
449 
450 	auto &uvls = segp->unique_segment::sides[side].uvls;
451 	uv0.u = uvls[v0].u;
452 	uv0.v = uvls[v0].v;
453 
454 	uv1.u = uvls[v1].u;
455 	uv1.v = uvls[v1].v;
456 
457 	auto &vcvertptr = Vertices.vcptr;
458 	assign_uvs_to_side(vcvertptr, segp, side, uv0, uv1, v0, v1);
459 }
460 
461 // --------------------------------------------------------------------------------------------------------------
462 //	Assign default uvs to a segment.
assign_default_uvs_to_segment(const vmsegptridx_t segp)463 void assign_default_uvs_to_segment(const vmsegptridx_t segp)
464 {
465 	range_for (const uint_fast32_t s, xrange(MAX_SIDES_PER_SEGMENT))
466 	{
467 		assign_default_uvs_to_side(segp,s);
468 		assign_light_to_side(segp, s);
469 	}
470 }
471 
472 
473 // -- mk021394 -- // --------------------------------------------------------------------------------------------------------------
474 // -- mk021394 -- //	Find the face:poly:vertex index in base_seg:base_common_side which is segment relative vertex v1
475 // -- mk021394 -- //	This very specific routine is subsidiary to med_assign_uvs_to_side.
476 // -- mk021394 -- void get_face_and_vert(segment *base_seg, int base_common_side, int v1, int *ff, int *vv, int *pi)
477 // -- mk021394 -- {
478 // -- mk021394 -- 	int	p,f,v;
479 // -- mk021394 --
480 // -- mk021394 -- 	for (f=0; f<base_seg->sides[base_common_side].num_faces; f++) {
481 // -- mk021394 -- 		face *fp = &base_seg->sides[base_common_side].faces[f];
482 // -- mk021394 -- 		for (p=0; p<fp->num_polys; p++) {
483 // -- mk021394 -- 			poly *pp = &fp->polys[p];
484 // -- mk021394 -- 			for (v=0; v<pp->num_vertices; v++)
485 // -- mk021394 -- 				if (pp->verts[v] == v1) {
486 // -- mk021394 -- 					*ff = f;
487 // -- mk021394 -- 					*vv = v;
488 // -- mk021394 -- 					*pi = p;
489 // -- mk021394 -- 					return;
490 // -- mk021394 -- 				}
491 // -- mk021394 -- 		}
492 // -- mk021394 -- 	}
493 // -- mk021394 --
494 // -- mk021394 -- 	Assert(0);	// Error -- Couldn't find face:vertex which matched vertex v1 on base_seg:base_common_side
495 // -- mk021394 -- }
496 
497 // -- mk021394 -- // --------------------------------------------------------------------------------------------------------------
498 // -- mk021394 -- //	Find the vertex index in base_seg:base_common_side which is segment relative vertex v1
499 // -- mk021394 -- //	This very specific routine is subsidiary to med_assign_uvs_to_side.
500 // -- mk021394 -- void get_side_vert(segment *base_seg,int base_common_side,int v1,int *vv)
501 // -- mk021394 -- {
502 // -- mk021394 -- 	int	p,f,v;
503 // -- mk021394 --
504 // -- mk021394 -- 	Assert((base_seg->sides[base_common_side].tri_edge == 0) || (base_seg->sides[base_common_side].tri_edge == 1));
505 // -- mk021394 -- 	Assert(base_seg->sides[base_common_side].num_faces <= 2);
506 // -- mk021394 --
507 // -- mk021394 -- 	for (f=0; f<base_seg->sides[base_common_side].num_faces; f++) {
508 // -- mk021394 -- 		face *fp = &base_seg->sides[base_common_side].faces[f];
509 // -- mk021394 -- 		for (p=0; p<fp->num_polys; p++) {
510 // -- mk021394 -- 			poly	*pp = &fp->polys[p];
511 // -- mk021394 -- 			for (v=0; v<pp->num_vertices; v++)
512 // -- mk021394 -- 				if (pp->verts[v] == v1) {
513 // -- mk021394 -- 					if (pp->num_vertices == 4) {
514 // -- mk021394 -- 						*vv = v;
515 // -- mk021394 -- 						return;
516 // -- mk021394 -- 					}
517 // -- mk021394 --
518 // -- mk021394 -- 					if (base_seg->sides[base_common_side].tri_edge == 0) {	// triangulated 012, 023, so if f==0, *vv = v, if f==1, *vv = v if v=0, else v+1
519 // -- mk021394 -- 						if ((f == 1) && (v > 0))
520 // -- mk021394 -- 							v++;
521 // -- mk021394 -- 						*vv = v;
522 // -- mk021394 -- 						return;
523 // -- mk021394 -- 					} else {								// triangulated 013, 123
524 // -- mk021394 -- 						if (f == 0) {
525 // -- mk021394 -- 							if (v == 2)
526 // -- mk021394 -- 								v++;
527 // -- mk021394 -- 						} else
528 // -- mk021394 -- 							v++;
529 // -- mk021394 -- 						*vv = v;
530 // -- mk021394 -- 						return;
531 // -- mk021394 -- 					}
532 // -- mk021394 -- 				}
533 // -- mk021394 -- 		}
534 // -- mk021394 -- 	}
535 // -- mk021394 --
536 // -- mk021394 -- 	Assert(0);	// Error -- Couldn't find face:vertex which matched vertex v1 on base_seg:base_common_side
537 // -- mk021394 -- }
538 
539 //--rotate_uvs-- // --------------------------------------------------------------------------------------------------------------
540 //--rotate_uvs-- //	Rotate uvl coordinates uva, uvb about their center point by heading
541 //--rotate_uvs-- void rotate_uvs(uvl *uva, uvl *uvb, vms_vector *rvec)
542 //--rotate_uvs-- {
543 //--rotate_uvs-- 	uvl	uvc, uva1, uvb1;
544 //--rotate_uvs--
545 //--rotate_uvs-- 	uvc.u = (uva->u + uvb->u)/2;
546 //--rotate_uvs-- 	uvc.v = (uva->v + uvb->v)/2;
547 //--rotate_uvs--
548 //--rotate_uvs-- 	uva1.u = fixmul(uva->u - uvc.u, rvec->x) - fixmul(uva->v - uvc.v, rvec->z);
549 //--rotate_uvs-- 	uva1.v = fixmul(uva->u - uvc.u, rvec->z) + fixmul(uva->v - uvc.v, rvec->x);
550 //--rotate_uvs--
551 //--rotate_uvs-- 	uva->u = uva1.u + uvc.u;
552 //--rotate_uvs-- 	uva->v = uva1.v + uvc.v;
553 //--rotate_uvs--
554 //--rotate_uvs-- 	uvb1.u = fixmul(uvb->u - uvc.u, rvec->x) - fixmul(uvb->v - uvc.v, rvec->z);
555 //--rotate_uvs-- 	uvb1.v = fixmul(uvb->u - uvc.u, rvec->z) + fixmul(uvb->v - uvc.v, rvec->x);
556 //--rotate_uvs--
557 //--rotate_uvs-- 	uvb->u = uvb1.u + uvc.u;
558 //--rotate_uvs-- 	uvb->v = uvb1.v + uvc.v;
559 //--rotate_uvs-- }
560 
561 namespace {
562 
563 // --------------------------------------------------------------------------------------------------------------
564 //	Assign u,v coordinates to con_seg, con_common_side from base_seg, base_common_side
565 //	They are connected at the edge defined by the vertices abs_id1, abs_id2.
med_assign_uvs_to_side(const vmsegptridx_t con_seg,const unsigned con_common_side,const cscusegment base_seg,const unsigned base_common_side,const vertnum_t abs_id1,const vertnum_t abs_id2)566 static void med_assign_uvs_to_side(const vmsegptridx_t con_seg, const unsigned con_common_side, const cscusegment base_seg, const unsigned base_common_side, const vertnum_t abs_id1, const vertnum_t abs_id2)
567 {
568 	auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
569 	auto &Vertices = LevelSharedVertexState.get_vertices();
570         int             bv1,bv2, vv1, vv2;
571         int             cv1=0, cv2=0;
572 
573 	bv1 = -1;	bv2 = -1;
574 
575 	// Find which vertices in segment match abs_id1, abs_id2
576 	for (const auto &&[v, b, c] : enumerate(zip(base_seg.s.verts, con_seg->verts)))
577 	{
578 		if (b == abs_id1)
579 			bv1 = v;
580 		if (b == abs_id2)
581 			bv2 = v;
582 		if (c == abs_id1)
583 			cv1 = v;
584 		if (c == abs_id2)
585 			cv2 = v;
586 	}
587 
588 	//	Now, bv1, bv2 are segment relative vertices in base segment which are the same as absolute vertices abs_id1, abs_id2
589 	//	     cv1, cv2 are segment relative vertices in conn segment which are the same as absolute vertices abs_id1, abs_id2
590 
591 	Assert((bv1 != -1) && (bv2 != -1) && (cv1 != -1) && (cv2 != -1));
592 
593 	//	Now, scan 4 vertices in base side and 4 vertices in connected side.
594 	//	Set uv1, uv2 to uv coordinates from base side which correspond to vertices bv1, bv2.
595 	//	Set vv1, vv2 to relative vertex ids (in 0..3) in connecting side which correspond to cv1, cv2
596 	vv1 = -1;	vv2 = -1;
597 	auto &base_uvls = base_seg.u.sides[base_common_side].uvls;
598 	const uvl *uv1 = nullptr, *uv2 = nullptr;
599 	auto &svbase = Side_to_verts[base_common_side];
600 	auto &svconn = Side_to_verts[con_common_side];
601 	for (auto &&[v, base_uvl, svb, svc] : enumerate(zip(base_uvls, svbase, svconn)))
602 	{
603 		if (bv1 == svb)
604 			uv1 = &base_uvl;
605 		if (bv2 == svb)
606 			uv2 = &base_uvl;
607 		if (cv1 == svc)
608 			vv1 = v;
609 		if (cv2 == svc)
610 			vv2 = v;
611 	}
612 
613 	assert(uv1->u != uv2->u || uv1->v != uv2->v);
614 	Assert( (vv1 != -1) && (vv2 != -1) );
615 	auto &vcvertptr = Vertices.vcptr;
616 	assign_uvs_to_side(vcvertptr, con_seg, con_common_side, *uv1, *uv2, vv1, vv2);
617 }
618 
619 
620 // -----------------------------------------------------------------------------
621 //	Given a base and a connecting segment, a side on each of those segments and two global vertex ids,
622 //	determine which side in each of the segments shares those two vertices.
623 //	This is used to propagate a texture map id to a connecting segment in an expected and desired way.
624 //	Since we can attach any side of a segment to any side of another segment, and do so in each case in
625 //	four different rotations (for a total of 6*6*4 = 144 ways), not having this nifty function will cause
626 //	great confusion.
get_side_ids(const shared_segment & base_seg,const shared_segment & con_seg,int base_side,int con_side,const vertnum_t abs_id1,const vertnum_t abs_id2,int * base_common_side,int * con_common_side)627 static void get_side_ids(const shared_segment &base_seg, const shared_segment &con_seg, int base_side, int con_side, const vertnum_t abs_id1, const vertnum_t abs_id2, int *base_common_side, int *con_common_side)
628 {
629 	*base_common_side = -1;
630 
631 	//	Find side in base segment which contains the two global vertex ids.
632 	for (const auto &&[idx, base_vp] : enumerate(Side_to_verts))
633 	{
634 		if (idx != base_side) {
635 			for (const auto v0 : xrange(4u))
636 			{
637 				auto &verts = base_seg.verts;
638 				if ((verts[static_cast<int>(base_vp[v0])] == abs_id1 && verts[static_cast<int>(base_vp[(v0+1) % 4])] == abs_id2) ||
639 					(verts[static_cast<int>(base_vp[v0])] == abs_id2 && verts[static_cast<int>(base_vp[ (v0+1) % 4])] == abs_id1))
640 				{
641 					Assert(*base_common_side == -1);		// This means two different sides shared the same edge with base_side == impossible!
642 					*base_common_side = idx;
643 				}
644 			}
645 		}
646 	}
647 
648 	// Note: For connecting segment, process vertices in reversed order.
649 	*con_common_side = -1;
650 
651 	//	Find side in connecting segment which contains the two global vertex ids.
652 	for (const auto &&[idx, con_vp] : enumerate(Side_to_verts))
653 	{
654 		if (idx != con_side) {
655 			for (const auto v0 : xrange(4u))
656 			{
657 				auto &verts = con_seg.verts;
658 				if ((verts[static_cast<int>(con_vp[(v0 + 1) % 4])] == abs_id1 && verts[static_cast<int>(con_vp[v0])] == abs_id2) ||
659 					(verts[static_cast<int>(con_vp[(v0 + 1) % 4])] == abs_id2 && verts[static_cast<int>(con_vp[v0])] == abs_id1))
660 				{
661 					Assert(*con_common_side == -1);		// This means two different sides shared the same edge with con_side == impossible!
662 					*con_common_side = idx;
663 				}
664 			}
665 		}
666 	}
667 
668 	Assert((*base_common_side != -1) && (*con_common_side != -1));
669 }
670 
671 // -----------------------------------------------------------------------------
672 //	Propagate texture map u,v coordinates from base_seg:base_side to con_seg:con_side.
673 //	The two vertices abs_id1 and abs_id2 are the only two vertices common to the two sides.
674 //	If uv_only_flag is 1, then don't assign texture map ids, only update the uv coordinates
675 //	If uv_only_flag is -1, then ONLY assign texture map ids, don't update the uv coordinates
propagate_tmaps_to_segment_side(const vcsegptridx_t base_seg,const int base_side,const vmsegptridx_t con_seg,const int con_side,const vertnum_t abs_id1,const vertnum_t abs_id2,const int uv_only_flag)676 static void propagate_tmaps_to_segment_side(const vcsegptridx_t base_seg, const int base_side, const vmsegptridx_t con_seg, const int con_side, const vertnum_t abs_id1, const vertnum_t abs_id2, const int uv_only_flag)
677 {
678 	int		base_common_side,con_common_side;
679 
680 	Assert ((uv_only_flag == -1) || (uv_only_flag == 0) || (uv_only_flag == 1));
681 
682 	// Set base_common_side = side in base_seg which contains edge abs_id1:abs_id2
683 	// Set con_common_side = side in con_seg which contains edge abs_id1:abs_id2
684 	if (base_seg != con_seg)
685 		get_side_ids(base_seg, con_seg, base_side, con_side, abs_id1, abs_id2, &base_common_side, &con_common_side);
686 	else {
687 		base_common_side = base_side;
688 		con_common_side = con_side;
689 	}
690 
691 	// Now, all faces in con_seg which are on side con_common_side get their tmap_num set to whatever tmap is assigned
692 	// to whatever face I find which is on side base_common_side.
693 	// First, find tmap_num for base_common_side.  If it doesn't exist (ie, there is a connection there), look at the segment
694 	// that is connected through it.
695 	if (!IS_CHILD(con_seg->children[con_common_side])) {
696 		if (!IS_CHILD(base_seg->children[base_common_side])) {
697 			// There is at least one face here, so get the tmap_num from there.
698 
699 			// Now assign all faces in the connecting segment on side con_common_side to tmap_num.
700 			if ((uv_only_flag == -1) || (uv_only_flag == 0))
701 			{
702 				const auto tmap_num = base_seg->unique_segment::sides[base_common_side].tmap_num;
703 				con_seg->unique_segment::sides[con_common_side].tmap_num = tmap_num;
704 			}
705 
706 			if (uv_only_flag != -1)
707 				med_assign_uvs_to_side(con_seg, con_common_side, base_seg, base_common_side, abs_id1, abs_id2);
708 
709 		} else {			// There are no faces here, there is a connection, trace through the connection.
710 			const auto &&csegp = base_seg.absolute_sibling(base_seg->children[base_common_side]);
711 			auto cside = find_connect_side(base_seg, csegp);
712 			propagate_tmaps_to_segment_side(csegp, cside, con_seg, con_side, abs_id1, abs_id2, uv_only_flag);
713 		}
714 	}
715 
716 }
717 
718 }
719 
720 }
721 
722 namespace dcx {
723 
724 constexpr int8_t Edge_between_sides[MAX_SIDES_PER_SEGMENT][MAX_SIDES_PER_SEGMENT][2] = {
725 //		left		top		right		bottom	back		front
726 	{ {-1,-1}, { 3, 7}, {-1,-1}, { 2, 6}, { 6, 7}, { 2, 3} },	// left
727 	{ { 3, 7}, {-1,-1}, { 0, 4}, {-1,-1}, { 4, 7}, { 0, 3} },	// top
728 	{ {-1,-1}, { 0, 4}, {-1,-1}, { 1, 5}, { 4, 5}, { 0, 1} },	// right
729 	{ { 2, 6}, {-1,-1}, { 1, 5}, {-1,-1}, { 5, 6}, { 1, 2} },	// bottom
730 	{ { 6, 7}, { 4, 7}, { 4, 5}, { 5, 6}, {-1,-1}, {-1,-1} },	// back
731 	{ { 2, 3}, { 0, 3}, { 0, 1}, { 1, 2}, {-1,-1}, {-1,-1} }};	// front
732 
733 }
734 
735 namespace dsx {
736 
737 // -----------------------------------------------------------------------------
738 //	Propagate texture map u,v coordinates to base_seg:back_side from base_seg:some-other-side
739 //	There is no easy way to figure out which side is adjacent to another side along some edge, so we do a bit of searching.
med_propagate_tmaps_to_back_side(const vmsegptridx_t base_seg,int back_side,int uv_only_flag)740 void med_propagate_tmaps_to_back_side(const vmsegptridx_t base_seg, int back_side, int uv_only_flag)
741 {
742         int     v1=0,v2=0;
743 
744 	if (IS_CHILD(base_seg->children[back_side]))
745 		return;		// connection, so no sides here.
746 
747 	//	Scan all sides, look for an occupied side which is not back_side or Side_opposite[back_side]
748 	for (const auto &&[s, ebs] : enumerate(Edge_between_sides))
749 	{
750 		if ((s != back_side) && (s != Side_opposite[back_side])) {
751 			v1 = ebs[back_side][0];
752 			v2 = ebs[back_side][1];
753 			propagate_tmaps_to_segment_side(base_seg, s, base_seg, back_side, base_seg->verts[v1], base_seg->verts[v2], uv_only_flag);
754 			goto found1;
755 		}
756 	}
757 	Assert(0);		// Error -- couldn't find edge != back_side and Side_opposite[back_side]
758 found1: ;
759 	Assert( (v1 != -1) && (v2 != -1));		// This means there was no shared edge between the two sides.
760 
761 	//	Assign an unused tmap id to the back side.
762 	//	Note that this can get undone by the caller if this was not part of a new attach, but a rotation or a scale (which
763 	//	both do attaches).
764 	//	First see if tmap on back side is anywhere else.
765 	if (!uv_only_flag) {
766 		const auto back_side_tmap = base_seg->unique_segment::sides[back_side].tmap_num;
767 		for (const auto &&[idx, value] : enumerate(base_seg->unique_segment::sides))
768 		{
769 			if (idx != back_side)
770 				if (value.tmap_num == back_side_tmap)
771 				{
772 					range_for (const uint_fast32_t tmap_num, xrange(MAX_SIDES_PER_SEGMENT))
773 					{
774 						range_for (const uint_fast32_t ss, xrange(MAX_SIDES_PER_SEGMENT))
775 							if (ss != back_side)
776 								if (base_seg->unique_segment::sides[ss].tmap_num == New_segment.unique_segment::sides[tmap_num].tmap_num)
777 									goto found2;		// current texture map (tmap_num) is used on current (ss) side, so try next one
778 						// Current texture map (tmap_num) has not been used, assign to all faces on back_side.
779 						base_seg->unique_segment::sides[back_side].tmap_num = New_segment.unique_segment::sides[tmap_num].tmap_num;
780 						goto done1;
781 					found2: ;
782 					}
783 				}
784 		}
785 	done1: ;
786 	}
787 
788 }
789 
fix_bogus_uvs_on_side(void)790 int fix_bogus_uvs_on_side(void)
791 {
792 	med_propagate_tmaps_to_back_side(Cursegp, Curside, 1);
793 	return 0;
794 }
795 
796 namespace {
797 
fix_bogus_uvs_on_side1(const vmsegptridx_t sp,const unsigned sidenum,const int uvonly_flag)798 static void fix_bogus_uvs_on_side1(const vmsegptridx_t sp, const unsigned sidenum, const int uvonly_flag)
799 {
800 	auto &uvls = sp->unique_segment::sides[sidenum].uvls;
801 	if (uvls[0].u == 0 && uvls[1].u == 0 && uvls[2].u == 0)
802 	{
803 		med_propagate_tmaps_to_back_side(sp, sidenum, uvonly_flag);
804 	}
805 }
806 
fix_bogus_uvs_seg(const vmsegptridx_t segp)807 static void fix_bogus_uvs_seg(const vmsegptridx_t segp)
808 {
809 	for (const auto &&[idx, value] : enumerate(segp->children))
810 	{
811 		if (!IS_CHILD(value))
812 			fix_bogus_uvs_on_side1(segp, idx, 1);
813 	}
814 }
815 
816 }
817 
fix_bogus_uvs_all(void)818 int fix_bogus_uvs_all(void)
819 {
820 	range_for (const auto &&segp, vmsegptridx)
821 	{
822 		if (segp->segnum != segment_none)
823 			fix_bogus_uvs_seg(segp);
824 	}
825 	return 0;
826 }
827 
828 namespace {
829 
830 // -----------------------------------------------------------------------------
831 //	Segment base_seg is connected through side base_side to segment con_seg on con_side.
832 //	For all walls in con_seg, find the wall in base_seg which shares an edge.  Copy tmap_num
833 //	from that side in base_seg to the wall in con_seg.  If the wall in base_seg is not present
834 //	(ie, there is another segment connected through it), follow the connection through that
835 //	segment to get the wall in the connected segment which shares the edge, and get tmap_num from there.
propagate_tmaps_to_segment_sides(const vcsegptridx_t base_seg,const int base_side,const vmsegptridx_t con_seg,const int con_side,const int uv_only_flag)836 static void propagate_tmaps_to_segment_sides(const vcsegptridx_t base_seg, const int base_side, const vmsegptridx_t con_seg, const int con_side, const int uv_only_flag)
837 {
838 	int		v;
839 
840 	auto &base_vp = Side_to_verts[base_side];
841 
842 	// Do for each edge on connecting face.
843 	for (v=0; v<4; v++) {
844 		const auto abs_id1 = base_seg->verts[static_cast<int>(base_vp[v])];
845 		const auto abs_id2 = base_seg->verts[static_cast<int>(base_vp[(v+1) % 4])];
846 		propagate_tmaps_to_segment_side(base_seg, base_side, con_seg, con_side, abs_id1, abs_id2, uv_only_flag);
847 	}
848 
849 }
850 
851 }
852 
853 // -----------------------------------------------------------------------------
854 //	Propagate texture maps in base_seg to con_seg.
855 //	For each wall in con_seg, find the wall in base_seg which shared an edge.  Copy tmap_num from that
856 //	wall in base_seg to the wall in con_seg.  If the wall in base_seg is not present, then look at the
857 //	segment connected through base_seg through the wall.  The wall with a common edge is the new wall
858 //	of interest.  Continue searching in this way until a wall of interest is present.
med_propagate_tmaps_to_segments(const vcsegptridx_t base_seg,const vmsegptridx_t con_seg,const int uv_only_flag)859 void med_propagate_tmaps_to_segments(const vcsegptridx_t base_seg, const vmsegptridx_t con_seg, const int uv_only_flag)
860 {
861 	for (const auto &&[idx, value] : enumerate(base_seg->children))
862 		if (value == con_seg)
863 			propagate_tmaps_to_segment_sides(base_seg, idx, con_seg, find_connect_side(base_seg, con_seg), uv_only_flag);
864 
865 	const unique_segment &ubase = base_seg;
866 	unique_segment &ucon = con_seg;
867 	ucon.static_light = ubase.static_light;
868 
869 	validate_uv_coordinates(con_seg);
870 }
871 
872 
873 // -------------------------------------------------------------------------------
874 //	Copy texture map uvs from srcseg to destseg.
875 //	If two segments have different face structure (eg, destseg has two faces on side 3, srcseg has only 1)
876 //	then assign uvs according to side vertex id, not face vertex id.
copy_uvs_seg_to_seg(unique_segment & destseg,const unique_segment & srcseg)877 void copy_uvs_seg_to_seg(unique_segment &destseg, const unique_segment &srcseg)
878 {
879 	range_for (const auto &&z, zip(destseg.sides, srcseg.sides))
880 	{
881 		auto &ds = std::get<0>(z);
882 		auto &ss = std::get<1>(z);
883 		ds.tmap_num = ss.tmap_num;
884 		ds.tmap_num2 = ss.tmap_num2;
885 	}
886 
887 	destseg.static_light = srcseg.static_light;
888 }
889 
890 }
891 
892 namespace dcx {
893 
894 //	_________________________________________________________________________________________________________________________
895 //	Maximum distance between a segment containing light to a segment to receive light.
896 #define	LIGHT_DISTANCE_THRESHOLD	(F1_0*80)
897 fix	Magical_light_constant = (F1_0*16);
898 
899 // int	Seg0, Seg1;
900 
901 //int	Bugseg = 27;
902 
903 namespace {
904 
905 struct hash_info {
906 	sbyte			flag, hit_type;
907 	vms_vector	vector;
908 };
909 
910 #define	FVI_HASH_SIZE 8
911 #define	FVI_HASH_AND_MASK (FVI_HASH_SIZE - 1)
912 
913 //	Note: This should be malloced.
914 //			Also, the vector should not be 12 bytes, you should only care about some smaller portion of it.
915 static std::array<hash_info, FVI_HASH_SIZE> fvi_cache;
916 static int Hash_hits=0, Hash_retries=0, Hash_calcs=0;
917 
918 //	-----------------------------------------------------------------------------------------
919 //	Set light from a light source.
920 //	Light incident on a surface is defined by the light incident at its points.
921 //	Light at a point = K * (V . N) / d
922 //	where:
923 //		K = some magical constant to make everything look good
924 //		V = normalized vector from light source to point
925 //		N = surface normal at point
926 //		d = distance from light source to point
927 //	(Note that the above equation can be simplified to K * (VV . N) / d^2 where VV = non-normalized V)
928 //	Light intensity emitted from a light source is defined to be cast from four points.
929 //	These four points are 1/64 of the way from the corners of the light source to the center
930 //	of its segment.  By assuming light is cast from these points, rather than from on the
931 //	light surface itself, light will be properly cast on the light surface.  Otherwise, the
932 //	vector V would be the null vector.
933 //	If quick_light set, then don't use find_vector_intersection
cast_light_from_side(const vmsegptridx_t segp,int light_side,fix light_intensity,int quick_light)934 static void cast_light_from_side(const vmsegptridx_t segp, int light_side, fix light_intensity, int quick_light)
935 {
936 	auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
937 	auto &Vertices = LevelSharedVertexState.get_vertices();
938 	auto &vcvertptr = Vertices.vcptr;
939 	auto &Walls = LevelUniqueWallSubsystemState.Walls;
940 	auto &vcwallptr = Walls.vcptr;
941 	const auto segment_center = compute_segment_center(vcvertptr, segp);
942 	//	Do for four lights, one just inside each corner of side containing light.
943 	range_for (const auto lightnum, Side_to_verts[light_side])
944 	{
945 		// fix			inverse_segment_magnitude;
946 
947 		const auto light_vertex_num = segp->verts[lightnum];
948 		auto light_location = *vcvertptr(light_vertex_num);
949 
950 	//	New way, 5/8/95: Move towards center irrespective of size of segment.
951 		const auto vector_to_center = vm_vec_normalized_quick(vm_vec_sub(segment_center, light_location));
952 	vm_vec_add2(light_location, vector_to_center);
953 
954 // -- Old way, before 5/8/95 --		// -- This way was kind of dumb.  In larger segments, you move LESS towards the center.
955 // -- Old way, before 5/8/95 --		//    Main problem, though, is vertices don't illuminate themselves well in oblong segments because the dot product is small.
956 // -- Old way, before 5/8/95 --		vm_vec_sub(&vector_to_center, &segment_center, &light_location);
957 // -- Old way, before 5/8/95 --		inverse_segment_magnitude = fixdiv(F1_0/5, vm_vec_mag(&vector_to_center));
958 // -- Old way, before 5/8/95 --		vm_vec_scale_add(&light_location, &light_location, &vector_to_center, inverse_segment_magnitude);
959 
960 		range_for (const auto &&rsegp, vmsegptr)
961 		{
962 			fix			dist_to_rseg;
963 
964 			range_for (auto &i, fvi_cache)
965 				i.flag = 0;
966 
967 			//	efficiency hack (I hope!), for faraway segments, don't check each point.
968 			const auto r_segment_center = compute_segment_center(vcvertptr, rsegp);
969 			dist_to_rseg = vm_vec_dist_quick(r_segment_center, segment_center);
970 
971 			if (dist_to_rseg <= LIGHT_DISTANCE_THRESHOLD) {
972 				for (const auto &&[sidenum, srside, urside] : enumerate(zip(rsegp->shared_segment::sides, rsegp->unique_segment::sides)))
973 				{
974 					if (WALL_IS_DOORWAY(GameBitmaps, Textures, vcwallptr, rsegp, sidenum) != WID_NO_WALL)
975 					{
976 						auto &side_normalp = srside.normals[0];	//	kinda stupid? always use vector 0.
977 
978 						for (const auto vertnum : xrange(4u))
979 						{
980 							const auto abs_vertnum = rsegp->verts[Side_to_verts[sidenum][vertnum]];
981 							vms_vector vert_location = *vcvertptr(abs_vertnum);
982 							const fix distance_to_point = vm_vec_dist_quick(vert_location, light_location);
983 							const auto vector_to_light = vm_vec_normalized(vm_vec_sub(light_location, vert_location));
984 
985 							//	Hack: In oblong segments, it's possible to get a very small dot product
986 							//	but the light source is very nearby (eg, illuminating light itself!).
987 							fix light_dot = vm_vec_dot(vector_to_light, side_normalp);
988 							if (distance_to_point < F1_0)
989 								if (light_dot > 0)
990 									light_dot = (light_dot + F1_0)/2;
991 
992 							if (light_dot > 0) {
993 								fix light_at_point = fixdiv(fixmul(light_dot, light_dot), distance_to_point);
994 								light_at_point = fixmul(light_at_point, Magical_light_constant);
995 								if (light_at_point >= 0) {
996 									fvi_info	hit_data;
997 									int		hit_type;
998 
999 									const auto r_vector_to_center = vm_vec_sub(r_segment_center, vert_location);
1000 									const auto inverse_segment_magnitude = fixdiv(F1_0/3, vm_vec_mag(r_vector_to_center));
1001 									const auto vert_location_1 = vm_vec_scale_add(vert_location, r_vector_to_center, inverse_segment_magnitude);
1002 									vert_location = vert_location_1;
1003 
1004 //if ((segp-Segments == 199) && (rsegp-Segments==199))
1005 //	Int3();
1006 // Seg0 = segp-Segments;
1007 // Seg1 = rsegp-Segments;
1008 									if (!quick_light) {
1009 										int hash_value = Side_to_verts[sidenum][vertnum];
1010 										hash_info	*hashp = &fvi_cache[hash_value];
1011 										while (1) {
1012 											if (hashp->flag) {
1013 												if ((hashp->vector.x == vector_to_light.x) && (hashp->vector.y == vector_to_light.y) && (hashp->vector.z == vector_to_light.z)) {
1014 													hit_type = hashp->hit_type;
1015 													Hash_hits++;
1016 													break;
1017 												} else {
1018 													Int3();	// How is this possible?  Should be no hits!
1019 													Hash_retries++;
1020 													hash_value = (hash_value+1) & FVI_HASH_AND_MASK;
1021 													hashp = &fvi_cache[hash_value];
1022 												}
1023 											} else {
1024 												fvi_query fq;
1025 
1026 												Hash_calcs++;
1027 												hashp->vector = vector_to_light;
1028 												hashp->flag = 1;
1029 
1030 												fq.p0						= &light_location;
1031 												fq.startseg				= segp;
1032 												fq.p1						= &vert_location;
1033 												fq.rad					= 0;
1034 												fq.thisobjnum			= object_none;
1035 												fq.ignore_obj_list.first = nullptr;
1036 												fq.flags					= 0;
1037 
1038 												hit_type = find_vector_intersection(fq, hit_data);
1039 												hashp->hit_type = hit_type;
1040 												break;
1041 											}
1042 										}
1043 									} else
1044 										hit_type = HIT_NONE;
1045 									switch (hit_type) {
1046 										case HIT_NONE:
1047 											light_at_point = fixmul(light_at_point, light_intensity);
1048 											urside.uvls[vertnum].l += light_at_point;
1049 											if (urside.uvls[vertnum].l > F1_0)
1050 												urside.uvls[vertnum].l = F1_0;
1051 											break;
1052 										case HIT_WALL:
1053 											break;
1054 										case HIT_OBJECT:
1055 											Int3();	// Hit object, should be ignoring objects!
1056 											break;
1057 										case HIT_BAD_P0:
1058 											Int3();	//	Ugh, this thing again, what happened, what does it mean?
1059 											break;
1060 									}
1061 								}	//	end if (light_at_point...
1062 							}	// end if (light_dot >...
1063 						}	//	end for (vertnum=0...
1064 					}	//	end if (rsegp...
1065 				}	//	end for (sidenum=0...
1066 			}	//	end if (dist_to_rseg...
1067 
1068 		}	//	end for (segnum=0...
1069 
1070 	}	//	end for (lightnum=0...
1071 }
1072 
1073 
1074 //	------------------------------------------------------------------------------------------
1075 //	Zero all lighting values.
calim_zero_light_values(void)1076 static void calim_zero_light_values(void)
1077 {
1078 	range_for (unique_segment &segp, vmsegptr)
1079 	{
1080 		range_for (auto &side, segp.sides)
1081 		{
1082 			range_for (auto &uvl, side.uvls)
1083 				uvl.l = F1_0/64;	// Put a tiny bit of light here.
1084 		}
1085 		segp.static_light = F1_0 / 64;
1086 	}
1087 }
1088 
1089 
1090 //	------------------------------------------------------------------------------------------
1091 //	Used in setting average light value in a segment, cast light from a side to the center
1092 //	of all segments.
cast_light_from_side_to_center(const vmsegptridx_t segp,int light_side,fix light_intensity,int quick_light)1093 static void cast_light_from_side_to_center(const vmsegptridx_t segp, int light_side, fix light_intensity, int quick_light)
1094 {
1095 	auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
1096 	auto &Vertices = LevelSharedVertexState.get_vertices();
1097 	auto &vcvertptr = Vertices.vcptr;
1098 	const auto &&segment_center = compute_segment_center(vcvertptr, segp);
1099 	//	Do for four lights, one just inside each corner of side containing light.
1100 	range_for (const auto lightnum, Side_to_verts[light_side])
1101 	{
1102 		const auto light_vertex_num = segp->verts[lightnum];
1103 		auto &vert_light_location = *vcvertptr(light_vertex_num);
1104 		const auto vector_to_center = vm_vec_sub(segment_center, vert_light_location);
1105 		const auto light_location = vm_vec_scale_add(vert_light_location, vector_to_center, F1_0/64);
1106 
1107 		for (const csmusegment &&rsegp : vmsegptr)
1108 		{
1109 			fix			dist_to_rseg;
1110 //if ((segp == &Segments[Bugseg]) && (rsegp == &Segments[Bugseg]))
1111 //	Int3();
1112 			const auto r_segment_center = compute_segment_center(vcvertptr, rsegp);
1113 			dist_to_rseg = vm_vec_dist_quick(r_segment_center, segment_center);
1114 
1115 			if (dist_to_rseg <= LIGHT_DISTANCE_THRESHOLD) {
1116 				fix	light_at_point;
1117 				if (dist_to_rseg > F1_0)
1118 					light_at_point = fixdiv(Magical_light_constant, dist_to_rseg);
1119 				else
1120 					light_at_point = Magical_light_constant;
1121 
1122 				if (light_at_point >= 0) {
1123 					int		hit_type;
1124 
1125 					if (!quick_light) {
1126 						fvi_query fq;
1127 						fvi_info	hit_data;
1128 
1129 						fq.p0						= &light_location;
1130 						fq.startseg				= segp;
1131 						fq.p1						= &r_segment_center;
1132 						fq.rad					= 0;
1133 						fq.thisobjnum			= object_none;
1134 						fq.ignore_obj_list.first = nullptr;
1135 						fq.flags					= 0;
1136 
1137 						hit_type = find_vector_intersection(fq, hit_data);
1138 					}
1139 					else
1140 						hit_type = HIT_NONE;
1141 
1142 					switch (hit_type) {
1143 						case HIT_NONE:
1144 							light_at_point = fixmul(light_at_point, light_intensity);
1145 							if (light_at_point >= F1_0)
1146 								light_at_point = F1_0-1;
1147 							{
1148 								auto &static_light = rsegp.u.static_light;
1149 								static_light += light_at_point;
1150 								if (static_light < 0)	// if it went negative, saturate
1151 									static_light = 0;
1152 							}
1153 							break;
1154 						case HIT_WALL:
1155 							break;
1156 						case HIT_OBJECT:
1157 							Int3();	// Hit object, should be ignoring objects!
1158 							break;
1159 						case HIT_BAD_P0:
1160 							Int3();	//	Ugh, this thing again, what happened, what does it mean?
1161 							break;
1162 					}
1163 				}	//	end if (light_at_point...
1164 			}	//	end if (dist_to_rseg...
1165 
1166 		}	//	end for (segnum=0...
1167 
1168 	}	//	end for (lightnum=0...
1169 
1170 }
1171 
1172 //	------------------------------------------------------------------------------------------
1173 //	Process all lights.
calim_process_all_lights(int quick_light)1174 static void calim_process_all_lights(int quick_light)
1175 {
1176 	auto &TmapInfo = LevelUniqueTmapInfoState.TmapInfo;
1177 	auto &Walls = LevelUniqueWallSubsystemState.Walls;
1178 	auto &vcwallptr = Walls.vcptr;
1179 	range_for (const auto &&segp, vmsegptridx)
1180 	{
1181 		for (const auto &&[sidenum, value] : enumerate(segp->unique_segment::sides))
1182 		{
1183 			if (WALL_IS_DOORWAY(GameBitmaps, Textures, vcwallptr, segp, sidenum) != WID_NO_WALL)
1184 			{
1185 				const auto sidep = &value;
1186 				fix	light_intensity;
1187 
1188 				light_intensity = TmapInfo[get_texture_index(sidep->tmap_num)].lighting + TmapInfo[get_texture_index(sidep->tmap_num2)].lighting;
1189 
1190 //				if (segp->sides[sidenum].wall_num != -1) {
1191 //					int	wall_num, bitmap_num, effect_num;
1192 //					wall_num = segp->sides[sidenum].wall_num;
1193 //					effect_num = Walls[wall_num].type;
1194 //					bitmap_num = effects_bm_num[effect_num];
1195 //
1196 //					light_intensity += TmapInfo[bitmap_num].lighting;
1197 //				}
1198 
1199 				if (light_intensity) {
1200 					light_intensity /= 4;			// casting light from four spots, so divide by 4.
1201 					cast_light_from_side(segp, sidenum, light_intensity, quick_light);
1202 					cast_light_from_side_to_center(segp, sidenum, light_intensity, quick_light);
1203 				}
1204 			}
1205 		}
1206 	}
1207 }
1208 
1209 //	------------------------------------------------------------------------------------------
1210 //	Apply static light in mine.
1211 //	First, zero all light values.
1212 //	Then, for all light sources, cast their light.
cast_all_light_in_mine(int quick_flag)1213 static void cast_all_light_in_mine(int quick_flag)
1214 {
1215 	validate_segment_all(LevelSharedSegmentState);
1216 	calim_zero_light_values();
1217 
1218 	calim_process_all_lights(quick_flag);
1219 }
1220 
1221 }
1222 
1223 }
1224 
1225 // int	Fvit_num = 1000;
1226 //
1227 // fix find_vector_intersection_test(void)
1228 // {
1229 // 	int		i;
1230 // 	fvi_info	hit_data;
1231 // 	int		p0_seg, p1_seg, this_objnum, ignore_obj, check_obj_flag;
1232 // 	fix		rad;
1233 // 	int		start_time = timer_get_milliseconds();;
1234 // 	vms_vector	p0,p1;
1235 //
1236 // 	ignore_obj = 1;
1237 // 	check_obj_flag = 0;
1238 // 	this_objnum = -1;
1239 // 	rad = F1_0/4;
1240 //
1241 // 	for (i=0; i<Fvit_num; i++) {
1242 //		p0_seg = d_rand()*(Highest_segment_index+1)/32768;
1243 // 		compute_segment_center(&p0, &Segments[p0_seg]);
1244 //
1245 //		p1_seg = d_rand()*(Highest_segment_index+1)/32768;
1246 // 		compute_segment_center(&p1, &Segments[p1_seg]);
1247 //
1248 // 		find_vector_intersection(&hit_data, &p0, p0_seg, &p1, rad, this_objnum, ignore_obj, check_obj_flag);
1249 // 	}
1250 //
1251 // 	return timer_get_milliseconds() - start_time;
1252 // }
1253