DevonDahon
DevonDahon

Reputation: 8350

Why cubes do not always animate with three.js

In the code below, cubes sometimes move with animation and sometimes without. How can I fix it ?

Also, how should I set rotation speed ?

new Vue({
  el: '#app',
  data () {
    return {
      camera: null,
      scene: null,
      renderer: null,
      cube: null,
      angle: null
    }
  },
  methods: {
    init: function () {

      this.camera = new THREE.PerspectiveCamera(1, 1);
      this.camera.position.z = 200;

      // Make a scene
      this.scene = new THREE.Scene();

      this.clock = new THREE.Clock();

      //
      this.renderer = new THREE.WebGLRenderer({
        antialias: true,
        alpha: true
      });
      let container = document.getElementById('container')
      this.renderer.setSize(container.offsetWidth, container.offsetHeight);
      container.appendChild(this.renderer.domElement);

      let cube2 = this.createCube()
      cube2.name = "cube2"
      //       cube2.position = new THREE.Vector3(1, 0)
      this.scene.add(cube2);

    },
    createCube: function () {

      // GEOMETRY

      // 1. Start with empty geometry
      let geometry = new THREE.Geometry();

      // 2. Add vertices to geometry
      geometry.vertices.push(
        // verts [0-3] are in in +z
        new THREE.Vector3(-1, 1, 1),
        new THREE.Vector3(-1, -1, 1),
        new THREE.Vector3(1, -1, 1),
        new THREE.Vector3(1, 1, 1),
        // verts [4-7] in -z
        new THREE.Vector3(-1, 1, -1),
        new THREE.Vector3(-1, -1, -1),
        new THREE.Vector3(1, -1, -1),
        new THREE.Vector3(1, 1, -1),
      );

      // 3. Connect vertices in desired order to make faces
      let b = 0x1db0ec
      let y = 0xffef3a
      let r = 0xea353d
      let w = 0xffffff

      // Set half faces
      geometry.faces.push(new THREE.Face3(0, 1, 2)); // blue
      geometry.faces.push(new THREE.Face3(0, 2, 3)); // yellow
      geometry.faces.push(new THREE.Face3(5, 4, 6)); // white
      geometry.faces.push(new THREE.Face3(6, 4, 7)); // red

      // Set whole faces
      geometry.faces.push(new THREE.Face3(1, 0, 5)); // blue
      geometry.faces.push(new THREE.Face3(5, 0, 4));
      geometry.faces.push(new THREE.Face3(1, 5, 2)); // white
      geometry.faces.push(new THREE.Face3(5, 6, 2));
      geometry.faces.push(new THREE.Face3(2, 6, 3)); // red
      geometry.faces.push(new THREE.Face3(3, 6, 7));
      geometry.faces.push(new THREE.Face3(0, 3, 4)); // yellow
      geometry.faces.push(new THREE.Face3(3, 7, 4));

      // Set faces colors
      geometry.faces[0].color.setHex(b); // Half face
      geometry.faces[1].color.setHex(y);
      geometry.faces[2].color.setHex(w);
      geometry.faces[3].color.setHex(r);
      geometry.faces[4].color.setHex(b); // Whole face
      geometry.faces[5].color.setHex(b);
      geometry.faces[6].color.setHex(w);
      geometry.faces[7].color.setHex(w);
      geometry.faces[8].color.setHex(r);
      geometry.faces[9].color.setHex(r);
      geometry.faces[10].color.setHex(y);
      geometry.faces[11].color.setHex(y);

      // MATERIAL

      // Make a material
      let material = new THREE.MeshBasicMaterial({
        // color: 0x00FF00,
        vertexColors: THREE.FaceColors,
        wireframe: false,
      });

      // MESH

      let cube = new THREE.Mesh(geometry, material);
      return cube
    },
    rotateTo: function (face) {

      if (face == 'yellow')
        this.angle = new THREE.Quaternion().setFromEuler(new THREE.Euler(0, Math.PI / 2, Math.PI / 2));
      else if (face == 'red')
        this.angle = new THREE.Quaternion().setFromEuler(new THREE.Euler(Math.PI / 2, 0, Math.PI / 2));
      else if (face == 'blue')
        this.angle = new THREE.Quaternion().setFromEuler(new THREE.Euler(Math.PI / 2, 0, - Math.PI / 2));
      else if (face == 'white')
        this.angle = new THREE.Quaternion().setFromEuler(new THREE.Euler(- Math.PI / 2, Math.PI / 2, 0));
      else if (face == 'yb')
        this.angle = new THREE.Quaternion().setFromEuler(new THREE.Euler(0, 0, 0));
      else if (face == 'rw')
        this.angle = new THREE.Quaternion().setFromEuler(new THREE.Euler(Math.PI, 0, 0));

      this.animate()

    },
    animate: function () {

      let id = requestAnimationFrame(this.animate);

      let delta = this.clock.getDelta();
      this.scene.children[0].quaternion.rotateTowards(this.angle, Math.PI * delta);

      this.renderer.render(this.scene, this.camera);

      if (this.scene.children[0].quaternion.equals(this.angle)) {
        cancelAnimationFrame(id)
      }

    }
  },
  mounted () {
    this.init();
    this.renderer.render(this.scene, this.camera);
  }
})
#container {
  background-color: #aaa;
  width: 20em;
  height: 20em;
}
<script src="https://unpkg.com/vue"></script>
<script src="https://threejs.org/build/three.min.js"></script>

<div id="app">
    <div>
      <button v-on:click="rotateTo('yellow')">yellow</button>
      <button v-on:click="rotateTo('red')">red</button>
      <button v-on:click="rotateTo('blue')">blue</button>
      <button v-on:click="rotateTo('white')">white</button>
      <button v-on:click="rotateTo('yb')">yellow/blue</button>
      <button v-on:click="rotateTo('rw')">red/white</button>
    </div>

    <div id="container"></div>
</div>

UPDATE

Code isolated to only the threejs portion.

var camera = null;
var scene = null;
var renderer = null;
var cube = null;
var angle = null;

init();
renderer.render(scene, camera);

function init() {

	camera = new THREE.PerspectiveCamera(1, 1);
	camera.position.z = 200;

	// Make a scene
	scene = new THREE.Scene();

	clock = new THREE.Clock();

	//
	renderer = new THREE.WebGLRenderer({
		antialias: true,
		alpha: true
	});
	let container = document.getElementById('container');
	renderer.setSize(container.offsetWidth, container.offsetHeight);
	container.appendChild(renderer.domElement);

	let cube2 = createCube();
	cube2.name = "cube2";
	//       cube2.position = new THREE.Vector3(1, 0)
	scene.add(cube2);

}

function createCube() {

	// GEOMETRY

	// 1. Start with empty geometry
	let geometry = new THREE.Geometry();

	// 2. Add vertices to geometry
	geometry.vertices.push(
		// verts [0-3] are in in +z
		new THREE.Vector3(-1, 1, 1),
		new THREE.Vector3(-1, -1, 1),
		new THREE.Vector3(1, -1, 1),
		new THREE.Vector3(1, 1, 1),
		// verts [4-7] in -z
		new THREE.Vector3(-1, 1, -1),
		new THREE.Vector3(-1, -1, -1),
		new THREE.Vector3(1, -1, -1),
		new THREE.Vector3(1, 1, -1),
	);

	// 3. Connect vertices in desired order to make faces
	let b = 0x1db0ec;
	let y = 0xffef3a;
	let r = 0xea353d;
	let w = 0xffffff;

	// Set half faces
	geometry.faces.push(new THREE.Face3(0, 1, 2)); // blue
	geometry.faces.push(new THREE.Face3(0, 2, 3)); // yellow
	geometry.faces.push(new THREE.Face3(5, 4, 6)); // white
	geometry.faces.push(new THREE.Face3(6, 4, 7)); // red

	// Set whole faces
	geometry.faces.push(new THREE.Face3(1, 0, 5)); // blue
	geometry.faces.push(new THREE.Face3(5, 0, 4));
	geometry.faces.push(new THREE.Face3(1, 5, 2)); // white
	geometry.faces.push(new THREE.Face3(5, 6, 2));
	geometry.faces.push(new THREE.Face3(2, 6, 3)); // red
	geometry.faces.push(new THREE.Face3(3, 6, 7));
	geometry.faces.push(new THREE.Face3(0, 3, 4)); // yellow
	geometry.faces.push(new THREE.Face3(3, 7, 4));

	// Set faces colors
	geometry.faces[0].color.setHex(b); // Half face
	geometry.faces[1].color.setHex(y);
	geometry.faces[2].color.setHex(w);
	geometry.faces[3].color.setHex(r);
	geometry.faces[4].color.setHex(b); // Whole face
	geometry.faces[5].color.setHex(b);
	geometry.faces[6].color.setHex(w);
	geometry.faces[7].color.setHex(w);
	geometry.faces[8].color.setHex(r);
	geometry.faces[9].color.setHex(r);
	geometry.faces[10].color.setHex(y);
	geometry.faces[11].color.setHex(y);

	// MATERIAL

	// Make a material
	let material = new THREE.MeshBasicMaterial({
		// color: 0x00FF00,
		vertexColors: THREE.FaceColors,
		wireframe: false,
	});

	// MESH

	let cube = new THREE.Mesh(geometry, material);
	return cube;
}

function rotateTo(face) {

	if (face == 'yellow')
		angle = new THREE.Quaternion().setFromEuler(new THREE.Euler(0, Math.PI / 2, Math.PI / 2));
	else if (face == 'red')
		angle = new THREE.Quaternion().setFromEuler(new THREE.Euler(Math.PI / 2, 0, Math.PI / 2));
	else if (face == 'blue')
		angle = new THREE.Quaternion().setFromEuler(new THREE.Euler(Math.PI / 2, 0, -Math.PI / 2));
	else if (face == 'white')
		angle = new THREE.Quaternion().setFromEuler(new THREE.Euler(-Math.PI / 2, Math.PI / 2, 0));
	else if (face == 'yb')
		angle = new THREE.Quaternion().setFromEuler(new THREE.Euler(0, 0, 0));
	else if (face == 'rw')
		angle = new THREE.Quaternion().setFromEuler(new THREE.Euler(Math.PI, 0, 0));

	animate();

}

function animate() {

	let id = requestAnimationFrame(animate);

	let delta = clock.getDelta();
	scene.children[0].quaternion.rotateTowards(angle, Math.PI * delta);

	renderer.render(scene, camera);

	if (scene.children[0].quaternion.equals(angle)) {
		cancelAnimationFrame(id);
	}

}
#container {
      background-color: #aaa;
      width: 20em;
      height: 20em;
    }
<script src="https://threejs.org/build/three.min.js"></script>

        <div>
          <button onclick="rotateTo('yellow')">yellow</button>
          <button onclick="rotateTo('red')">red</button>
          <button onclick="rotateTo('blue')">blue</button>
          <button onclick="rotateTo('white')">white</button>
          <button onclick="rotateTo('yb')">yellow/blue</button>
          <button onclick="rotateTo('rw')">red/white</button>
        </div>

        <div id="container"></div>

Upvotes: 1

Views: 67

Answers (1)

Jay Li
Jay Li

Reputation: 747

The reason for the animation not playing is because when you cancelAnimationFrame the clock is still running, so the next time you call rotateTo the delta is so high that the animation ends immediately.

you can avoid this by stopping the clock with cancelAnimationFrame, and start it again when calling rotateTo as follows:

function rotateTo(face) {
  clock.start()
  ...
}
function animate() {
    ...
    if (scene.children[0].quaternion.equals(angle)) {
        cancelAnimationFrame(id);
        clock.stop()
    }
}

as for rotation speed, the 2nd arg of rotateTowards is step, which determines how fast will it get there. So if you add a modifier var to it, you can control the speed.

let rotSpeedMotifier = 0.2 // the higher the faster
scene.children[0].quaternion.rotateTowards(angle, Math.PI * delta * rotSpeedMotifier );

Upvotes: 3

Related Questions