| … | … | @@ -150,6 +150,10 @@ void MeshToSDFGridNode::execute(ProcEvalContext &p_ctx, int p_node_idx) { |
| 150 | 150 | } |
| 151 | 151 | signed_volume /= 6.0; |
| 152 | 152 | const float sdf_sign = (signed_volume > 0.0) ? -1.0f : 1.0f; |
| 153 | // Closed means the flood from the grid boundary is a trustworthy sign, which |
| 154 | // is what lets the pseudonormals go. Decided here because load_arrays has to |
| 155 | // know before it builds anything. |
| 156 | const bool closed_precheck = Math::abs(signed_volume) >= 1e-9; |
| 153 | 157 | if (Math::abs(signed_volume) < 1e-9) { |
| 154 | 158 | // Flat or open: there is no inside, so a "signed" distance is only the |
| 155 | 159 | // side the faces happen to point. Say so rather than pretend. |
| … | … | @@ -161,6 +165,17 @@ void MeshToSDFGridNode::execute(ProcEvalContext &p_ctx, int p_node_idx) { |
| 161 | 165 | |
| 162 | 166 | const uint64_t t_setup = Time::get_singleton()->get_ticks_usec(); |
| 163 | 167 | DCGeneratorMeshSDF sdf; |
| 168 | // ⛔ NO PSEUDONORMALS. They exist only to sign a query, and they cost 5554 ms |
| 169 | // of a 6663 ms load_arrays here -- five times the BVH build -- because they |
| 170 | // need per-vertex AND per-EDGE angle-weighted normals over 5.16M triangles. |
| 171 | // This node already determines inside from outside globally: it floods the |
| 172 | // outside from the grid boundary, which is outside by construction because |
| 173 | // the bounds are padded. Asking for unsigned distance and applying that sign |
| 174 | // is the same answer without the 5.5 s. |
| 175 | // |
| 176 | // Only for a CLOSED mesh -- the flood is what supplies the sign, and it |
| 177 | // leaks through a hole. An open mesh keeps the pseudonormal path. |
| 178 | sdf.set_unsigned_only(closed_precheck); |
| 164 | 179 | if (!sdf.load_arrays(pos.data(), vcount, idx.data(), (int)(idx.size() / 3))) { |
| 165 | 180 | UtilityFunctions::push_error( |
| 166 | 181 | "proc-nodes: Mesh to SDF Grid could not build a BVH for this mesh."); |
| … | … | @@ -225,7 +240,7 @@ void MeshToSDFGridNode::execute(ProcEvalContext &p_ctx, int p_node_idx) { |
| 225 | 240 | const uint64_t t_bvh = Time::get_singleton()->get_ticks_usec(); |
| 226 | 241 | const uint64_t t_start = t_bvh; |
| 227 | 242 | uint64_t t_stamp = t_start, t_dilate = t_start, t_query = t_start; |
| 228 | | const bool closed = Math::abs(signed_volume) >= 1e-9; |
| 243 | const bool closed = closed_precheck; |
| 229 | 244 | const int bandv = MAX(1, (int)band); |
| 230 | 245 | |
| 231 | 246 | std::vector<uint8_t> in_band; |
| … | … | @@ -324,9 +339,11 @@ void MeshToSDFGridNode::execute(ProcEvalContext &p_ctx, int p_node_idx) { |
| 324 | 339 | if (banded && !in_band[(size_t)o]) { |
| 325 | 340 | continue; // filled by the flood below |
| 326 | 341 | } |
| 327 | | w[o] = sdf_sign * sdf.evaluate( |
| 328 | | mn[0] + x * voxel, mn[1] + y * voxel, |
| 329 | | mn[2] + z * voxel, voxel); |
| 342 | const float d = sdf.evaluate(mn[0] + x * voxel, |
| 343 | mn[1] + y * voxel, mn[2] + z * voxel, voxel); |
| 344 | // banded => unsigned; the flood signs it. Otherwise |
| 345 | // libdc signed it with pseudonormals as before. |
| 346 | w[o] = banded ? d : (sdf_sign * d); |
| 330 | 347 | } |
| 331 | 348 | } |
| 332 | 349 | } |
| … | … | @@ -356,14 +373,27 @@ void MeshToSDFGridNode::execute(ProcEvalContext &p_ctx, int p_node_idx) { |
| 356 | 373 | // `pad` above, so the whole grid boundary is outside the mesh and a flood |
| 357 | 374 | // from it reaches exactly the outside. |
| 358 | 375 | if (closed && !in_band.empty()) { |
| 359 | | // positive = solid after sdf_sign (see the winding note above) |
| 376 | // positive = solid after this pass (see the winding note above) |
| 360 | 377 | const float far_v = voxel * (float)(bandv + 1); |
| 378 | |
| 379 | // THE BARRIER is the shell the surface actually lies in: voxels within |
| 380 | // half a voxel of it. Everything else is unambiguously one side or the |
| 381 | // other at this resolution, so a flood from the grid boundary -- which |
| 382 | // is outside, because the bounds are padded -- labels it exactly. |
| 383 | // |
| 384 | // Only the barrier itself is decided by a neighbour, and a shell one |
| 385 | // voxel thick is precisely the width at which a grid cannot tell the |
| 386 | // two sides apart anyway. |
| 387 | const float half = voxel * 0.5f; |
| 361 | 388 | std::vector<uint8_t> outside((size_t)total, 0); |
| 362 | 389 | std::vector<int64_t> stack; |
| 363 | 390 | stack.reserve(4096); |
| 391 | auto passable = [&](int64_t o) { |
| 392 | return !in_band[(size_t)o] || w[o] >= half; |
| 393 | }; |
| 364 | 394 | auto push_if_open = [&](int64_t x, int64_t y, int64_t z) { |
| 365 | 395 | const int64_t o = at(x, y, z); |
| 366 | | if (in_band[(size_t)o] || outside[(size_t)o]) { |
| 396 | if (outside[(size_t)o] || !passable(o)) { |
| 367 | 397 | return; |
| 368 | 398 | } |
| 369 | 399 | outside[(size_t)o] = 1; |
| … | … | @@ -391,11 +421,40 @@ void MeshToSDFGridNode::execute(ProcEvalContext &p_ctx, int p_node_idx) { |
| 391 | 421 | if (z > 0) push_if_open(x, y, z - 1); |
| 392 | 422 | if (z < nz - 1) push_if_open(x, y, z + 1); |
| 393 | 423 | } |
| 394 | | for (int64_t o = 0; o < total; o++) { |
| 395 | | if (in_band[(size_t)o]) { |
| 396 | | continue; |
| 424 | |
| 425 | for (int64_t z = 0; z < nz; z++) { |
| 426 | for (int64_t y = 0; y < ny; y++) { |
| 427 | for (int64_t x = 0; x < nx; x++) { |
| 428 | const int64_t o = at(x, y, z); |
| 429 | if (!in_band[(size_t)o]) { |
| 430 | w[o] = outside[(size_t)o] ? -far_v : far_v; |
| 431 | continue; |
| 432 | } |
| 433 | const float d = w[o]; |
| 434 | if (d >= half) { |
| 435 | // Outside the barrier: the flood knows the side. |
| 436 | w[o] = outside[(size_t)o] ? -d : d; |
| 437 | continue; |
| 438 | } |
| 439 | // In the barrier. Take the side from a labelled neighbour; |
| 440 | // prefer an OUTSIDE one, so a voxel touching air is air -- |
| 441 | // the alternative closes the surface over thin gaps. |
| 442 | bool any_out = false, any_in = false; |
| 443 | const int64_t nbr[6][3] = {{x - 1, y, z}, {x + 1, y, z}, {x, y - 1, z}, |
| 444 | {x, y + 1, z}, {x, y, z - 1}, {x, y, z + 1}}; |
| 445 | for (int k = 0; k < 6; k++) { |
| 446 | const int64_t bx = nbr[k][0], by = nbr[k][1], bz = nbr[k][2]; |
| 447 | if (bx < 0 || bx >= nx || by < 0 || by >= ny || bz < 0 || bz >= nz) { |
| 448 | continue; |
| 449 | } |
| 450 | const int64_t no = at(bx, by, bz); |
| 451 | if (!in_band[(size_t)no] || w[no] >= half) { |
| 452 | if (outside[(size_t)no]) any_out = true; else any_in = true; |
| 453 | } |
| 454 | } |
| 455 | w[o] = (any_out && !any_in) ? -d : (any_in && !any_out ? d : -d); |
| 456 | } |
| 397 | 457 | } |
| 398 | | w[o] = outside[(size_t)o] ? -far_v : far_v; |
| 399 | 458 | } |
| 400 | 459 | } |
| 401 | 460 | |