3DObject event handler selecting nearby objects, should select self Three.js

80 views Asked by At

I have been digging into the documentation, tutorials, stackoverflow questions literally all day and cannot find the solution to this problem. Please please take pity on me and help me!

I have a watering can that I put an event listener on, but on mousedown, objects near and to the left get selected ie. the tree trunk or a leaf (code not included) and then I am able to drag and drop that object around until I put it down and it is out of range of the watering can. If I drop it near the watering can, it will get selected again when I click. This is NOT the intended behavior, I want the actual watering can I clicked to be selected and then proceed to get dragged and dropped etc. There is kind of a lot of code here but I am not sure where the problem lies anymore so I am including almost all of it. It's like the watering can thinks it is elsewhere or the ray thinks it is shooting elsewhere. PLEASE TELL ME IF I CAN CLARIFY ANYTHING OR SEND MORE CODE. I just started learning three.js yesterday so there are definitely things I don't understand but I am pretty clear on what is supposed to be happening here... and this is not it :D

const WateringCan = function() {
  this.mesh = new THREE.Object3D();
  const mat = new THREE.MeshStandardMaterial({ metalness: 0.8, color: 0xadb2bd });

  // Create the can
  const geomCan = new THREE.CylinderGeometry(15, 15, 25, 10, 10);
  geomCan.applyMatrix( new THREE.Matrix4().makeScale(1.1, 1.0, 0.6));
  geomCan.computeBoundingBox();
  geomCan.computeFaceNormals();
  const can = new THREE.Mesh(geomCan, mat);
  can.castShadow = true;
  can.receiveShadow = true;
  this.mesh.add(can);

  // Create the handle
  const geomHandle = new THREE.TorusGeometry( 10, 2, 8, 6, Math.PI);
  geomHandle.applyMatrix( new THREE.Matrix4().makeScale(0.9, 1.1, 1.0));
  const handle = new THREE.Mesh(geomHandle, mat);
  handle.rotation.z = 4.5;
  handle.position.x = 13.5;
  handle.castShadow = true;
  handle.receiveShadow = true;
  this.mesh.add(handle);

  // Create spout
  const geomSpout = new THREE.CylinderGeometry(1, 3, 20, 5, 5);
  const spout = new THREE.Mesh(geomSpout, mat);
  spout.rotation.z = 1;
  spout.position.x = -22;
  spout.position.y = 10;
  spout.position.z = 3;
  spout.castShadow = true;
  spout.receiveShadow = true;
  this.mesh.add(spout);

  const domEvents = new THREEx.DomEvents(camera, renderer.domElement);
  domEvents.addEventListener(can, 'mousedown', (e) => onWateringCanMouseDown(e));
};

let wateringCan;
function createWateringCan() {
  wateringCan = new WateringCan();
  wateringCan.name = "wateringCan";
  wateringCan.mesh.position.x = 120;
  wateringCan.mesh.position.y = -30;
  wateringCan.mesh.position.z = -10;
  scene.add(wateringCan.mesh);
  objects.push(wateringCan.mesh);
}

let plane;
function createPlane() {
  plane = new THREE.Mesh(new THREE.PlaneBufferGeometry(WIDTH, HEIGHT, 8, 8), new THREE.MeshBasicMaterial({ color: 0xffffff, alphaTest: 0, visible: false }));
  scene.add(plane);
}

let selection;
function onWateringCanMouseDown(e) {

  const mouseX = (e.clientX / WIDTH) * 2 - 1;
  const mouseY = -(e.clientY / HEIGHT) * 2 + 1;

  const mouse3D = new THREE.Vector3(mouseX, mouseY, 0.5);
  mouse3D.unproject(camera);

  raycaster.set(camera.position, mouse3D.sub(camera.position).normalize());

  const intersectedObjects = raycaster.intersectObjects(objects, true); 
  if (intersectedObjects.length > 0) {

    selection = intersectedObjects[0].object;

    const intersectPlane = raycaster.intersectObject(plane);
    offset.z = selection.position.z;
    offset.copy(intersectPlane[0].point).sub(plane.position);
  }
}

function onDocumentMouseMove(e) {
  const mouseX = (e.clientX / WIDTH) * 2 - 1;
  const mouseY = -(e.clientY / HEIGHT) * 2 + 1;

  const mouse3D = new THREE.Vector3(mouseX, mouseY, 0.5);
  mouse3D.unproject(camera);
  raycaster.set(camera.position, mouse3D.sub(camera.position).normalize());
  raycaster.setFromCamera( mouse3D.clone(), camera);
  if (selection) {
    offset.z = selection.position.z;
    const intersectPlane = raycaster.intersectObject(plane);
    selection.position.copy(intersectPlane[0].point.sub(offset));
  } else {
    const intersectedObjects = raycaster.intersectObjects(objects);
    if (intersectedObjects.length > 0) {
      plane.position.copy(intersectedObjects[0].object.position);
      plane.lookAt(camera.position);
    }
  }
}

function onDocumentMouseUp(e) {
  selection = null;
}

Things I have already tried: computeBoundingBox() on everything I have applyMatrix on, refactoring mouse3D/raycaster around in slightly different ways, moving the can far away (then nothing gets selected). I also had the event listener set on the document for a while and I did not have this problem, but the watering can pieces did not stick to each other, the individual spout/can/handle would get dragged and dropped. But at least it knew then that I was clicking on them! Also having the ability to drag and drop literally everything on the page was fun but also not the point, like I don't need all the leaves on my tree to move around.

Please please help me if you can!! I have been messing around with this all day and it is killing my soul. :D

1

There are 1 answers

0
b-insh On

Okay, so. I did not find out what the reason behind this strange behavior was so I decided to kind of restructure what I was doing and I am no longer having this problem.

What I did is:

  • removed the event listener from the watering can and put it back on the document

  • removed all objects but the watering can from my objects array that gets looked through on raycaster.intersectObjects so now nothing but that can be dragged and dropped

  • things are mostly fine now