Live Demo | An online Rubiks Cube built in Three.JS.
- Clone and load the
index.html
file in your browser or visit the Live Demo. - Click and drag on the canvas to spin the cube.
- Right-click on any face to rotate in that direction.
The 3D-Cube is built using the Three.js library. There are 27 cubes rendered on the scene each representing a piece of the Rubik's Cube. The cubes are created with THREE.BoxGeometry
and THREE.Mesh
.
boxGeometries = [];
for (var i = 0; i < 27; i++) {
boxGeometries[i] = new THREE.BoxGeometry(size, size, size, 3, 3, 3);
}
cubes = [];
for (var i = 0; i < 27; i++) {
cubes[i] = new THREE.Mesh(boxGeometries[i], cubeMaterials);
cubes[i].position.x = 0;
cubes[i].position.y = 0;
cubes[i].position.z = 0;
globalCubeState[i + 1] = cubes[i];
}
scene = new THREE.Scene();
for (var i = 0; i < 27; i++) {
scene.add(cubes[i]);
}
Three Event Listeners on the page intercept mouse clicks to determine how to interact with the cube. When the user left-clicks on the Three.JS Scene two data points are taken, the Date().getTime()
of the mouse movement and the event.x
and event.y
. The time is used to determine the speed of cube's rotation and the x and y are used to create a Three.Vector
and THREE.Quaternion
that determine the cube's direction of rotation.
function onDocumentMouseUp(event) {
if (
new Date().getTime() - lastMoveTimestamp.getTime() >
moveReleaseTimeDelta
) {
deltaX = event.x - startPoint.x;
deltaY = event.y - startPoint.y;
}
...
}
When the user right-clicks the correct cube must be selected. This is accomplished by using THREE.Raycaster
to get the closest intersected cube to scene's camera. With the correct cube selected, the appropriate movement is determined and the cube's faces get rotated.
function handleClickedFace(event) {
...
var ray = new THREE.Raycaster();
ray.setFromCamera(mouse, camera);
var intersects = ray.intersectObjects(scene.children);
if (intersects.length === 0) return null;
if (intersects.length > 0) {
for (var i = 0; i < intersects.length; i++) {
if (intersects[i].face.materialIndex > 0) {
physicalFace = intersects[i].faceIndex;
break;
}
}
}
...
}
There are two kinds of rotation:
- The rotation of the cube on the canvas.
- Rotation of the faces around the cube.
Each cube has a local X, Y, and Z axis and as the faces are turned those axes change and are tracked to make sure that the cubes are rotated correctly after each click event. The direction of the cube's rotation is determined by calculating a quaternion using the mouse's position to create a Three.Vector
and THREE.Quaternion
.
function rotateMatrix(rotateStart, rotateEnd) {
axis = new THREE.Vector3();
quaternion = new THREE.Quaternion();
var angle = Math.acos(
rotateStart.dot(rotateEnd) / rotateStart.length() / rotateEnd.length()
);
if (angle) {
axis.crossVectors(rotateStart, rotateEnd).normalize();
angle *= cubeRotationSpeed;
quaternion.setFromAxisAngle(axis, angle);
}
return quaternion;
}
Finally in the handleRotation
method, setRotationFromQuaternion
is called on each of the selected cubes to change the position of the cube on the scene.
function handleRotation() {
rotateEndPoint = projectOnTrackball(deltaX, deltaY);
var rotateQuaternion = rotateMatrix(rotateStartPoint, rotateEndPoint);
cubes.forEach(function(cube) {
var cubeQuaternion = cube.quaternion;
cubeQuaternion.multiplyQuaternions(rotateQuaternion, cubeQuaternion);
cubeQuaternion.normalize();
cube.setRotationFromQuaternion(cubeQuaternion);
});
rotateEndPoint = rotateStartPoint;
}
-
Cube rotation was inspired by defmech's work on object rotation with quaternion angles. Repo
-
Referenced stemkoski's tutorial on three.js 'Mouse Click'. Repo
MIT. Copyright (c) Rewel Garcia