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 | // } |