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