├── DistanceOcclusionLib.cpp ├── DistanceOcclusionLib.h ├── DistanceOcclusionTool.cpp ├── LICENSE.md ├── MeshSupport.cpp ├── MeshSupport.h ├── README.md ├── data ├── kiwi.png ├── plant2.obj └── tiger.png ├── images ├── kiwi-sdf-delta.png ├── kiwi-sdf-rg.png ├── kiwi-sdf.png ├── tiger-sdf-delta.png ├── tiger-sdf-rg.png └── tiger-sdf.png └── stb_image_mini.h /DistanceOcclusionLib.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // Purpose: Distance Field and Occlusion Generation 3 | // Author: Andrew Willmott 4 | //------------------------------------------------------------------------------ 5 | 6 | #include "DistanceOcclusionLib.h" 7 | 8 | #include 9 | #include 10 | 11 | using namespace DOL; 12 | 13 | #if defined(DOS_DEBUG) || defined(DEBUG) 14 | #include 15 | #else 16 | #define assert(e) ((void) 0) 17 | #endif 18 | 19 | #if !defined(__cplusplus) || (__cplusplus <= 201100) 20 | #define constexpr const 21 | #endif 22 | 23 | // Declarations 24 | 25 | namespace 26 | { 27 | inline int RNG(uint32_t& seed, int limit) 28 | { 29 | seed = uint32_t(seed * uint64_t(1103515245)) + 12345; 30 | return int((seed * (uint64_t(limit))) >> 32); 31 | } 32 | } 33 | 34 | // Convenience macro for iterating over set bits (and also testing different strategies) 35 | #ifdef _MSC_VER 36 | #include 37 | uint32_t TrailingZeroes32(uint32_t mask) 38 | { 39 | unsigned long index; 40 | _BitScanForward(&index, mask); 41 | return index; 42 | } 43 | #elif defined(__GNUC__) 44 | #define TrailingZeroes32 __builtin_ctz 45 | #else 46 | // Hash table approach is actually comparable in speed to intrinsics, faster than hierarchical bit tricks 47 | constexpr int kTrailingZeroesHashTable[37] = { 32, 0, 1, 26, 2, 23, 27, 0, 3, 16, 24, 30, 28, 11, 0, 13, 4, 7, 17, 0, 25, 22, 31, 15, 29, 10, 12, 6, 0, 21, 14, 9, 5, 20, 8, 19, 18 }; 48 | 49 | int TrailingZeroes32(uint32_t x) 50 | { 51 | return kTrailingZeroesHashTable[(x & -x) % 37]; 52 | } 53 | #endif 54 | 55 | #define ITER_SET_BITS_BEGIN(MASK) \ 56 | while (MASK) \ 57 | { \ 58 | int i = TrailingZeroes32(MASK); \ 59 | MASK &= MASK - 1; \ 60 | \ 61 | if (j * 32 + i >= w) \ 62 | break; \ 63 | 64 | #define ITER_SET_BITS_BEGIN_BF(MASK) \ 65 | for (int i = 0; i < 32; i++) \ 66 | if (MASK & (1 << i)) \ 67 | { \ 68 | if (j * 32 + i >= w) \ 69 | break; \ 70 | 71 | #define ITER_SET_BITS_END } 72 | 73 | 74 | 75 | 76 | // --- 2D Images --------------------------------------------------------------- 77 | 78 | uint32_t* DOL::CreateBitMask(int w, int h) 79 | { 80 | size_t maskSize = MaskSize(w, h); 81 | uint32_t* mask = new uint32_t[maskSize]; 82 | 83 | memset(mask, 0, maskSize * sizeof(uint32_t)); 84 | 85 | return mask; 86 | } 87 | 88 | void DOL::DestroyBitMask(uint32_t*& mask) 89 | { 90 | delete[] mask; 91 | mask = 0; 92 | } 93 | 94 | void DOL::BitMaskAddPoints(int w, int h, int count, uint32_t seed, uint32_t mask[]) 95 | { 96 | int ws = MaskSize(w); 97 | 98 | for (int i = 0; i < count; i++) 99 | { 100 | int x = RNG(seed, w); 101 | int y = RNG(seed, h); 102 | 103 | assert(x < w); 104 | assert(y < h); 105 | 106 | int fx = x & 31; 107 | x >>= 5; 108 | 109 | int index = y * ws + x; 110 | mask[index] |= (1 << fx); 111 | } 112 | } 113 | 114 | namespace 115 | { 116 | uint32_t LeftMask(int n) 117 | { 118 | assert(n < 32); 119 | uint32_t bit = (1 << n); 120 | return ~(bit - 1); 121 | } 122 | 123 | uint32_t RightMask(int n) 124 | { 125 | assert(n < 32); 126 | uint32_t bit = 1 << n; 127 | return bit + (bit - 1); // Inclusive 128 | } 129 | 130 | void FillMaskBlock(int rs, int x0, int x1, int y0, int y1, uint32_t mask[]) 131 | { 132 | int sx0 = x0 / 32; 133 | int sx1 = x1 / 32; 134 | 135 | uint32_t mask0 = LeftMask (x0 - sx0 * 32); 136 | uint32_t mask1 = RightMask(x1 - sx1 * 32); 137 | 138 | if (sx0 == sx1) 139 | { 140 | mask0 = mask0 & mask1; 141 | mask1 = 0; 142 | } 143 | 144 | for (int y = y0; y <= y1; y++) 145 | { 146 | uint32_t* row = mask + y * rs; 147 | 148 | row[sx0] |= mask0; 149 | 150 | for (int sx = sx0 + 1; sx < sx1; sx++) 151 | row[sx] = ~0; 152 | 153 | row[sx1] |= mask1; 154 | } 155 | } 156 | } 157 | 158 | void DOL::BitMaskAddBlock(int w, int h, int sides, uint32_t mask[]) 159 | { 160 | int x0 = w / 4; 161 | int x1 = x0 + w / 2 - 1; 162 | int y0 = h / 3; 163 | int y1 = y0 + h / 3 - 1; 164 | 165 | int sw = MaskSize(w); 166 | 167 | if (sides > 4) 168 | return FillMaskBlock(sw, x0, x1, y0, y1, mask); 169 | 170 | if (sides > 0) 171 | FillMaskBlock(sw, x0, x1, y0, y0, mask); 172 | if (sides > 1) 173 | FillMaskBlock(sw, x0, x0, y0, y1, mask); 174 | if (sides > 2) 175 | FillMaskBlock(sw, x0, x1, y1, y1, mask); 176 | if (sides > 3) 177 | FillMaskBlock(sw, x1, x1, y0, y1, mask); 178 | } 179 | 180 | namespace 181 | { 182 | template void InitDistancesFromBitMask(int w, int h, const uint32_t mask[], T distances[], T maxD) 183 | { 184 | // mask is expected to have rows padded to whole numbers of uint32_ts 185 | int s = w * h; 186 | for (int i = 0; i < s; i++) 187 | distances[i] = maxD; 188 | 189 | int maskStride = MaskSize(w); 190 | 191 | for (int y = 0; y < h; y++) 192 | { 193 | for (int j = 0; j < maskStride; j++) 194 | { 195 | uint32_t m = *mask++; 196 | 197 | if (m == 0) 198 | continue; 199 | 200 | T* block = distances + y * w + 32 * j; 201 | 202 | ITER_SET_BITS_BEGIN(m) 203 | block[i] = 0; 204 | ITER_SET_BITS_END 205 | } 206 | } 207 | } 208 | } 209 | 210 | void DOL::InitDistancesFromBitMask(int w, int h, const uint32_t mask[], int16_t distances[], int16_t maxD) 211 | { 212 | return ::InitDistancesFromBitMask(w, h, mask, distances, maxD); 213 | } 214 | void DOL::InitDistancesFromBitMask(int w, int h, const uint32_t mask[], int32_t distances[], int32_t maxD) 215 | { 216 | return ::InitDistancesFromBitMask(w, h, mask, distances, maxD); 217 | } 218 | void DOL::InitDistancesFromBitMask(int w, int h, const uint32_t mask[], float distances[], float maxD) 219 | { 220 | return ::InitDistancesFromBitMask(w, h, mask, distances, maxD); 221 | } 222 | 223 | void DOL::InitDeltasFromBitMask(int w, int h, const uint32_t mask[], cCellDelta2 deltas[], bool invert) 224 | { 225 | const cCellDelta2 maxDelta = { kMaxDelta2, kMaxDelta2 }; 226 | const cCellDelta2 minDelta = { 0, 0 }; 227 | 228 | for (int i = 0; i < w * h; i++) 229 | deltas[i] = maxDelta; 230 | 231 | const int maskStride = MaskSize(w); 232 | const uint32_t maskInvert = invert ? ~uint32_t(0) : 0; 233 | 234 | for (int y = 0; y < h; y++) 235 | for (int j = 0; j < maskStride * 32; j += 32) 236 | { 237 | uint32_t m = (*mask++) ^ maskInvert; 238 | 239 | if (m == 0) 240 | continue; 241 | 242 | cCellDelta2* block = deltas + y * w + j; 243 | const int n = (w - j) >= 32 ? 32 : (w - j); 244 | 245 | for (int i = 0; i < n; i++) // XXX 246 | { 247 | if (m & 1) 248 | block[i] = minDelta; 249 | 250 | m >>= 1; 251 | } 252 | } 253 | } 254 | 255 | namespace 256 | { 257 | // Utilities for setting deltas for cells adjacent to the interior/exterior 258 | // boundary, in units of half a cell width. E.g., a cell immediately to the 259 | // left of the boundary will have a delta of (+1, 0), reflecting that the 260 | // boundary is half a cell to right of the cell's centre. 261 | // By initialising the SDF this way, we can calculate both interior and 262 | // exterior distances in a single pass, and distances are consistent across 263 | // the border. (The standard techique of calculating the exterior distances, 264 | // then inverting the problem to calculate interior distances, and combining 265 | // the two results, is both 2x as expensive, and leads to inconsistent 266 | // distances on border cells. The border effectively has a width of -ve 1. 267 | 268 | constexpr cCellDelta2 kBH0 = { 0, +1 }; // level set is half a cell up 269 | constexpr cCellDelta2 kBH1 = { 0, -1 }; // level set is half a cell down 270 | constexpr cCellDelta2 kBW0 = { +1, 0 }; // level set is half a cell right 271 | constexpr cCellDelta2 kBW1 = { -1, 0 }; // level set is half a cell left 272 | 273 | #ifdef USE_BORDER_REFERENCE 274 | // These routines treat each border case separately, in turn, for clarity. 275 | // They are useful both for understanding the logic, but also for sanity- 276 | // checking the more complex all-in-one routine. (See SetBorderCellsCombo.) 277 | void SetBorderCellsH(int w, int h, const uint32_t* const mask, cCellDelta2 deltas[]) 278 | { 279 | // Find horizontal edges... 280 | // x . 281 | // . or x 282 | // and set the cells to either side accordingly 283 | 284 | const int maskStride = MaskSize(w); 285 | 286 | for (int y = 0; y < h - 1; y++) 287 | { 288 | const uint32_t* maskRow0 = mask + (y + 0) * maskStride; 289 | const uint32_t* maskRow1 = mask + (y + 1) * maskStride; 290 | cCellDelta2* deltasRow0 = deltas + (y + 0) * w; 291 | cCellDelta2* deltasRow1 = deltas + (y + 1) * w; 292 | 293 | for (int j = 0; j < maskStride; j++) 294 | { 295 | uint32_t m0 = maskRow0[j]; 296 | uint32_t m1 = maskRow1[j]; 297 | uint32_t mx = m0 ^ m1; // 1 on change 298 | 299 | if (mx) 300 | { 301 | cCellDelta2* block0 = deltasRow0 + j * 32; 302 | cCellDelta2* block1 = deltasRow1 + j * 32; 303 | 304 | for (int i = 0; i < 32; i++) 305 | if ((mx & (1 << i))) 306 | { 307 | if (j * 32 + i >= w) // delayed check as we get here relatively rarely 308 | break; 309 | 310 | assert(j * 32 + i >= 0); 311 | assert(j * 32 + i < w); 312 | 313 | block0[i] = kBH0; 314 | block1[i] = kBH1; 315 | } 316 | } 317 | } 318 | } 319 | } 320 | 321 | void SetBorderCellsW(int w, int h, const uint32_t* const mask, cCellDelta2 deltas[]) 322 | { 323 | // Find vertical edges... 324 | // .|x or x|. 325 | // and set the cells to either side accordingly 326 | 327 | const int maskStride = MaskSize(w); 328 | 329 | for (int y = 0; y < h; y++) 330 | { 331 | const uint32_t* maskRow = mask + y * maskStride; 332 | cCellDelta2* deltasRow = deltas + y * w; 333 | 334 | uint32_t lastBit = maskRow[0] & 1; // repeat border cell, avoids extra logic to loop from 1 rather than 0. 335 | 336 | for (int j = 0; j < maskStride; j++) 337 | { 338 | uint32_t m0 = maskRow[j]; 339 | uint32_t m1 = (m0 << 1) | lastBit; 340 | lastBit = m0 >> 31; 341 | 342 | // Effectively bit 'i' indexes cell i (m0) and cell i-1 (m1). It's 343 | // done this way to avoid having to look ahead one word up front. 344 | 345 | uint32_t mx = m0 ^ m1; 346 | 347 | if (mx) 348 | { 349 | cCellDelta2* block = deltasRow + j * 32; 350 | 351 | for (int i = 0; i < 32; i++) 352 | if ((mx & (1 << i))) 353 | { 354 | if (j * 32 + i >= w) // delayed check as we get here relatively rarely 355 | break; 356 | 357 | assert(j * 32 + i > 0); 358 | assert(j * 32 + i < w); 359 | 360 | block[i - 1] = kBW0; 361 | block[i + 0] = kBW1; 362 | } 363 | } 364 | } 365 | } 366 | } 367 | 368 | constexpr cCellDelta2 kBC00 = { -1, +1 }; 369 | constexpr cCellDelta2 kBC01 = { +1, -1 }; 370 | constexpr cCellDelta2 kBC10 = { +1, +1 }; 371 | constexpr cCellDelta2 kBC11 = { -1, -1 }; 372 | 373 | void SetBorderCellsC(int w, int h, const uint32_t* const mask, cCellDelta2 deltas[]) 374 | { 375 | // Look for diagonal borders affecting corners, i.e., 376 | // . x x . 377 | // x . or . x 378 | // and set the diagonal cell deltas to +-1, +-1 as appropriate. 379 | // These may be overwritten by horizontal/vertical deltas later, as they 380 | // will always be closer. 381 | 382 | const int maskStride = MaskSize(w); 383 | 384 | for (int y = 0; y < h - 1; y++) 385 | { 386 | const uint32_t* maskRow0 = mask + (y + 0) * maskStride; 387 | const uint32_t* maskRow1 = mask + (y + 1) * maskStride; 388 | cCellDelta2* deltasRow0 = deltas + (y + 0) * w; 389 | cCellDelta2* deltasRow1 = deltas + (y + 1) * w; 390 | 391 | uint32_t lastBit0 = maskRow1[0] & 1; // pick to avoid detection on i=j=0 without explicit check 392 | uint32_t lastBit1 = maskRow0[0] & 1; 393 | 394 | for (int j = 0; j < maskStride; j++) 395 | { 396 | uint32_t m00 = maskRow0[j]; 397 | uint32_t m01 = maskRow1[j]; 398 | 399 | uint32_t m10 = (m00 << 1) | lastBit0; 400 | uint32_t m11 = (m01 << 1) | lastBit1; 401 | 402 | lastBit0 = m00 >> 31; 403 | lastBit1 = m01 >> 31; 404 | 405 | uint32_t mc0 = m00 ^ m11; // forward diagonal 406 | uint32_t mc1 = m01 ^ m10; // backward diagonal 407 | 408 | if (mc0 | mc1) 409 | { 410 | cCellDelta2* block[2] = { deltasRow0 + j * 32, deltasRow1 + j * 32 }; 411 | 412 | for (int i = 0; i < 32; i++) 413 | { 414 | if ((mc0 & (1 << i))) 415 | { 416 | if (j * 32 + i >= w) // delayed check as we get here relatively rarely 417 | break; 418 | 419 | assert(j * 32 + i > 0); 420 | assert(j * 32 + i < w); 421 | 422 | block[0][i + 0] = kBC00; 423 | block[1][i - 1] = kBC01; 424 | } 425 | if ((mc1 & (1 << i)) && (j * 32 + i < w)) 426 | { 427 | if (j * 32 + i >= w) // delayed check as we get here relatively rarely 428 | break; 429 | 430 | assert(j * 32 + i > 0); 431 | assert(j * 32 + i < w); 432 | 433 | block[0][i - 1] = kBC10; 434 | block[1][i + 0] = kBC11; 435 | } 436 | } 437 | } 438 | } 439 | } 440 | } 441 | 442 | void SetBorderCellsC2(int w, int h, const uint32_t* const mask, cCellDelta2 deltas[]) 443 | { 444 | // Smarter version that figures out which particular cell 445 | // of the diagonal should be set. Reduces writes by 2x. 446 | 447 | const int maskStride = MaskSize(w); 448 | 449 | for (int y = 0; y < h - 1; y++) 450 | { 451 | const uint32_t* maskRow0 = mask + (y + 0) * maskStride; 452 | const uint32_t* maskRow1 = mask + (y + 1) * maskStride; 453 | cCellDelta2* deltasRow0 = deltas + (y + 0) * w; 454 | cCellDelta2* deltasRow1 = deltas + (y + 1) * w; 455 | 456 | uint32_t lastBit0 = maskRow0[0] & 1; 457 | uint32_t lastBit1 = maskRow1[0] & 1; 458 | 459 | for (int j = 0; j < maskStride; j++) 460 | { 461 | uint32_t m00 = maskRow0[j]; 462 | uint32_t m01 = maskRow1[j]; 463 | 464 | uint32_t m10 = (m00 << 1) | lastBit0; 465 | uint32_t m11 = (m01 << 1) | lastBit1; 466 | 467 | lastBit0 = m00 >> 31; 468 | lastBit1 = m01 >> 31; 469 | 470 | // we have a corner cell if both verticals (or horizontals) feature one flip 471 | uint32_t mv0 = m00 ^ m01; // 1 on vert change 472 | uint32_t mv1 = m10 ^ m11; // 1 on vert change 473 | uint32_t mh0 = m00 ^ m10; // 1 on change 474 | 475 | uint32_t mc = (mv0 ^ mv1); 476 | 477 | if (mc) 478 | { 479 | cCellDelta2* block[2] = { deltasRow0 + j * 32, deltasRow1 + j * 32 }; 480 | 481 | for (int i = 0; i < 32; i++) 482 | if (mc & (1 << i)) 483 | { 484 | if (j * 32 + i >= w) // delayed check as we get here relatively rarely 485 | break; 486 | 487 | assert(j * 32 + i > 0); 488 | assert(j * 32 + i < w); 489 | 490 | int dx = (mv1 >> i) & 1; 491 | int dy = (mh0 >> i) & 1; 492 | 493 | block[dy][i + dx - 1].x = 1 - 2 * dx; 494 | block[dy][i + dx - 1].y = 1 - 2 * dy; 495 | } 496 | } 497 | } 498 | } 499 | } 500 | 501 | #else 502 | 503 | void SetBorderCellsCombo(int w, int h, const uint32_t* const mask, cCellDelta2 deltas[]) 504 | { 505 | // Rolls everything into one pass 506 | const int maskStride = MaskSize(w); 507 | 508 | for (int y = 0; y < h - 1; y++) 509 | { 510 | const uint32_t* maskRow0 = mask + (y + 0) * maskStride; 511 | const uint32_t* maskRow1 = mask + (y + 1) * maskStride; 512 | cCellDelta2* deltasRow0 = deltas + (y + 0) * w; 513 | cCellDelta2* deltasRow1 = deltas + (y + 1) * w; 514 | 515 | uint32_t lastBit0 = maskRow0[0] & 1; // pick to avoid vertical i=0 check 516 | uint32_t lastBit1 = maskRow1[0] & 1; 517 | 518 | for (int j = 0; j < maskStride; j++) 519 | { 520 | uint32_t m00 = maskRow0[j]; 521 | uint32_t m01 = maskRow1[j]; 522 | uint32_t m10 = (m00 << 1) | lastBit0; 523 | uint32_t m11 = (m01 << 1) | lastBit1; 524 | 525 | lastBit0 = m00 >> 31; 526 | lastBit1 = m01 >> 31; 527 | 528 | cCellDelta2* block[2] = { deltasRow0 + j * 32, deltasRow1 + j * 32 }; 529 | 530 | // Horizontal 531 | uint32_t ey = m00 ^ m01; // 1 on change 532 | 533 | // Corner. These are much rarer, and we must check to ensure we don't 534 | // overwrite edge deltas, as they are larger. 535 | uint32_t ex = m00 ^ m10; 536 | uint32_t eyx = m10 ^ m11; 537 | uint32_t bc = ey ^ eyx; 538 | 539 | // The order here is important as iterating over the masks can destroy them 540 | 541 | // if (ex | ey | bc) early out currently not worth it 542 | ITER_SET_BITS_BEGIN(bc) 543 | assert(j * 32 + i > 0); 544 | 545 | int dx = (eyx >> i) & 1; 546 | int dy = (ex >> i) & 1; 547 | 548 | cCellDelta2& corner = block[dy][i + dx - 1]; 549 | 550 | if (corner.x != kMaxDelta2) 551 | continue; 552 | 553 | corner.x = 1 - 2 * dx; 554 | corner.y = 1 - 2 * dy; 555 | ITER_SET_BITS_END 556 | 557 | ITER_SET_BITS_BEGIN(ex) 558 | assert(j * 32 + i > 0); 559 | block[0][i - 1] = kBW0; 560 | block[0][i + 0] = kBW1; 561 | ITER_SET_BITS_END 562 | 563 | ITER_SET_BITS_BEGIN(ey) 564 | block[0][i] = kBH0; 565 | block[1][i] = kBH1; 566 | ITER_SET_BITS_END 567 | } 568 | } 569 | 570 | // Finish last vertical row 571 | { 572 | const int y = h - 1; 573 | const uint32_t* maskRow = mask + y * maskStride; 574 | cCellDelta2* deltasRow = deltas + y * w; 575 | 576 | uint32_t lastBit = maskRow[0] & 1; // repeat border cell, avoids extra logic to loop from 1 rather than 0. 577 | 578 | for (int j = 0; j < maskStride; j++) 579 | { 580 | uint32_t m0 = maskRow[j]; 581 | uint32_t m1 = (m0 << 1) | lastBit; 582 | lastBit = m0 >> 31; 583 | 584 | uint32_t mvx = m0 ^ m1; 585 | 586 | if (mvx) 587 | { 588 | cCellDelta2* block = deltasRow + j * 32; 589 | 590 | ITER_SET_BITS_BEGIN(mvx) 591 | assert(j * 32 + i > 0); 592 | block[i - 1] = kBW0; 593 | block[i + 0] = kBW1; 594 | ITER_SET_BITS_END 595 | } 596 | } 597 | } 598 | } 599 | #endif 600 | } 601 | 602 | 603 | void DOL::InitBorderDeltasFromBitMask(int w, int h, const uint32_t* const mask, cCellDelta2 deltas[]) 604 | { 605 | const cCellDelta2 maxDelta = { kMaxDelta2, kMaxDelta2 }; 606 | 607 | int s = w * h; 608 | for (int i = 0; i < s; i++) 609 | deltas[i] = maxDelta; 610 | 611 | #ifdef USE_BORDER_REFERENCE 612 | SetBorderCellsC(w, h, mask, deltas); 613 | SetBorderCellsH(w, h, mask, deltas); 614 | SetBorderCellsW(w, h, mask, deltas); 615 | #else 616 | SetBorderCellsCombo(w, h, mask, deltas); 617 | #endif 618 | } 619 | 620 | namespace 621 | { 622 | /// Returns true if d1 is closer than d0 623 | inline bool Closer(cCellDelta2 d0, cCellDelta2 d1) 624 | { 625 | int dw0 = d0.x * d0.x + d0.y * d0.y; 626 | int dw1 = d1.x * d1.x + d1.y * d1.y; 627 | 628 | return dw0 > dw1; 629 | } 630 | } 631 | 632 | void DOL::FastSweep(int w, int h, cCellDelta2 deltas[], int cw) 633 | { 634 | // Fast sweep approach -- sweep each diagonal in turn. 635 | // This is really the canonical sweeping method -- each sweep calculates 636 | // distances over a quadrant, as for each cell in the sweep, you can 637 | // guarantee all cells in the corresponding quadrant defined by that cell 638 | // and the start point have already been visited. 639 | // Other approaches such as Danielsson and Chamfer combine sweeps and vary 640 | // the inspected neighbours to trade speed vs. accuracy. 641 | // 642 | // See, e.g., "A fast sweeping method for Eikonal equations" Zhao 2004. 643 | 644 | const int lastRow = w * (h - 1); 645 | cCellDelta2 cell; 646 | 647 | for (int sweep = 0; sweep < 4; sweep++) 648 | { 649 | const int sx = (sweep >> 0) & 1; 650 | const int sy = (sweep >> 1) & 1; 651 | 652 | const int dx = 1 - 2 * sx; 653 | const int dy = 1 - 2 * sy; 654 | const int cx = -cw * dx; 655 | const int cy = -cw * dy; 656 | 657 | const int ib = sx * (w - 1); 658 | const int ie = w - sx * (w + 1); 659 | 660 | cCellDelta2* row0 = deltas + sy * lastRow; 661 | 662 | for (int j = 1; j < h; j++) 663 | { 664 | cCellDelta2* row1 = row0 + w * dy; 665 | 666 | for (int i = ib; i != ie; i += dx) 667 | { 668 | // Testing showed early out check for c = 0, 0 not worth it 669 | // even with large contiguous areas of interior cells. Actively 670 | // harmful in other cases due to branch overhead. 671 | cell.x = row0[i].x; 672 | cell.y = row0[i].y + cy; 673 | 674 | if (Closer(row1[i], cell)) 675 | row1[i] = cell; 676 | } 677 | 678 | for (int i = ib + dx; i != ie; i += dx) 679 | { 680 | cell.x = row1[i - dx].x + cx; 681 | cell.y = row1[i - dx].y; 682 | 683 | if (Closer(row1[i], cell)) 684 | row1[i] = cell; 685 | 686 | cell.x = row0[i - dx].x + cx; 687 | cell.y = row0[i - dx].y + cy; 688 | 689 | if (Closer(row1[i], cell)) 690 | row1[i] = cell; 691 | } 692 | 693 | row0 = row1; 694 | } 695 | } 696 | } 697 | 698 | // Danielsson is similar to the quadrant sweep approach (FastSweep), except it 699 | // wraps alternating x scans into one vertical sweep. 700 | 701 | void DOL::Danielsson(int w, int h, cCellDelta2 deltas[], int cw) 702 | { 703 | // store dx^2, dy^2 to nearest point. distance = sqrt(dx^2 + dy^2). 704 | // Much more accurate, doesn't over-estimate like Chamfer. 705 | cCellDelta2* row0 = deltas; 706 | cCellDelta2 cell; 707 | 708 | for (int j = 1; j < h; j++) 709 | { 710 | cCellDelta2* row1 = row0 + w; 711 | 712 | for (int i = 0; i < w; i++) 713 | { 714 | cell.x = row0[i].x; 715 | cell.y = row0[i].y - cw; 716 | 717 | if (Closer(row1[i], cell)) 718 | row1[i] = cell; 719 | } 720 | 721 | for (int i = 1; i < w; i++) 722 | { 723 | cell.x = row1[i - 1].x - cw; 724 | cell.y = row1[i - 1].y; 725 | 726 | if (Closer(row1[i], cell)) 727 | row1[i] = cell; 728 | } 729 | 730 | for (int i = w - 2; i >= 0; i--) 731 | { 732 | cell.x = row1[i + 1].x + cw; 733 | cell.y = row1[i + 1].y; 734 | 735 | if (Closer(row1[i], cell)) 736 | row1[i] = cell; 737 | } 738 | 739 | row0 = row1; 740 | } 741 | 742 | row0 = deltas + h * w - w; 743 | 744 | for (int j = h - 1; j > 0; j--) 745 | { 746 | cCellDelta2* row1 = row0 - w; 747 | 748 | for (int i = 0; i < w; i++) 749 | { 750 | cell.x = row0[i].x; 751 | cell.y = row0[i].y + cw; 752 | 753 | if (Closer(row1[i], cell)) 754 | row1[i] = cell; 755 | } 756 | 757 | for (int i = 1; i < w; i++) 758 | { 759 | cell.x = row1[i - 1].x - cw; 760 | cell.y = row1[i - 1].y; 761 | 762 | if (Closer(row1[i], cell)) 763 | row1[i] = cell; 764 | } 765 | 766 | for (int i = w - 2; i >= 0; i--) 767 | { 768 | cell.x = row1[i + 1].x + cw; 769 | cell.y = row1[i + 1].y; 770 | 771 | if (Closer(row1[i], cell)) 772 | row1[i] = cell; 773 | } 774 | 775 | row0 = row1; 776 | } 777 | } 778 | 779 | namespace 780 | { 781 | void JumpFlood(int step, int w, int h, cCellDelta2 deltasIn[], cCellDelta2 deltasOut[], int cw) 782 | { 783 | const cCellDelta2* row = deltasIn; 784 | cCellDelta2* rowOut = deltasOut; 785 | 786 | for (int j = 0; j < h; j++) 787 | { 788 | for (int i = 0; i < w; i++) 789 | { 790 | cCellDelta2 minCell = row[i]; 791 | 792 | if (i - step >= 0) 793 | { 794 | cCellDelta2 cell = row[i - step]; 795 | cell.x -= cw * step; 796 | 797 | if (Closer(minCell, cell)) 798 | minCell = cell; 799 | 800 | if (j - step >= 0) 801 | { 802 | cCellDelta2 cell = row[i - step - step * w]; 803 | cell.x -= cw * step; 804 | cell.y -= cw * step; 805 | 806 | if (Closer(minCell, cell)) 807 | minCell = cell; 808 | } 809 | 810 | if (j + step < h) 811 | { 812 | cCellDelta2 cell = row[i - step + step * w]; 813 | cell.x -= cw * step; 814 | cell.y += cw * step; 815 | 816 | if (Closer(minCell, cell)) 817 | minCell = cell; 818 | } 819 | } 820 | 821 | if (i + step < w) 822 | { 823 | cCellDelta2 cell = row[i + step]; 824 | cell.x += cw * step; 825 | 826 | if (Closer(minCell, cell)) 827 | minCell = cell; 828 | 829 | if (j - step >= 0) 830 | { 831 | cCellDelta2 cell = row[i + step - step * w]; 832 | cell.x += cw * step; 833 | cell.y -= cw * step; 834 | 835 | if (Closer(minCell, cell)) 836 | minCell = cell; 837 | } 838 | 839 | if (j + step < h) 840 | { 841 | cCellDelta2 cell = row[i + step + step * w]; 842 | cell.x += cw * step; 843 | cell.y += cw * step; 844 | 845 | if (Closer(minCell, cell)) 846 | minCell = cell; 847 | } 848 | } 849 | 850 | if (j - step >= 0) 851 | { 852 | cCellDelta2 cell = row[i - step * w]; 853 | cell.y -= cw * step; 854 | 855 | if (Closer(minCell, cell)) 856 | minCell = cell; 857 | } 858 | 859 | if (j + step < h) 860 | { 861 | cCellDelta2 cell = row[i + step * w]; 862 | cell.y += cw * step; 863 | 864 | if (Closer(minCell, cell)) 865 | minCell = cell; 866 | } 867 | 868 | rowOut[i] = minCell; 869 | } 870 | 871 | row += w; 872 | rowOut += w; 873 | } 874 | } 875 | 876 | int FloorPow2(int v) 877 | { 878 | v |= v >> 1; 879 | v |= v >> 2; 880 | v |= v >> 4; 881 | v |= v >> 8; 882 | v |= v >> 16; 883 | 884 | return (v + 1) >> 1; 885 | } 886 | } 887 | 888 | void DOL::JumpFlood(int w, int h, cCellDelta2 deltas[], int cw) 889 | { 890 | cCellDelta2* temp = new cCellDelta2[w * h]; 891 | cCellDelta2* d0 = deltas; 892 | cCellDelta2* d1 = temp; 893 | 894 | int step = FloorPow2((w >= h ? w : h) - 1); 895 | 896 | for ( ; step > 0; step /= 2) 897 | { 898 | ::JumpFlood(step, w, h, d0, d1, cw); 899 | 900 | cCellDelta2* dt = d0; 901 | d0 = d1; 902 | d1 = dt; 903 | } 904 | 905 | if (d0 != deltas) 906 | ::JumpFlood(1, w, h, d0, deltas, cw); // we could copy, but might as well do another round 907 | 908 | delete[] temp; 909 | } 910 | 911 | 912 | namespace 913 | { 914 | // G. Borgefors. 915 | // Distance transformations in digital images. 916 | 917 | template int Chamfer(int w, int h, T distances[]) 918 | { 919 | // according to Borgefors, using these values leads to more 920 | // stable and accurate results than 1, sqrt(2) with double precision. 921 | // See Table 1 in Borgefors '86. The resulting distances must be 922 | // divided by d0 once the algorithm is complete. 923 | 924 | const T d0 = 3; 925 | const T d1 = 4; 926 | 927 | T* row0 = distances; 928 | 929 | for (int x = 1; x < w; x++) // restricted mask for y=0 930 | if (row0[x] > row0[x - 1] + d0) 931 | row0[x] = row0[x - 1] + d0; 932 | 933 | for (int y = 1; y < h; y++) 934 | { 935 | T* row1 = row0 + w; 936 | 937 | if (row1[0] > row0[0] + d0) // restricted mask for x=0 938 | row1[0] = row0[0] + d0; 939 | if (row1[0] > row0[1] + d1) 940 | row1[0] = row0[1] + d1; 941 | 942 | for (int x = 1; x < w - 1; x++) // full mask 943 | { 944 | if (row1[x] > row0[x - 1] + d1) 945 | row1[x] = row0[x - 1] + d1; 946 | if (row1[x] > row0[x ] + d0) 947 | row1[x] = row0[x ] + d0; 948 | if (row1[x] > row0[x + 1] + d1) 949 | row1[x] = row0[x + 1] + d1; 950 | 951 | if (row1[x] > row1[x - 1] + d0) 952 | row1[x] = row1[x - 1] + d0; 953 | } 954 | 955 | if (row1[w - 1] > row0[w - 2] + d1) // restricted mask for x=w-1 956 | row1[w - 1] = row0[w - 2] + d1; 957 | if (row1[w - 1] > row0[w - 1] + d0) 958 | row1[w - 1] = row0[w - 1] + d0; 959 | 960 | if (row1[w - 1] > row1[w - 2] + d0) 961 | row1[w - 1] = row1[w - 2] + d0; 962 | 963 | row0 = row1; 964 | } 965 | 966 | row0 = distances + h * w - w; 967 | 968 | for (int x = w - 2; x >= 0; x--) // restricted reverse mask for y=h-1 969 | if (row0[x] > row0[x + 1] + d0) 970 | row0[x] = row0[x + 1] + d0; 971 | 972 | for (int y = h - 2; y >= 0; y--) 973 | { 974 | T* row1 = row0 - w; 975 | 976 | if (row1[w - 1] > row0[w - 1] + d0) // restricted mask for x=w-1 977 | row1[w - 1] = row0[w - 1] + d0; 978 | if (row1[w - 1] > row0[w - 2] + d1) 979 | row1[w - 1] = row0[w - 2] + d1; 980 | 981 | for (int x = w - 2; x > 0; x--) // full reverse mask 982 | { 983 | if (row1[x] > row0[x + 1] + d1) 984 | row1[x] = row0[x + 1] + d1; 985 | if (row1[x] > row0[x ] + d0) 986 | row1[x] = row0[x ] + d0; 987 | if (row1[x] > row0[x - 1] + d1) 988 | row1[x] = row0[x - 1] + d1; 989 | 990 | if (row1[x] > row1[x + 1] + d0) 991 | row1[x] = row1[x + 1] + d0; 992 | } 993 | 994 | if (row1[0] > row0[1] + d1) // restricted mask for x=0 995 | row1[0] = row0[1] + d1; 996 | if (row1[0] > row0[0] + d0) 997 | row1[0] = row0[0] + d0; 998 | 999 | if (row1[0] > row1[1] + d0) 1000 | row1[0] = row1[1] + d0; 1001 | 1002 | row0 = row1; 1003 | } 1004 | 1005 | return d0; 1006 | } 1007 | } 1008 | 1009 | int DOL::Chamfer(int w, int h, int16_t distances[]) 1010 | { 1011 | return ::Chamfer(w, h, distances); 1012 | } 1013 | 1014 | int DOL::Chamfer(int w, int h, int32_t distances[]) 1015 | { 1016 | return ::Chamfer(w, h, distances); 1017 | } 1018 | 1019 | namespace 1020 | { 1021 | // "Distance Transforms of Sampled Functions", Felzenszwalb and Huttenlocher 1022 | // Uses parabolas to track distance. Is still O(n), and allows float-valued 1023 | // initial per-cell distance estimates, but the parabolas are still centred 1024 | // on the cell, which limits accuracy for higher-than-cell-resolution 1025 | // boundaries. Poor locality, is noticeably slower than the other O(n) 1026 | // algorithms unless everything is in cache. 1027 | void Felzenszwalb(float f[], int n, int v[/* n */], float z[/*n + 1*/]) // 1D version 1028 | { 1029 | int k = 0; 1030 | 1031 | v[0] = 0; 1032 | z[0] = -FLT_MAX; 1033 | z[1] = +FLT_MAX; 1034 | 1035 | for (int q = 1; q < n; q++) 1036 | { 1037 | float s = ((f[q] + (q * q)) - (f[v[k]] + (v[k] * v[k]))) / (2 * q - 2 * v[k]); 1038 | 1039 | while (s <= z[k]) 1040 | { 1041 | k--; 1042 | s = ((f[q] + (q * q)) - (f[v[k]] + (v[k] * v[k]))) / (2 * q - 2 * v[k]); 1043 | } 1044 | 1045 | k++; 1046 | v[k] = q; 1047 | z[k] = s; 1048 | z[k+1] = +FLT_MAX; 1049 | } 1050 | 1051 | k = 0; 1052 | for (int q = 0; q < n; q++) 1053 | { 1054 | while (z[k + 1] < q) 1055 | k++; 1056 | 1057 | f[q] = (q - v[k]) * (q - v[k]) + f[v[k]]; 1058 | } 1059 | } 1060 | } 1061 | 1062 | void DOL::Felzenszwalb(int w, int h, float distances[]) 1063 | { 1064 | int whMax = w >= h ? w : h; 1065 | float* f = new float[whMax]; 1066 | int* v = new int[whMax]; 1067 | float* z = new float[whMax + 1]; 1068 | 1069 | // Transform along columns 1070 | for (int x = 0; x < w; x++) 1071 | { 1072 | for (int y = 0; y < h; y++) 1073 | f[y] = distances[x + y * w]; 1074 | 1075 | ::Felzenszwalb(f, h, v, z); 1076 | 1077 | for (int y = 0; y < h; y++) 1078 | distances[x + y * w] = f[y]; 1079 | } 1080 | 1081 | // transform along rows 1082 | for (int y = 0; y < h; y++) 1083 | ::Felzenszwalb(distances + y * w, w, v, z); 1084 | 1085 | delete[] z; 1086 | delete[] v; 1087 | delete[] f; 1088 | } 1089 | 1090 | 1091 | 1092 | // Brute force -- for checking only! 1093 | void DOL::BruteForce(int w, int h, cCellDelta2 deltas[], int cw) 1094 | { 1095 | for (int i = 0; i < h; i++) 1096 | for (int j = 0; j < w; j++) 1097 | { 1098 | cCellDelta2* cell = deltas + i * w + j; 1099 | 1100 | if (abs(cell->x) >= cw || abs(cell->y) >= cw) // non-boundary cell? 1101 | continue; 1102 | 1103 | // For each occupied cell, iterate over all other cells and check for minimal distance 1104 | 1105 | for (int y = 0; y < h; y++) 1106 | for (int x = 0; x < w; x++) 1107 | { 1108 | cCellDelta2 d2 = { int16_t((j - x) * cw + cell->x), int16_t((i - y) * cw + cell->y) }; 1109 | cCellDelta2* cell2 = deltas + y * w + x; 1110 | 1111 | if (Closer(*cell2, d2)) 1112 | *cell2 = d2; 1113 | } 1114 | } 1115 | } 1116 | 1117 | // dirW is a w x h array of the fractional solid angle 1118 | // obscured in one of the four quadrants we're sweeping over. 1119 | void DOL::InitDirWFromBitMask(int w, int h, const uint32_t mask[], float dirW[]) 1120 | { 1121 | // mask is expected to have rows padded to whole numbers of uint32_ts 1122 | int s = w * h; 1123 | for (int i = 0; i < s * 4; i++) 1124 | dirW[i] = 0.0f; 1125 | 1126 | int maskStride = MaskSize(w); 1127 | 1128 | for (int y = 0; y < h; y++) 1129 | for (int j = 0; j < maskStride; j++) 1130 | { 1131 | uint32_t m = *mask++; 1132 | 1133 | if (m == 0) 1134 | continue; 1135 | 1136 | float* block = dirW + y * w + 32 * j; 1137 | 1138 | for (int i = 0; i < 32; i++) 1139 | { 1140 | if (m & 1) 1141 | { 1142 | block[i + 0 * s] = 1.0f; 1143 | block[i + 1 * s] = 1.0f; 1144 | block[i + 2 * s] = 1.0f; 1145 | block[i + 3 * s] = 1.0f; 1146 | } 1147 | 1148 | m >>= 1; 1149 | } 1150 | } 1151 | } 1152 | 1153 | namespace 1154 | { 1155 | inline float QuadrantCombine(float a, float b) 1156 | { 1157 | // Find new quadrant estimate given same quad estimates from neighbour cells in 1158 | // quad direction. 1159 | // Simplest is to assume a and b cover half the quadrant each. 1160 | // Need a=b=1 -> 1 to avoid light leaking. 1161 | 1162 | #if 0 1163 | return 0.5f * (a + b) - 0.1f * a * b; 1164 | #elif 0 1165 | return 0.5f * (a + b); 1166 | #else 1167 | // if either leads to complete coverage, a + b - ab 1168 | // if max k coverage from either, ka + kb - ksab? but would like a = b = 1 -> 1, get 2k - s = 1, s = 2k-1 1169 | float k = 0.475f; 1170 | float t = 0.975f; 1171 | 1172 | return t * (k * (a + b) - (2 * k - 1) * a * b); 1173 | #endif 1174 | } 1175 | } 1176 | 1177 | 1178 | void DOL::OcclusionSweep(int w, int h, float dirW[]) 1179 | { 1180 | const int sliceStride = w * h; 1181 | const int lastRow = w * (h - 1); 1182 | 1183 | for (int sweep = 0; sweep < 4; sweep++) 1184 | { 1185 | const int sx = (sweep >> 0) & 1; 1186 | const int sy = (sweep >> 1) & 1; 1187 | 1188 | const int dx = 1 - 2 * sx; 1189 | const int dy = 1 - 2 * sy; 1190 | 1191 | const int ib = sx * (w - 1); 1192 | const int ie = w - sx * (w + 1); 1193 | 1194 | float* row0 = (dirW + sweep * sliceStride) + sy * lastRow; 1195 | 1196 | // do first row -- only depends on itself 1197 | for (int i = ib + dx; i != ie; i += dx) 1198 | row0[i] += (1.0f - row0[i]) * QuadrantCombine(row0[i - dx], 0.0f); 1199 | 1200 | for (int j = 1; j < h; j++) 1201 | { 1202 | float* row1 = row0 + w * dy; 1203 | 1204 | // do first cell, only depends on prev row 1205 | row1[ib] += (1.0f - row1[ib]) * QuadrantCombine(row0[ib], 0.0f); 1206 | 1207 | // rest of row -- prev cell and prev row 1208 | for (int i = ib + dx; i != ie; i += dx) 1209 | row1[i] += (1.0f - row1[i]) * QuadrantCombine(row1[i - dx], row0[i]); 1210 | 1211 | row0 = row1; 1212 | } 1213 | } 1214 | } 1215 | 1216 | 1217 | // --- 3D Volumes -------------------------------------------------------------- 1218 | 1219 | uint32_t* DOL::CreateBitMask(int w, int h, int d) 1220 | { 1221 | size_t maskSize = MaskSize(w, h, d); 1222 | uint32_t* mask = new uint32_t[maskSize]; 1223 | 1224 | memset(mask, 0, maskSize * sizeof(uint32_t)); 1225 | 1226 | return mask; 1227 | } 1228 | 1229 | void DOL::BitMaskAddPoints(int w, int h, int d, int count, uint32_t seed, uint32_t mask[]) 1230 | { 1231 | int ws = MaskSize(w); 1232 | 1233 | for (int i = 0; i < count; i++) 1234 | { 1235 | int x = RNG(seed, w); 1236 | int y = RNG(seed, h); 1237 | int z = RNG(seed, d); 1238 | 1239 | assert(x < w); 1240 | assert(y < h); 1241 | assert(z < d); 1242 | 1243 | int fx = x & 31; 1244 | x >>= 5; 1245 | 1246 | int index = z * h * ws + y * ws + x; 1247 | mask[index] |= 1 << fx; 1248 | } 1249 | } 1250 | 1251 | namespace 1252 | { 1253 | void FillMaskBlock(int rs, int ss, int x0, int x1, int y0, int y1, int z0, int z1, uint32_t mask[]) 1254 | { 1255 | for (int z = z0; z <= z1; z++) 1256 | { 1257 | uint32_t* sliceMask = mask + z * ss; 1258 | 1259 | FillMaskBlock(rs, x0, x1, y0, y1, sliceMask); 1260 | } 1261 | } 1262 | } 1263 | 1264 | void DOL::BitMaskAddBlock(int w, int h, int d, int sides, uint32_t mask[]) 1265 | { 1266 | int sw = MaskSize(w); 1267 | int rowStride = sw; 1268 | int sliceStride = sw * h; 1269 | 1270 | int x0 = w / 4; 1271 | int x1 = x0 + w / 2 - 1; 1272 | int y0 = h / 3; 1273 | int y1 = y0 + h / 3 - 1; 1274 | int z0 = d / 3; 1275 | int z1 = z0 + d / 3 - 1; 1276 | 1277 | if (sides > 6) 1278 | return FillMaskBlock(rowStride, sliceStride, x0, x1, y0, y1, z0, z1, mask); 1279 | 1280 | if (sides > 0) // -Z 1281 | FillMaskBlock(rowStride, sliceStride, x0, x1, y0, y1, z0, z0, mask); 1282 | if (sides > 1) // -X 1283 | FillMaskBlock(rowStride, sliceStride, x0, x0, y0, y1, z0, z1, mask); 1284 | if (sides > 2) // -Y 1285 | FillMaskBlock(rowStride, sliceStride, x0, x1, y0, y0, z0, z1, mask); 1286 | if (sides > 3) // +X 1287 | FillMaskBlock(rowStride, sliceStride, x1, x1, y0, y1, z0, z1, mask); 1288 | if (sides > 4) // +Y 1289 | FillMaskBlock(rowStride, sliceStride, x0, x1, y1, y1, z0, z1, mask); 1290 | if (sides > 5) // +Z 1291 | FillMaskBlock(rowStride, sliceStride, x0, x1, y0, y1, z1, z1, mask); 1292 | } 1293 | 1294 | void DOL::InitDeltasFromBitMask(int w, int h, int d, const uint32_t mask[], cCellDelta3 deltas[], bool invert) 1295 | { 1296 | cCellDelta3 maxDelta = { kMaxDelta3, kMaxDelta3, kMaxDelta3 }; 1297 | cCellDelta3 minDelta = { 0, 0, 0 }; 1298 | 1299 | int s = w * h * d; 1300 | for (int i = 0; i < s; i++) 1301 | deltas[i] = maxDelta; 1302 | 1303 | const int maskStride = MaskSize(w); 1304 | const uint32_t allClear = invert ? ~uint32_t(0) : 0; 1305 | 1306 | for (int z = 0; z < d; z++) 1307 | for (int y = 0; y < h; y++) 1308 | for (int j = 0; j < maskStride * 32; j += 32) 1309 | { 1310 | uint32_t m = (*mask++); 1311 | 1312 | if (m == allClear) 1313 | continue; 1314 | 1315 | cCellDelta3* block = deltas + z * h * w + y * w + j; 1316 | const int n = (w - j) > 32 ? 32 : (w - j); 1317 | 1318 | for (int i = 0; i < n; i++) 1319 | { 1320 | if ((m & 1) ^ invert) 1321 | block[i] = minDelta; 1322 | 1323 | m >>= 1; 1324 | } 1325 | } 1326 | } 1327 | 1328 | namespace 1329 | { 1330 | constexpr cCellDelta3 kB3W0 = { +1, 0, 0 }; // level set is half a cell right 1331 | constexpr cCellDelta3 kB3W1 = { -1, 0, 0 }; // level set is half a cell left 1332 | constexpr cCellDelta3 kB3H0 = { 0, +1, 0 }; // level set is half a cell up 1333 | constexpr cCellDelta3 kB3H1 = { 0, -1, 0 }; // level set is half a cell down 1334 | constexpr cCellDelta3 kB3D0 = { 0, 0, +1 }; // level set is half a cell in 1335 | constexpr cCellDelta3 kB3D1 = { 0, 0, -1 }; // level set is half a cell out 1336 | 1337 | #ifdef USE_BORDER_REFERENCE 1338 | void SetBorderCellsBC(int w, int h, int d, const uint32_t* const mask, cCellDelta3 deltas[]) 1339 | { 1340 | int maskStride = MaskSize(w); 1341 | int maskSliceStride = MaskSize(w, h); 1342 | int stride = w; 1343 | int sliceStride = w * h; 1344 | 1345 | for (int z = 0; z < d - 1; z++) 1346 | { 1347 | const uint32_t* maskSlice0 = mask + (z + 0) * maskSliceStride; 1348 | const uint32_t* maskSlice1 = mask + (z + 1) * maskSliceStride; 1349 | 1350 | cCellDelta3* deltasSlice[2] = { deltas + (z + 0) * sliceStride, deltas + (z + 1) * sliceStride }; 1351 | 1352 | const uint32_t* maskRow00 = maskSlice0; 1353 | const uint32_t* maskRow01 = maskSlice0 + maskStride; 1354 | const uint32_t* maskRow10 = maskSlice1; 1355 | const uint32_t* maskRow11 = maskSlice1 + maskStride; 1356 | 1357 | cCellDelta3* rows[2][2] = 1358 | { 1359 | { deltasSlice[0], deltasSlice[0] + stride }, 1360 | { deltasSlice[1], deltasSlice[1] + stride }, 1361 | }; 1362 | 1363 | for (int y = 0; y < h - 1; y++) 1364 | { 1365 | cCellDelta3* block[2][2] = 1366 | { 1367 | { rows[0][0] - 1, rows[0][1] - 1 }, 1368 | { rows[1][0] - 1, rows[1][1] - 1 }, 1369 | }; 1370 | 1371 | uint32_t lastBit00 = maskRow00[0] & 1; 1372 | uint32_t lastBit01 = maskRow01[0] & 1; 1373 | uint32_t lastBit10 = maskRow10[0] & 1; 1374 | uint32_t lastBit11 = maskRow11[0] & 1; 1375 | 1376 | for (int j = 0; j < maskStride; j++) 1377 | { 1378 | // find all cells in 2x2 box (well, 32 shifted sets of them) 1379 | uint32_t m000 = (maskRow00[j] << 1) | lastBit00; 1380 | uint32_t m001 = maskRow00[j]; 1381 | uint32_t m010 = (maskRow01[j] << 1) | lastBit01; 1382 | uint32_t m011 = maskRow01[j]; 1383 | uint32_t m100 = (maskRow10[j] << 1) | lastBit10; 1384 | uint32_t m101 = maskRow10[j]; 1385 | uint32_t m110 = (maskRow11[j] << 1) | lastBit11; 1386 | uint32_t m111 = maskRow11[j]; 1387 | 1388 | lastBit00 = m001 >> 31; 1389 | lastBit01 = m011 >> 31; 1390 | lastBit10 = m101 >> 31; 1391 | lastBit11 = m111 >> 31; 1392 | 1393 | uint32_t ex = (m000 ^ m001); 1394 | uint32_t ey = (m000 ^ m010); 1395 | uint32_t ez = (m000 ^ m100); 1396 | 1397 | uint32_t ezx = (m001 ^ m101); 1398 | uint32_t eyz = (m100 ^ m110); 1399 | uint32_t exy = (m010 ^ m011); 1400 | 1401 | uint32_t fx = (ey ^ eyz); // set if we have a diagonal within x=0 face 1402 | uint32_t fy = (ez ^ ezx); 1403 | uint32_t fz = (ex ^ exy); 1404 | 1405 | uint32_t fzz = eyz ^ m101 ^ m111; // (m100 ^ m101 ^ m110 ^ m111); 1406 | uint32_t bc = (fz ^ fzz); 1407 | 1408 | if (bc) 1409 | for (int i = 0; i < 32; i++) 1410 | { 1411 | if (j * 32 + i >= w) // delayed check as we get here relatively rarely 1412 | break; 1413 | 1414 | if (bc & (1 << i)) // necessary but not sufficient 1415 | { 1416 | int dx = (fx >> i) & 1; 1417 | int dy = (fy >> i) & 1; 1418 | int dz = (fz >> i) & 1; 1419 | 1420 | cCellDelta3& corner = block[dz][dy][dx + i]; 1421 | corner.x = 1 - 2 * dx; 1422 | corner.y = 1 - 2 * dy; 1423 | corner.z = 1 - 2 * dz; 1424 | } 1425 | 1426 | } 1427 | 1428 | block[0][0] += 32; 1429 | block[0][1] += 32; 1430 | block[1][0] += 32; 1431 | block[1][1] += 32; 1432 | } 1433 | 1434 | maskRow00 += maskStride; 1435 | maskRow01 += maskStride; 1436 | maskRow10 += maskStride; 1437 | maskRow11 += maskStride; 1438 | 1439 | rows[0][0] += stride; 1440 | rows[0][1] += stride; 1441 | rows[1][0] += stride; 1442 | rows[1][1] += stride; 1443 | } 1444 | } 1445 | } 1446 | 1447 | void SetBorderCellsFCX(int w, int h, int d, const uint32_t* const mask, cCellDelta3 deltas[]) 1448 | { 1449 | int maskStride = MaskSize(w); 1450 | int maskSliceStride = MaskSize(w, h); 1451 | int stride = w; 1452 | int sliceStride = w * h; 1453 | 1454 | for (int z = 0; z < d - 1; z++) 1455 | { 1456 | const uint32_t* maskSlice0 = mask + (z + 0) * maskSliceStride; 1457 | const uint32_t* maskSlice1 = mask + (z + 1) * maskSliceStride; 1458 | 1459 | cCellDelta3* deltasSlice[2] = { deltas + (z + 0) * sliceStride, deltas + (z + 1) * sliceStride }; 1460 | 1461 | const uint32_t* maskRow00 = maskSlice0; 1462 | const uint32_t* maskRow01 = maskSlice0 + maskStride; 1463 | const uint32_t* maskRow10 = maskSlice1; 1464 | const uint32_t* maskRow11 = maskSlice1 + maskStride; 1465 | 1466 | cCellDelta3* rows[2][2] = 1467 | { 1468 | { deltasSlice[0], deltasSlice[0] + stride }, 1469 | { deltasSlice[1], deltasSlice[1] + stride }, 1470 | }; 1471 | 1472 | for (int y = 0; y < h - 1; y++) 1473 | { 1474 | cCellDelta3* block[2][2] = 1475 | { 1476 | { rows[0][0], rows[0][1] }, 1477 | { rows[1][0], rows[1][1] }, 1478 | }; 1479 | 1480 | for (int j = 0; j < maskStride; j++) 1481 | { 1482 | // find all cells in 2x2 box (well, 32 shifted sets of them) 1483 | uint32_t m000 = maskRow00[j]; 1484 | uint32_t m010 = maskRow01[j]; 1485 | uint32_t m100 = maskRow10[j]; 1486 | uint32_t m110 = maskRow11[j]; 1487 | 1488 | uint32_t ey = (m000 ^ m010); 1489 | uint32_t ez = (m000 ^ m100); 1490 | 1491 | uint32_t eyz = (m100 ^ m110); 1492 | 1493 | uint32_t fx = (ey ^ eyz); // set if we have a diagonal within x=0 face 1494 | 1495 | if (fx) 1496 | for (int i = 0; i < 32; i++) 1497 | { 1498 | if (j * 32 + i >= w) // delayed check as we get here relatively rarely 1499 | break; 1500 | 1501 | if (fx & (1 << i)) 1502 | { 1503 | int dy = (ez >> i) & 1; 1504 | int dz = (ey >> i) & 1; 1505 | 1506 | cCellDelta3& corner = block[dz][dy][i]; 1507 | corner.x = 0; 1508 | corner.y = 1 - 2 * dy; 1509 | corner.z = 1 - 2 * dz; 1510 | } 1511 | } 1512 | 1513 | block[0][0] += 32; 1514 | block[0][1] += 32; 1515 | block[1][0] += 32; 1516 | block[1][1] += 32; 1517 | } 1518 | 1519 | maskRow00 += maskStride; 1520 | maskRow01 += maskStride; 1521 | maskRow10 += maskStride; 1522 | maskRow11 += maskStride; 1523 | 1524 | rows[0][0] += stride; 1525 | rows[0][1] += stride; 1526 | rows[1][0] += stride; 1527 | rows[1][1] += stride; 1528 | } 1529 | } 1530 | } 1531 | 1532 | void SetBorderCellsFCY(int w, int h, int d, const uint32_t* const mask, cCellDelta3 deltas[]) 1533 | { 1534 | int maskStride = MaskSize(w); 1535 | int maskSliceStride = MaskSize(w, h); 1536 | int stride = w; 1537 | int sliceStride = w * h; 1538 | 1539 | for (int z = 0; z < d - 1; z++) 1540 | { 1541 | const uint32_t* maskSlice0 = mask + (z + 0) * maskSliceStride; 1542 | const uint32_t* maskSlice1 = mask + (z + 1) * maskSliceStride; 1543 | 1544 | cCellDelta3* deltasSlice[2] = { deltas + (z + 0) * sliceStride, deltas + (z + 1) * sliceStride }; 1545 | 1546 | const uint32_t* maskRow00 = maskSlice0; 1547 | const uint32_t* maskRow10 = maskSlice1; 1548 | 1549 | cCellDelta3* rows[2] = 1550 | { 1551 | deltasSlice[0], 1552 | deltasSlice[1], 1553 | }; 1554 | 1555 | for (int y = 0; y < h; y++) 1556 | { 1557 | cCellDelta3* block[2] = 1558 | { 1559 | rows[0] - 1, 1560 | rows[1] - 1, 1561 | }; 1562 | 1563 | uint32_t lastBit00 = maskRow00[0] & 1; 1564 | uint32_t lastBit10 = maskRow10[0] & 1; 1565 | 1566 | for (int j = 0; j < maskStride; j++) 1567 | { 1568 | // find all cells in 2x2 box (well, 32 shifted sets of them) 1569 | uint32_t m000 = (maskRow00[j] << 1) | lastBit00; 1570 | uint32_t m001 = maskRow00[j]; 1571 | uint32_t m100 = (maskRow10[j] << 1) | lastBit10; 1572 | uint32_t m101 = maskRow10[j]; 1573 | 1574 | lastBit00 = m001 >> 31; 1575 | lastBit10 = m101 >> 31; 1576 | 1577 | uint32_t ex = (m000 ^ m001); 1578 | uint32_t ez = (m000 ^ m100); 1579 | uint32_t ezx = (m001 ^ m101); 1580 | 1581 | uint32_t fy = (ez ^ ezx); 1582 | 1583 | if (fy) 1584 | for (int i = 0; i < 32; i++) 1585 | { 1586 | if (j * 32 + i >= w) // delayed check as we get here relatively rarely 1587 | break; 1588 | 1589 | if (fy & (1 << i)) 1590 | { 1591 | int dx = (ez >> i) & 1; 1592 | int dz = (ex >> i) & 1; 1593 | 1594 | cCellDelta3& corner = block[dz][dx + i]; 1595 | corner.x = 1 - 2 * dx; 1596 | corner.y = 0; 1597 | corner.z = 1 - 2 * dz; 1598 | } 1599 | } 1600 | 1601 | block[0] += 32; 1602 | block[1] += 32; 1603 | } 1604 | 1605 | maskRow00 += maskStride; 1606 | maskRow10 += maskStride; 1607 | 1608 | rows[0] += stride; 1609 | rows[1] += stride; 1610 | } 1611 | } 1612 | } 1613 | 1614 | void SetBorderCellsFCZ(int w, int h, int d, const uint32_t* const mask, cCellDelta3 deltas[]) 1615 | { 1616 | int maskStride = MaskSize(w); 1617 | int maskSliceStride = MaskSize(w, h); 1618 | int stride = w; 1619 | int sliceStride = w * h; 1620 | 1621 | for (int z = 0; z < d; z++) 1622 | { 1623 | const uint32_t* maskSlice = mask + z * maskSliceStride; 1624 | cCellDelta3* deltasSlice = deltas + z * sliceStride; 1625 | 1626 | const uint32_t* maskRow00 = maskSlice; 1627 | const uint32_t* maskRow01 = maskSlice + maskStride; 1628 | 1629 | cCellDelta3* rows[2] = { deltasSlice, deltasSlice + stride }; 1630 | 1631 | for (int y = 0; y < h - 1; y++) 1632 | { 1633 | cCellDelta3* block[2] = { rows[0] - 1, rows[1] - 1 }; 1634 | 1635 | uint32_t lastBit00 = maskRow00[0] & 1; 1636 | uint32_t lastBit01 = maskRow01[0] & 1; 1637 | 1638 | for (int j = 0; j < maskStride; j++) 1639 | { 1640 | // find all cells in 2x2 box (well, 32 shifted sets of them) 1641 | uint32_t m000 = (maskRow00[j] << 1) | lastBit00; 1642 | uint32_t m001 = maskRow00[j]; 1643 | uint32_t m010 = (maskRow01[j] << 1) | lastBit01; 1644 | uint32_t m011 = maskRow01[j]; 1645 | 1646 | lastBit00 = m001 >> 31; 1647 | lastBit01 = m011 >> 31; 1648 | 1649 | uint32_t ex = (m000 ^ m001); 1650 | uint32_t ey = (m000 ^ m010); 1651 | uint32_t exy = (m010 ^ m011); 1652 | 1653 | uint32_t fz = (ex ^ exy); 1654 | 1655 | if (fz) 1656 | for (int i = 0; i < 32; i++) 1657 | { 1658 | if (j * 32 + i >= w) // delayed check as we get here relatively rarely 1659 | break; 1660 | 1661 | if (fz & (1 << i)) 1662 | { 1663 | int dx = (ey >> i) & 1; 1664 | int dy = (ex >> i) & 1; 1665 | 1666 | cCellDelta3& corner = block[dy][dx + i]; 1667 | corner.x = 1 - 2 * dx; 1668 | corner.y = 1 - 2 * dy; 1669 | corner.z = 0; 1670 | } 1671 | } 1672 | 1673 | block[0] += 32; 1674 | block[1] += 32; 1675 | } 1676 | 1677 | maskRow00 += maskStride; 1678 | maskRow01 += maskStride; 1679 | 1680 | rows[0] += stride; 1681 | rows[1] += stride; 1682 | } 1683 | } 1684 | } 1685 | 1686 | void SetBorderCellsD(int w, int h, int d, const uint32_t* const mask, cCellDelta3 deltas[]) 1687 | { 1688 | // Do depth edges... 1689 | int maskStride = MaskSize(w); 1690 | int maskSliceStride = MaskSize(w, h); 1691 | 1692 | for (int z = 0; z < d - 1; z++) 1693 | for (int y = 0; y < h; y++) 1694 | { 1695 | const uint32_t* maskRow0 = mask + (z + 0) * maskSliceStride + y * maskStride; 1696 | const uint32_t* maskRow1 = mask + (z + 1) * maskSliceStride + y * maskStride; 1697 | cCellDelta3* deltasRow0 = deltas + (z + 0) * w * h + y * w; 1698 | cCellDelta3* deltasRow1 = deltas + (z + 1) * w * h + y * w; 1699 | 1700 | for (int j = 0; j < maskStride; j++) 1701 | { 1702 | uint32_t m0 = maskRow0[j]; 1703 | uint32_t m1 = maskRow1[j]; 1704 | uint32_t mx = m0 ^ m1; // 1 on change 1705 | 1706 | if (mx) 1707 | { 1708 | cCellDelta3* block0 = deltasRow0 + j * 32; 1709 | cCellDelta3* block1 = deltasRow1 + j * 32; 1710 | 1711 | for (int i = 0; i < 32; i++) 1712 | if (mx & (1 << i)) 1713 | { 1714 | if (j * 32 + i >= w) // delayed check as we get here relatively rarely 1715 | break; 1716 | 1717 | assert(j * 32 + i < w); 1718 | 1719 | block0[i] = kB3D0; 1720 | block1[i] = kB3D1; 1721 | } 1722 | } 1723 | } 1724 | } 1725 | } 1726 | 1727 | void SetBorderCellsH(int w, int h, int d, const uint32_t* const mask, cCellDelta3 deltas[]) 1728 | { 1729 | int maskStride = MaskSize(w); 1730 | int maskSliceStride = MaskSize(w, h); 1731 | 1732 | for (int z = 0; z < d; z++) 1733 | { 1734 | const uint32_t* maskSlice = mask + z * maskSliceStride; 1735 | cCellDelta3* deltaSlice = deltas + z * (w * h); 1736 | 1737 | // Do horizontal edges... 1738 | 1739 | for (int y = 0; y < h - 1; y++) 1740 | { 1741 | const uint32_t* maskRow0 = maskSlice + (y + 0) * maskStride; 1742 | const uint32_t* maskRow1 = maskSlice + (y + 1) * maskStride; 1743 | cCellDelta3* deltasRow0 = deltaSlice + (y + 0) * w; 1744 | cCellDelta3* deltasRow1 = deltaSlice + (y + 1) * w; 1745 | 1746 | for (int j = 0; j < maskStride; j++) 1747 | { 1748 | uint32_t m0 = maskRow0[j]; 1749 | uint32_t m1 = maskRow1[j]; 1750 | uint32_t mx = m0 ^ m1; // 1 on change 1751 | 1752 | if (mx) 1753 | { 1754 | cCellDelta3* block0 = deltasRow0 + j * 32; 1755 | cCellDelta3* block1 = deltasRow1 + j * 32; 1756 | 1757 | for (int i = 0; i < 32; i++) 1758 | if (mx & (1 << i)) 1759 | { 1760 | if (j * 32 + i >= w) // delayed check as we get here relatively rarely 1761 | break; 1762 | 1763 | assert(j * 32 + i < w); 1764 | 1765 | block0[i] = kB3H0; 1766 | block1[i] = kB3H1; 1767 | } 1768 | } 1769 | } 1770 | } 1771 | } 1772 | } 1773 | 1774 | void SetBorderCellsW(int w, int h, int d, const uint32_t* const mask, cCellDelta3 deltas[]) 1775 | { 1776 | int maskStride = MaskSize(w); 1777 | int maskSliceStride = MaskSize(w, h); 1778 | 1779 | for (int z = 0; z < d; z++) 1780 | { 1781 | const uint32_t* maskSlice = mask + maskSliceStride * z; 1782 | cCellDelta3* deltaSlice = deltas + w * h * z; 1783 | 1784 | // Do vertical edges... 1785 | 1786 | for (int y = 0; y < h; y++) 1787 | { 1788 | const uint32_t* maskRow = maskSlice + y * maskStride; 1789 | cCellDelta3* deltasRow = deltaSlice + y * w; 1790 | 1791 | uint32_t lastBit = maskRow[0] & 1; 1792 | 1793 | for (int j = 0; j < maskStride; j++) 1794 | { 1795 | uint32_t m0 = maskRow[j]; 1796 | uint32_t m1 = (m0 << 1) | lastBit; 1797 | lastBit = m0 >> 31; 1798 | 1799 | uint32_t mx = m0 ^ m1; 1800 | 1801 | if (mx) 1802 | { 1803 | cCellDelta3* block = deltasRow + j * 32; 1804 | 1805 | for (int i = 0; i < 32; i++) 1806 | if (mx & (1 << i)) 1807 | { 1808 | if (j * 32 + i >= w) // delayed check as we get here relatively rarely 1809 | break; 1810 | 1811 | assert(j * 32 + i > 0); 1812 | assert(j * 32 + i < w); 1813 | 1814 | block[i - 1] = kB3W0; 1815 | block[i + 0] = kB3W1; 1816 | } 1817 | } 1818 | } 1819 | } 1820 | } 1821 | } 1822 | 1823 | #else 1824 | 1825 | void SetBorderCellsCombo(int w, int h, int d, const uint32_t* const mask, cCellDelta3 deltas[]) 1826 | { 1827 | // Do depth edges... 1828 | int maskStride = MaskSize(w); 1829 | int maskSliceStride = MaskSize(w, h); 1830 | int stride = w; 1831 | int sliceStride = w * h; 1832 | 1833 | for (int z = 0; z < d - 1; z++) 1834 | { 1835 | const uint32_t* maskSlice0 = mask + (z + 0) * maskSliceStride; 1836 | const uint32_t* maskSlice1 = mask + (z + 1) * maskSliceStride; 1837 | 1838 | cCellDelta3* deltasSlice[2] = { deltas + (z + 0) * sliceStride, deltas + (z + 1) * sliceStride }; 1839 | 1840 | const uint32_t* maskRow00 = maskSlice0; 1841 | const uint32_t* maskRow01 = maskSlice0 + maskStride; 1842 | const uint32_t* maskRow10 = maskSlice1; 1843 | const uint32_t* maskRow11 = maskSlice1 + maskStride; 1844 | 1845 | cCellDelta3* rows[2][2] = 1846 | { 1847 | { deltasSlice[0], deltasSlice[0] + stride }, 1848 | { deltasSlice[1], deltasSlice[1] + stride }, 1849 | }; 1850 | 1851 | for (int y = 0; y < h - 1; y++) 1852 | { 1853 | cCellDelta3* block[2][2] = 1854 | { 1855 | { rows[0][0] - 1, rows[0][1] - 1 }, 1856 | { rows[1][0] - 1, rows[1][1] - 1 }, 1857 | }; 1858 | 1859 | uint32_t lastBit00 = maskRow00[0] & 1; 1860 | uint32_t lastBit01 = maskRow01[0] & 1; 1861 | uint32_t lastBit10 = maskRow10[0] & 1; 1862 | uint32_t lastBit11 = maskRow11[0] & 1; 1863 | 1864 | for (int j = 0; j < maskStride; j++) 1865 | { 1866 | // find all cells in 2x2 box (well, 32 shifted sets of them) 1867 | uint32_t m000 = (maskRow00[j] << 1) | lastBit00; 1868 | uint32_t m001 = maskRow00[j]; 1869 | uint32_t m010 = (maskRow01[j] << 1) | lastBit01; 1870 | uint32_t m011 = maskRow01[j]; 1871 | uint32_t m100 = (maskRow10[j] << 1) | lastBit10; 1872 | uint32_t m101 = maskRow10[j]; 1873 | uint32_t m110 = (maskRow11[j] << 1) | lastBit11; 1874 | uint32_t m111 = maskRow11[j]; 1875 | 1876 | lastBit00 = m001 >> 31; 1877 | lastBit01 = m011 >> 31; 1878 | lastBit10 = m101 >> 31; 1879 | lastBit11 = m111 >> 31; 1880 | 1881 | uint32_t ex = (m000 ^ m001); 1882 | uint32_t ey = (m000 ^ m010); 1883 | uint32_t ez = (m000 ^ m100); 1884 | 1885 | uint32_t ezx = (m001 ^ m101); 1886 | uint32_t eyz = (m100 ^ m110); 1887 | uint32_t exy = (m010 ^ m011); 1888 | 1889 | uint32_t fx = (ey ^ eyz); // set if we have a diagonal within x=0 face 1890 | uint32_t fy = (ez ^ ezx); 1891 | uint32_t fz = (ex ^ exy); 1892 | 1893 | uint32_t eyx = (m001 ^ m011); 1894 | uint32_t eyxz = (m101 ^ m111); 1895 | 1896 | uint32_t fxx = eyx ^ eyxz; 1897 | 1898 | uint32_t fzz = eyz ^ eyxz; 1899 | uint32_t bc = (fz ^ fzz); // Necessary but not sufficient, we're just checking z=0 has three cells the same vs z=1 doesn't (or vice versa). Incorrectly identified box corners will always be overwritten however. 1900 | 1901 | // if (ex | ey | ez | fxx | fy | fz | bc) { early out makes no appreciable difference 1902 | { 1903 | // The order here is important as iterating over the masks destroys them 1904 | ITER_SET_BITS_BEGIN(bc) 1905 | assert(j * 32 + i > 0); 1906 | 1907 | int dx = (fx >> i) & 1; 1908 | int dy = (fy >> i) & 1; 1909 | int dz = (fz >> i) & 1; 1910 | 1911 | cCellDelta3& corner = block[dz][dy][dx + i]; 1912 | if (corner.x == kMaxDelta3) 1913 | { 1914 | corner.x = 1 - 2 * dx; 1915 | corner.y = 1 - 2 * dy; 1916 | corner.z = 1 - 2 * dz; 1917 | } 1918 | } 1919 | 1920 | ITER_SET_BITS_BEGIN(fxx) 1921 | int dy = (ezx >> i) & 1; 1922 | int dz = (eyx >> i) & 1; 1923 | 1924 | cCellDelta3& corner = block[dz][dy][1 + i]; 1925 | if (corner.x == kMaxDelta3 || (abs(corner.x) + abs(corner.y) + abs(corner.z) > 1)) 1926 | { 1927 | corner.x = 0; 1928 | corner.y = 1 - 2 * dy; 1929 | corner.z = 1 - 2 * dz; 1930 | } 1931 | ITER_SET_BITS_END 1932 | 1933 | ITER_SET_BITS_BEGIN(fy) 1934 | assert(j * 32 + i > 0); 1935 | 1936 | int dx = (ez >> i) & 1; 1937 | int dz = (ex >> i) & 1; 1938 | 1939 | cCellDelta3& corner = block[dz][0][dx + i]; 1940 | if (corner.x == kMaxDelta3 || (abs(corner.x) + abs(corner.y) + abs(corner.z) > 1)) 1941 | { 1942 | corner.x = 1 - 2 * dx; 1943 | corner.y = 0; 1944 | corner.z = 1 - 2 * dz; 1945 | } 1946 | ITER_SET_BITS_END 1947 | 1948 | ITER_SET_BITS_BEGIN(fz) 1949 | assert(j * 32 + i > 0); 1950 | 1951 | int dx = (ey >> i) & 1; 1952 | int dy = (ex >> i) & 1; 1953 | 1954 | cCellDelta3& corner = block[0][dy][dx + i]; 1955 | if (corner.x == kMaxDelta3 || (abs(corner.x) + abs(corner.y) + abs(corner.z) > 1)) 1956 | { 1957 | corner.x = 1 - 2 * dx; 1958 | corner.y = 1 - 2 * dy; 1959 | corner.z = 0; 1960 | } 1961 | ITER_SET_BITS_END 1962 | 1963 | ITER_SET_BITS_BEGIN(ex) 1964 | assert(j * 32 + i > 0); 1965 | 1966 | block[0][0][i + 0] = kB3W0; 1967 | block[0][0][i + 1] = kB3W1; 1968 | ITER_SET_BITS_END 1969 | 1970 | ITER_SET_BITS_BEGIN(eyx) 1971 | block[0][0][i + 1] = kB3H0; 1972 | block[0][1][i + 1] = kB3H1; 1973 | ITER_SET_BITS_END 1974 | 1975 | ITER_SET_BITS_BEGIN (ezx) 1976 | block[0][0][i + 1] = kB3D0; 1977 | block[1][0][i + 1] = kB3D1; 1978 | ITER_SET_BITS_END 1979 | } 1980 | 1981 | block[0][0] += 32; 1982 | block[0][1] += 32; 1983 | block[1][0] += 32; 1984 | block[1][1] += 32; 1985 | } 1986 | 1987 | maskRow00 += maskStride; 1988 | maskRow01 += maskStride; 1989 | maskRow10 += maskStride; 1990 | maskRow11 += maskStride; 1991 | 1992 | rows[0][0] += stride; 1993 | rows[0][1] += stride; 1994 | rows[1][0] += stride; 1995 | rows[1][1] += stride; 1996 | } 1997 | 1998 | // do last fy without everything else 1999 | { 2000 | cCellDelta3* block[2] = 2001 | { 2002 | rows[0][0] - 1, 2003 | rows[1][0] - 1, 2004 | }; 2005 | 2006 | uint32_t lastBit00 = maskRow00[0] & 1; 2007 | uint32_t lastBit10 = maskRow10[0] & 1; 2008 | 2009 | for (int j = 0; j < maskStride; j++) 2010 | { 2011 | uint32_t m000 = (maskRow00[j] << 1) | lastBit00; 2012 | uint32_t m001 = maskRow00[j]; 2013 | uint32_t m100 = (maskRow10[j] << 1) | lastBit10; 2014 | uint32_t m101 = maskRow10[j]; 2015 | 2016 | lastBit00 = m001 >> 31; 2017 | lastBit10 = m101 >> 31; 2018 | 2019 | uint32_t ex = (m000 ^ m001); 2020 | uint32_t ez = (m000 ^ m100); 2021 | uint32_t ezx = (m001 ^ m101); 2022 | 2023 | uint32_t fy = (ez ^ ezx); 2024 | 2025 | ITER_SET_BITS_BEGIN(fy) 2026 | assert(j * 32 + i > 0); 2027 | 2028 | int dx = (ez >> i) & 1; 2029 | int dz = (ex >> i) & 1; 2030 | 2031 | cCellDelta3& corner = block[dz][dx + i]; 2032 | if (corner.x == kMaxDelta3 || (abs(corner.x) + abs(corner.y) + abs(corner.z) > 1)) 2033 | { 2034 | corner.x = 1 - 2 * dx; 2035 | corner.y = 0; 2036 | corner.z = 1 - 2 * dz; 2037 | } 2038 | ITER_SET_BITS_END 2039 | 2040 | ITER_SET_BITS_BEGIN(ex) 2041 | assert(j * 32 + i > 0); 2042 | 2043 | block[0][i + 0] = kB3W0; 2044 | block[0][i + 1] = kB3W1; 2045 | ITER_SET_BITS_END 2046 | 2047 | ITER_SET_BITS_BEGIN(ezx) 2048 | block[0][i + 1] = kB3D0; 2049 | block[1][i + 1] = kB3D1; 2050 | ITER_SET_BITS_END 2051 | 2052 | block[0] += 32; 2053 | block[1] += 32; 2054 | } 2055 | } 2056 | } 2057 | 2058 | // Now for the last slice, do the fz faces, and ex/ey edges 2059 | { 2060 | int z = d - 1; 2061 | const uint32_t* maskSlice0 = mask + z * maskSliceStride; 2062 | cCellDelta3* deltasSlice = deltas + z * sliceStride; 2063 | 2064 | const uint32_t* maskRow00 = maskSlice0; 2065 | const uint32_t* maskRow01 = maskSlice0 + maskStride; 2066 | 2067 | cCellDelta3* rows[2] = { deltasSlice, deltasSlice + stride }; 2068 | 2069 | for (int y = 0; y < h - 1; y++) 2070 | { 2071 | cCellDelta3* block[2] = { rows[0] - 1, rows[1] - 1 }; 2072 | 2073 | uint32_t lastBit00 = maskRow00[0] & 1; 2074 | uint32_t lastBit01 = maskRow01[0] & 1; 2075 | 2076 | for (int j = 0; j < maskStride; j++) 2077 | { 2078 | uint32_t m000 = (maskRow00[j] << 1) | lastBit00; 2079 | uint32_t m001 = maskRow00[j]; 2080 | uint32_t m010 = (maskRow01[j] << 1) | lastBit01; 2081 | uint32_t m011 = maskRow01[j]; 2082 | 2083 | lastBit00 = m001 >> 31; 2084 | lastBit01 = m011 >> 31; 2085 | 2086 | uint32_t ex = (m000 ^ m001); 2087 | uint32_t ey = (m000 ^ m010); 2088 | uint32_t eyx = (m001 ^ m011); 2089 | 2090 | uint32_t fz = ey ^ eyx; 2091 | 2092 | ITER_SET_BITS_BEGIN(fz) 2093 | assert(j * 32 + i > 0); 2094 | 2095 | int dx = (ey >> i) & 1; 2096 | int dy = (ex >> i) & 1; 2097 | 2098 | assert(i != 0 || j != 0 || dx != 0); 2099 | 2100 | cCellDelta3& corner = block[dy][dx + i]; 2101 | if (corner.x == kMaxDelta3 || (abs(corner.x) + abs(corner.y) + abs(corner.z) > 1)) 2102 | { 2103 | corner.x = 1 - 2 * dx; 2104 | corner.y = 1 - 2 * dy; 2105 | corner.z = 0; 2106 | } 2107 | ITER_SET_BITS_END 2108 | 2109 | ITER_SET_BITS_BEGIN(ex) 2110 | assert(j * 32 + i > 0); 2111 | 2112 | block[0][i + 0] = kB3W0; 2113 | block[0][i + 1] = kB3W1; 2114 | ITER_SET_BITS_END 2115 | 2116 | ITER_SET_BITS_BEGIN(eyx) 2117 | block[0][i + 1] = kB3H0; 2118 | block[1][i + 1] = kB3H1; 2119 | ITER_SET_BITS_END 2120 | 2121 | block[0] += 32; 2122 | block[1] += 32; 2123 | } 2124 | 2125 | maskRow00 += maskStride; 2126 | maskRow01 += maskStride; 2127 | 2128 | rows[0] += stride; 2129 | rows[1] += stride; 2130 | } 2131 | 2132 | // do last ex without everything else 2133 | cCellDelta3* block = rows[0] - 1; 2134 | uint32_t lastBit00 = maskRow00[0] & 1; 2135 | 2136 | for (int j = 0; j < maskStride; j++) 2137 | { 2138 | uint32_t m000 = (maskRow00[j] << 1) | lastBit00; 2139 | uint32_t m001 = maskRow00[j]; 2140 | 2141 | lastBit00 = m001 >> 31; 2142 | 2143 | uint32_t ex = (m000 ^ m001); 2144 | 2145 | ITER_SET_BITS_BEGIN(ex) 2146 | assert(j * 32 + i > 0); 2147 | 2148 | block[i + 0] = kB3W0; 2149 | block[i + 1] = kB3W1; 2150 | ITER_SET_BITS_END 2151 | 2152 | block += 32; 2153 | } 2154 | } 2155 | } 2156 | #endif 2157 | } 2158 | 2159 | void DOL::InitBorderDeltasFromBitMask(int w, int h, int d, const uint32_t mask[], cCellDelta3 deltas[]) 2160 | { 2161 | const cCellDelta3 maxDelta = { kMaxDelta3, kMaxDelta3, kMaxDelta3 }; 2162 | 2163 | int s = w * h * d; 2164 | for (int i = 0; i < s; i++) 2165 | deltas[i] = maxDelta; 2166 | 2167 | #ifdef USE_BORDER_REFERENCE 2168 | SetBorderCellsBC (w, h, d, mask, deltas); 2169 | SetBorderCellsFCZ(w, h, d, mask, deltas); 2170 | SetBorderCellsFCY(w, h, d, mask, deltas); 2171 | SetBorderCellsFCX(w, h, d, mask, deltas); 2172 | SetBorderCellsD (w, h, d, mask, deltas); 2173 | SetBorderCellsH (w, h, d, mask, deltas); 2174 | SetBorderCellsW (w, h, d, mask, deltas); 2175 | #else 2176 | SetBorderCellsCombo(w, h, d, mask, deltas); 2177 | #endif 2178 | } 2179 | 2180 | namespace 2181 | { 2182 | inline bool Closer(cCellDelta3 d0, cCellDelta3 d1) 2183 | { 2184 | int dw0 = d0.x * d0.x + d0.y * d0.y + d0.z * d0.z; 2185 | int dw1 = d1.x * d1.x + d1.y * d1.y + d1.z * d1.z; 2186 | 2187 | return dw0 > dw1; 2188 | } 2189 | } 2190 | 2191 | void DOL::FastSweep(int w, int h, int d, cCellDelta3 deltas[], int cw) 2192 | { 2193 | // Fast sweep approach -- sweep each diagonal in turn. 2194 | // C B 2195 | // A x 2196 | const int sliceStride = w * h; 2197 | const int lastRow = w * (h - 1); 2198 | 2199 | for (int sweep = 0; sweep < 8; sweep++) 2200 | { 2201 | const int sx = (sweep >> 0) & 1; 2202 | const int sy = (sweep >> 1) & 1; 2203 | const int sz = (sweep >> 2) & 1; 2204 | 2205 | const int dx = 1 - 2 * sx; 2206 | const int dy = 1 - 2 * sy; 2207 | const int dz = 1 - 2 * sz; 2208 | 2209 | const int cx = -cw * dx; 2210 | const int cy = -cw * dy; 2211 | const int cz = -cw * dz; 2212 | 2213 | const int ib = sx * (w - 1); 2214 | const int ie = w - sx * (w + 1); 2215 | 2216 | cCellDelta3* slice0 = deltas + sz * (d - 1) * sliceStride; 2217 | cCellDelta3 cell; 2218 | 2219 | for (int k = 1; k < d; k++) 2220 | { 2221 | cCellDelta3* slice1 = slice0 + sliceStride * dz; 2222 | 2223 | cCellDelta3* row00 = slice0 + sy * lastRow; 2224 | cCellDelta3* row01 = slice1 + sy * lastRow; 2225 | 2226 | for (int j = 1; j < h; j++) 2227 | { 2228 | cCellDelta3* row10 = row00 + w * dy; 2229 | cCellDelta3* row11 = row01 + w * dy; 2230 | 2231 | for (int i = ib; i != ie; i += dx) 2232 | { 2233 | cell.x = row10[i].x; 2234 | cell.y = row10[i].y; 2235 | cell.z = row10[i].z + cz; 2236 | 2237 | if (Closer(row11[i], cell)) 2238 | row11[i] = cell; 2239 | 2240 | cell.x = row01[i].x; 2241 | cell.y = row01[i].y + cy; 2242 | cell.z = row01[i].z; 2243 | 2244 | if (Closer(row11[i], cell)) 2245 | row11[i] = cell; 2246 | } 2247 | 2248 | for (int i = ib + dx; i != ie; i += dx) 2249 | { 2250 | cell.x = row11[i - dx].x + cx; 2251 | cell.y = row11[i - dx].y; 2252 | cell.z = row11[i - dx].z; 2253 | 2254 | if (Closer(row11[i], cell)) 2255 | row11[i] = cell; 2256 | } 2257 | 2258 | row00 = row10; 2259 | row01 = row11; 2260 | } 2261 | 2262 | slice0 = slice1; 2263 | } 2264 | } 2265 | } 2266 | 2267 | namespace 2268 | { 2269 | void DanielssonSlice(int w, int h, cCellDelta3 deltas[], int cw) 2270 | { 2271 | cCellDelta3* row0 = deltas; 2272 | cCellDelta3 cell; 2273 | 2274 | for (int j = 1; j < h; j++) 2275 | { 2276 | cCellDelta3* row1 = row0 + w; 2277 | 2278 | for (int i = 0; i < w; i++) 2279 | { 2280 | cell.x = row0[i].x; 2281 | cell.y = row0[i].y - cw; 2282 | cell.z = row0[i].z; 2283 | 2284 | if (Closer(row1[i], cell)) 2285 | row1[i] = cell; 2286 | } 2287 | 2288 | for (int i = 1; i < w; i++) 2289 | { 2290 | cell.x = row1[i - 1].x - cw; 2291 | cell.y = row1[i - 1].y; 2292 | cell.z = row1[i - 1].z; 2293 | 2294 | if (Closer(row1[i], cell)) 2295 | row1[i] = cell; 2296 | } 2297 | 2298 | for (int i = w - 2; i >= 0; i--) 2299 | { 2300 | 2301 | cell.x = row1[i + 1].x + cw; 2302 | cell.y = row1[i + 1].y; 2303 | cell.z = row1[i + 1].z; 2304 | 2305 | if (Closer(row1[i], cell)) 2306 | row1[i] = cell; 2307 | } 2308 | 2309 | row0 = row1; 2310 | } 2311 | 2312 | row0 = deltas + h * w - w; 2313 | 2314 | for (int j = h - 1; j > 0; j--) 2315 | { 2316 | cCellDelta3* row1 = row0 - w; 2317 | 2318 | for (int i = 0; i < w; i++) 2319 | { 2320 | cell.x = row0[i].x; 2321 | cell.y = row0[i].y + cw; 2322 | cell.z = row0[i].z; 2323 | 2324 | if (Closer(row1[i], cell)) 2325 | row1[i] = cell; 2326 | } 2327 | 2328 | for (int i = 1; i < w; i++) 2329 | { 2330 | cell.x = row1[i - 1].x - cw; 2331 | cell.y = row1[i - 1].y; 2332 | cell.z = row1[i - 1].z; 2333 | 2334 | if (Closer(row1[i], cell)) 2335 | row1[i] = cell; 2336 | } 2337 | 2338 | for (int i = w - 2; i >= 0; i--) 2339 | { 2340 | cell.x = row1[i + 1].x + cw; 2341 | cell.y = row1[i + 1].y; 2342 | cell.z = row1[i + 1].z; 2343 | 2344 | if (Closer(row1[i], cell)) 2345 | row1[i] = cell; 2346 | } 2347 | 2348 | row0 = row1; 2349 | } 2350 | } 2351 | } 2352 | 2353 | void DOL::Danielsson(int w, int h, int d, cCellDelta3 deltas[], int cw) 2354 | { 2355 | int sliceStride = w * h; 2356 | 2357 | cCellDelta3* slice0 = deltas; 2358 | cCellDelta3 cell; 2359 | 2360 | for (int k = 1; k < d; k++) 2361 | { 2362 | cCellDelta3* slice1 = slice0 + sliceStride; 2363 | 2364 | for (int i = 0; i < sliceStride; i++) 2365 | { 2366 | cell.x = slice0[i].x; 2367 | cell.y = slice0[i].y; 2368 | cell.z = slice0[i].z - cw; 2369 | 2370 | if (Closer(slice1[i], cell)) 2371 | slice1[i] = cell; 2372 | } 2373 | 2374 | DanielssonSlice(w, h, slice1, cw); 2375 | 2376 | slice0 = slice1; 2377 | } 2378 | 2379 | slice0 = deltas + sliceStride * (d - 1); 2380 | 2381 | for (int k = d - 2; k >= 0; k--) 2382 | { 2383 | cCellDelta3* slice1 = slice0 - sliceStride; 2384 | 2385 | for (int i = 0; i < sliceStride; i++) 2386 | { 2387 | cell.x = slice0[i].x; 2388 | cell.y = slice0[i].y; 2389 | cell.z = slice0[i].z + cw; 2390 | 2391 | if (Closer(slice1[i], cell)) 2392 | slice1[i] = cell; 2393 | } 2394 | 2395 | DanielssonSlice(w, h, slice1, cw); 2396 | 2397 | slice0 = slice1; 2398 | } 2399 | } 2400 | 2401 | namespace 2402 | { 2403 | void JumpFlood(int step, int w, int h, int d, const cCellDelta3 deltasIn[], cCellDelta3 deltasOut[], int cw) 2404 | { 2405 | const cCellDelta3* row = deltasIn; 2406 | cCellDelta3* rowOut = deltasOut; 2407 | 2408 | int wh = w * h; 2409 | 2410 | for (int k = 0; k < d; k++) 2411 | { 2412 | int kc0 = (k - step) >= 0 ? -step : 0; 2413 | int kc1 = (k + step) < d ? +step : 0; 2414 | 2415 | for (int j = 0; j < h; j++) 2416 | { 2417 | int jc0 = (j - step) >= 0 ? -step : 0; 2418 | int jc1 = (j + step) < h ? +step : 0; 2419 | 2420 | for (int i = 0; i < w; i++) 2421 | { 2422 | cCellDelta3 minCell = row[i]; 2423 | 2424 | int ic0 = (i - step) >= 0 ? -step : 0; 2425 | int ic1 = (i + step) < w ? +step : 0; 2426 | 2427 | for (int kc = kc0; kc <= kc1; kc += step) 2428 | for (int jc = jc0; jc <= jc1; jc += step) 2429 | for (int ic = ic0; ic <= ic1; ic += step) 2430 | { 2431 | cCellDelta3 cell = row[i + ic + jc * w + kc * wh]; 2432 | 2433 | if (cell.x != kMaxDelta3) 2434 | { 2435 | cell.x += cw * ic; 2436 | cell.y += cw * jc; 2437 | cell.z += cw * kc; 2438 | 2439 | if (Closer(minCell, cell)) 2440 | minCell = cell; 2441 | } 2442 | } 2443 | 2444 | rowOut[i] = minCell; 2445 | } 2446 | 2447 | row += w; 2448 | rowOut += w; 2449 | } 2450 | } 2451 | } 2452 | } 2453 | 2454 | void DOL::JumpFlood(int w, int h, int d, cCellDelta3 deltas[], int cw) 2455 | { 2456 | cCellDelta3* temp = new cCellDelta3[w * h * d]; 2457 | cCellDelta3* d0 = deltas; 2458 | cCellDelta3* d1 = temp; 2459 | 2460 | int wh = w >= h ? w : h; 2461 | int step = FloorPow2((wh >= d ? wh : d) - 1); 2462 | 2463 | for ( ; step > 0; step /= 2) 2464 | { 2465 | ::JumpFlood(step, w, h, d, d0, d1, cw); 2466 | 2467 | cCellDelta3* dt = d0; 2468 | d0 = d1; 2469 | d1 = dt; 2470 | } 2471 | 2472 | if (d0 != deltas) 2473 | ::JumpFlood(1, w, h, d, d0, deltas, cw); // we could copy, but might as well do another round 2474 | 2475 | delete[] temp; 2476 | } 2477 | 2478 | 2479 | // Brute force -- for checking only! 2480 | void DOL::BruteForce(int w, int h, int d, cCellDelta3 deltas[], int cw) 2481 | { 2482 | for (int k = 0; k < d; k++) 2483 | for (int j = 0; j < h; j++) 2484 | for (int i = 0; i < w; i++) 2485 | { 2486 | cCellDelta3* cell = deltas + k * w * h + j * w + i; 2487 | 2488 | if (abs(cell->x) >= cw || abs(cell->y) >= cw || abs(cell->z) >= cw) // non-border cell 2489 | continue; 2490 | 2491 | // For each occupied cell, iterate over all other cells and check for minimal distance 2492 | for (int z = 0; z < d; z++) 2493 | for (int y = 0; y < h; y++) 2494 | for (int x = 0; x < w; x++) 2495 | { 2496 | cCellDelta3* cell = deltas + z * w * h + y * w + x; 2497 | 2498 | int d0 = (i - x) * (i - x) + (j - y) * (j - y) + (k - z) * (k - z); // d2 to i, j 2499 | int d1 = cell->x * cell->x + cell->y * cell->y + cell->z * cell->z; // current 2500 | 2501 | if (d0 < d1) 2502 | { 2503 | cell->x = i - x; 2504 | cell->y = j - y; 2505 | cell->z = k - z; 2506 | } 2507 | } 2508 | } 2509 | } 2510 | 2511 | void DOL::InitDirWFromBitMask(int w, int h, int d, const uint32_t mask[], float dirW[]) 2512 | /// Initialise octants from bit mask. 2513 | { 2514 | int s = w * h * d; 2515 | for (int i = 0; i < s * 8; i++) 2516 | dirW[i] = 0.0f; 2517 | 2518 | const int maskStride = MaskSize(w); 2519 | 2520 | for (int z = 0; z < d; z++) 2521 | for (int y = 0; y < h; y++) 2522 | for (int j = 0; j < maskStride * 32; j += 32) 2523 | { 2524 | uint32_t m = (*mask++); 2525 | 2526 | if (m == 0) 2527 | continue; 2528 | 2529 | float* block = dirW + z * h * w + y * w + j; 2530 | 2531 | for (int i = 0; i < 32; i++) 2532 | { 2533 | if (m & 1) 2534 | { 2535 | for (int io = 0; io < 8; io++) 2536 | block[i + s * io] = 1.0f; 2537 | } 2538 | 2539 | m >>= 1; 2540 | } 2541 | } 2542 | } 2543 | 2544 | 2545 | namespace 2546 | { 2547 | inline float OctantCombine(float a, float b, float c) 2548 | { 2549 | #if 1 2550 | return 0.33333333f * (a + b + c - a * b * c); 2551 | #elif 1 2552 | return 0.33333333f * (a + b + c); 2553 | #else 2554 | 2555 | // a + b + c - (ab + bc + ca) + abc -> abc =s -> 3s - 3s^2 + s^3 2556 | // if max k coverage from either, ka + kb + kc - (ab + bc + ac - abc) but would like a = b = 1 -> 1, get 2k - s = 1, s = 2k-1 2557 | float k = 0.4f; 2558 | float t = 0.975f; 2559 | return t * (k * (a + b) - (2 * k - 1) * a * b); 2560 | #endif 2561 | } 2562 | 2563 | // ix = input occlusion, ox = output occlusion 2564 | inline float OctantCombine(float oa, float ob, float oc, float ia, float ib, float ic) 2565 | { 2566 | float a = ia + (1.0f - ia) * oa; 2567 | float b = ib + (1.0f - ib) * ob; 2568 | float c = ic + (1.0f - ic) * oc; 2569 | 2570 | return 0.3333333f * (a + b + c - a * b * c); 2571 | } 2572 | 2573 | void OcclusionSweep 2574 | ( 2575 | int w, int h, int d, // volume size 2576 | int sx, int sy, int sz, // signs of sweep direction 2577 | 2578 | float dirW[] // in/out: seed occlusion, will be updated with swept occlusion 2579 | ) 2580 | { 2581 | const int dx = 1 - 2 * sx; 2582 | const int dy = 1 - 2 * sy; 2583 | const int dz = 1 - 2 * sz; 2584 | 2585 | const int ib = sx * (w - 1); 2586 | const int ie = w - sx * (w + 1); 2587 | 2588 | const int wh = w * h; // slice stride 2589 | const int whd = w * h * d; // volume stride 2590 | 2591 | const int rowStart = sy * (wh - w); 2592 | const int sliceStart = sz * (whd - wh); 2593 | 2594 | float* slice0 = dirW + sliceStart; 2595 | 2596 | // do first slice -- only depends on itself 2597 | { 2598 | float* row0 = slice0 + rowStart; 2599 | 2600 | // as does its first row 2601 | for (int i = ib + dx; i != ie; i += dx) 2602 | row0[i] += (1.0f - row0[i]) * OctantCombine(row0[i - dx], 0.0f, 0.0f); 2603 | 2604 | for (int j = 1; j < h; j++) 2605 | { 2606 | float* row1 = row0 + w * dy; 2607 | 2608 | // do first cell, only depends on prev row 2609 | row1[ib] += (1.0f - row1[ib]) * OctantCombine(0.0f, row0[ib], 0.0f); 2610 | 2611 | // rest of row -- prev cell and prev row 2612 | for (int i = ib + dx; i != ie; i += dx) 2613 | row1[i] += (1.0f - row1[i]) * OctantCombine(row1[i - dx], row0[i], 0.0f); 2614 | 2615 | row0 = row1; 2616 | } 2617 | } 2618 | 2619 | // do remaining slices 2620 | for (int k = 1; k < d; k++) 2621 | { 2622 | float* slice1 = slice0 + wh * dz; 2623 | 2624 | float* row00 = slice0 + rowStart; 2625 | float* row01 = slice1 + rowStart; 2626 | 2627 | // do first cell of first row, only depends on prev slice 2628 | row01[ib] += (1.0f - row01[ib]) * OctantCombine(0.0f, 0.0f, row00[ib]); 2629 | 2630 | // do first row -- only depends on itself and prev slice 2631 | for (int i = ib + dx; i != ie; i += dx) 2632 | row01[i] += (1.0f - row01[i]) * OctantCombine(row01[i - dx], 0.0f, row00[i]); 2633 | 2634 | for (int j = 1; j < h; j++) 2635 | { 2636 | float* row10 = row00 + w * dy; 2637 | float* row11 = row01 + w * dy; 2638 | 2639 | // do first cell, only depends on prev row and prev slice 2640 | row11[ib] += (1.0f - row11[ib]) * OctantCombine(0.0f, row01[ib], row10[ib]); 2641 | 2642 | // rest of row -- prev cell and prev row and prev slice 2643 | for (int i = ib + dx; i != ie; i += dx) 2644 | row11[i] += (1.0f - row11[i]) * OctantCombine(row11[i - dx], row01[i], row10[i]); 2645 | 2646 | row00 = row10; 2647 | row01 = row11; 2648 | } 2649 | 2650 | slice0 = slice1; 2651 | } 2652 | } 2653 | 2654 | void OcclusionSweep 2655 | ( 2656 | int w, int h, int d, // volume size 2657 | int sx, int sy, int sz, // signs of sweep direction 2658 | 2659 | const float* inDirW, // occlusion in this direction 2660 | float* dirW // derived occlusion, not including original occlusion 2661 | ) 2662 | { 2663 | const int dx = 1 - 2 * sx; 2664 | const int dy = 1 - 2 * sy; 2665 | const int dz = 1 - 2 * sz; 2666 | 2667 | const int ib = sx * (w - 1); 2668 | const int ie = w - sx * (w + 1); 2669 | 2670 | const int wh = w * h; 2671 | const int whd = w * h * d; 2672 | 2673 | const int rowStart = sy * (wh - w); 2674 | const int sliceStart = sz * (whd - wh); 2675 | 2676 | const float* islice0 = inDirW + sliceStart; 2677 | float* oslice0 = dirW + sliceStart; 2678 | 2679 | // do first slice -- only depends on itself 2680 | { 2681 | const float* irow0 = islice0 + rowStart; 2682 | float* orow0 = oslice0 + rowStart; 2683 | 2684 | // as does its first row 2685 | for (int i = ib + dx; i != ie; i += dx) 2686 | orow0[i] = OctantCombine(orow0[i - dx], 0.0f, 0.0f, irow0[i - dx], 0.0f, 0.0f); 2687 | 2688 | for (int j = 1; j < h; j++) 2689 | { 2690 | const float* irow1 = irow0 + w * dy; 2691 | float* orow1 = orow0 + w * dy; 2692 | 2693 | // do first cell, only depends on prev row 2694 | orow1[ib] = OctantCombine(0.0f, orow0[ib], 0.0f, 0.0f, irow0[ib], 0.0f); 2695 | 2696 | // rest of row -- prev cell and prev row 2697 | for (int i = ib + dx; i != ie; i += dx) 2698 | orow1[i] = OctantCombine(orow1[i - dx], orow0[i], 0.0f, irow1[i - dx], irow0[i], 0.0f); 2699 | 2700 | irow0 = irow1; 2701 | orow0 = orow1; 2702 | } 2703 | } 2704 | 2705 | // do remaining slices 2706 | for (int k = 1; k < d; k++) 2707 | { 2708 | const float* islice1 = islice0 + wh * dz; 2709 | float* oslice1 = oslice0 + wh * dz; 2710 | 2711 | const float* irow00 = islice0 + rowStart; 2712 | float* orow00 = oslice0 + rowStart; 2713 | const float* irow01 = islice1 + rowStart; 2714 | float* orow01 = oslice1 + rowStart; 2715 | 2716 | // do first cell of first row, only depends on prev slice 2717 | orow01[ib] = OctantCombine(0.0f, 0.0f, orow00[ib], 0.0f, 0.0f, irow00[ib]); 2718 | 2719 | // do first row -- only depends on itself and prev slice 2720 | for (int i = ib + dx; i != ie; i += dx) 2721 | orow01[i] = OctantCombine(orow01[i - dx], 0.0f, orow00[i], irow01[i - dx], 0.0f, irow00[i]); 2722 | 2723 | for (int j = 1; j < h; j++) 2724 | { 2725 | const float* irow10 = irow00 + w * dy; 2726 | float* orow10 = orow00 + w * dy; 2727 | const float* irow11 = irow01 + w * dy; 2728 | float* orow11 = orow01 + w * dy; 2729 | 2730 | // do first cell, only depends on prev row and prev slice 2731 | orow11[ib] = OctantCombine(0.0f, orow01[ib], orow10[ib], 0.0f, irow01[ib], irow10[ib]); 2732 | 2733 | // rest of row -- prev cell and prev row and prev slice 2734 | for (int i = ib + dx; i != ie; i += dx) 2735 | orow11[i] = OctantCombine(orow11[i - dx], orow01[i], orow10[i], irow11[i - dx], irow01[i], irow10[i]); 2736 | 2737 | irow00 = irow10; 2738 | orow00 = orow10; 2739 | irow01 = irow11; 2740 | orow01 = orow11; 2741 | } 2742 | 2743 | islice0 = islice1; 2744 | oslice0 = oslice1; 2745 | } 2746 | } 2747 | } 2748 | 2749 | void DOL::OcclusionSweep(int w, int h, int d, float* dirW) 2750 | { 2751 | for (int sweep = 0; sweep < 8; sweep++) 2752 | { 2753 | const int sx = (sweep >> 0) & 1; 2754 | const int sy = (sweep >> 1) & 1; 2755 | const int sz = (sweep >> 2) & 1; 2756 | 2757 | ::OcclusionSweep(w, h, d, sx, sy, sz, dirW + sweep * w * h * d); 2758 | } 2759 | } 2760 | 2761 | void DOL::OcclusionSweep(int w, int h, int d, const float* occDirW, float* dirW) 2762 | { 2763 | int whd = w * h * d; 2764 | 2765 | for (int sweep = 0; sweep < 8; sweep++) 2766 | { 2767 | const int sx = (sweep >> 0) & 1; 2768 | const int sy = (sweep >> 1) & 1; 2769 | const int sz = (sweep >> 2) & 1; 2770 | 2771 | ::OcclusionSweep(w, h, d, sx, sy, sz, occDirW + sweep * whd, dirW + sweep * whd); 2772 | } 2773 | } 2774 | -------------------------------------------------------------------------------- /DistanceOcclusionLib.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // Purpose: Distance Field and Occlusion Generation 3 | // Author: Andrew Willmott 4 | //------------------------------------------------------------------------------ 5 | 6 | #ifndef DISTANCE_OCCLUSION_H 7 | #define DISTANCE_OCCLUSION_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace DOL 15 | { 16 | // --- Bit Masks ----------------------------------------------------------- 17 | 18 | // We store input occlusion as 1-bit bitmasks, padded to word boundaries on 19 | // each row. E.g., a 100 x 100 image would be 100 x 4 uint32_ts. 20 | 21 | int MaskSize(int w); // return mask size in words 22 | int MaskSize(int w, int h); // return mask size in words 23 | int MaskSize(int w, int h, int d); // return mask size in words 24 | 25 | uint32_t* CreateBitMask (int w, int h); // Create an empty image bit mask 26 | uint32_t* CreateBitMask (int w, int h, int d); // Create an empty volume bit mask 27 | void DestroyBitMask(uint32_t*& mask); // Free the given mask 28 | 29 | // Routines for generating simple 1-bit 'occlusion' test images/volumes 30 | void BitMaskAddPoints(int w, int h, int n, uint32_t seed, uint32_t mask[]); 31 | void BitMaskAddPoints(int w, int h, int d, int n, uint32_t seed, uint32_t mask[]); 32 | // Add 'n' random points to the bit mask 33 | 34 | void BitMaskAddBlock(int w, int h, int sides, uint32_t mask[]); 35 | void BitMaskAddBlock(int w, int h, int d, int sides, uint32_t mask[]); 36 | // Add a box with the given number of sides to the bit mask 37 | 38 | 39 | // --- Cell deltas --------------------------------------------------------- 40 | 41 | struct cCellDelta2 42 | { 43 | int16_t x; 44 | int16_t y; 45 | }; 46 | // Stores delta from current cell to closest solid boundary 47 | 48 | struct cCellDelta3 49 | { 50 | int16_t x; 51 | int16_t y; 52 | int16_t z; 53 | }; 54 | // Stores delta from current cell to closest solid boundary 55 | 56 | const int16_t kMaxDelta2 = INT16_MAX; 57 | const int16_t kMaxDelta3 = 25000; // INT16_MAX will overflow 32-bit arithmetic when calculating distances, reduce max to fix. 58 | 59 | // --- Distance ------------------------------------------------------------ 60 | 61 | void InitDistancesFromBitMask(int w, int h, const uint32_t mask[], int16_t distances[], int16_t maxD = INT16_MAX); 62 | void InitDistancesFromBitMask(int w, int h, const uint32_t mask[], int32_t distances[], int32_t maxD = INT32_MAX); 63 | void InitDistancesFromBitMask(int w, int h, const uint32_t mask[], float distances[], float maxD = FLT_MAX); 64 | // Given a mask, initialises a distance field to 0 where bits are one, INT16_MAX/INT32_MAX otherwise 65 | 66 | int Chamfer(int w, int h, int16_t distances[]); 67 | int Chamfer(int w, int h, int32_t distances[]); 68 | // Use Chamfer algorithm to propagate distances directly. Not as accurate as the delta-based approaches below. 69 | // Returns multiplier -- divide through by this to get distance in cells. 70 | 71 | void Felzenszwalb(int w, int h, float distances[]); 72 | // Use Felzenszwalb/Huttenlocher algorithm to propagate squared distances via parabolas. 73 | 74 | // Delta-based algorithms 75 | void InitDeltasFromBitMask(int w, int h, const uint32_t mask[], cCellDelta2 delta[], bool invert = false); 76 | void InitDeltasFromBitMask(int w, int h, int d, const uint32_t mask[], cCellDelta3 delta[], bool invert = false); 77 | // Given a mask, initialises a distance field to 0 where bits are one, kMaxDelta otherwise 78 | 79 | void InitBorderDeltasFromBitMask(int w, int h, const uint32_t mask[], cCellDelta2 delta[]); 80 | void InitBorderDeltasFromBitMask(int w, int h, int d, const uint32_t mask[], cCellDelta3 delta[]); 81 | // Given a mask, initialises a distance field correctly at 0/1 boundaries, fills the remainder with kMaxDelta. 82 | // This is the variant to use to generate signed distance fields (in conjunction with the original mask to indicate sign.) 83 | 84 | void FastSweep (int w, int h, cCellDelta2 delta[], int cw = 1); 85 | void FastSweep (int w, int h, int d, cCellDelta3 delta[], int cw = 1); 86 | // Find final distance field from initial distance field using fast sweep algorithm. 87 | 88 | void Danielsson(int w, int h, cCellDelta2 delta[], int cw = 1); 89 | void Danielsson(int w, int h, int d, cCellDelta3 delta[], int cw = 1); 90 | // Use Danielsson algorithm to propagate cell deltas -- similar to FastSweep but combines passes. 91 | 92 | void JumpFlood (int w, int h, cCellDelta2 delta[], int cw = 1); 93 | void JumpFlood (int w, int h, int d, cCellDelta3 delta[], int cw = 1); 94 | // Use "jump flooding" -- intended as a parallel algorithm, here for result comparison. 95 | 96 | void BruteForce(int w, int h, cCellDelta2 delta[], int cw = 1); 97 | void BruteForce(int w, int h, int d, cCellDelta3 delta[], int cw = 1); 98 | // Finds optimal delta values using brute force. Useful for checking results of faster algorithms. 99 | 100 | // --- Occlusion ----------------------------------------------------------- 101 | 102 | void InitDirWFromBitMask(int w, int h, const uint32_t mask[], float dirW4[]); 103 | void InitDirWFromBitMask(int w, int h, int d, const uint32_t mask[], float dirW8[]); 104 | // Initialise 4-way or 8-way direction field from given bitmask. 105 | 106 | void OcclusionSweep(int w, int h, float dirW4[]); 107 | // Calculate occlusion in each quadrant's direction. dirW4 should have been initialised with some InitDirW variant. 108 | void OcclusionSweep(int w, int h, int d, float dirW8[]); 109 | void OcclusionSweep(int w, int h, int d, const float inDirW8[], float outDirW8[]); 110 | // Stores occlusion caused by inDirW in outDirW in each octant's direction. (Hence can be used to avoid self occlusion.) 111 | 112 | uint8_t EncodeAO4 (const float dirW[4]); // Encode 2D direction components into overall occlusion. 113 | uint8_t EncodeAO8 (const float dirW[8]); // Encode 3D direction components into overall occlusion. 114 | void EncodeDirW4(const float dirW[4], uint8_t aoDir[4]); // Encode 2D direction components into directed occlusion in x/y and overall occlusion in z. 115 | void EncodeDirW8(const float dirW[8], uint8_t aoDir[4]); // Encode 3D direction components into directed occlusion in x/y/z and overall occlusion in a. 116 | 117 | 118 | // --- Inlines ------------------------------------------------------------- 119 | 120 | inline int MaskSize(int w) 121 | { 122 | return (w + 31) / 32; 123 | } 124 | inline int MaskSize(int w, int h) 125 | { 126 | return ((w + 31) / 32) * h; 127 | } 128 | inline int MaskSize(int w, int h, int d) 129 | { 130 | return ((w + 31) / 32) * h * d; 131 | } 132 | 133 | inline uint8_t EncodeU8(float v) 134 | { 135 | v = (v < 0.0f) ? 0.0f : ((v > 1.0f) ? 1.0f : v); 136 | return (uint8_t) lrintf(v * 255); 137 | } 138 | 139 | inline uint8_t EncodeAO4(const float dirW[4]) 140 | { 141 | float aoF = dirW[0] + dirW[1] + dirW[2] + dirW[3]; 142 | 143 | return EncodeU8(1.0f - 0.25f * aoF); 144 | } 145 | 146 | inline uint8_t EncodeAO8(const float dirW[8]) 147 | { 148 | float aoF = dirW[0] + dirW[1] + dirW[2] + dirW[3] + dirW[4] + dirW[5] + dirW[6] + dirW[7]; 149 | 150 | return EncodeU8(1.0f - 0.125f * aoF); 151 | } 152 | 153 | inline void EncodeDirW4(const float dirW[4], uint8_t aoDir[4]) 154 | { 155 | float aoFW = dirW[0] + dirW[1] + dirW[2] + dirW[3]; 156 | float aoFX = dirW[0] - dirW[1] + dirW[2] - dirW[3]; 157 | float aoFY = dirW[0] + dirW[1] - dirW[2] - dirW[3]; 158 | 159 | float invLen = 1.0f / sqrtf(aoFX * aoFX + aoFY * aoFY); 160 | 161 | aoFX *= invLen; 162 | aoFY *= invLen; 163 | aoFW *= 0.25f; 164 | 165 | aoFX = aoFW * aoFX * 0.5f + 0.5f; 166 | aoFY = aoFW * aoFY * 0.5f + 0.5f; 167 | 168 | aoDir[0] = EncodeU8(aoFX); 169 | aoDir[1] = EncodeU8(aoFY); 170 | aoDir[2] = EncodeU8(aoFW); 171 | aoDir[3] = 255; 172 | } 173 | 174 | inline void EncodeDirW8(const float dirW[8], uint8_t aoDir[4]) 175 | { 176 | float aoFW = dirW[0] + dirW[1] + dirW[2] + dirW[3] + dirW[4] + dirW[5] + dirW[6] + dirW[7]; 177 | float aoFX = dirW[0] - dirW[1] + dirW[2] - dirW[3] + dirW[4] - dirW[5] + dirW[6] - dirW[7]; 178 | float aoFY = dirW[0] + dirW[1] - dirW[2] - dirW[3] + dirW[4] + dirW[5] - dirW[6] - dirW[7]; 179 | float aoFZ = dirW[0] + dirW[1] + dirW[2] + dirW[3] - dirW[4] - dirW[5] - dirW[6] - dirW[7]; 180 | 181 | float invLen = 1.0f / sqrtf(aoFX * aoFX + aoFY * aoFY + aoFZ * aoFZ); 182 | 183 | aoFX *= invLen; 184 | aoFY *= invLen; 185 | aoFZ *= invLen; 186 | aoFW *= 0.125f; 187 | 188 | aoFX = aoFW * aoFX * 0.5f + 0.5f; 189 | aoFY = aoFW * aoFY * 0.5f + 0.5f; 190 | aoFZ = aoFW * aoFZ * 0.5f + 0.5f; 191 | 192 | aoDir[0] = EncodeU8(aoFX); 193 | aoDir[1] = EncodeU8(aoFY); 194 | aoDir[2] = EncodeU8(aoFZ); 195 | aoDir[3] = EncodeU8(aoFW); 196 | } 197 | } 198 | 199 | #endif 200 | -------------------------------------------------------------------------------- /DistanceOcclusionTool.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // Purpose: Distance Field and Occlusion generator tool 3 | // Author: Andrew Willmott 4 | //------------------------------------------------------------------------------ 5 | 6 | #include "DistanceOcclusionLib.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #ifdef _MSC_VER 15 | #pragma warning (disable: 4996) // let's not make fopen an error. 16 | #define strlcpy(d, s, ds) strcpy_s(d, ds, s) 17 | #elif defined(__GNUC__) && !defined(__clang__) 18 | #define strlcpy strncpy // thanks glibc guys 19 | #endif 20 | 21 | #ifndef USE_STB 22 | #define USE_STB 1 23 | #endif 24 | 25 | #ifndef USE_MESH 26 | #define USE_MESH 1 27 | #endif 28 | 29 | #if USE_STB 30 | #include "stb_image_mini.h" 31 | #endif 32 | 33 | #if USE_MESH 34 | #include "MeshSupport.cpp" 35 | using namespace MSL; 36 | #endif 37 | 38 | using namespace DOL; 39 | 40 | namespace 41 | { 42 | //////////////////////////////////////////////////////////////////////////// 43 | // MARK: - Utilities 44 | //////////////////////////////////////////////////////////////////////////// 45 | 46 | inline int32_t Dist2(const cCellDelta2& cd) 47 | { 48 | return cd.x * cd.x + cd.y * cd.y; 49 | } 50 | inline int32_t Dist2(const cCellDelta3& cd) 51 | { 52 | return cd.x * cd.x + cd.y * cd.y + cd.z * cd.z; 53 | } 54 | inline float Dist(const cCellDelta2& cd) 55 | { 56 | return sqrtf((float) Dist2(cd)); 57 | } 58 | inline float Dist(const cCellDelta3& cd) 59 | { 60 | return sqrtf((float) Dist2(cd)); 61 | } 62 | inline bool IsBoundary(const cCellDelta2& cd, int cw = 1) 63 | { 64 | return abs(cd.x) < cw && abs(cd.y) < cw; 65 | } 66 | inline bool IsBoundary(const cCellDelta3& cd, int cw = 1) 67 | { 68 | return abs(cd.x) < cw && abs(cd.y) < cw && abs(cd.z) < cw; 69 | } 70 | 71 | inline uint8_t EncodeU8Signed(float v) 72 | { 73 | v = (v < -1.0f) ? -1.0f : ((v > 1.0f) ? 1.0f : v); 74 | return (uint8_t) lrintf(128 + v * 127); // ensure 0 = 128 75 | } 76 | 77 | // MARK: - Printing 78 | 79 | void PrintMask(int w, int h, const uint32_t* mask, FILE* s) 80 | { 81 | for (int y = 0; y < h; y++) 82 | { 83 | for (int j = 0; j < w; j += 32) 84 | { 85 | uint32_t m = *mask++; 86 | 87 | int n = 32; 88 | if (w - j < n) 89 | n = w - j; 90 | 91 | for (int i = 0; i < n; i++) 92 | { 93 | if (m & 1) 94 | fprintf(s, "X"); 95 | else 96 | fprintf(s, "."); 97 | 98 | m >>= 1; 99 | } 100 | } 101 | 102 | fprintf(s, "\n"); 103 | } 104 | } 105 | 106 | void PrintMask(int w, int h, int d, const uint32_t mask[], FILE* s) 107 | { 108 | for (int z = 0; z < d; z++) 109 | { 110 | for (int y = 0; y < h; y++) 111 | { 112 | for (int j = 0; j < w; j += 32) 113 | { 114 | uint32_t m = (*mask++); 115 | 116 | int n = 32; 117 | if (w - j < n) 118 | n = w - j; 119 | 120 | for (int i = 0; i < n; i++) 121 | { 122 | if (m & 1) 123 | fprintf(s, "X"); 124 | else 125 | fprintf(s, "."); 126 | 127 | m >>= 1; 128 | } 129 | } 130 | 131 | fprintf(s, "\n"); 132 | } 133 | 134 | fprintf(s, "\n"); 135 | } 136 | } 137 | 138 | void PrintDistances(int w, int h, const int16_t distances[], FILE* s) 139 | { 140 | for (int i = 0; i < h; i++) 141 | { 142 | for (int i = 0; i < w - 1; i++) 143 | fprintf(s, "%2d ", (*distances++)); 144 | 145 | fprintf(s, "%2d\n", (*distances++)); 146 | } 147 | } 148 | 149 | void PrintDistances(int w, int h, const float* dist, FILE* s) 150 | { 151 | for (int i = 0; i < h; i++) 152 | { 153 | for (int i = 0; i < w - 1; i++) 154 | fprintf(s, "%1.3f ", (*dist++)); 155 | 156 | fprintf(s, "%1.3f\n", (*dist++)); 157 | } 158 | } 159 | 160 | void PrintDistances(int w, int h, int d, const float* dist, FILE* s) 161 | { 162 | for (int k = 0; k < d; k++) 163 | { 164 | fprintf(s, "slice %d>\n", k); 165 | 166 | for (int j = 0; j < h; j++) 167 | { 168 | for (int i = 0; i < w - 1; i++) 169 | fprintf(s, "%1.3f ", (*dist++)); 170 | 171 | fprintf(s, "%1.3f\n", (*dist++)); 172 | } 173 | } 174 | } 175 | 176 | void PrintDeltas(int w, int h, const cCellDelta2 deltas[], FILE* s) 177 | { 178 | for (int j = 0; j < h; j++) 179 | for (int i = 0; i < w; i++, deltas++) 180 | { 181 | if (deltas->x >= kMaxDelta2) 182 | fprintf(s, " x "); 183 | else 184 | fprintf(s, "%3d %3d", deltas->x, deltas->y); 185 | 186 | if (i < w - 1) 187 | fprintf(s, "|"); 188 | else 189 | fprintf(s, "\n"); 190 | } 191 | 192 | fprintf(s, "\n"); 193 | } 194 | 195 | void PrintDeltas(int w, int h, int d, const cCellDelta3 deltas[], FILE* s) 196 | { 197 | for (int k = 0; k < d; k++) 198 | { 199 | fprintf(s, "slice %d>\n", k); 200 | 201 | for (int j = 0; j < h; j++) 202 | for (int i = 0; i < w; i++, deltas++) 203 | { 204 | if (deltas->x >= kMaxDelta3) 205 | fprintf(s, " x "); 206 | else 207 | fprintf(s, "%3d %3d %3d", deltas->x, deltas->y, deltas->z); 208 | 209 | if (i < w - 1) 210 | fprintf(s, "|"); 211 | else 212 | fprintf(s, "\n"); 213 | } 214 | } 215 | } 216 | 217 | void PrintDiff(int w, int h, const cCellDelta2 deltas[], const cCellDelta2 refs[], FILE* s) 218 | { 219 | for (int i = 0; i < h; i++) 220 | { 221 | for (int j = 0; j < w; j++, deltas++, refs++) 222 | { 223 | if (deltas->x == refs->x && deltas->y == refs->y) 224 | fprintf(s, " - "); 225 | else 226 | { 227 | float dist = Dist(*deltas) - Dist(*refs); 228 | 229 | if (fabsf(dist) < 1e-6f) 230 | fprintf(s, " x "); 231 | else 232 | fprintf(s, "%7.2f", dist); 233 | } 234 | 235 | if (j < w - 1) 236 | fprintf(s, ", "); 237 | else 238 | fprintf(s, "\n"); 239 | } 240 | } 241 | } 242 | 243 | void PrintDiff(int w, int h, const float distances[], const cCellDelta2 refs[], FILE* s) 244 | { 245 | for (int i = 0; i < h; i++) 246 | { 247 | for (int j = 0; j < w; j++, distances++, refs++) 248 | { 249 | float dist = *distances - Dist(*refs); 250 | if (dist == 0.0f) 251 | fprintf(s, " - "); 252 | else if (fabsf(dist) < 1e-6f) 253 | fprintf(s, " x "); 254 | else 255 | fprintf(s, "%7.2f", dist); 256 | 257 | if (j < w - 1) 258 | fprintf(s, ", "); 259 | else 260 | fprintf(s, "\n"); 261 | } 262 | } 263 | } 264 | 265 | bool PrintError(int w, int h, const cCellDelta2 deltas[], const cCellDelta2 refs[], FILE* s) 266 | { 267 | float maxDist = 0.0f; 268 | 269 | for (int i = 0; i < h; i++) 270 | for (int j = 0; j < w; j++, deltas++, refs++) 271 | { 272 | float dist = Dist(*deltas) - Dist(*refs); 273 | 274 | if (maxDist < dist) 275 | maxDist = dist; 276 | } 277 | 278 | fprintf(s, "Max error: %g\n", maxDist); 279 | 280 | if (maxDist >= 1.0f) 281 | { 282 | fprintf(s, "FAILED\n"); 283 | return false; 284 | } 285 | 286 | return true; 287 | } 288 | 289 | bool PrintError(int w, int h, const float distances[], const cCellDelta2 refs[], FILE* s) 290 | { 291 | float maxDist = 0.0f; 292 | 293 | for (int i = 0; i < h; i++) 294 | for (int j = 0; j < w; j++, distances++, refs++) 295 | { 296 | float dist = *distances - Dist(*refs); 297 | 298 | if (maxDist < dist) 299 | maxDist = dist; 300 | } 301 | 302 | fprintf(s, "Max error: %g\n", maxDist); 303 | 304 | if (maxDist >= 1.0f) 305 | { 306 | fprintf(s, "FAILED\n"); 307 | return false; 308 | } 309 | 310 | return true; 311 | } 312 | 313 | void PrintDiff(int w, int h, int d, const cCellDelta3 deltas[], const cCellDelta3* refs, FILE* s) 314 | { 315 | for (int k = 0; k < d; k++) 316 | { 317 | fprintf(s, "slice %d>\n", k); 318 | 319 | for (int i = 0; i < h; i++) 320 | { 321 | for (int j = 0; j < w; j++, deltas++, refs++) 322 | { 323 | if (deltas->x == refs->x && deltas->y == refs->y && deltas->z == refs->z) 324 | fprintf(s, " - "); 325 | else 326 | { 327 | float dist = Dist(*deltas) - Dist(*refs); 328 | 329 | if (fabsf(dist) < 1e-6f) 330 | fprintf(s, " x "); 331 | else 332 | fprintf(s, "%7.2f", dist); 333 | } 334 | 335 | if (j < w - 1) 336 | fprintf(s, ", "); 337 | else 338 | fprintf(s, "\n"); 339 | } 340 | } 341 | } 342 | } 343 | 344 | bool PrintError(int w, int h, int d, const cCellDelta3 deltas[], const cCellDelta3* refs, FILE* s) 345 | { 346 | float maxDist = 0.0f; 347 | 348 | for (int k = 0; k < d; k++) 349 | for (int i = 0; i < h; i++) 350 | for (int j = 0; j < w; j++, deltas++, refs++) 351 | { 352 | float dist = Dist(*deltas) - Dist(*refs); 353 | 354 | if (maxDist < dist) 355 | maxDist = dist; 356 | } 357 | 358 | fprintf(s, "Max error: %g\n", maxDist); 359 | 360 | if (maxDist >= 1.0f) 361 | { 362 | fprintf(s, "FAILED\n"); 363 | return false; 364 | } 365 | 366 | return true; 367 | } 368 | 369 | // MARK: - Sanity checking 370 | 371 | bool CheckDeltas(int w, int h, cCellDelta2* deltas, FILE* s, int cw = 1) 372 | { 373 | bool result = true; 374 | 375 | for (int j = 0; j < h; j++) 376 | for (int i = 0; i < w; i++) 377 | { 378 | cCellDelta2 cell = deltas[i + w * j]; 379 | cell.x += i * cw; 380 | cell.y += j * cw; 381 | 382 | if (cell.x < 0 || cell.x >= cw * w || cell.y < 0 || cell.y >= cw * h) 383 | { 384 | fprintf(s, "out of bounds delta (%d:%d) @ cell %d:%d\n", cell.x, cell.y, i, j); 385 | result = false; 386 | continue; 387 | } 388 | 389 | int ri = cell.x / cw; 390 | int rj = cell.y / cw; 391 | 392 | cCellDelta2 rcell = deltas[ri + w * rj]; 393 | 394 | if (!IsBoundary(rcell, cw)) 395 | { 396 | result = false; 397 | fprintf(s, "non-boundary delta (%d:%d) @ cell %d:%d referenced by %d:%d\n", rcell.x, rcell.y, ri, rj, i, j); 398 | } 399 | } 400 | 401 | return result; 402 | } 403 | 404 | bool CheckDeltas(int w, int h, int d, cCellDelta3* deltas, FILE* s, int cw = 1) 405 | { 406 | bool result = true; 407 | 408 | for (int k = 0; k < d; k++) 409 | for (int j = 0; j < h; j++) 410 | for (int i = 0; i < w; i++) 411 | { 412 | cCellDelta3 cell = deltas[i + w * j + w * h * k]; 413 | 414 | cell.x += i * cw; 415 | cell.y += j * cw; 416 | cell.z += k * cw; 417 | 418 | if (cell.x < 0 || cell.x >= cw * w || cell.y < 0 || cell.y >= cw * h || cell.z < 0 || cell.z >= cw * d) 419 | { 420 | fprintf(s, "out of bounds delta (%d:%d:%d) @ cell %d:%d:%d\n", cell.x, cell.y, cell.z, i, j, k); 421 | result = false; 422 | continue; 423 | } 424 | 425 | int ri = cell.x / cw; 426 | int rj = cell.y / cw; 427 | int rk = cell.z / cw; 428 | 429 | cCellDelta3 rcell = deltas[ri + w * rj + w * h * rk]; 430 | 431 | if (!IsBoundary(rcell, cw)) 432 | { 433 | result = false; 434 | fprintf(s, "non-boundary delta (%d:%d:%d) @ cell %d:%d:%d referenced by %d:%d:%d\n", rcell.x, rcell.y, rcell.z, ri, rj, rk, i, j, k); 435 | } 436 | } 437 | 438 | return result; 439 | } 440 | 441 | 442 | // Check border-style init 443 | inline bool Occupied(const uint32_t mask[], int w, int h, int x, int y) 444 | { 445 | int ms = MaskSize(w); 446 | 447 | if (x < 0) 448 | x = 0; 449 | else if (x >= w) 450 | x = w - 1; 451 | 452 | if (y < 0) 453 | y = 0; 454 | else if (y >= h) 455 | y = h - 1; 456 | 457 | return (mask[y * ms + (x >> 5)] & (1 << (x & 0x1F))) != 0; 458 | } 459 | 460 | inline bool Expect(bool value, const uint32_t mask[], int w, int h, int x, int y, int dx, int dy, FILE* s) 461 | { 462 | if (Occupied(mask, w, h, x + dx, y + dy) != value) 463 | { 464 | fprintf(s, "bad cell (%d %d), offset (%d, %d) != %d\n", x, y, dx, dy, value); 465 | return false; 466 | } 467 | return true; 468 | } 469 | 470 | bool CheckBorderDeltas(int w, int h, const uint32_t mask[], cCellDelta2 deltas[], FILE* s) 471 | { 472 | bool good = true; 473 | 474 | for (int x = 0; x < h; x++) 475 | for (int y = 0; y < w; y++) 476 | { 477 | cCellDelta2* cell = deltas + y * w + x; 478 | bool occ = Occupied(mask, w, h, x, y); 479 | 480 | if (IsBoundary(*cell, 2)) 481 | { 482 | good = Expect(!occ, mask, w, h, x, y, cell->x, cell->y, s) && good; 483 | 484 | if (cell->x != 0 && cell->y != 0) // if diagonal check if there was a closer edge... 485 | { 486 | good = Expect(occ, mask, w, h, x, y, -1, 0, s) && good; 487 | good = Expect(occ, mask, w, h, x, y, +1, 0, s) && good; 488 | good = Expect(occ, mask, w, h, x, y, 0, -1, s) && good; 489 | good = Expect(occ, mask, w, h, x, y, 0, +1, s) && good; 490 | } 491 | } 492 | else 493 | { 494 | for (int dy = -1; dy <= +1; dy++) 495 | for (int dx = -1; dx <= +1; dx++) 496 | good = Expect(occ, mask, w, h, x, y, dx, dy, s) && good; 497 | } 498 | } 499 | 500 | return good; 501 | } 502 | 503 | inline bool Occupied(const uint32_t mask[], int w, int h, int , int x, int y, int z) 504 | { 505 | int ms = MaskSize(w); 506 | 507 | return (mask[(z * h + y) * ms + (x >> 5)] & (1 << (x & 0x1F))) != 0; 508 | } 509 | 510 | inline bool Expect(const uint32_t mask[], int w, int h, int d, int x, int y, int z, bool value) 511 | { 512 | if (x < 0 || x >= w) 513 | return true; 514 | 515 | if (y < 0 || y >= h) 516 | return true; 517 | 518 | if (z < 0 || z >= d) 519 | return true; 520 | 521 | return Occupied(mask, w, h, d, x, y, z) == value; 522 | } 523 | 524 | inline bool Expect(bool value, const uint32_t mask[], int w, int h, int d, int x, int y, int z, int dx, int dy, int dz, FILE* s) 525 | { 526 | if (!Expect(mask, w, h, d, x + dx, y + dy, z + dz, value)) 527 | { 528 | if (s) 529 | fprintf(s, "bad cell (%d %d %d), offset (%d, %d, %d) != %d\n", x, y, z, dx, dy, dz, value); 530 | return false; 531 | } 532 | return true; 533 | } 534 | 535 | bool CheckBorderDeltas(int w, int h, int d, const uint32_t mask[], cCellDelta3 deltas[], FILE* s) 536 | { 537 | bool good = true; 538 | 539 | for (int z = 0; z < d; z++) 540 | for (int x = 0; x < h; x++) 541 | for (int y = 0; y < w; y++) 542 | { 543 | cCellDelta3* cell = deltas + z * w * h + y * w + x; 544 | bool occ = Occupied(mask, w, h, d, x, y, z); 545 | 546 | if (IsBoundary(*cell, 2)) 547 | { 548 | good = Expect(!occ, mask, w, h, d, x, y, z, cell->x, cell->y, cell->z, s) && good; 549 | 550 | if (cell->x != 0 && cell->y != 0 && cell->z != 0) // if box diagonal check if there was a closer face diagonal... 551 | { 552 | good = Expect(occ, mask, w, h, d, x, y, z, -1, -1, 0, s) && good; 553 | good = Expect(occ, mask, w, h, d, x, y, z, -1, +1, 0, s) && good; 554 | good = Expect(occ, mask, w, h, d, x, y, z, +1, -1, 0, s) && good; 555 | good = Expect(occ, mask, w, h, d, x, y, z, +1, +1, 0, s) && good; 556 | 557 | good = Expect(occ, mask, w, h, d, x, y, z, -1, 0, -1, s) && good; 558 | good = Expect(occ, mask, w, h, d, x, y, z, -1, 0, +1, s) && good; 559 | good = Expect(occ, mask, w, h, d, x, y, z, +1, 0, -1, s) && good; 560 | good = Expect(occ, mask, w, h, d, x, y, z, +1, 0, +1, s) && good; 561 | 562 | good = Expect(occ, mask, w, h, d, x, y, z, 0, -1, -1, s) && good; 563 | good = Expect(occ, mask, w, h, d, x, y, z, 0, -1, +1, s) && good; 564 | good = Expect(occ, mask, w, h, d, x, y, z, 0, +1, -1, s) && good; 565 | good = Expect(occ, mask, w, h, d, x, y, z, 0, +1, +1, s) && good; 566 | } 567 | if (cell->x != 0 && cell->y != 0) // if diagonal check if there was a closer edge... 568 | { 569 | good = Expect(occ, mask, w, h, d, x, y, z, -1, 0, 0, s) && good; 570 | good = Expect(occ, mask, w, h, d, x, y, z, +1, 0, 0, s) && good; 571 | good = Expect(occ, mask, w, h, d, x, y, z, 0, -1, 0, s) && good; 572 | good = Expect(occ, mask, w, h, d, x, y, z, 0, +1, 0, s) && good; 573 | } 574 | if (cell->y != 0 && cell->z != 0) // if diagonal check if there was a closer edge... 575 | { 576 | good = Expect(occ, mask, w, h, d, x, y, z, 0, -1, 0, s) && good; 577 | good = Expect(occ, mask, w, h, d, x, y, z, 0, +1, 0, s) && good; 578 | good = Expect(occ, mask, w, h, d, x, y, z, 0, 0, -1, s) && good; 579 | good = Expect(occ, mask, w, h, d, x, y, z, 0, 0, +1, s) && good; 580 | } 581 | if (cell->z != 0 && cell->x != 0) // if diagonal check if there was a closer edge... 582 | { 583 | good = Expect(occ, mask, w, h, d, x, y, z, -1, 0, 0, s) && good; 584 | good = Expect(occ, mask, w, h, d, x, y, z, +1, 0, 0, s) && good; 585 | good = Expect(occ, mask, w, h, d, x, y, z, 0, 0, -1, s) && good; 586 | good = Expect(occ, mask, w, h, d, x, y, z, 0, 0, +1, s) && good; 587 | } 588 | } 589 | else 590 | { 591 | for (int dz = -1; dz <= +1; dz++) 592 | for (int dy = -1; dy <= +1; dy++) 593 | for (int dx = -1; dx <= +1; dx++) 594 | good = Expect(occ, mask, w, h, d, x, y, z, dx, dy, dz, s) && good; 595 | } 596 | } 597 | 598 | return good; 599 | } 600 | 601 | 602 | // MARK: - Image IO 603 | void WriteImage(const char* name, const char* type, int w, int h, uint8_t data[], int index = -1) 604 | { 605 | #if USE_STB 606 | char nameAndExt[128]; 607 | 608 | if (index >= 0) 609 | sprintf(nameAndExt, "%s-%s-%03d.png", name, type, index); 610 | else 611 | sprintf(nameAndExt, "%s-%s.png", name, type); 612 | 613 | stbi_write_png(nameAndExt, w, h, 1, data, 0); 614 | printf("written %s\n", nameAndExt); 615 | #endif 616 | } 617 | 618 | void WriteImage(const char* name, const char* type, int w, int h, uint8_t data[][4], int index = -1) 619 | { 620 | #if USE_STB 621 | char nameAndExt[128]; 622 | 623 | if (index >= 0) 624 | sprintf(nameAndExt, "%s-%s-%03d.png", name, type, index); 625 | else 626 | sprintf(nameAndExt, "%s-%s.png", name, type); 627 | 628 | stbi_write_png(nameAndExt, w, h, 4, data, 0); 629 | printf("written %s\n", nameAndExt); 630 | #endif 631 | } 632 | 633 | #if USE_STB 634 | uint32_t* BitMaskFromImageFile(const char* filename, int threshold, bool lessThan, int& w, int& h) 635 | { 636 | stbi_uc* data = stbi_load(filename, &w, &h, 0, 1); 637 | if (!data) 638 | { 639 | printf("error reading %s: %s\n", filename, stbi_failure_reason()); 640 | return 0; 641 | } 642 | 643 | const int sw = MaskSize(w); 644 | uint32_t* mask = new uint32_t[sw * h]; 645 | 646 | for (int y = 0; y < h; y++) 647 | for (int i = 0; i < sw; i++) 648 | { 649 | uint32_t* maskCell = mask + y * sw + i; 650 | *maskCell = 0; 651 | 652 | for (int j = 0; j < 32; j++) 653 | { 654 | int x = i * 32 + j; 655 | 656 | if (x >= w) 657 | break; 658 | 659 | uint8_t v; 660 | v = data[y * w + x]; 661 | 662 | if ((v >= threshold) ^ lessThan) 663 | *maskCell |= 1 << j; 664 | } 665 | } 666 | 667 | stbi_image_free(data); 668 | 669 | return mask; 670 | } 671 | #endif 672 | 673 | 674 | //////////////////////////////////////////////////////////////////////////// 675 | // MARK: - Distance generation 676 | //////////////////////////////////////////////////////////////////////////// 677 | 678 | void WriteDistances(const char* name, const char* type, int w, int h, float maxLen, const cCellDelta2 deltas[]) 679 | { 680 | uint8_t* dist8 = new uint8_t[w * h]; 681 | 682 | for (int i = 0, n = w * h; i < n; i++) 683 | dist8[i] = EncodeU8(Dist(deltas[i]) / maxLen); 684 | 685 | WriteImage(name, type, w, h, dist8); 686 | delete[] dist8; 687 | } 688 | 689 | template void WriteDistances(const char* name, const char* type, int w, int h, float maxLen, const T distances[]) 690 | { 691 | uint8_t* dist8 = new uint8_t[w * h]; 692 | 693 | for (int i = 0, n = w * h; i < n; i++) 694 | dist8[i] = EncodeU8(distances[i] / maxLen); 695 | 696 | WriteImage(name, type, w, h, dist8); 697 | delete[] dist8; 698 | } 699 | 700 | enum tMethod 701 | { 702 | kMethodDanielsson, // O(n), fastest delta technique, decent accuracy 703 | kMethodFastSweep, // O(n), ~50% slower, close to perfect accuracy 704 | kMethodJumpFlood, // O(n log n), GPU-oriented algorithm, for checking accuracy only 705 | kMethodBruteForce, // O(n^2), accurate, for checking other algorithms at low n. 706 | kMethodChamfer, // O(n), has issues on diagonals. Cheapest but quality suffers. 707 | kMethodFelzenszwalb, // O(n) but much larger overhead than the others. Accurate. 708 | kNumMethods 709 | }; 710 | 711 | void FindDeltas(int method, int w, int h, cCellDelta2 deltas[], FILE* s, int cw = 1) 712 | { 713 | switch (method) 714 | { 715 | case kMethodDanielsson: 716 | if (s) 717 | fprintf(s, "\nDanielsson:\n"); 718 | Danielsson(w, h, deltas, cw); 719 | break; 720 | case kMethodFastSweep: 721 | if (s) 722 | fprintf(s, "\nFast Sweep:\n"); 723 | FastSweep(w, h, deltas, cw); 724 | break; 725 | case kMethodJumpFlood: 726 | if (s) 727 | fprintf(s, "\nJump Flood:\n"); 728 | JumpFlood(w, h, deltas, cw); 729 | break; 730 | case kMethodBruteForce: 731 | if (s) 732 | fprintf(s, "\nBrute Force:\n"); 733 | BruteForce(w, h, deltas, cw); 734 | break; 735 | default: 736 | fprintf(stderr, "\nUnknown method for this technique\n"); 737 | break; 738 | } 739 | } 740 | 741 | bool GenerateDistanceFelzenszwalb(const char* name, int w, int h, float maxLen, uint32_t mask[], FILE* s, bool check) 742 | { 743 | // This does not use the more accurate (and more generalisable) delta cell approach, so has a different flow 744 | float* distances = new float[w * h]; // one of these things... 745 | 746 | InitDistancesFromBitMask(w, h, mask, distances); 747 | 748 | if (s) 749 | PrintDistances(w, h, distances, s); 750 | 751 | Felzenszwalb(w, h, distances); 752 | 753 | for (int i = 0; i < w * h; i++) 754 | distances[i] = sqrtf(distances[i]); 755 | 756 | if (s) 757 | PrintDistances(w, h, distances, s); 758 | 759 | if (check) 760 | { 761 | cCellDelta2* optDeltas = new cCellDelta2[w * h]; 762 | InitDeltasFromBitMask(w, h, mask, optDeltas); 763 | BruteForce(w, h, optDeltas); 764 | 765 | if (s) 766 | { 767 | fprintf(s, "\nOptimal:\n"); 768 | PrintDeltas(w, h, optDeltas, s); 769 | fprintf(s, "Diff from optimal:\n"); 770 | PrintDiff(w, h, distances, optDeltas, s); 771 | } 772 | 773 | PrintError(w, h, distances, optDeltas, stderr); 774 | 775 | delete[] optDeltas; 776 | } 777 | 778 | WriteDistances(name, "df", w, h, maxLen, distances); 779 | 780 | delete[] distances; 781 | return true; 782 | } 783 | 784 | bool GenerateDistanceChamfer(const char* name, int w, int h, float maxLen, uint32_t mask[], FILE* s, bool check) 785 | { 786 | // This does not use the more accurate (and more generalisable) delta cell approach, so has a different flow 787 | int16_t* distances = new int16_t[w * h]; // one of these things... 788 | 789 | InitDistancesFromBitMask(w, h, mask, distances, INT16_MAX - 16); // -16 to avoid overflow with Chamfer adds 790 | 791 | if (s) 792 | PrintDistances(w, h, distances, s); 793 | 794 | int cm = Chamfer(w, h, distances); 795 | 796 | if (s) 797 | PrintDistances(w, h, distances, s); 798 | 799 | if (check) 800 | { 801 | cCellDelta2* deltas = new cCellDelta2[w * h]; // easiest just to convert to delta form 802 | for (int i = 0; i < w * h; i++) 803 | { 804 | deltas[i].x = distances[i]; 805 | deltas[i].y = 0; 806 | } 807 | 808 | cCellDelta2* optDeltas = new cCellDelta2[w * h]; 809 | InitDeltasFromBitMask(w, h, mask, optDeltas); 810 | BruteForce(w, h, optDeltas, cm); 811 | 812 | if (s) 813 | { 814 | fprintf(s, "\nOptimal:\n"); 815 | PrintDeltas(w, h, optDeltas, s); 816 | fprintf(s, "Diff from optimal:\n"); 817 | PrintDiff(w, h, deltas, optDeltas, s); 818 | } 819 | 820 | PrintError(w, h, deltas, optDeltas, stderr); 821 | 822 | delete[] deltas; 823 | delete[] optDeltas; 824 | } 825 | 826 | WriteDistances(name, "df", w, h, maxLen * cm, distances); 827 | 828 | delete[] distances; 829 | return true; 830 | } 831 | 832 | bool GenerateDistance(const char* name, int w, int h, float maxLen, uint32_t mask[], int method, FILE* s, bool check) 833 | { 834 | // the non-delta approaches have special cases 835 | if (method == kMethodChamfer) 836 | return GenerateDistanceChamfer(name, w, h, maxLen, mask, s, check); 837 | else if (method == kMethodFelzenszwalb) 838 | return GenerateDistanceFelzenszwalb(name, w, h, maxLen, mask, s, check); 839 | 840 | cCellDelta2* deltas = new cCellDelta2[w * h]; 841 | 842 | InitDeltasFromBitMask(w, h, mask, deltas); 843 | 844 | if (s) 845 | { 846 | PrintDeltas(w, h, deltas, s); 847 | fprintf(s, "Generating exterior distances\n"); 848 | } 849 | 850 | FindDeltas(method, w, h, deltas, s); 851 | 852 | if (s) 853 | PrintDeltas(w, h, deltas, s); 854 | 855 | if (check) 856 | { 857 | if (!CheckDeltas(w, h, deltas, stderr)) 858 | return false; 859 | 860 | if (method != kMethodBruteForce) 861 | { 862 | cCellDelta2* optDeltas = new cCellDelta2[w * h]; 863 | InitDeltasFromBitMask(w, h, mask, optDeltas); 864 | BruteForce(w, h, optDeltas); 865 | 866 | if (s) 867 | { 868 | fprintf(s, "\nOptimal:\n"); 869 | PrintDeltas(w, h, optDeltas, s); 870 | fprintf(s, "Diff from optimal:\n"); 871 | PrintDiff(w, h, deltas, optDeltas, s); 872 | } 873 | 874 | PrintError(w, h, deltas, optDeltas, stderr); 875 | delete[] optDeltas; 876 | } 877 | } 878 | 879 | WriteDistances(name, "df", w, h, maxLen, deltas); 880 | delete[] deltas; 881 | 882 | return true; 883 | } 884 | 885 | // Volumes 886 | void WriteDistances(const char* name, const char* type, int w, int h, int d, float maxLen, cCellDelta3 deltas[]) 887 | { 888 | // Write as stack of images 889 | uint8_t* dist8 = new uint8_t[w * h]; 890 | 891 | for (int slice = 0; slice < d; slice++) 892 | { 893 | cCellDelta3* sliceDeltas = deltas + w * h * slice; 894 | 895 | for (int i = 0, n = w * h; i < n; i++) 896 | dist8[i] = EncodeU8(Dist(sliceDeltas[i]) / maxLen); 897 | 898 | WriteImage(name, type, w, h, dist8, slice); 899 | } 900 | 901 | delete[] dist8; 902 | } 903 | 904 | #if USE_MESH 905 | uint32_t* BitMaskFromObjFile(int w, int h, int d, const char* sourceFile, FILE* ) 906 | { 907 | FILE* meshFile = fopen(sourceFile, "r"); 908 | 909 | if (!meshFile) 910 | { 911 | fprintf(stderr, "Can't open %s\n", sourceFile); 912 | return 0; 913 | } 914 | 915 | cMesh mesh; 916 | ReadObjFile(meshFile, &mesh); 917 | 918 | Bounds3f bbox = FindBounds(mesh); 919 | 920 | int maskSize = MaskSize(w, h, d); 921 | uint32_t* mask = new uint32_t[maskSize](); 922 | 923 | CreateBitMaskFromTriangles 924 | ( 925 | (int) mesh.mPositionIndices.size() / 3, 926 | mesh.mPositionIndices.data(), 927 | mesh.mPositions.data(), 928 | bbox, 929 | w, h, d, 930 | mask 931 | ); 932 | 933 | return mask; 934 | } 935 | #endif 936 | 937 | void FindDeltas(int method, int w, int h, int d, cCellDelta3 deltas[], FILE* s, int cw = 1) 938 | { 939 | switch (method) 940 | { 941 | case kMethodDanielsson: 942 | if (s) 943 | fprintf(s, "\nDanielsson:\n"); 944 | Danielsson(w, h, d, deltas, cw); 945 | break; 946 | case kMethodFastSweep: 947 | if (s) 948 | fprintf(s, "\nFast Sweep:\n"); 949 | FastSweep(w, h, d, deltas, cw); 950 | break; 951 | case kMethodJumpFlood: 952 | if (s) 953 | fprintf(s, "\nJump Flood:\n"); 954 | JumpFlood(w, h, d, deltas, cw); 955 | break; 956 | case kMethodBruteForce: 957 | if (s) 958 | fprintf(s, "\nBrute Force:\n"); 959 | BruteForce(w, h, d, deltas, cw); 960 | break; 961 | default: 962 | fprintf(stderr, "\nUnknown method for this technique\n"); 963 | break; 964 | } 965 | } 966 | 967 | bool GenerateDistance(const char* name, const int w, const int h, const int d, float maxLen, const uint32_t mask[], int method, FILE* s, bool check) 968 | { 969 | cCellDelta3* deltas = new cCellDelta3[w * h * d]; 970 | 971 | InitDeltasFromBitMask(w, h, d, mask, deltas); 972 | 973 | if (s) 974 | fprintf(s, "Generating exterior distances\n"); 975 | 976 | FindDeltas(method, w, h, d, deltas, s); 977 | 978 | if (s) 979 | PrintDeltas(w, h, d, deltas, s); 980 | 981 | if (check) 982 | { 983 | if (!CheckDeltas(w, h, d, deltas, stderr)) 984 | { 985 | delete[] deltas; 986 | return false; 987 | } 988 | 989 | if (method != kMethodBruteForce) 990 | { 991 | fprintf(s, "Optimal:\n"); 992 | 993 | cCellDelta3* optDeltas = new cCellDelta3[w * h * d]; 994 | InitDeltasFromBitMask(w, h, d, mask, optDeltas); 995 | BruteForce(w, h, d, optDeltas); 996 | 997 | if (s) 998 | { 999 | PrintDeltas(w, h, d, optDeltas, s); 1000 | fprintf(s, "Diff from optimal:\n"); 1001 | PrintDiff(w, h, d, deltas, optDeltas, s); 1002 | } 1003 | 1004 | PrintError(w, h, d, deltas, optDeltas, stderr); 1005 | 1006 | delete[] optDeltas; 1007 | } 1008 | } 1009 | 1010 | WriteDistances(name, "df", w, h, d, maxLen, deltas); 1011 | delete[] deltas; 1012 | 1013 | return true; 1014 | } 1015 | 1016 | 1017 | //////////////////////////////////////////////////////////////////////////// 1018 | // MARK: - Signed Distance generation 1019 | //////////////////////////////////////////////////////////////////////////// 1020 | 1021 | enum tOutputType 1022 | { 1023 | kImageStandard, 1024 | kImageDeltas, 1025 | kImageRG, 1026 | }; 1027 | 1028 | void WriteDistances(const char* name, int w, int h, float maxLen, const cCellDelta2 deltas0[], const cCellDelta2 deltas1[], int type) 1029 | { 1030 | if (type == kImageStandard) 1031 | { 1032 | uint8_t* dist8 = new uint8_t[w * h]; 1033 | 1034 | for (int i = 0, n = w * h; i < n; i++) 1035 | dist8[i] = EncodeU8Signed((Dist(deltas0[i]) - Dist(deltas1[i])) / maxLen); 1036 | 1037 | WriteImage(name, "sdf", w, h, dist8); 1038 | delete[] dist8; 1039 | } 1040 | else 1041 | { 1042 | uint8_t (*dist32)[4] = new uint8_t[w * h][4]; 1043 | 1044 | for (int i = 0, n = w * h; i < n; i++) 1045 | { 1046 | if (type == kImageDeltas) 1047 | { 1048 | dist32[i][0] = EncodeU8Signed((deltas0[i].x + deltas1[i].x) / maxLen); 1049 | dist32[i][1] = EncodeU8Signed((deltas0[i].y + deltas1[i].y) / maxLen); 1050 | } 1051 | else 1052 | { 1053 | dist32[i][0] = EncodeU8(Dist(deltas0[i]) / maxLen); 1054 | dist32[i][1] = EncodeU8(Dist(deltas1[i]) / maxLen); 1055 | } 1056 | dist32[i][2] = 0; 1057 | dist32[i][3] = 255; 1058 | } 1059 | 1060 | WriteImage(name, type == kImageDeltas ? "sdf-delta" : "sdf-rg", w, h, dist32); 1061 | delete[] dist32; 1062 | } 1063 | } 1064 | 1065 | bool GenerateSignedDistance(const char* name, int w, int h, float maxLen, uint32_t mask[], int method, int imageType, FILE* s, bool check) 1066 | { 1067 | cCellDelta2* deltas0 = new cCellDelta2[w * h]; 1068 | cCellDelta2* deltas1 = new cCellDelta2[w * h]; 1069 | 1070 | InitDeltasFromBitMask(w, h, mask, deltas0, false); 1071 | InitDeltasFromBitMask(w, h, mask, deltas1, true); 1072 | 1073 | if (s) 1074 | { 1075 | PrintDeltas(w, h, deltas0, s); 1076 | PrintDeltas(w, h, deltas1, s); 1077 | 1078 | fprintf(s, "Generating exterior and interior distances\n"); 1079 | } 1080 | 1081 | FindDeltas(method, w, h, deltas0, s); 1082 | FindDeltas(method, w, h, deltas1, s); 1083 | 1084 | if (s) 1085 | { 1086 | PrintDeltas(w, h, deltas0, s); 1087 | PrintDeltas(w, h, deltas1, s); 1088 | } 1089 | 1090 | if (check) 1091 | { 1092 | if (!CheckDeltas(w, h, deltas0, stderr)) 1093 | return false; 1094 | 1095 | if (!CheckDeltas(w, h, deltas1, stderr)) 1096 | return false; 1097 | 1098 | // Not much point checking vs. optimal as covered by distance generation path 1099 | } 1100 | 1101 | WriteDistances(name, w, h, maxLen, deltas0, deltas1, imageType); 1102 | 1103 | delete[] deltas0; 1104 | delete[] deltas1; 1105 | 1106 | return true; 1107 | } 1108 | 1109 | void WriteDistances(const char* name, int w, int h, int d, float maxLen, cCellDelta3 deltas0[], cCellDelta3 deltas1[], int type) 1110 | { 1111 | if (type == kImageStandard) 1112 | { 1113 | uint8_t* dist8 = new uint8_t[w * h]; 1114 | 1115 | for (int slice = 0; slice < d; slice++) 1116 | { 1117 | cCellDelta3* sliceDeltas0 = deltas0 + w * h * slice; 1118 | cCellDelta3* sliceDeltas1 = deltas1 + w * h * slice; 1119 | 1120 | for (int i = 0, n = w * h; i < n; i++) 1121 | dist8[i] = EncodeU8Signed((Dist(sliceDeltas0[i]) - Dist(sliceDeltas1[i])) / maxLen); 1122 | 1123 | WriteImage(name, "sdf", w, h, dist8, slice); 1124 | } 1125 | 1126 | delete[] dist8; 1127 | } 1128 | else 1129 | { 1130 | uint8_t (*dist32)[4] = new uint8_t[w * h][4]; 1131 | 1132 | for (int slice = 0; slice < d; slice++) 1133 | { 1134 | cCellDelta3* sliceDeltas0 = deltas0 + w * h * slice; 1135 | cCellDelta3* sliceDeltas1 = deltas1 + w * h * slice; 1136 | 1137 | for (int i = 0, n = w * h; i < n; i++) 1138 | { 1139 | if (type == kImageDeltas) 1140 | { 1141 | dist32[i][0] = EncodeU8Signed((sliceDeltas0[i].x + sliceDeltas1[i].x) / maxLen); 1142 | dist32[i][1] = EncodeU8Signed((sliceDeltas0[i].y + sliceDeltas1[i].y) / maxLen); 1143 | dist32[i][2] = EncodeU8Signed((sliceDeltas0[i].z + sliceDeltas1[i].z) / maxLen); 1144 | } 1145 | else 1146 | { 1147 | dist32[i][0] = EncodeU8(Dist(sliceDeltas0[i]) / maxLen); 1148 | dist32[i][1] = EncodeU8(Dist(sliceDeltas1[i]) / maxLen); 1149 | dist32[i][2] = 0; 1150 | } 1151 | dist32[i][3] = 255; 1152 | } 1153 | 1154 | WriteImage(name, type == kImageDeltas ? "sdf-delta" : "sdf-rg", w, h, dist32, slice); 1155 | } 1156 | 1157 | delete[] dist32; 1158 | } 1159 | } 1160 | 1161 | bool GenerateSignedDistance(const char* name, const int w, const int h, const int d, float maxLen, const uint32_t mask[], int method, int imageType, FILE* s, bool check) 1162 | { 1163 | cCellDelta3* deltas0 = new cCellDelta3[w * h * d]; 1164 | cCellDelta3* deltas1 = new cCellDelta3[w * h * d]; 1165 | 1166 | InitDeltasFromBitMask(w, h, d, mask, deltas0, false); 1167 | InitDeltasFromBitMask(w, h, d, mask, deltas1, true); 1168 | 1169 | if (s) 1170 | fprintf(s, "Generating exterior and interior distances\n"); 1171 | 1172 | FindDeltas(method, w, h, d, deltas0, s); 1173 | FindDeltas(method, w, h, d, deltas1, s); 1174 | 1175 | if (s) 1176 | { 1177 | PrintDeltas(w, h, d, deltas0, s); 1178 | PrintDeltas(w, h, d, deltas1, s); 1179 | } 1180 | 1181 | if (check) 1182 | { 1183 | if (!CheckDeltas(w, h, d, deltas0, stderr)) 1184 | return false; 1185 | 1186 | if (!CheckDeltas(w, h, d, deltas1, stderr)) 1187 | return false; 1188 | } 1189 | 1190 | WriteDistances(name, w, h, d, maxLen, deltas0, deltas1, imageType); 1191 | 1192 | delete[] deltas0; 1193 | delete[] deltas1; 1194 | 1195 | return true; 1196 | } 1197 | 1198 | 1199 | 1200 | //////////////////////////////////////////////////////////////////////////// 1201 | // MARK: - Signed Distance generation using border technique 1202 | //////////////////////////////////////////////////////////////////////////// 1203 | 1204 | void WriteDistances(const char* name, int w, int h, float maxLen, const uint32_t mask[], const cCellDelta2 deltas[], int type) 1205 | { 1206 | if (type == kImageStandard) 1207 | { 1208 | uint8_t* dist8 = new uint8_t[w * h]; 1209 | 1210 | for (int y = 0; y < h; y++) 1211 | { 1212 | const cCellDelta2* deltasRow = deltas + w * y; 1213 | uint8_t* dist8Row = dist8 + w * y; 1214 | 1215 | for (int j = 0; j < w; j += 32) 1216 | { 1217 | uint32_t m = *mask++; 1218 | 1219 | int n = 32; 1220 | if (w - j < n) 1221 | n = w - j; 1222 | 1223 | for (int i = 0; i < n; i++) 1224 | { 1225 | float d = Dist(deltasRow[j + i]) / maxLen; 1226 | dist8Row[j + i] = EncodeU8Signed((m & 1) ? -d : d); 1227 | 1228 | m >>= 1; 1229 | } 1230 | } 1231 | } 1232 | 1233 | WriteImage(name, "sdf", w, h, dist8); 1234 | delete[] dist8; 1235 | } 1236 | else 1237 | { 1238 | uint8_t (*dist32)[4] = new uint8_t[w * h][4]; 1239 | 1240 | for (int y = 0; y < h; y++) 1241 | { 1242 | const cCellDelta2* deltasRow = deltas + w * y; 1243 | uint8_t (*dist32Row)[4] = dist32 + w * y; 1244 | 1245 | if (type == kImageDeltas) 1246 | { 1247 | for (int x = 0; x < w; x++) 1248 | { 1249 | dist32Row[x][0] = EncodeU8Signed(deltasRow[x].x / maxLen); 1250 | dist32Row[x][1] = EncodeU8Signed(deltasRow[x].y / maxLen); 1251 | dist32Row[x][2] = 0; 1252 | dist32Row[x][3] = 255; 1253 | } 1254 | } 1255 | else 1256 | for (int j = 0; j < w; j += 32) 1257 | { 1258 | uint32_t m = *mask++; 1259 | 1260 | int n = 32; 1261 | if (w - j < n) 1262 | n = w - j; 1263 | 1264 | for (int i = 0; i < n; i++) 1265 | { 1266 | dist32Row[j + i][ m & 1 ] = EncodeU8(Dist(deltasRow[j + i]) / maxLen); 1267 | dist32Row[j + i][(m & 1) ^ 1] = 0; 1268 | dist32Row[j + i][2] = 0; 1269 | dist32Row[j + i][3] = 255; 1270 | 1271 | m >>= 1; 1272 | } 1273 | } 1274 | } 1275 | 1276 | WriteImage(name, type == kImageDeltas ? "sdf-delta" : "sdf-rg", w, h, dist32); 1277 | delete[] dist32; 1278 | } 1279 | } 1280 | 1281 | bool GenerateSignedDistanceBorder(const char* name, int w, int h, float maxLen, uint32_t mask[], int method, int imageType, FILE* s, bool check) 1282 | { 1283 | cCellDelta2* deltas = new cCellDelta2[w * h]; 1284 | 1285 | InitBorderDeltasFromBitMask(w, h, mask, deltas); 1286 | 1287 | if (s) 1288 | PrintDeltas(w, h, deltas, s); 1289 | 1290 | if (check && !CheckBorderDeltas(w, h, mask, deltas, stderr)) 1291 | return false; 1292 | 1293 | if (s) 1294 | fprintf(s, "Generating signed distances from border init\n"); 1295 | 1296 | FindDeltas(method, w, h, deltas, s, 2); 1297 | 1298 | if (s) 1299 | PrintDeltas(w, h, deltas, s); 1300 | 1301 | if (check) 1302 | { 1303 | if (!CheckDeltas(w, h, deltas, stderr, 2)) 1304 | return false; 1305 | 1306 | // Not much point checking vs. optimal as covered by distance generation path 1307 | } 1308 | 1309 | WriteDistances(name, w, h, maxLen * 2, mask, deltas, imageType); 1310 | delete[] deltas; 1311 | 1312 | return true; 1313 | } 1314 | 1315 | void WriteDistances(const char* name, int w, int h, int d, float maxLen, const uint32_t mask[], cCellDelta3 deltas[], int type) 1316 | { 1317 | if (type == kImageStandard) 1318 | { 1319 | // Write as images 1320 | uint8_t* dist8 = new uint8_t[w * h]; 1321 | 1322 | for (int slice = 0; slice < d; slice++) 1323 | { 1324 | for (int y = 0; y < h; y++) 1325 | { 1326 | const cCellDelta3* deltasRow = deltas + w * y; 1327 | uint8_t* dist8Row = dist8 + w * y; 1328 | 1329 | for (int j = 0; j < w; j += 32) 1330 | { 1331 | uint32_t m = *mask++; 1332 | 1333 | int n = 32; 1334 | if (w - j < n) 1335 | n = w - j; 1336 | 1337 | for (int i = 0; i < n; i++) 1338 | { 1339 | float d = Dist(deltasRow[j + i]) / maxLen; 1340 | dist8Row[j + i] = EncodeU8Signed((m & 1) ? -d : d); 1341 | 1342 | m >>= 1; 1343 | } 1344 | } 1345 | } 1346 | 1347 | WriteImage(name, "sdf", w, h, dist8, slice); 1348 | deltas += w * h; 1349 | } 1350 | 1351 | delete[] dist8; 1352 | } 1353 | else 1354 | { 1355 | uint8_t (*dist32)[4] = new uint8_t[w * h][4]; 1356 | 1357 | for (int slice = 0; slice < d; slice++) 1358 | { 1359 | for (int y = 0; y < h; y++) 1360 | { 1361 | const cCellDelta3* deltasRow = deltas + w * y; 1362 | uint8_t (*dist32Row)[4] = dist32 + w * y; 1363 | 1364 | if (type == kImageDeltas) 1365 | { 1366 | for (int x = 0; x < w; x++) 1367 | { 1368 | dist32Row[x][0] = EncodeU8Signed(deltasRow[x].x / maxLen); 1369 | dist32Row[x][1] = EncodeU8Signed(deltasRow[x].y / maxLen); 1370 | dist32Row[x][2] = EncodeU8Signed(deltasRow[x].z / maxLen); 1371 | dist32Row[x][3] = 255; 1372 | } 1373 | } 1374 | else 1375 | { 1376 | for (int j = 0; j < w; j += 32) 1377 | { 1378 | uint32_t m = *mask++; 1379 | 1380 | int n = 32; 1381 | if (w - j < n) 1382 | n = w - j; 1383 | 1384 | for (int i = 0; i < n; i++) 1385 | { 1386 | dist32Row[j + i][ m & 1 ] = EncodeU8(Dist(deltasRow[j + i]) / maxLen); 1387 | dist32Row[j + i][(m & 1) ^ 1] = 0; 1388 | dist32Row[j + i][2] = 0; 1389 | dist32Row[j + i][3] = 255; 1390 | 1391 | m >>= 1; 1392 | } 1393 | } 1394 | } 1395 | } 1396 | 1397 | WriteImage(name, type == kImageDeltas ? "sdf-delta" : "sdf-rg", w, h, dist32, slice); 1398 | deltas += w * h; 1399 | } 1400 | 1401 | delete[] dist32; 1402 | } 1403 | } 1404 | 1405 | bool GenerateSignedDistanceBorder(const char* name, const int w, const int h, const int d, float maxLen, const uint32_t mask[], int method, int imageType, FILE* s, bool check) 1406 | { 1407 | cCellDelta3* deltas = new cCellDelta3[w * h * d]; 1408 | 1409 | InitBorderDeltasFromBitMask(w, h, d, mask, deltas); 1410 | 1411 | if (s) 1412 | PrintDeltas(w, h, d, deltas, s); 1413 | 1414 | if (check && !CheckBorderDeltas(w, h, d, mask, deltas, stderr)) 1415 | return false; 1416 | 1417 | if (s) 1418 | fprintf(s, "Generating signed distances from border init\n"); 1419 | 1420 | FindDeltas(method, w, h, d, deltas, s, 2); 1421 | 1422 | if (s) 1423 | PrintDeltas(w, h, d, deltas, s); 1424 | 1425 | if (check && !CheckDeltas(w, h, d, deltas, stderr, 2)) 1426 | return false; 1427 | 1428 | WriteDistances(name, w, h, d, maxLen * 2, mask, deltas, imageType); 1429 | delete[] deltas; 1430 | 1431 | return true; 1432 | } 1433 | 1434 | 1435 | 1436 | 1437 | //////////////////////////////////////////////////////////////////////////// 1438 | // MARK: - Occlusion testing 1439 | //////////////////////////////////////////////////////////////////////////// 1440 | 1441 | void GenerateOcclusion(const char* name, const int w, const int h, float dirW[], int method, FILE* s) 1442 | { 1443 | OcclusionSweep(w, h, dirW); 1444 | 1445 | const int imageSize = w * h; 1446 | float* dirW4[4] = { dirW, dirW + imageSize, dirW + imageSize * 2, dirW + imageSize * 3 }; 1447 | 1448 | if (s) 1449 | for (int i = 0; i < 4; i++) 1450 | { 1451 | fprintf(s, "quadrant %d:\n", i); 1452 | PrintDistances(w, h, dirW4[i], s); 1453 | } 1454 | 1455 | uint8_t* ao = new uint8_t[w * h]; 1456 | 1457 | for (int i = 0, n = w * h; i < n; i++) 1458 | { 1459 | const float dirWCell[4] = { dirW4[0][i], dirW4[1][i], dirW4[2][i], dirW4[3][i] }; 1460 | 1461 | ao[i] = EncodeAO4(dirWCell); 1462 | } 1463 | 1464 | WriteImage(name, "ao", w, h, ao); 1465 | 1466 | uint8_t (*aoDir)[4] = new uint8_t[w * h][4]; 1467 | 1468 | for (int i = 0, n = w * h; i < n; i++) 1469 | { 1470 | const float dirWCell[4] = { dirW4[0][i], dirW4[1][i], dirW4[2][i], dirW4[3][i] }; 1471 | 1472 | EncodeDirW4(dirWCell, aoDir[i]); 1473 | 1474 | if (method == 2) 1475 | aoDir[i][2] = aoDir[i][1]; 1476 | } 1477 | 1478 | WriteImage(name, "dirW", w, h, aoDir); 1479 | 1480 | for (int d = 0; d < 4; d++) 1481 | { 1482 | for (int i = 0, n = w * h; i < n; i++) 1483 | ao[i] = EncodeU8(1.0f - dirW4[d][i]); 1484 | 1485 | WriteImage(name, "dirW", w, h, ao, d); 1486 | } 1487 | 1488 | delete[] ao; 1489 | delete[] aoDir; 1490 | } 1491 | 1492 | void GenerateOcclusion(const char* name, const int w, const int h, const int d, float* dirW, int method, FILE* s) 1493 | { 1494 | const size_t volumeStride = w * h * d; 1495 | 1496 | if (s) 1497 | fprintf(s, "\nMethod %d:\n", method); 1498 | 1499 | if (method & 1) 1500 | { 1501 | float* outDirW = new float[8 * volumeStride]; 1502 | 1503 | OcclusionSweep(w, h, d, dirW, outDirW); 1504 | memcpy(dirW, outDirW, 8 * volumeStride * sizeof(float)); 1505 | 1506 | delete[] outDirW; 1507 | } 1508 | else 1509 | OcclusionSweep(w, h, d, dirW); 1510 | 1511 | if (s) 1512 | for (int i = 0; i < 8; i++) 1513 | { 1514 | fprintf(s, "octant %d:\n", i); 1515 | PrintDistances(w, h, d, dirW + volumeStride * i, s); 1516 | } 1517 | 1518 | // Write as images 1519 | const size_t sliceStride = w * h; 1520 | uint8_t* ao = new uint8_t[sliceStride]; 1521 | uint8_t (*aoDir)[4] = new uint8_t[sliceStride][4]; 1522 | 1523 | for (int slice = 0; slice < d; slice++) 1524 | { 1525 | float* sDirW[8]; 1526 | 1527 | for (int d = 0; d < 8; d++) 1528 | sDirW[d] = dirW + sliceStride * slice + volumeStride * d; 1529 | 1530 | for (int i = 0, n = w * h; i < n; i++) 1531 | { 1532 | const float dirWCell[8] = { sDirW[0][i], sDirW[1][i], sDirW[2][i], sDirW[3][i], 1533 | sDirW[4][i], sDirW[5][i], sDirW[6][i], sDirW[7][i] }; 1534 | 1535 | ao[i] = EncodeAO8(dirWCell); 1536 | } 1537 | 1538 | WriteImage(name, "ao", w, h, ao, slice); 1539 | 1540 | for (int i = 0, n = w * h; i < n; i++) 1541 | { 1542 | const float dirWCell[8] = { sDirW[0][i], sDirW[1][i], sDirW[2][i], sDirW[3][i], 1543 | sDirW[4][i], sDirW[5][i], sDirW[6][i], sDirW[7][i] }; 1544 | 1545 | EncodeDirW8(dirWCell, aoDir[i]); 1546 | 1547 | if (method == 2) 1548 | aoDir[i][3] = 255; 1549 | } 1550 | 1551 | WriteImage(name, "dirW", w, h, aoDir, slice); 1552 | } 1553 | 1554 | delete[] ao; 1555 | delete[] aoDir; 1556 | } 1557 | 1558 | #if USE_MESH 1559 | float* DirWFromFile(const int w, const int h, const int d, const char* sourceFile) 1560 | { 1561 | FILE* meshFile = fopen(sourceFile, "r"); 1562 | 1563 | if (!meshFile) 1564 | { 1565 | fprintf(stderr, "Can't open %s\n", sourceFile); 1566 | return 0; 1567 | } 1568 | 1569 | cMesh mesh; 1570 | ReadObjFile(meshFile, &mesh); 1571 | 1572 | const size_t volumeStride = w * h * d; 1573 | 1574 | float* dirW = new float[8 * volumeStride](); 1575 | 1576 | Bounds3f bbox = FindBounds(mesh); 1577 | bbox = FindAOBounds(0.5f, bbox); 1578 | 1579 | CreateDirW8FromTriangles 1580 | ( 1581 | (int) mesh.mPositionIndices.size() / 3, 1582 | mesh.mPositionIndices.data(), 1583 | mesh.mPositions.data(), 1584 | bbox, 1585 | w, h, d, 1586 | dirW 1587 | ); 1588 | 1589 | return dirW; 1590 | } 1591 | #endif 1592 | } 1593 | 1594 | //////////////////////////////////////////////////////////////////////////// 1595 | // MARK: - Tool 1596 | //////////////////////////////////////////////////////////////////////////// 1597 | 1598 | namespace 1599 | { 1600 | inline const char* Next(int& argc, const char**& argv) 1601 | { 1602 | const char* result = argc > 0 ? argv[0] : 0; 1603 | argc--; argv++; 1604 | return result; 1605 | } 1606 | inline bool NextIsOption(int argc, const char* argv[]) 1607 | { 1608 | return argc > 0 && argv[0][0] == '-' && isalpha(argv[0][1]); 1609 | } 1610 | inline bool NextIsArg(int argc, const char* argv[]) 1611 | { 1612 | return argc > 0 && (argv[0][0] != '-' || !isalpha(argv[0][1])); 1613 | } 1614 | 1615 | void GetFileName(char* buffer, size_t bufferSize, const char* path) 1616 | { 1617 | const char* lastSlash = strrchr(path, '/'); 1618 | if (!lastSlash) 1619 | lastSlash = strrchr(path, '\\'); 1620 | 1621 | if (lastSlash) 1622 | strlcpy(buffer, lastSlash + 1, bufferSize); 1623 | else 1624 | strlcpy(buffer, path, bufferSize); 1625 | 1626 | char* lastDot = strrchr(buffer, '.'); 1627 | 1628 | if (lastDot) 1629 | *lastDot = 0; 1630 | } 1631 | } 1632 | 1633 | int main(int argc, const char* argv[]) 1634 | { 1635 | if (argc == 1) 1636 | { 1637 | printf 1638 | ( 1639 | "Distance/Occlusion generation tool\n" 1640 | "\n" 1641 | "Options:\n" 1642 | " -d: generate distance field. Methods: 0, Danielsson; 1, Fast Sweep; 2, Jump Flood; 3, Brute Force; 4, Chamfer; 5, Felzenszwalb\n" 1643 | " -s: generate signed distance field in a single pass. Methods: as for -d but Chamfer/Felzenszwalb unsupported.\n" 1644 | " -S: generate signed distance field via orthodox two distance field passes.\n" 1645 | " -x: generate occlusion. Methods 0: standard, 1: no self-occlusion, 2: directional components only\n" 1646 | "\n" 1647 | " -m : select method variant for above\n" 1648 | "\n" 1649 | " -w [] : set dimensions of image source\n" 1650 | " -W [ ]: set dimensions of volume source\n" 1651 | " -p [ ] : add random points to image/volume (default)\n" 1652 | " -b : add test box with the given number of sides to image/volume\n" 1653 | #if USE_STB 1654 | "\n" 1655 | " -f : specify input image\n" 1656 | " -t : specify 0-255 threshold for creating mask from image\n" 1657 | " -r : reverse so white is occupied rather than black\n" 1658 | #endif 1659 | #if USE_MESH 1660 | "\n" 1661 | " -F : use given obj file to define mask.\n" 1662 | #endif 1663 | "\n" 1664 | " -o : set base name for output file(s)\n" 1665 | #if USE_STB || USE_MESH 1666 | " -v : log detailed output\n" 1667 | #endif 1668 | " -c : run checks on output\n" 1669 | ); 1670 | 1671 | return -1; 1672 | } 1673 | 1674 | Next(argc, argv); // command name 1675 | 1676 | enum tAlgorithm 1677 | { 1678 | kNone, 1679 | kDistance, 1680 | kSignedDistance, 1681 | kSignedDistanceBorder, 1682 | kOcclusion, 1683 | kMaxAlgorithms 1684 | }; 1685 | 1686 | int algorithm = kNone; 1687 | int method = 0; 1688 | int imageType = kImageStandard; 1689 | int dim[3] = { 0, 0, 0 }; 1690 | float maxLen = 0.0f; 1691 | bool volume = false; 1692 | 1693 | int pointsSeed = 12345; 1694 | int numPoints = 0; 1695 | int numSides = 0; 1696 | 1697 | char outName[1024] = "out"; 1698 | bool haveName = false; 1699 | int check = 0; 1700 | 1701 | #if USE_STB || USE_MESH 1702 | int threshold = 128; 1703 | bool lessThan = true; 1704 | const char* sourceFile = 0; 1705 | FILE* out = 0; 1706 | #else 1707 | FILE* out = stdout; 1708 | #endif 1709 | 1710 | while (NextIsOption(argc, argv)) 1711 | { 1712 | const char* option = Next(argc, argv) + 1; 1713 | 1714 | switch (option[0]) 1715 | { 1716 | case 'd': 1717 | algorithm = kDistance; 1718 | break; 1719 | case 's': 1720 | algorithm = kSignedDistanceBorder; 1721 | break; 1722 | case 'S': 1723 | algorithm = kSignedDistance; 1724 | break; 1725 | case 'x': 1726 | algorithm = kOcclusion; 1727 | break; 1728 | case 'v': 1729 | out = stdout; 1730 | break; 1731 | case 'c': 1732 | check = 1; 1733 | break; 1734 | case 'm': 1735 | if (NextIsArg(argc, argv)) 1736 | method = atoi(Next(argc, argv)); 1737 | break; 1738 | case 'i': 1739 | if (NextIsArg(argc, argv)) 1740 | { 1741 | imageType = atoi(Next(argc, argv)); 1742 | 1743 | if (maxLen == 0 && imageType == kImageDeltas) 1744 | maxLen = 127; // default to 1:1 1745 | } 1746 | break; 1747 | case 'w': 1748 | case 'W': 1749 | for (int i = 0; i < 3 && NextIsArg(argc, argv); i++) 1750 | dim[i] = atoi(Next(argc, argv)); 1751 | 1752 | volume = (option[0] == 'W'); 1753 | break; 1754 | case 'p': 1755 | if (NextIsArg(argc, argv)) 1756 | numPoints = atoi(Next(argc, argv)); 1757 | else 1758 | numPoints = (dim[0] + dim[1] + dim[2]) / 8; 1759 | 1760 | if (NextIsArg(argc, argv)) 1761 | pointsSeed = atoi(Next(argc, argv)); 1762 | break; 1763 | case 'b': 1764 | if (NextIsArg(argc, argv)) 1765 | numSides = atoi(Next(argc, argv)); 1766 | else 1767 | numSides = 2; 1768 | break; 1769 | #if USE_STB || USE_MESH 1770 | case 't': 1771 | if (NextIsArg(argc, argv)) 1772 | threshold = atoi(Next(argc, argv)); 1773 | break; 1774 | case 'r': 1775 | lessThan = !lessThan; 1776 | break; 1777 | case 'l': 1778 | if (NextIsArg(argc, argv)) 1779 | maxLen = atoi(Next(argc, argv)); 1780 | break; 1781 | case 'f': 1782 | case 'F': 1783 | if (NextIsArg(argc, argv)) 1784 | sourceFile = Next(argc, argv); 1785 | 1786 | volume = volume || (option[0] == 'F'); 1787 | break; 1788 | #endif 1789 | case 'o': 1790 | if (NextIsArg(argc, argv)) 1791 | { 1792 | strncpy(outName, Next(argc, argv), sizeof(outName)); 1793 | haveName = true; 1794 | } 1795 | break; 1796 | default: 1797 | fprintf(stderr, "Unknown option: -%s\n", option); 1798 | return -1; 1799 | } 1800 | } 1801 | 1802 | if (argc > 0) 1803 | { 1804 | fprintf(stderr, "Unknown argument: %s\n", argv[0]); 1805 | return -1; 1806 | } 1807 | 1808 | if (dim[0] == 0) 1809 | dim[0] = volume ? 32 : 256; 1810 | 1811 | if (dim[1] == 0) 1812 | dim[1] = dim[0]; 1813 | 1814 | if (volume && dim[2] == 0) 1815 | dim[2] = dim[1]; 1816 | 1817 | if (!sourceFile && numSides == 0 && numPoints == 0) 1818 | numPoints = (dim[0] + dim[1] + dim[2]) / 8; 1819 | 1820 | uint32_t* mask = 0; 1821 | float* dirW = 0; 1822 | 1823 | if (out) 1824 | fprintf(out, "Generating presence mask\n"); 1825 | 1826 | if (!volume) 1827 | { 1828 | #if USE_STB 1829 | if (sourceFile) 1830 | mask = BitMaskFromImageFile(sourceFile, threshold, lessThan, dim[0], dim[1]); 1831 | else 1832 | #endif 1833 | mask = CreateBitMask(dim[0], dim[1]); 1834 | 1835 | if (!mask) 1836 | return -1; 1837 | 1838 | if (numSides > 0) 1839 | BitMaskAddBlock(dim[0], dim[1], numSides, mask); 1840 | if (numPoints > 0) 1841 | BitMaskAddPoints(dim[0], dim[1], numPoints, pointsSeed, mask); 1842 | 1843 | if (out) 1844 | PrintMask(dim[0], dim[1], mask, out); 1845 | 1846 | if (algorithm == kOcclusion) 1847 | { 1848 | dirW = new float[4 * dim[0] * dim[1]]; 1849 | InitDirWFromBitMask(dim[0], dim[1], mask, dirW); 1850 | delete[] mask; 1851 | mask = 0; 1852 | } 1853 | } 1854 | else 1855 | { 1856 | #if USE_MESH 1857 | if (sourceFile) 1858 | { 1859 | if (algorithm == kOcclusion) 1860 | dirW = DirWFromFile(dim[0], dim[1], dim[2], sourceFile); // construct DirW directly so we can take advantage of triangle normal info 1861 | else 1862 | mask = BitMaskFromObjFile(dim[0], dim[1], dim[2], sourceFile, out); 1863 | } 1864 | else 1865 | #endif 1866 | mask = CreateBitMask(dim[0], dim[1], dim[2]); 1867 | 1868 | if (!mask && !dirW) 1869 | return -1; 1870 | 1871 | if (mask) 1872 | { 1873 | if (numSides > 0) 1874 | BitMaskAddBlock(dim[0], dim[1], dim[2], numSides, mask); 1875 | if (numPoints > 0) 1876 | BitMaskAddPoints(dim[0], dim[1], dim[2], numPoints, pointsSeed, mask); 1877 | if (out) 1878 | PrintMask(dim[0], dim[1], dim[1], mask, out); 1879 | 1880 | if (algorithm == kOcclusion) 1881 | { 1882 | dirW = new float[8 * dim[0] * dim[1] * dim[2]]; 1883 | InitDirWFromBitMask(dim[0], dim[1], dim[2], mask, dirW); 1884 | delete[] mask; 1885 | mask = 0; 1886 | } 1887 | } 1888 | } 1889 | 1890 | if (maxLen == 0.0f) 1891 | maxLen = 0.25f * (dim[0] >= dim[1] ? dim[0] : dim[1]); 1892 | 1893 | bool success = true; 1894 | 1895 | if (out) 1896 | fprintf(out, "Generating results\n"); 1897 | 1898 | #if USE_STB || USE_MESH 1899 | if (!haveName && sourceFile) 1900 | GetFileName(outName, sizeof(outName), sourceFile); 1901 | #endif 1902 | 1903 | switch (algorithm) 1904 | { 1905 | case kDistance: 1906 | if (dim[2] == 0) 1907 | success = GenerateDistance(outName, dim[0], dim[1], maxLen, mask, method, out, check); 1908 | else 1909 | success = GenerateDistance(outName, dim[0], dim[1], dim[2], maxLen, mask, method, out, check); 1910 | break; 1911 | 1912 | case kSignedDistance: 1913 | if (dim[2] == 0) 1914 | success = GenerateSignedDistance(outName, dim[0], dim[1], maxLen, mask, method, imageType, out, check); 1915 | else 1916 | success = GenerateSignedDistance(outName, dim[0], dim[1], dim[2], maxLen, mask, method, imageType, out, check); 1917 | break; 1918 | 1919 | case kSignedDistanceBorder: 1920 | if (dim[2] == 0) 1921 | success = GenerateSignedDistanceBorder(outName, dim[0], dim[1], maxLen, mask, method, imageType, out, check); 1922 | else 1923 | success = GenerateSignedDistanceBorder(outName, dim[0], dim[1], dim[2], maxLen, mask, method, imageType, out, check); 1924 | break; 1925 | 1926 | case kOcclusion: 1927 | if (dim[2] == 0) 1928 | GenerateOcclusion(outName, dim[0], dim[1], dirW, method, out); 1929 | else 1930 | GenerateOcclusion(outName, dim[0], dim[1], dim[2], dirW, method, out); 1931 | break; 1932 | 1933 | default: 1934 | fprintf(stderr, "Please specify an algorithm (-d/-s/-S/-x)\n"); 1935 | success = false; 1936 | } 1937 | 1938 | delete[] mask; 1939 | delete[] dirW; 1940 | 1941 | return success ? 0 : -1; 1942 | } 1943 | 1944 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /MeshSupport.cpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // Purpose: Quick and dirty mesh support 3 | // Author: Andrew Willmott 4 | //------------------------------------------------------------------------------ 5 | 6 | #define _CRT_SECURE_NO_WARNINGS 7 | #define _USE_MATH_DEFINES 8 | 9 | #include "MeshSupport.h" 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #ifdef _MSC_VER 19 | #pragma warning (disable: 4244) 20 | #define strcasecmp _stricmp 21 | #define strtok_r strtok_s 22 | #endif 23 | 24 | using namespace MSL; 25 | 26 | // Finds size of volume bbox given allowed error 'eps'. Derivation from Malmer et al. 27 | Bounds3f MSL::FindAOBounds(float eps, Bounds3f modelBounds) 28 | { 29 | float s = 1.0f / (4.0f * float(M_PI) * eps); 30 | 31 | Vec3f d = 32 | { 33 | modelBounds.mMax.x - modelBounds.mMin.x, 34 | modelBounds.mMax.y - modelBounds.mMin.y, 35 | modelBounds.mMax.z - modelBounds.mMin.z 36 | }; 37 | 38 | Vec3f a = 39 | { 40 | sqrtf(d.y * d.z * s), 41 | sqrtf(d.z * d.x * s), 42 | sqrtf(d.x * d.y * s) 43 | }; 44 | 45 | Bounds3f result = 46 | { 47 | { 48 | modelBounds.mMin.x - a.x, 49 | modelBounds.mMin.y - a.y, 50 | modelBounds.mMin.z - a.z, 51 | }, 52 | { 53 | modelBounds.mMax.x + a.x, 54 | modelBounds.mMax.y + a.y, 55 | modelBounds.mMax.z + a.z, 56 | } 57 | }; 58 | 59 | return result; 60 | } 61 | 62 | // This version allows you to supply your own axial projected area bounds 63 | Bounds3f MSL::FindAOBounds(float eps, Bounds3f modelBounds, Vec3f minArea, Vec3f maxArea) 64 | { 65 | float s = 1.0f / (4.0f * float(M_PI) * eps); 66 | 67 | Bounds3f result = 68 | { 69 | { 70 | modelBounds.mMin.x - sqrtf(minArea.x * s), 71 | modelBounds.mMin.y - sqrtf(minArea.y * s), 72 | modelBounds.mMin.z - sqrtf(minArea.z * s) 73 | }, 74 | { 75 | modelBounds.mMax.x + sqrtf(maxArea.x * s), 76 | modelBounds.mMax.y + sqrtf(maxArea.y * s), 77 | modelBounds.mMax.z + sqrtf(maxArea.z * s) 78 | } 79 | }; 80 | 81 | return result; 82 | } 83 | 84 | namespace MSL 85 | { 86 | inline Vec3f operator+ (Vec3f a, Vec3f b) { Vec3f v = { a.x + b.x, a.y + b.y, a.z + b.z }; return v; } 87 | inline Vec3f operator- (Vec3f a, Vec3f b) { Vec3f v = { a.x - b.x, a.y - b.y, a.z - b.z }; return v; } 88 | inline Vec3f operator* (Vec3f a, Vec3f b) { Vec3f v = { a.x * b.x, a.y * b.y, a.z * b.z }; return v; } 89 | inline Vec3f operator/ (Vec3f a, Vec3f b) { Vec3f v = { a.x / b.x, a.y / b.y, a.z / b.z }; return v; } 90 | inline Vec3f operator* (Vec3f a, float s) { Vec3f v = { a.x * s, a.y * s, a.z * s }; return v; } 91 | inline Vec3f& operator+=(Vec3f& a, Vec3f b) { a.x += b.x; a.y += b.y; a.z += b.z; return a; } 92 | inline Vec3f& operator-=(Vec3f& a, Vec3f b) { a.x -= b.x; a.y -= b.y; a.z -= b.z; return a; } 93 | 94 | inline float dot(Vec3f a, Vec3f b) { return a.x * b.x + a.y * b.y + a.z * b.z; } 95 | inline Vec3f abs(Vec3f v) { Vec3f r = { fabsf(v.x), fabsf(v.y), fabsf(v.z) }; return r; } 96 | inline float len(Vec3f v) { return sqrtf(v.x * v.x + v.y * v.y + v.z * v.z); } 97 | 98 | template inline void swap(T& a, T& b) { T t(a); a = b; b = t; } 99 | 100 | template inline T Max(T a, T b) 101 | { 102 | return b < a ? a : b; 103 | } 104 | 105 | template inline T Min(T a, T b) 106 | { 107 | return a < b ? a : b; 108 | } 109 | 110 | inline Vec3f MinElts(const Vec3f& a, const Vec3f& b) 111 | { 112 | Vec3f result = 113 | { 114 | Min(a.x, b.x), 115 | Min(a.y, b.y), 116 | Min(a.z, b.z) 117 | }; 118 | return result; 119 | } 120 | 121 | inline Vec3f MaxElts(const Vec3f& a, const Vec3f& b) 122 | { 123 | Vec3f result = 124 | { 125 | Max(a.x, b.x), 126 | Max(a.y, b.y), 127 | Max(a.z, b.z) 128 | }; 129 | return result; 130 | } 131 | 132 | inline int FloorToInt32(float x) 133 | { 134 | return int(floorf(x)); 135 | } 136 | inline int CeilToInt32(float x) 137 | { 138 | return int(ceilf(x)); 139 | } 140 | 141 | Vec3f TriDoubleAreaNormal 142 | ( 143 | const Vec3f& a, 144 | const Vec3f& b, 145 | const Vec3f& c 146 | ) 147 | { 148 | Vec3f n = 149 | { 150 | (a.y - b.y) * (a.z + b.z) + (b.y - c.y) * (b.z + c.z) + (c.y - a.y) * (c.z + a.z), 151 | (a.z - b.z) * (a.x + b.x) + (b.z - c.z) * (b.x + c.x) + (c.z - a.z) * (c.x + a.x), 152 | (a.x - b.x) * (a.y + b.y) + (b.x - c.x) * (b.y + c.y) + (c.x - a.x) * (c.y + a.y) 153 | }; 154 | 155 | return n; 156 | } 157 | 158 | struct cTriCellIntersectDelta 159 | /// Helper class for intersecting a triangle with many identically-sized, 160 | /// axis-aligned cells, as in voxelisation. It is assumed the cells will 161 | /// be visited in z/y/x order, and helpers are provided for early outs where 162 | /// an entire slice or row is guaranteed not to intersect. 163 | { 164 | cTriCellIntersectDelta(Vec3f p1, Vec3f p2, Vec3f p3, Vec3f hw); ///< Initialise from triangle and cell half-width 165 | 166 | bool IntersectZ(const Vec3f& c); ///< Returns true if cell extended infinitely in z would intersect (slice). 167 | bool IntersectY(const Vec3f& c); ///< Returns true if cell extended infinitely in y would intersect (row), assuming IntersectDeltaZ(). 168 | bool IntersectX(const Vec3f& c); ///< Returns true if cell would intersect, assuming IntersectDeltaZ() && IntersectDeltaY(). 169 | 170 | // Pre-calculated triangle intersection data 171 | Vec3f e1, t1, u1; // Triangle edges + axis testing info for axis testing 172 | Vec3f e2, t2, u2; 173 | Vec3f e3, t3, u3; 174 | 175 | Vec3f pMin; // For range testing 176 | Vec3f pMax; 177 | Vec3f hw; // half cell width 178 | 179 | Vec3f normal; // For plane testing 180 | float nDotVMin; 181 | float nDotVMax; 182 | float nd; 183 | }; 184 | 185 | cTriCellIntersectDelta::cTriCellIntersectDelta(Vec3f p1, Vec3f p2, Vec3f p3, Vec3f hwIn) : 186 | hw(hwIn) 187 | { 188 | e1 = p2 - p1; 189 | e2 = p3 - p2; 190 | e3 = p1 - p3; 191 | 192 | normal = TriDoubleAreaNormal(p1, p2, p3); 193 | 194 | Vec3f vmin, vmax; 195 | 196 | for (int i = 0; i < 3; i++) 197 | if ((&normal.x)[i] > 0.0f) 198 | { 199 | (&vmin.x)[i] = -(&hw.x)[i]; 200 | (&vmax.x)[i] = +(&hw.x)[i]; 201 | } 202 | else 203 | { 204 | (&vmin.x)[i] = +(&hw.x)[i]; 205 | (&vmax.x)[i] = -(&hw.x)[i]; 206 | } 207 | 208 | nDotVMin = dot(normal, vmin); 209 | nDotVMax = dot(normal, vmax); 210 | nd = dot(normal, p1); 211 | 212 | Vec3f ae1 = abs(e1); 213 | Vec3f ae2 = abs(e2); 214 | Vec3f ae3 = abs(e3); 215 | 216 | t1.x = e1.z * p1.y - e1.y * p1.z; 217 | t1.y = -e1.z * p1.x + e1.x * p1.z; 218 | t1.z = e1.y * p2.x - e1.x * p2.y; 219 | 220 | u1.x = e1.z * p3.y - e1.y * p3.z; 221 | u1.y = -e1.z * p3.x + e1.x * p3.z; 222 | u1.z = e1.y * p3.x - e1.x * p3.y; 223 | 224 | if (t1.x >= u1.x) swap(t1.x, u1.x); 225 | if (t1.y >= u1.y) swap(t1.y, u1.y); 226 | if (t1.z >= u1.z) swap(t1.z, u1.z); 227 | 228 | t2.x = e2.z * p1.y - e2.y * p1.z; 229 | t2.y = -e2.z * p1.x + e2.x * p1.z; 230 | t2.z = e2.y * p1.x - e2.x * p1.y; 231 | 232 | u2.x = e2.z * p3.y - e2.y * p3.z; 233 | u2.y = -e2.z * p3.x + e2.x * p3.z; 234 | u2.z = e2.y * p2.x - e2.x * p2.y; 235 | 236 | if (t2.x >= u2.x) swap(t2.x, u2.x); 237 | if (t2.y >= u2.y) swap(t2.y, u2.y); 238 | if (t2.z >= u2.z) swap(t2.z, u2.z); 239 | 240 | t3.x = e3.z * p1.y - e3.y * p1.z; 241 | t3.y = -e3.z * p1.x + e3.x * p1.z; 242 | t3.z = e3.y * p2.x - e3.x * p2.y; 243 | 244 | u3.x = e3.z * p2.y - e3.y * p2.z; 245 | u3.y = -e3.z * p2.x + e3.x * p2.z; 246 | u3.z = e3.y * p3.x - e3.x * p3.y; 247 | 248 | if (t3.x >= u3.x) swap(t3.x, u3.x); 249 | if (t3.y >= u3.y) swap(t3.y, u3.y); 250 | if (t3.z >= u3.z) swap(t3.z, u3.z); 251 | 252 | Vec3f r1, r2, r3; 253 | r1.x = ae1.z * hw.y + ae1.y * hw.z; 254 | r1.y = ae1.z * hw.x + ae1.x * hw.z; 255 | r1.z = ae1.y * hw.x + ae1.x * hw.y; 256 | 257 | r2.x = ae2.z * hw.y + ae2.y * hw.z; 258 | r2.y = ae2.z * hw.x + ae2.x * hw.z; 259 | r2.z = ae2.y * hw.x + ae2.x * hw.y; 260 | 261 | r3.x = ae3.z * hw.y + ae3.y * hw.z; 262 | r3.y = ae3.z * hw.x + ae3.x * hw.z; 263 | r3.z = ae3.y * hw.x + ae3.x * hw.y; 264 | 265 | t1 -= r1; 266 | t2 -= r2; 267 | t3 -= r3; 268 | 269 | u1 += r1; 270 | u2 += r2; 271 | u3 += r3; 272 | 273 | pMin = MinElts(p1, p2); 274 | pMin = MinElts(pMin, p3); 275 | 276 | pMax = MaxElts(p1, p2); 277 | pMax = MaxElts(pMax, p3); 278 | } 279 | 280 | inline bool cTriCellIntersectDelta::IntersectZ(const Vec3f& c) 281 | { 282 | if (pMin.z - c.z > hw.z || pMax.z - c.z < -hw.z) 283 | return false; 284 | 285 | return true; 286 | } 287 | 288 | bool cTriCellIntersectDelta::IntersectY(const Vec3f& c) 289 | { 290 | float dr; 291 | 292 | dr = e1.z * c.y - e1.y * c.z; 293 | 294 | if (t1.x > dr || u1.x < dr) 295 | return false; 296 | 297 | dr = e2.z * c.y - e2.y * c.z; 298 | 299 | if (t2.x > dr || u2.x < dr) 300 | return false; 301 | 302 | dr = e3.z * c.y - e3.y * c.z; 303 | 304 | if (t3.x > dr || u3.x < dr) 305 | return false; 306 | 307 | if (pMin.y - c.y > hw.y || pMax.y - c.y < -hw.y) 308 | return false; 309 | 310 | return true; 311 | } 312 | 313 | bool cTriCellIntersectDelta::IntersectX(const Vec3f& c) 314 | { 315 | float dr; 316 | 317 | dr = -e1.z * c.x + e1.x * c.z; 318 | 319 | if (t1.y > dr || u1.y < dr) 320 | return false; 321 | 322 | dr = e1.y * c.x - e1.x * c.y; 323 | 324 | if (t1.z > dr || u1.z < dr) 325 | return false; 326 | 327 | dr = -e2.z * c.x + e2.x * c.z; 328 | 329 | if (t2.y > dr || u2.y < dr) 330 | return false; 331 | 332 | dr = e2.y * c.x - e2.x * c.y; 333 | 334 | if (t2.z > dr || u2.z < dr) 335 | return false; 336 | 337 | dr = -e3.z * c.x + e3.x * c.z; 338 | 339 | if (t3.y > dr || u3.y < dr) 340 | return false; 341 | 342 | dr = e3.y * c.x - e3.x * c.y; 343 | 344 | if (t3.z > dr || u3.z < dr) 345 | return false; 346 | 347 | if (pMin.x - c.x > hw.x || pMax.x - c.x < -hw.x) 348 | return false; 349 | 350 | // Finally test against plane. Timing tests confirm it's best to have this last. 351 | float d = nd - dot(normal, c); 352 | return (nDotVMin <= d) && (nDotVMax >= d); 353 | } 354 | 355 | inline Bounds3f TriangleBounds(const Vec3f& a, const Vec3f& b, const Vec3f& c) 356 | { 357 | Bounds3f box; 358 | 359 | box.mMin = MinElts(a, b); 360 | box.mMin = MinElts(box.mMin, c); 361 | 362 | box.mMax = MaxElts(a, b); 363 | box.mMax = MaxElts(box.mMax, c); 364 | 365 | return box; 366 | } 367 | } 368 | 369 | void MSL::CreateBitMaskFromTriangles 370 | ( 371 | int triCount, 372 | const int indices[], 373 | const Vec3f vertices[], 374 | const Bounds3f& bbox, 375 | int w, int h, int d, 376 | uint32_t mask[] 377 | ) 378 | { 379 | int rowStride = (w + 31) / 32; 380 | int sliceStride = rowStride * h; 381 | 382 | Vec3f whd = { float(w), float(h), float(d) }; 383 | 384 | Vec3f cellMin = bbox.mMin; 385 | Vec3f cellW = (bbox.mMax - bbox.mMin) / whd; 386 | Vec3f cellInvW = whd / (bbox.mMax - bbox.mMin); 387 | 388 | Vec3f hw = cellW * 0.500001f; 389 | 390 | for (int iv = 0; iv < triCount * 3; iv += 3) 391 | { 392 | int iv1 = iv + 0; 393 | int iv2 = iv + 1; 394 | int iv3 = iv + 2; 395 | 396 | if (indices) 397 | { 398 | iv1 = indices[iv1]; 399 | iv2 = indices[iv2]; 400 | iv3 = indices[iv3]; 401 | } 402 | 403 | Vec3f p1 = vertices[indices[iv + 0]]; 404 | Vec3f p2 = vertices[indices[iv + 1]]; 405 | Vec3f p3 = vertices[indices[iv + 2]]; 406 | 407 | Bounds3f triBounds = TriangleBounds(p1, p2, p3); 408 | 409 | Vec3f cellsMin = (triBounds.mMin - cellMin) * cellInvW; 410 | Vec3f cellsMax = (triBounds.mMax - cellMin) * cellInvW; 411 | 412 | int cx0 = Max(FloorToInt32(cellsMin.x), 0); 413 | int cy0 = Max(FloorToInt32(cellsMin.y), 0); 414 | int cz0 = Max(FloorToInt32(cellsMin.z), 0); 415 | 416 | int cx1 = Min( CeilToInt32(cellsMax.x), w); 417 | int cy1 = Min( CeilToInt32(cellsMax.y), h); 418 | int cz1 = Min( CeilToInt32(cellsMax.z), d); 419 | 420 | if (cx0 == cx1) 421 | continue; 422 | 423 | if (cx0 + 1 == cx1 && cy0 + 1 == cy1 && cz0 + 1 == cz1) // quick out for single-cell triangle 424 | { 425 | size_t cellBits = sliceStride * cz0 + rowStride * cy0 + (cx0 >> 5); 426 | mask[cellBits] |= 1 << (cx0 & 0x1F); 427 | continue; 428 | } 429 | 430 | Vec3f centre = { 0, 0, 0 }; 431 | int sliceBits = sliceStride * cz0 + rowStride * cy0 + (cx0 >> 5); 432 | 433 | cTriCellIntersectDelta triCell(p1, p2, p3, hw); 434 | 435 | for (int z = cz0; z < cz1; z++) 436 | { 437 | int rowBits = sliceBits; 438 | sliceBits += sliceStride; 439 | 440 | centre.z = cellMin.z + cellW.z * (z + 0.5f); 441 | 442 | if (!triCell.IntersectZ(centre)) 443 | continue; 444 | 445 | for (int y = cy0; y < cy1; y++) 446 | { 447 | int cellBits = rowBits; 448 | rowBits += rowStride; 449 | 450 | centre.y = cellMin.y + cellW.y * (y + 0.5f); 451 | 452 | if (!triCell.IntersectY(centre)) 453 | continue; 454 | 455 | int i = (cx0 & 0x1F); 456 | 457 | for (int x = cx0; x < cx1; x++) 458 | { 459 | assert(cellBits >= 0 && cellBits < w * h * d); 460 | 461 | centre.x = cellMin.x + cellW.x * (x + 0.5f); 462 | 463 | if (triCell.IntersectX(centre)) 464 | mask[cellBits] |= 1 << i; 465 | 466 | if (++i == 32) 467 | { 468 | cellBits++; 469 | i = 0; 470 | } 471 | } 472 | } 473 | } 474 | } 475 | } 476 | 477 | void MSL::CreateDirW8FromTriangles 478 | ( 479 | int triCount, 480 | const int indices[], 481 | const Vec3f vertices[], 482 | const Bounds3f& bbox, 483 | int w, int h, int d, 484 | float dirW8[] 485 | ) 486 | { 487 | int rowStride = w; 488 | int sliceStride = rowStride * h; 489 | int volumeStride = sliceStride * d; 490 | 491 | Vec3f whd = { float(w), float(h), float(d) }; 492 | 493 | Vec3f cellMin = bbox.mMin; 494 | Vec3f cellW = (bbox.mMax - bbox.mMin) / whd; 495 | Vec3f cellInvW = whd / (bbox.mMax - bbox.mMin); 496 | 497 | Vec3f hw = cellW * 0.500001f; 498 | 499 | for (int iv = 0; iv < triCount * 3; iv += 3) 500 | { 501 | int iv1 = iv + 0; 502 | int iv2 = iv + 1; 503 | int iv3 = iv + 2; 504 | 505 | if (indices) 506 | { 507 | iv1 = indices[iv1]; 508 | iv2 = indices[iv2]; 509 | iv3 = indices[iv3]; 510 | } 511 | 512 | Vec3f p1 = vertices[indices[iv + 0]]; 513 | Vec3f p2 = vertices[indices[iv + 1]]; 514 | Vec3f p3 = vertices[indices[iv + 2]]; 515 | 516 | Bounds3f triBounds = TriangleBounds(p1, p2, p3); 517 | 518 | Vec3f cellsMin = (triBounds.mMin - cellMin) * cellInvW; 519 | Vec3f cellsMax = (triBounds.mMax - cellMin) * cellInvW; 520 | 521 | int cx0 = Max(FloorToInt32(cellsMin.x), 0); 522 | int cy0 = Max(FloorToInt32(cellsMin.y), 0); 523 | int cz0 = Max(FloorToInt32(cellsMin.z), 0); 524 | 525 | int cx1 = Min( CeilToInt32(cellsMax.x), w); 526 | int cy1 = Min( CeilToInt32(cellsMax.y), h); 527 | int cz1 = Min( CeilToInt32(cellsMax.z), d); 528 | 529 | if (cx0 == cx1) 530 | continue; 531 | 532 | cTriCellIntersectDelta triCell(p1, p2, p3, hw); 533 | 534 | float area = 0.5f * len(triCell.normal); 535 | 536 | // We have two conflicting desires here -- we want the reconstruction of the occlusion direction 537 | // to be the same as the triangle normal, and ideally the occlusion amount = 0.5 = the hemisphere. 538 | // Luckily for our basis M, MtM = 8I, and more so, if you zero negative weights, 539 | // MtM = 4I. As we treat each weight as an octant coverage value however, we have an issue 540 | // in that e_z -> b_i = 1, and e_111 -> b_i = (3, 1, 1, 1) / sqrt(3), whose max coverage > 1, which 541 | // our sweep algorithm doesn't handle. 542 | 543 | float bs = 0.577350269f / area; // 1 / ||a|| sqrt(3) 544 | 545 | int dirOffset[8]; 546 | float dirValue [8]; 547 | int dirCount = 0; 548 | 549 | for (int i = 0; i < 8; i++) 550 | { 551 | float bc = (i & 1) ? -triCell.normal.x : triCell.normal.x; 552 | bc += (i & 2) ? -triCell.normal.y : triCell.normal.y; 553 | bc += (i & 4) ? -triCell.normal.z : triCell.normal.z; 554 | 555 | if (bc > 1e-6f) 556 | { 557 | dirOffset[dirCount] = i * volumeStride; 558 | dirValue [dirCount] = bc * bs; 559 | dirCount++; 560 | } 561 | } 562 | 563 | Vec3f centre = { 0, 0, 0 }; 564 | 565 | for (int z = cz0; z < cz1; z++) 566 | { 567 | centre.z = cellMin.z + cellW.z * (z + 0.5f); 568 | 569 | if (!triCell.IntersectZ(centre)) 570 | continue; 571 | 572 | for (int y = cy0; y < cy1; y++) 573 | { 574 | centre.y = cellMin.y + cellW.y * (y + 0.5f); 575 | 576 | if (!triCell.IntersectY(centre)) 577 | continue; 578 | 579 | for (int x = cx0; x < cx1; x++) 580 | { 581 | centre.x = cellMin.x + cellW.x * (x + 0.5f); 582 | 583 | if (triCell.IntersectX(centre)) 584 | { 585 | int index = z * sliceStride + y * rowStride + x; 586 | 587 | for (int i = 0; i < dirCount; i++) 588 | { 589 | int dirIndex = dirOffset[i] + index; 590 | 591 | assert(dirIndex >= 0 && dirIndex < volumeStride * 8); 592 | 593 | if (dirW8[dirIndex] < dirValue[i]) 594 | dirW8[dirIndex] = dirValue[i]; 595 | } 596 | } 597 | } 598 | } 599 | } 600 | } 601 | } 602 | 603 | 604 | namespace 605 | { 606 | void InPlaceTriangulate(int numVerts, std::vector& indices) 607 | { 608 | // Assume polygon of numVerts indices at the end of 'indices', and triangulate it in place. 609 | 610 | int baseVertexIndex = (int) indices.size() - numVerts; 611 | int baseEltIndex = indices[baseVertexIndex]; 612 | 613 | for (int i = 1; i < numVerts - 2; i++) 614 | { 615 | indices.insert(indices.begin() + baseVertexIndex + 3 * i, indices[baseVertexIndex + 3 * i - 1]); 616 | indices.insert(indices.begin() + baseVertexIndex + 3 * i, baseEltIndex); 617 | } 618 | } 619 | 620 | bool FaceCommand(cMesh* mesh, int argc, const char* va[]) 621 | { 622 | argc--; 623 | va++; 624 | 625 | char* end; 626 | 627 | for (int i = 0; i < argc; i++) 628 | { 629 | long ip = strtol(va[i], &end, 10); 630 | 631 | if (end != va[i]) 632 | mesh->mPositionIndices.push_back(int(ip) - 1); 633 | 634 | if (end[0] == '/') 635 | { 636 | const char* next = end + 1; 637 | long it = strtol(next, &end, 10); 638 | 639 | if (end != next) 640 | mesh->mUVIndices.push_back(int(it) - 1); 641 | } 642 | 643 | if (end[0] == '/') 644 | { 645 | const char* next = end + 1; 646 | long in = strtol(end + 1, &end, 10); 647 | 648 | if (end != next) 649 | mesh->mNormalIndices.push_back(int(in) - 1); 650 | } 651 | } 652 | 653 | // quick and dirty in-place triangulation. 654 | if (argc > 3) 655 | { 656 | InPlaceTriangulate(argc, mesh->mPositionIndices); 657 | 658 | if (!mesh->mNormalIndices.empty()) 659 | InPlaceTriangulate(argc, mesh->mNormalIndices); 660 | 661 | if (!mesh->mUVIndices.empty()) 662 | InPlaceTriangulate(argc, mesh->mUVIndices); 663 | } 664 | 665 | return true; 666 | } 667 | 668 | bool PositionCommand(cMesh* mesh, int argc, const char* va[]) 669 | { 670 | if (argc < 4) 671 | return false; 672 | 673 | Vec3f p; 674 | p.x = atof(va[1]); 675 | p.y = atof(va[2]); 676 | p.z = atof(va[3]); 677 | 678 | mesh->mPositions.push_back(p); 679 | 680 | return true; 681 | } 682 | 683 | bool NormalCommand(cMesh* mesh, int argc, const char* va[]) 684 | { 685 | if (argc < 4) 686 | return false; 687 | 688 | Vec3f p; 689 | p.x = atof(va[1]); 690 | p.y = atof(va[2]); 691 | p.z = atof(va[3]); 692 | 693 | mesh->mNormals.push_back(p); 694 | 695 | return true; 696 | } 697 | 698 | bool TexCoordCommand(cMesh* mesh, int argc, const char* va[]) 699 | { 700 | if (argc < 3) 701 | return false; 702 | 703 | Vec2f p; 704 | p.x = atof(va[1]); 705 | p.y = atof(va[2]); 706 | 707 | mesh->mUVs.push_back(p); 708 | 709 | return true; 710 | } 711 | 712 | bool ObjectCommand(cMesh*, int, const char**) 713 | { 714 | return true; 715 | } 716 | 717 | bool GroupCommand(cMesh*, int, const char**) 718 | { 719 | return true; 720 | } 721 | 722 | bool SmoothingGroupCommand(cMesh*, int, const char**) 723 | { 724 | return true; 725 | } 726 | 727 | bool MaterialCommand(cMesh*, int, const char**) 728 | { 729 | return true; 730 | } 731 | bool MaterialLibraryCommand(cMesh*, int, const char**) 732 | { 733 | return true; 734 | } 735 | 736 | 737 | bool ProcessObjCommand(cMesh* mesh, int argc, const char* argv[]) 738 | { 739 | assert(argc > 0); 740 | 741 | switch (argv[0][0]) 742 | { 743 | case 'f': 744 | return FaceCommand(mesh, argc, argv); 745 | case 'v': 746 | if (argv[0][1] == 'n') 747 | return NormalCommand(mesh, argc, argv); 748 | else if (argv[0][1] == 't') 749 | return TexCoordCommand(mesh, argc, argv); 750 | else if (argv[0][1] == 0) 751 | return PositionCommand(mesh, argc, argv); 752 | break; 753 | case 'o': 754 | return ObjectCommand(mesh, argc, argv); 755 | case 'g': 756 | return GroupCommand(mesh, argc, argv); 757 | case 's': 758 | return SmoothingGroupCommand(mesh, argc, argv); 759 | case 'u': 760 | if (strcasecmp(argv[0], "usemtl") == 0) 761 | return MaterialCommand(mesh, argc, argv); 762 | break; 763 | case 'm': 764 | if (strcasecmp(argv[0], "mtllib") == 0) 765 | return MaterialLibraryCommand(mesh, argc, argv); 766 | break; 767 | case '#': 768 | return true; 769 | } 770 | 771 | return false; 772 | } 773 | 774 | 775 | int Split(char* buffer, int maxArgs, const char** argv) 776 | { 777 | char* last = 0; 778 | maxArgs--; // always reserve the last spot for 0 terminator 779 | 780 | for (int argc = 0; argc < maxArgs; argc++) 781 | { 782 | argv[argc] = strtok_r(buffer, " \t\n\r", &last); 783 | buffer = 0; 784 | 785 | if (!argv[argc] || !argv[argc][0]) 786 | return argc; 787 | } 788 | 789 | fprintf(stderr, "Warning: ignored arguments past %d\n", maxArgs - 1); 790 | argv[maxArgs] = 0; 791 | return maxArgs; 792 | } 793 | } 794 | 795 | bool MSL::ReadObjFile(FILE* file, cMesh* mesh) 796 | { 797 | *mesh = cMesh(); 798 | 799 | const int kMaxArgs = 256; 800 | const char* argv[kMaxArgs]; 801 | 802 | char lineBuffer[1024]; 803 | 804 | while (fgets(lineBuffer, 1024, file)) 805 | { 806 | int argc = Split(lineBuffer, kMaxArgs, argv); 807 | 808 | if (argc > 0) 809 | { 810 | if (!ProcessObjCommand(mesh, argc, argv)) 811 | { 812 | fprintf(stderr, "Can't parse command: '%s'\n", argv[0]); 813 | return false; 814 | } 815 | } 816 | } 817 | 818 | printf("Read %zu positions, %zu normals, %zu uvs\n", 819 | mesh->mPositions.size(), 820 | mesh->mNormals.size(), 821 | mesh->mUVs.size() 822 | ); 823 | 824 | printf(" %zd position indices, %zd normal indices, %zd uv indices\n", 825 | mesh->mPositionIndices.size(), 826 | mesh->mNormalIndices.size(), 827 | mesh->mUVIndices.size() 828 | ); 829 | 830 | return true; 831 | } 832 | 833 | Bounds3f MSL::FindBounds(const cMesh& mesh) 834 | { 835 | Bounds3f bbox = { { +FLT_MAX, +FLT_MAX, +FLT_MAX }, { -FLT_MAX, -FLT_MAX, -FLT_MAX } }; 836 | 837 | for (int i = 0, n = (int) mesh.mPositions.size(); i < n; i++) 838 | { 839 | Vec3f p = mesh.mPositions[i]; 840 | 841 | if (bbox.mMin.x > p.x) 842 | bbox.mMin.x = p.x; 843 | else 844 | if (bbox.mMax.x < p.x) 845 | bbox.mMax.x = p.x; 846 | 847 | if (bbox.mMin.y > p.y) 848 | bbox.mMin.y = p.y; 849 | else 850 | if (bbox.mMax.y < p.y) 851 | bbox.mMax.y = p.y; 852 | 853 | if (bbox.mMin.z > p.z) 854 | bbox.mMin.z = p.z; 855 | else 856 | if (bbox.mMax.z < p.z) 857 | bbox.mMax.z = p.z; 858 | } 859 | 860 | return bbox; 861 | } 862 | 863 | 864 | -------------------------------------------------------------------------------- /MeshSupport.h: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // Purpose: Quick and dirty mesh support 3 | // Author: Andrew Willmott 4 | //------------------------------------------------------------------------------ 5 | 6 | #ifndef MESH_SUPPORT_H 7 | #define MESH_SUPPORT_H 8 | 9 | #include 10 | #include 11 | 12 | namespace MSL 13 | { 14 | // Mesh 15 | struct Vec2f 16 | { 17 | float x; 18 | float y; 19 | }; 20 | 21 | struct Vec3f 22 | { 23 | float x; 24 | float y; 25 | float z; 26 | }; 27 | 28 | struct Bounds3f 29 | { 30 | Vec3f mMin; 31 | Vec3f mMax; 32 | }; 33 | 34 | Bounds3f FindBounds(); 35 | 36 | Bounds3f FindAOBounds(float eps, Bounds3f modelBounds); 37 | // Finds size of volume bbox given allowed error 'eps'. Derivation from Malmer et al. 38 | Bounds3f FindAOBounds(float eps, Bounds3f modelBounds, Vec3f minArea, Vec3f maxArea); 39 | // Variant of FindAOBounds that lets you supply your own axial projected area bounds 40 | 41 | void CreateBitMaskFromTriangles 42 | ( 43 | int triCount, 44 | const int indices[], 45 | const Vec3f vertices[], 46 | const Bounds3f& bbox, 47 | int w, int h, int d, 48 | uint32_t mask[] 49 | ); 50 | ///< Initialises occupancy mask from the given mesh. 'indices' may be null. 51 | 52 | void CreateDirW8FromTriangles 53 | ( 54 | int triCount, 55 | const int indices[], 56 | const Vec3f vertices[], 57 | const Bounds3f& bbox, 58 | int w, int h, int d, 59 | float dirW8[] 60 | ); 61 | ///< Initialises directional occlusion volumes from the given mesh. 'indices' may be null. 62 | 63 | struct cMesh 64 | { 65 | std::vector mPositions; 66 | std::vector mNormals; 67 | std::vector mUVs; 68 | 69 | std::vector mPositionIndices; 70 | std::vector mNormalIndices; 71 | std::vector mUVIndices; 72 | }; 73 | 74 | bool ReadObjFile(FILE* file, cMesh* mesh); 75 | 76 | Bounds3f FindBounds(const cMesh& mesh); 77 | } 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Distance/Occlusion Library + Tool 2 | =============== 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | From left to right: original, signed distance with zero at 0.5, 15 | red/green SDF, delta vectors to closest boundary point. 16 | 17 | Intro 18 | ----- 19 | 20 | The DistanceOcclusionLib.* files here implement a number of algorithms for 21 | 22 | * Distance field generation 23 | * Signed distance field generation 24 | * Scalar and directional occlusion generation 25 | 26 | for both 2d (images) and 3d (volumes). Bitmask inputs are supported to keep 27 | memory overhead down, which is particularly important for volumes. 28 | 29 | The included tool, distocc, which uses this lib, will process 2D images or 3D 30 | .obj files, and can output the results in a number of different formats. By 31 | default the tool uses the 'Danielsson' algorithm, and signed border 32 | initialisation, in order to generate accurate results as quickly as possible, 33 | but other algorithms can be be selected via -m (see below). These include: 34 | 35 | FastSweep O(n), most accurate delta technique 36 | Danielsson O(n), ~30% faster than FastSweep, decent accuracy 37 | JumpFlood O(n log n), GPU-oriented algorithm, included to check accuracy 38 | BruteForce O(n^2), exact, for checking other algorithms at low n. 39 | Chamfer O(n), has issues on diagonals. Cheapest but quality suffers. 40 | Felzenszwalb O(n) but much larger overhead than the others. Accurate. 41 | 42 | The first four algorithms provide deltas from cell to closest point, which can 43 | be used to evaluate distance, but also to answer nearest-point queries, or 44 | generate Voronoi diagrams. The last two are strictly distance-only. Finally, 45 | signed distances can be optionally generated via the more classic two-pass 46 | interior/exterior algorithm, but this is twice as slow and uses twice as much 47 | memory as the default. 48 | 49 | A reasonably efficient mesh voxeliser is included in the support file 50 | MeshSupport.cpp. 51 | 52 | 53 | Building and Running 54 | -------------------- 55 | 56 | To build the tool, run 57 | 58 | c++ DistanceOcclusionTool.cpp DistanceOcclusionLib.cpp -o distocc 59 | 60 | Then to see the tool options run 61 | 62 | ./distocc 63 | 64 | which should give you something like: 65 | 66 | Options: 67 | -d: generate distance field. Methods: 0, Danielsson; 1, Fast Sweep; 2, Jump Flood; 3, Brute Force; 4, Chamfer; 5, Felzenszwalb 68 | -s: generate signed distance field in a single pass. Methods: as for -d but Chamfer/Felzenszwalb unsupported. 69 | -S: generate signed distance field via orthodox two distance field passes. 70 | -x: generate occlusion. Methods 0: standard, 1: no self-occlusion, 2: directional components only 71 | 72 | -m : select method variant for above 73 | 74 | -w [] : set dimensions of image source 75 | -W [ ]: set dimensions of volume source 76 | -p [ ] : add random points to image/volume (default) 77 | -b : add test box with the given number of sides to image/volume 78 | 79 | -f : specify input image 80 | -t : specify 0-255 threshold for creating mask from image 81 | -r : reverse so white is occupied rather than black 82 | 83 | -F : use given obj file to define mask. 84 | 85 | -o : set base name for output file(s) 86 | -v : log detailed output 87 | -c : run checks on output 88 | 89 | E.g., 90 | 91 | ./distocc -d -p 100 # Generate a DF for 100 random points 92 | ./distocc -d -W 16 -b 6 # Generate a DF for a test box within a 16^3 volume 93 | ./distocc -s -f data/tiger.png # Generate an SDF for the given image 94 | ./distocc -d -W 32 -F data/plant2.obj # Generate a 32^3 DF for the given 3D mesh 95 | 96 | On Windows just include these files in your visual studio project. The code has 97 | been tested under clang, gcc, and Visual C. 98 | -------------------------------------------------------------------------------- /data/kiwi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distance-occlusion/ed8f02c0d82592762fd58a919cd101bbf69908af/data/kiwi.png -------------------------------------------------------------------------------- /data/tiger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distance-occlusion/ed8f02c0d82592762fd58a919cd101bbf69908af/data/tiger.png -------------------------------------------------------------------------------- /images/kiwi-sdf-delta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distance-occlusion/ed8f02c0d82592762fd58a919cd101bbf69908af/images/kiwi-sdf-delta.png -------------------------------------------------------------------------------- /images/kiwi-sdf-rg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distance-occlusion/ed8f02c0d82592762fd58a919cd101bbf69908af/images/kiwi-sdf-rg.png -------------------------------------------------------------------------------- /images/kiwi-sdf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distance-occlusion/ed8f02c0d82592762fd58a919cd101bbf69908af/images/kiwi-sdf.png -------------------------------------------------------------------------------- /images/tiger-sdf-delta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distance-occlusion/ed8f02c0d82592762fd58a919cd101bbf69908af/images/tiger-sdf-delta.png -------------------------------------------------------------------------------- /images/tiger-sdf-rg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distance-occlusion/ed8f02c0d82592762fd58a919cd101bbf69908af/images/tiger-sdf-rg.png -------------------------------------------------------------------------------- /images/tiger-sdf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distance-occlusion/ed8f02c0d82592762fd58a919cd101bbf69908af/images/tiger-sdf.png --------------------------------------------------------------------------------