Skip to content

fix(raycaster): full precision result.facePoints#205

Open
Kurtil wants to merge 1 commit intoThatOpen:mainfrom
bimdata:Kurtil-patch-1
Open

fix(raycaster): full precision result.facePoints#205
Kurtil wants to merge 1 commit intoThatOpen:mainfrom
bimdata:Kurtil-patch-1

Conversation

@Kurtil
Copy link
Copy Markdown
Contributor

@Kurtil Kurtil commented May 6, 2026

Description

I have implemented a face hoverer in the same idea of the engine_component Hoverer, by creating a mesh from the raycast result facePoints and faceIndices. However on models with large coordinates, the raycast result face points array is lacking precision due to float32 limitation. This PR just change the Float32Array facePoints array by a Float64Array.

In the formatRaycastResult method, the duo matrix get from TransformHelper (set in this._temp.m3) + untransformed facePoints have all precision, but as soon as this code execute:

          this._temp.v1.set(x, y, z);
          this._temp.v1.applyMatrix4(this._temp.m3);
          result.facePoints[i] = this._temp.v1.x;
          result.facePoints[i + 1] = this._temp.v1.y;
          result.facePoints[i + 2] = this._temp.v1.z;

Because facePoints is a Float32Array and the transformation matrix is not provided in the result, precision is lost.

Before:

Misaligned face:

Screenshot 2026-05-06 at 11 39 16

After:

Perfectly aligned face:

Screenshot 2026-05-06 at 11 29 02

Additional context

With the fix and thanks to the full precision facePoints: Float64Array , I recreate the face mesh with a position computed from the bound sphere:

    const result = await caster.castRay({ position: mousePosition });

    // change Float64Array into Array<Vector3>
    const worldVertices = result.facePoints.reduce((acc, value, index) => {
      const vecIndex = Math.floor(index / 3);
      if (!acc[vecIndex]) acc[vecIndex] = new THREE.Vector3();
      acc[vecIndex].setComponent(index % 3, value);
      return acc;
    }, []);

    if (worldVertices.length < 3) return null;

    const bounds = new THREE.Box3();
    worldVertices.forEach((vertex) => bounds.expandByPoint(vertex));
    const renderOrigin = bounds.isEmpty()
      ? worldVertices[0].clone()
      : bounds.getCenter(new THREE.Vector3());
      
    // ...
    
    faceMesh.position.copy(renderOrigin);

Additional context


What is the purpose of this pull request?

  • Bug fix
  • New Feature
  • Documentation update
  • Other

Before submitting the PR, please make sure you do the following:

  • Check that there isn't already a PR that solves the problem the same way to avoid creating a duplicate.
  • Follow the Conventional Commits v1.0.0 standard for PR naming (e.g. feat(examples): add hello-world example).
  • Provide a description in this PR that addresses what the PR is solving, or reference the issue that it solves (e.g. fixes #123).
  • Ideally, include relevant tests that fail without this PR but pass with it.

@agviegas
Copy link
Copy Markdown
Contributor

agviegas commented May 6, 2026

@Kurtil cool! Curious, did you check out the latest raycaster / hoverer implementation? I'm saying this because it might be a lot faster than recreating the geometry from the face points. The meshes fragments generate have an face id buffer attribute you could use for this. That said, this looks good, I'll review it and see possible ripple effects if any. Otherwise, happy to merge

@Kurtil
Copy link
Copy Markdown
Contributor Author

Kurtil commented May 6, 2026

@agviegas thanks for the feedback, I will test the new Hoverer implementation 👍

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants