Raycaster Fill Issue

I have a raycaster function that works well on the first attachment to a scene.

See below, as the mouse moves, the mesh is filled appropriately.

Recording 2025-02-23 at 21.11.51

After updating my scene though with some clicks on the 3d plots, there are some hard to reproduce fringe cases where the fill is no longer occurring, notice a few of the blue triangles not being filled on hover.

Recording 2025-02-23 at 20.49.02

My raycaster function is this:

export function setupRaycastingForResults(scene, camera, renderer) {
    const raycaster = new THREE.Raycaster();
    const mouse = new THREE.Vector2();
    let hoveredObject = null;
    let hoveredRebar = null;
    let originalRebarColor = new THREE.Color();

    function updateRaycaster(event) {
        const rect = renderer.domElement.getBoundingClientRect();
        mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
        mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;

        raycaster.setFromCamera(mouse, camera);
        return raycaster.intersectObjects(scene.children, true); // ✅ Always get fresh objects
    }

    // ✅ Mouse Move Event - Highlight Object on Hover
    renderer.domElement.addEventListener('mousemove', (event) => {
        let intersects = updateRaycaster(event);
        let meshFound = false;
        let rebarFound = false;

        for (const intersect of intersects) {
            const object = intersect.object;

            if (object instanceof THREE.Mesh && !meshFound) {
                if (hoveredObject !== object) {
                    if (hoveredObject) hoveredObject.material.wireframe = true;
                    hoveredObject = object;
                    hoveredObject.material.wireframe = false;
                }
                meshFound = true;
            }

            if (object instanceof THREE.Points && !rebarFound) {
                if (hoveredRebar !== object) {
                    if (hoveredRebar) hoveredRebar.material.color.set(originalRebarColor);
                    originalRebarColor.copy(object.material.color);
                    object.material.color.set(0x00FF00); // Highlight rebar as green
                    hoveredRebar = object;
                }
                rebarFound = true;
            }
        }

        // ✅ Restore previous properties when mouse leaves
        if (!meshFound && hoveredObject) {
            hoveredObject.material.wireframe = true;
            hoveredObject = null;
        }

        if (!rebarFound && hoveredRebar) {
            hoveredRebar.material.color.set(originalRebarColor);
            hoveredRebar = null;
        }
    });

My scene being update is shown in the following code, essentially I am updating the position and colors of the mesh objects when selecting a point on the 3d plot.

// ✅ Add Click Event Listener
        document.getElementById("pmPlot").on('plotly_click', (data) => {
            let clickedIndex = data.points[0].customdata; // Extract strain profile index
            window.selectedStrainProfileIndex = clickedIndex;
            window.selectedConcShape.generate3dStressPlot(window.selectedAngle, selectedConcShape.strainProfiles[window.selectedAngle][window.selectedStrainProfileIndex]);
            // ✅ Reinitialize raycasting since scene was modified
            setTimeout(() => {
                console.log("🔄 Reinitializing raycasting after PMM selection...");
                setupRaycastingForResults(scene, camera, renderer);
            }, 100);
        });

Part of the updating in generate3dStressPlot function

this.FEMmesh.forEach((object) => {
            if (!object.geometry || !object.geometry.attributes.position) return;

            let positions = object.geometry.attributes.position.array;
            let stress = calculateStress(object, strainProfile, angle, concreteMat);
            let zOffset = (stress / 4000) * concreteScaleFactor;

            for (let i = 2; i < positions.length; i += 9) {
                let newZ = zOffset;
                minZ = Math.min(minZ, newZ);
                maxZ = Math.max(maxZ, newZ);
            }
        });
        
        // Second pass to update position and apply colors
        this.FEMmesh.forEach((object) => {
            let positions = object.geometry.attributes.position.array;
            let colors = object.geometry.attributes.color.array;
            let stress = calculateStress(object, strainProfile, angle, concreteMat);
            let zOffset = (stress / 4000) * concreteScaleFactor;
        
            for (let i = 0; i < positions.length; i += 3) { // Loop through ALL vertices
                positions[i + 2] = zOffset; // Modify Z-coordinate
        
                let normalizedZ = (positions[i + 2] - minZ) / (maxZ - minZ);
        
                // Assign color per vertex
                colors[i] = 1 - normalizedZ;  // Red channel
                colors[i + 1] = 0;            // Green channel
                colors[i + 2] = normalizedZ;  // Blue channel
            }
        
            object.geometry.attributes.position.needsUpdate = true;
            object.geometry.attributes.color.needsUpdate = true;
        });

Any ideas on why the raycaster might not be working as intended?

Thanks!

You probably need to recompute the boundingboxes of the FEMesh geometries after building them.

object.geometry.computeBoundingBox();

4 Likes

Thanks this did the trick!

1 Like

Thanks for making a thorough post with all the right info. :smiley:

1 Like