Can i change the color of an gltf file with raycasting?

i’ve been trying for a while now but it seams i just can’t make the textures change color, it’s supposed to be a map of a small traintrack tha’ll change its color to blue when you click on it. I also don’t know if i’m doing the right thing by using raycasting so if anyone could help me out it would be much appreciated

//import area
import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.177.0/build/three.module.min.js';
import { GLTFLoader } from 'https://cdn.jsdelivr.net/npm/three@0.177.0/examples/jsm/loaders/GLTFLoader.js';

const scene = new THREE.Scene();


let camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.set(-0, 0, 14);

const canvas = document.querySelector('#map');
const renderer = new THREE.WebGLRenderer({ antialias: true, canvas });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);


//loaded mesh

//main area
const loader = new GLTFLoader();

loader.load('../assets/3D modules/FLOREST.gltf', (gltf) => {
    gltf.scene.scale.set(102, 102, 102);
    scene.add(gltf.scene);
}, undefined, (error) => {
    console.error('Error loading GLTF model:', error);
});

loader.load('../assets/3D modules/trilhos -D -.gltf', (gltf) => {
    gltf.scene.scale.set(102, 102, 102);
    scene.add(gltf.scene);
}, undefined, (error) => {
    console.error('Error loading GLTF model:', error);
});

//trail -d 
loader.load('../assets/3D modules/trilho -d.gltf', (gltf) => {
    gltf.scene.scale.set(102, 102, 102);
    scene.add(gltf.scene);
}, undefined, (error) => {
    console.error('Error loading GLTF model:', error);
});

//Turn1 button
loader.load('../assets/3D modules/T1B.gltf', (gltf) => {
    gltf.scene.scale.set(102, 102, 102);
    scene.add(gltf.scene);
}, undefined, (error) => {
    console.error('Error loading GLTF model:', error);
});


//Turn2 button
loader.load('../assets/3D modules/T2B.gltf', (gltf) => {
    gltf.scene.scale.set(102, 102, 102);
    scene.add(gltf.scene);
}, undefined, (error) => {
    console.error('Error loading GLTF model:', error);
});

//Switch Sides button
loader.load('../assets/3D modules/SSB.gltf', (gltf) => {
    gltf.scene.scale.set(102, 102, 102);
    scene.add(gltf.scene);
}, undefined, (error) => {
    console.error('Error loading GLTF model:', error);
});

const light = new THREE.AmbientLight(0xffffff, 1);

//interectability
const raycaster = new THREE.Raycaster(); 
renderer.domElement.addEventListener("click", onmousedown);

function onmousedown(event){
    console.log("click confirmed")
    
    const coords = new THREE.Vector2(
        (event.clientX / window.innerWidth) * 2 - 1,
        -(event.clientY / window.innerHeight) * 2 + 1 
    );
    raycaster.setFromCamera(coords, camera);

    const intersections = raycaster.intersectObjects(scene.children, true);
    if (intersections.length > 0) {
        const selectedObject = intersections[0].object;
        const color = new THREE.Color(0, 0, 1); 
    
        if (selectedObject.material) {
            selectedObject.material.color.set(color);
        } else if (selectedObject.parent) {
            selectedObject.parent.traverse((child) => {
                if (child.isMesh) child.material.color.set(color);
            });
        }
    }
};

scene.add(light);

function animate() {
    requestAnimationFrame(animate);
    renderer.render(scene, camera);
};
animate();

Try selectedObject.material.color.set(0,0,1);
Or selectedObject.material.color.copy(color); which will be less performant because you’re creating a new color every click

well, changing it didn’t solve it, i’m getting a confirmation through the console that the mesh is detecting the clicks, but it simply isn’t changing color for some reason

If you have other objects in the scene whose bounds would be overlapping the road geometry the first hit object (intersections[0].object) may not be the mesh you’re trying to click on, you can either raycast against an array of selectable objects…

const selectable = [] 
selectable.push(meshA, meshB, etc) 
raycaster.intersectObjects(selectable , true);

Or raycast against scene.children but filter the children array by some property of any selectable objects eg… mesh.userData.selectable = true for instance

but for the mesh names i just use the files name? or do i have rename them or smth

It depends on the structure of the gltf files you’re loading, do the files contain single models or are they multi part models? If they are multi part I’d suggest using a naming convention for any selectable objects such as “road-selectable” for instance and then filter the intersections array with something like

    const intersections = raycaster.intersectObjects(scene.children, true);
    const selectable = intersections.filter(o=> o.name.includes("selectable")
    if (selectable.length > 0) {
        const selectedObject = selectable[0].object;

i tried to integrate it and made the filter myself but i’m not sure its the correct way to do it(tried reading through the documentation but it is really confusing)

  //filter to only "Selectable" objects
    const selectable = intersections.filter(intersection => {
        let hasSelectableName = false;
    
        intersection.object.traverse((child) => {
            if (child.name && child.name.includes("Selectable")) {
                hasSelectableName = true;
            }
        });
    
        return hasSelectableName;
    });

    if (selectable.length > 0) {
        const selectedObject = selectable[0].object;
        console.log("Selected Object Name:", selectedObject.name);

        const color = new THREE.Color(0, 0, 1); // Blue

        if (selectedObject.material) {
            selectedObject.material.color.set(color);
        } else {
            selectedObject.traverse((child) => {
                if (child.isMesh && child.material) {
                    child.material.color.set(color);
                }
            });
        }
    }

Here you’re returning a boolean instead of the list of objects, raycaster provides a list of intersected objects regardless of thier parent or child so you should just need to do this…

const selectable = intersections.filter(intersection => 
 intersection.name.includes("Selectable") );

If not can you provide a gltf file with the structure your using inside it?

i dont really know what you mean but this is the file C:\Users\Administrador\Desktop\AplicativoTLB-1\assets\3Dmodules\trilhodSelectable.gltf

also it’s still not working :sob:

Can you upload this to inspect, it’s likely a practical example is going to be a good way to help

if you say so, here’s the file

trilhosDSelectable.gltf (1.6 MB)