question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

mergeVertices truncates coordinates which misses some neighbors

See original GitHub issue

Describe the bug

BufferGeometryUtils.mergeVertices() truncates all coordinates causing it to miss some neighboring vertices which are closer than the threshold sensitivity given to mergeVertices().

Two vertices should merge when all their coordinates (x, y, z, u, v, nx, etc…) have values closer than the threshold. But if one vertex has at least one coordinate with value that is lower than an integer multiplication of the threshold, they would not be merged. For example if the threshold is 1e-4 and vertices a and b are the same except x:

  • if a.x = 1 and b.x = 1.000001 they would merge.
  • if a.x = 1 and b.x = 0.999999 they would not merge.

This is caused when creating the hash for each vertex using ~~ to truncate/floor each of the vertices coordinates.

(wanted to know what are anyone’s thoughts about the matter before i might just solve this in a PR)

To Reproduce

Steps to reproduce the behavior:

  1. Go to https://codesandbox.io/s/threejs-cubes-merge-problem-zhtbtt?file=/src/index.js
  2. See the lower cube is not merged well.

Code

const material = new THREE.MeshStandardMaterial({ color: 0x00ff00, side: THREE.DoubleSide });
{
    let geometry = new THREE.BoxGeometry(1, 1, 1);
    geometry.deleteAttribute("normal");
    geometry.deleteAttribute("uv");
    geometry.attributes.position.array[0] += 0.000001;
    geometry = BufferGeometryUtils.mergeVertices(geometry);
    geometry.computeVertexNormals();
    const cube = new THREE.Mesh(geometry, material);
    cube.position.y = 2;
    scene.add(cube);
}
{
    let geometry = new THREE.BoxGeometry(1, 1, 1);
    geometry.deleteAttribute("normal");
    geometry.deleteAttribute("uv");
    geometry = BufferGeometryUtils.mergeVertices(geometry);
    geometry.computeVertexNormals();
    const cube = new THREE.Mesh(geometry, material);
    cube.position.y = 0;
    scene.add(cube);
}
{
    let geometry = new THREE.BoxGeometry(1, 1, 1);
    geometry.deleteAttribute("normal");
    geometry.deleteAttribute("uv");
    geometry.attributes.position.array[0] -= 0.000001;
    geometry = BufferGeometryUtils.mergeVertices(geometry);
    geometry.computeVertexNormals();
    const cube = new THREE.Mesh(geometry, material);
    cube.position.y = -2;
    scene.add(cube);
}

Expected behavior

There are almost three identical cubes, applying mergeVertices() on their positions (after removing normals and uvs). The difference between them is the x-coordinate of the first vertex: the top is 0.500001, the middle is 0.5 and the bottom has 0.499999. Since the difference is well below the 1e-4 default threshold of mergeVertices() we would expect the vertices to be merged the same way. But the top and middle vertex is merged to its neighbors (since they are truncated to 0.5) and the bottom is not merged (since it is truncated to 0.4999)

Screenshots

image

Platform:

  • Device: Desktop
  • OS: MacOS
  • Browser: Chrome
  • Three.js version: r144

(the problem exists on all platforms)

Issue Analytics

  • State:open
  • Created a year ago
  • Comments:9 (2 by maintainers)

github_iconTop GitHub Comments

2reactions
donmccurdycommented, Sep 10, 2022

It sounds like we should be explicitly rounding to N digits, rather than truncating? That sounds like a bug to me as well, and a PR to fix it would be welcome I think. Thank you!

FWIW I recently implemented a version of this that explicitly compares against the tolerance value rather than rounding or truncating. It isn’t as slow as N^2 if you sort the vertices first and are selective about which comparisons you make using sort order, but it’s still quite a bit slower than what three.js currently does.

1reaction
gkjohnsoncommented, Sep 11, 2022

It sounds like we should be explicitly rounding to N digits, rather than truncating?

This will have the same problem - both truncating and rounding bin the space and merge vertices in common bins. There will always be cases where two numbers are very close to each other but otherwise fall on opposite sides of the binning operation (rounding or truncation in this case).

FWIW I recently implemented a version of this that explicitly compares against the tolerance value rather than rounding or truncating. It isn’t as slow as N^2 if you sort the vertices first and are selective about which comparisons you make using sort order, but it’s still quite a bit slower than what three.js currently does.

Performance is the reason I chose to use the truncation method. If there are other methods that are comparably performant I can support a change (or possibly an option if the performance is noticeably worse). I’ve been wondering if there’s a way to rely on two binning methods (ie rounding and truncation) to enable a fast search for neighbor vertices without falling into this issue.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Issues · mrdoob/three.js · GitHub
Actions Automate any workflow Packages Host and manage packages Security Find and ... mergeVertices truncates coordinates which misses some neighbors Needs ...
Read more >
3D mesh compression - CiteSeerX
Quantization truncates the vertex coordinates to a desired accuracy and maps them into integers that can be represented with a limited number of...
Read more >
ProMesh User Manual: Recent Changes - GitHub Pages
This solves some issues with unregistered mouse clicks in the SceneInspector. ... Fixes: - Coordinate values are now truncated during tikz/tex export.
Read more >
qh_triangulate_link - Qhull
MERGING is almost always set if notverified>0 at end of routine some points ... 6407, "qhull internal error (qh_checkpolygon): missing vertex neighbors for ......
Read more >
On matchings and related problems in graphs, hypergraphs ...
2.3.2 TruncRW: Truncated random walk with nonuniform sampling . ... 2.2 Sample: Algorithm to sample a random neighbor of a column vertex c ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found