Loaded an object using OBJLoader; how to compute the position of something in it? (three.js)

I am rendering a 3D human head using three.js and OBJLoader:

let renderer, camera, scene, head, light, projectiles;

new THREE.OBJLoader().load(objUrl, initialize);

function initialize(obj) {
  renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);

  camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight);

  scene = new THREE.Scene();

  head = obj.clone();
  head.children.forEach(child => child.material = new THREE.MeshPhongMaterial({ color: "#ffc700" }));
  head.position.y = -34;
  head.position.z = -110;
  scene.add(head);
  
  light = new THREE.SpotLight();
  light.target = head;
  scene.add(light);
  
  projectiles = [];

  window.addEventListener("mousedown", createProjectile, false);

  animate();
}

function animate() {
  head.rotation.y += THREE.Math.degToRad(1);

  projectiles.forEach(updateProjectile);

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

function createProjectile() {
  let projectile = new THREE.Mesh();
  projectile.material = new THREE.MeshToonMaterial({ color: "#ff0000" });
  projectile.geometry = new THREE.SphereGeometry(3, 20, 20);
  projectile.position.copy(getMouthPosition());
  scene.add(projectile);
  projectiles.push(projectile);
}

function updateProjectile(projectile) {
  // TODO: Move projectile in the direction the mouth was facing when projectile was first created.
  projectile.position.x += 2;
}

function getMouthPosition() {
  // TODO: Determine the world position of the mouth.
  let box = new THREE.Box3().setFromObject(head);
  return box.getCenter();
}
body {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
}

canvas {
  display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/86/three.js">
</script>

<script src="https://wzrd.in/standalone/[email protected]">
</script>

<script>
  threeObjLoader(THREE);
  objUrl = "https://cdn.rawgit.com/mrdoob/three.js/f32fc45/examples/obj/walt/WaltHead.obj";
</script>

When the mouse is clicked, I want to "shoot" a projectile/bullet from the rotating head's mouth. But as you can see from the TODO comments in the code, there are two functions I don't know how to implement: getMouthPosition() and updateProjectile().

For getMouthPosition(), I want to determine the current position of the mouth and spawn the projectile at this location (ideally, just in front of the mouth).

For updateProjectile(), I want to move the projectile in the direction the head was facing at the time when the projectile was first created, like this:

example

If someone could shed light on how to write these functions, that would be great. Thanks.

Upvotes: 1

Views: 546

Answers (1)

prisoner849
prisoner849

Reputation: 17596

Look. Somehow you'll get where the mouth locates (in coordinates of the head group it locates at about [0, 25, 20]). Then, to get position of the mouth of the spinning head, you can use .localToWorld(v) method, like so:

head.localToWorld(mouthPosition.copy(spawnPoint.position));

spawnPoint is a "helper" object to indicate where our spawn point is.

Further, you have to know where your head points at. You can get it with another method .getWorldDirection() of the head object.

Concluding all of this: you know position of the head's mouth, you know its direction, thus you can cast a projectile, using those values.

let renderer, camera, scene, head, light, projectiles, spawnPoint, clock = new THREE.Clock(), delta = 0;

new THREE.OBJLoader().load(objUrl, initialize);

function initialize(obj) {
  renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);

  camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight);

  scene = new THREE.Scene();

  head = obj.clone();
  head.children.forEach(child => child.material = new THREE.MeshPhongMaterial({ color: Math.random() * 0xffffff }));
  head.position.y = -34;
  head.position.z = -110;
  scene.add(head);
  
  light = new THREE.SpotLight();
  light.target = head;
  scene.add(light);
  
  spawnPoint = new THREE.Mesh(new THREE.SphereGeometry(1, 4, 2), new THREE.MeshBasicMaterial({color: "red", wireframe: true}));
  spawnPoint.position.set(0, 25, 20);
  head.add(spawnPoint);
  
  projectiles = [];

  window.addEventListener("mousedown", event => { createProjectile(); }, false);
  
  animate();
}

function animate() {
  delta = clock.getDelta();
  
  requestAnimationFrame(animate);
  
  head.rotation.y += THREE.Math.degToRad(20) * delta;

  projectiles.forEach(p => {
    p.position.addScaledVector(p.userData.direction, p.userData.speed * delta);
  });

  renderer.render(scene, camera);
}

function createProjectile() {
  let projectile = new THREE.Mesh();
  projectile.material = new THREE.MeshToonMaterial({ color: 0xff0000 });
  projectile.geometry = new THREE.SphereGeometry(3, 16, 12);
  let pos = getMouthPosition();
  console.log("pos", pos);
  projectile.position.copy(pos);
  projectile.userData.direction = new THREE.Vector3().copy(head.getWorldDirection().normalize());
  console.log(projectile.userData.direction);
  projectile.userData.speed = 50;
  scene.add(projectile);
  projectiles.push(projectile);
  console.log(projectiles);
}

function getMouthPosition() {
  let mouthPosition = new THREE.Vector3();
  console.log("spawnPoint", spawnPoint);
  head.localToWorld(mouthPosition.copy(spawnPoint.position));
  console.log("mouthPosition", mouthPosition);
  return mouthPosition;
}
body {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
}

canvas {
  display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/86/three.js">
</script>

<script src="https://wzrd.in/standalone/[email protected]">
</script>

<script>
  threeObjLoader(THREE);
  objUrl = "https://cdn.rawgit.com/mrdoob/three.js/f32fc45/examples/obj/walt/WaltHead.obj";
</script>

Upvotes: 1

Related Questions