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