Patryk
Patryk

Reputation: 55

Position child mesh based on local vectors in Three.js

I need help positioning mesh to specific points that are given according to local coordinates. I created pink cylinders with height 0.1 to demonstrate my local points of cubes that I want to make touch each other.

Also I want cube2 to be a child of cube1, but that is not a deal breaker for a moment.

Expected result: Cubes are touching with their specific corners and in proper rotation - pink cylinders should be pixel perfect covering each other.

I tried to solve it with:

cube2.position.copy(cube_position.clone().add(cube2_position))
cube2.rotation.setFromVector3(cube_rotation.clone().add(cube2_rotation))

But that's not working like expected. Should I use some kind of Matrix transform?

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(25, 25, 12);
// setup cubes

var geometry = new THREE.CubeGeometry(6, 6, 6);
var material = new THREE.MeshLambertMaterial({
  color: 0x00fff0
});
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);

cube.position.set(0.3, 1, -2);
cube.rotation.set(Math.PI / 3, Math.PI / 2, 1)

var geometry2 = new THREE.CubeGeometry(4, 4, 4);
var material2 = new THREE.MeshLambertMaterial({
  color: 0x0fff00
});
var cube2 = new THREE.Mesh(geometry2, material2);
cube.add(cube2);

cube_position = new THREE.Vector3(3, 3, 3)
cube_rotation = new THREE.Vector3(Math.PI / 3, Math.PI / 4, 0)
cube2_position = new THREE.Vector3(2, -2, 2)
cube2_rotation = new THREE.Vector3(Math.PI / 2, Math.PI / 2, 0)

// visualize points
vmat = new THREE.MeshLambertMaterial({
  color: 0xFF00FF,
  opacity: 0.5,
  transparent: true
});
v1 = new THREE.Mesh(new THREE.CylinderGeometry(1, 1, 0.1, 10), vmat);
cube.add(v1)
v1.position.copy(cube_position)
v1.rotation.setFromVector3(cube_rotation)

v2 = new THREE.Mesh(new THREE.CylinderGeometry(1, 1, 0.1, 10), vmat);
cube2.add(v2)
v2.position.copy(cube2_position)
v2.rotation.setFromVector3(cube2_rotation)

// BUG: connect cube with cube2 on specified points

cube2.position.copy(cube_position.clone().add(cube2_position))
cube2.rotation.setFromVector3(cube_rotation.clone().add(cube2_rotation))

// setup rest
var pointLight = new THREE.PointLight(0xFFFFFF);
pointLight.position.x = 10;
pointLight.position.y = 50;
pointLight.position.z = 130;
scene.add(pointLight)

const renderer = new THREE.WebGLRenderer({
  antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x20252f);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);

const controls = new THREE.OrbitControls(camera, renderer.domElement);

animate();

function animate() {
  requestAnimationFrame(animate);
  render();
}

function render() {
  renderer.render(scene, camera);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.2.3/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r82/three.js"></script>
<script src="https://yume.human-interactive.org/examples/buffer-geometry/OrbitControls.js"></script>

Upvotes: 4

Views: 904

Answers (1)

Craig.Li
Craig.Li

Reputation: 1366

So, Matrix is really powerful to solve rotation problems. I flow two steps to set the cube2 rotation and position properly.

  • Rotation

    The relationship between cube1,cube2,v1,v2 is: enter image description here What we want is v2 world rotation equals to v1 opposite world rotation. When two objects have same rotation, they have same rotation matrix(world matrix). We have these formulae:

    V1opposite.w = V2.w
    V1opposite.l * C1.w = V2.l * C2.l * C1.l
    V1opposite.l = V2.l * C2.l
    C2.l = V1opposite.l * V2.l(-1)
    

    Where V1opposite.w means v1 opposite world matrix, V2.w is v2 world matrix, C means cube, V2.l(-1) means the inverse matrix of v2 local matrix. now, we can calculate the cube2 rotation matrix.

  • Position

    What we need is making v1 and v2 world position same. Here is a graph to show the coordinate space:enter image description here Because v2 is a child of cube2, we need to move cube2. In the world space, we should translate the cube2 a distance from v2 to v1, now we get the cube2 world position, cube2 is a child of cube1, just apply the inverse matrix of cube1 to get the cube2 local position.

Here is a jsfiddle example.

Wish it helps.

Update

I wrote a function, you can use it without v1 and v2.

//connect obj1 and obj2 on obj1's point1 and obj2's point2.
function connect(obj1,obj2,point1,point2)
{
    var point1World = new THREE.Vector3().copy(point1).applyMatrix4(obj1.matrixWorld);
    var point2World = new THREE.Vector3().copy(point2).applyMatrix4(obj2.matrixWorld);
    var translation = new THREE.Vector3().subVectors(point1World,point2World);
    var obj2World = new THREE.Vector3().copy(obj2.getWorldPosition());
    obj2World.add(translation);
    if(obj2.parent !== null)
    {
        var inverseMatrix = new THREE.Matrix4().getInverse(obj2.parent.matrixWorld);
        obj2World.applyMatrix4(inverseMatrix);
    }
    obj2.position.copy(obj2World);
}

I also add a grandfather cube to contain cube1 and cube2. jsfiddle

Upvotes: 1

Related Questions