Kamlesh
Kamlesh

Reputation: 23

Why my setColorAt in InstancedMesh not working

I have a plane made of >10k squares and I want only the individual squares to change color that I hover my mouse above after click. But setColorAt isnt working.

let plane
let intersects
let color = new THREE.Color();

const geometry = new THREE.BoxGeometry(1, 1, .1);


//using instance method
function createInstancePlane(){
  const material = new THREE.MeshBasicMaterial({
    color: 0xffff00,
    transparent: true,
    opacity: 1,
  });
  plane = new THREE.InstancedMesh(geometry, material, 11856);
  scene.add(plane);

  //dummy
  const dummy = new THREE.Object3D();

  let mm = 0;

  for (let i = 0; i < 152; i++) {
    for (let j = 0; j < 78; j++) {
      dummy.position.x = i - 76;
      dummy.position.y = j - 39;
      dummy.position.z = 0;

      dummy.updateMatrix();
      plane.setMatrixAt(mm, dummy.matrix);
      mm++;
    }
  }
}
createInstancePlane()
console.log(plane)

//RAYCASTER
const rayCaster = new THREE.Raycaster();

let mouse = new Vector2();
window.addEventListener("mousemove", (e) => {
  mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
});


let funcWhileMouseMove = () => {
  rayCaster.setFromCamera(mouse, camera);
  intersects = rayCaster.intersectObject(plane);
  for (let obj of intersects) {
    console.log(obj.instanceId)
    plane.setColorAt(obj.instanceId, color.set(0xff0000)); //THIS NOT WORKING
    plane.instanceColor.needsUpdate = true;
  }
};

//TICK FUNCTION
function tick() {
  stats.begin();

  //RAYCASTER obj test
  window.addEventListener("mouseup", () => {
    window.removeEventListener("mousemove", funcWhileMouseMove);
    setTimeout(() => {
      plane.material.color.set("pink");
    }, 1000);

  });

  window.addEventListener("mousedown", () => {
    window.addEventListener("mousemove", funcWhileMouseMove);
  });
  stats.end();

  window.requestAnimationFrame(tick);
}
tick();

I get the desired plane made of >10k squares and when I click and move mouse I get instanceId too, but color stays the same.

I've tried different ways to implement color like-

plane.setColorAt(obj.instanceId, color.setHex(0xff0000)) plane.setColorAt(obj.instanceId, color.set('red'))

Upvotes: 1

Views: 521

Answers (1)

Mugen87
Mugen87

Reputation: 31026

If you add color data after the InstancedMesh has been rendered at least once, you have to set the needsUpdate property of the respective material to true. However, I recommend to setup an instanced color attribute with a default color right from the beginning like shown below:

let camera, scene, renderer;

let plane;
let color = new THREE.Color();
let mouse = new THREE.Vector2();
const rayCaster = new THREE.Raycaster();

init();
animate();

function init() {

  camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 1000);
  camera.position.z = 100;

  scene = new THREE.Scene();

  createInstancePlane()

  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setPixelRatio( window.devicePixelRatio );
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);

  window.addEventListener("pointermove", onPointerMove);

}

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

function onPointerMove(e) {
  mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;

  rayCaster.setFromCamera(mouse, camera);
  const intersects = rayCaster.intersectObject(plane);
  for (let obj of intersects) {
    plane.setColorAt(obj.instanceId, color.set(0xff0000));
    plane.instanceColor.needsUpdate = true;
  }
}

function createInstancePlane() {
    const geometry = new THREE.BoxGeometry(1, 1, 0.1);
  const material = new THREE.MeshBasicMaterial({
    transparent: true,
    opacity: 1,
  });
  plane = new THREE.InstancedMesh(geometry, material, 11856);
  scene.add(plane);

  //dummy
  const dummy = new THREE.Object3D();
  const defaultColor = new THREE.Color(0xffff00);

  let mm = 0;

  for (let i = 0; i < 152; i++) {
    for (let j = 0; j < 78; j++) {
      dummy.position.x = i - 76;
      dummy.position.y = j - 39;
      dummy.position.z = 0;

      dummy.updateMatrix();
      plane.setMatrixAt(mm, dummy.matrix);
      plane.setColorAt(mm, defaultColor);
      mm++;
    }
  }
}
body {
      margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.min.js"></script>

Upvotes: 1

Related Questions