I want to match the rotation of a reference object in three.js. The object3d I want to rotate and the reference object might have different parents in different local transforms
Note: I'd normally ask what you have tried so far, because it sounds like a fairly easy thing to accomplish. But when I tried it myself, I wasn't able to make it work, and I had to jump through several hoops to make it work correctly.
In the example code below, I start with two Group
s, each containing a cube. Each Group
also has its own transformation/rotation. This makes the cubes initially face in different directions.
In the animate
function, you can see when the "Spin" checkbox is checked, I tear apart (decompose
) the redCube
's world matrix, saving its quaternion
(used for rotation) into tempQ
. I do the same thing to greenCube
. I then re-build (compose
) greenCube
's world matrix, using its original translation and scale, but using redCube
's quaternion. (And remember, this is the world quaternion, so it's not affected by local transformations.)
This works because I took a direct shortcut to the most important part of transforming the object: its world matrix. I disabled updating matrices across the board, which removes a lot of the convenience of three.js, but it allowed me to work directly with the world matrices without anything overwriting them.
Here's hoping someone can find a more elegant solution for you.
var renderer, scene, camera, controls, stats, redCube, greenCube, t, q, tempQ, s;
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight,
FOV = 35,
NEAR = 1,
FAR = 1000;
var doUpdates = false;
var spinBox = document.getElementById("spin");
spinBox.addEventListener("change", function(){
doUpdates = spinBox.checked;
});
function populateScene(){
var axis = new THREE.Vector3();
var pos = new THREE.Vector3();
var cubeGeo = new THREE.BoxBufferGeometry(10, 10, 10);
axis.set(Math.random(), Math.random(), Math.random());
pos.set(-10, 0, 0);
var p1 = new THREE.Group();
p1.matrixWorld.makeRotationAxis(axis, Math.random());
p1.matrixWorld.setPosition(pos);
redCube = new THREE.Mesh(cubeGeo, new THREE.MeshPhongMaterial({ color: "red" }));
p1.add(redCube);
redCube.updateMatrixWorld(true);
scene.add(p1);
axis.set(Math.random(), Math.random(), Math.random());
pos.set(10, 0, 0);
var p2 = new THREE.Group();
p2.matrixWorld.makeRotationAxis(axis, Math.random());
p2.matrixWorld.setPosition(pos);
greenCube = new THREE.Mesh(cubeGeo, new THREE.MeshPhongMaterial({ color: "green" }));
p2.add(greenCube);
greenCube.updateMatrixWorld(true);
scene.add(p2);
}
function init() {
document.body.style.backgroundColor = "slateGray";
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
document.body.appendChild(renderer.domElement);
document.body.style.overflow = "hidden";
document.body.style.margin = "0";
document.body.style.padding = "0";
scene = new THREE.Scene();
scene.autoUpdate = false;
camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
camera.position.z = 50;
scene.add(camera);
controls = new THREE.TrackballControls(camera, renderer.domElement);
controls.dynamicDampingFactor = 0.5;
controls.rotateSpeed = 3;
var light = new THREE.PointLight(0xffffff, 1, Infinity);
camera.add(light);
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0';
document.body.appendChild(stats.domElement);
resize();
window.onresize = resize;
populateScene();
t = new THREE.Vector3();
q = new THREE.Quaternion();
tempQ = new THREE.Quaternion();
s = new THREE.Vector3();
animate();
}
function resize() {
WIDTH = window.innerWidth;
HEIGHT = window.innerHeight;
if (renderer && camera && controls) {
renderer.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
controls.handleResize();
}
}
function render() {
renderer.render(scene, camera);
}
function animate() {
requestAnimationFrame(animate);
camera.updateMatrixWorld(true);
if(doUpdates){
redCube.rotation.x += 0.01;
redCube.rotation.y += 0.02;
redCube.rotation.z += 0.03;
redCube.updateMatrixWorld(true);
redCube.matrixWorld.decompose(t, tempQ, s);
greenCube.matrixWorld.decompose(t, q, s);
greenCube.matrixWorld.compose(t, tempQ, s);
}
render();
controls.update();
stats.update();
}
function threeReady() {
init();
}
(function () {
function addScript(url, callback) {
callback = callback || function () { };
var script = document.createElement("script");
script.addEventListener("load", callback);
script.setAttribute("src", url);
document.head.appendChild(script);
}
addScript("https://threejs.org/build/three.js", function () {
addScript("https://threejs.org/examples/js/controls/TrackballControls.js", function () {
addScript("https://threejs.org/examples/js/libs/stats.min.js", function () {
threeReady();
})
})
})
})();
<label style="display: block; float: right; width: 200px;">Spin:<input type="checkbox" id="spin" /></label>