Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tiny hole in the mesh of VoxelLodTerrain #641

Open
Piratux opened this issue Jun 1, 2024 · 8 comments
Open

Tiny hole in the mesh of VoxelLodTerrain #641

Piratux opened this issue Jun 1, 2024 · 8 comments

Comments

@Piratux
Copy link
Contributor

Piratux commented Jun 1, 2024

Describe the bug
As title says, I found a case where the meshed SDF of VoxelLodTerrain contains tiny hole.

To Reproduce
Download project and run it. Upon launch you should see this which contains triangle shaped hole:

image

Expected behavior
There should be no holes in the mesh.

Environment

  • OS: Windows 10
  • Graphics card: GTX 650 ti
  • Versions: Godot 4.2.2 stable + voxel tools 1.2.0
  • Renderer: Forward+

Test Mesh issue.zip

@Zylann
Copy link
Owner

Zylann commented Jun 1, 2024

It appears to be caused by this:

{
// Transvoxel paper:
// It is possible to generate triangles having zero area when one or more of the corner sample
// values for a cell is zero. For example, when we triangulate a cell for which one corner
// sample value is zero and the seven remaining corner sample values are negative, then we
// generate the single triangle of equivalence class #1 (see Table 3.2). However, all three
// vertices lie exactly at the corner having zero sample value. Such triangles are eliminated
// after a simple area calculation indicates that they are degenerate.
//
// Not fixing this used to work fine actually, but Jolt physics integration makes this
// problematic. Jolt checks for degenerate triangles, but instead of just skipping them, it
// throws errors. Also, the fact Godot enforces passing a de-indexed mesh through the
// Physics3DServer requires Jolt to re-index it. This is not only a waste of time, but also,
// Jolt eliminates vertices at the same location or below a hardcoded threshold in the process.
// This in turn causes further issues, as degenerate or microscopic triangles cause the
// same errors, instead of just being ignored. So a workaround is to actively remove those
// triangles here, at the cost of extra CPU work.
//
// Note, this workaround means there can be unused vertices in the final mesh.
// Another workaround could have been to alter the SDF to never have 0, but that would not
// cover the case of triangles that are too thin.
//
// Profiling results, in average time per chunk (16^3).
// With it: 75 us
// Without it: 65 us
// So fixing the 0.5% of meshes with at least 1 degenerate/superthin triangle isn't negligible
// unfortunately.
//
// About Jolt re-indexing meshes, see PR (abandoned?):
// https://github.com/godotengine/godot/pull/72868
//
const Vector3f p0 = output.vertices[i0];
const Vector3f p1 = output.vertices[i1];
const Vector3f p2 = output.vertices[i2];
if (math::is_triangle_degenerate_approx(p0, p1, p2, 0.000001f)) {
--effective_triangle_count;
continue;
}

The mesher eliminates triangles that are too small or degenerate, because that makes Jolt Physics spam errors.
Maybe there can be a way to turn this off, but then if you use Jolt you'll end up with the problem again.

image

@Piratux
Copy link
Contributor Author

Piratux commented Jun 1, 2024

I see.
This is not very noticeable so it's not a big problem in my case.
Just wanted to let you know.

Feel free to close the issue.

@Zylann
Copy link
Owner

Zylann commented Jun 1, 2024

Another option would be to snap/clamp edge interpolation factors so that vertices cannot be generated too close to corners of marching cube cells, which then tends to produce less thin/small triangles, at the cost of slightly biasing the resulting mesh.
This post mentions a similar idea: https://stackoverflow.com/questions/35112181/marching-cubes-very-small-triangles (unfortunately without any code example so it's unclear what they mean by "snapping iso values")

@Piratux
Copy link
Contributor Author

Piratux commented Jun 1, 2024

From the description and provided images in the post of your link, it sounds like if mesh vertices are very close to integer coordinates (which are corners of the cubes), then vertices get set to those integer coordinates.

Something like this I'd assume:

float epsilon = 0.05;
float rounded_pos_x = round(pos.x);
float rounded_pos_y = round(pos.y);
float rounded_pos_z = round(pos.z);
float diff_x = abs(pos.x - rounded_pos_x);
float diff_y = abs(pos.y - rounded_pos_y);
float diff_z = abs(pos.z - rounded_pos_z);
if (diff_x * diff_x + diff_y * diff_y + diff_z * diff_z < epsilon * epsilon){
    pos.x = rounded_pos_x;
    pos.y = rounded_pos_y;
    pos.z = rounded_pos_z;
}

@Zylann
Copy link
Owner

Zylann commented Jun 1, 2024

No, you're only describing the result of it. Vertices are generated on edges of marching cube cells, and that depends on an interpolation factor between pairs of corners. That factor can be clamped/snapped, but the SDF values can also be clamped/snapped. I'm not sure which one they are referring to. One downside of doing it on the SDF is that it requires to know how gradients progress, which in turn depends on them being good gradients. On the other hand, the interpolation factor has no unit and is normalized, so is easier to clamp (however it is known after case selection so triangle count won't change)

@Piratux
Copy link
Contributor Author

Piratux commented Jun 2, 2024

Considering the author of post there mentioned percentages, I can only imagine this working with the interpolation factor since it can be a fixed range of [0, 1], where 0 is starting point of the edge, and 1 is ending point.

If SDF can be "infinitely large", then I don't see how percentages can be used to "snap" the SDF.

@Zylann
Copy link
Owner

Zylann commented Jun 4, 2024

I removed the code that was removing small triangles, replaced it with a margin property that clamps edge interpolation.

@Piratux
Copy link
Contributor Author

Piratux commented Jun 5, 2024

Nice, I've tested it and it works.

Just small suggestion: If you add silent clamping for the value (in this case edge_clamp_margin) please mention it in the docs.

image

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants