math3dgeometryrotationbabylonjs

How to calculate rotation for a box based on a set of four points?


I’m a bit new to working with 3D space and rotation. I have a list of four Vector3 points that represent the corners of a rectangle. What I want to do is use those points to create a box mesh that is rotated to exactly match the angle of rotation of the points.

Here is a babylonjs playground demo showing what I mean. In it you can see I’ve drawn a simple line mesh between the points. That is great and the rectangle drawn is at the expected angle given the data. I’ve also created a box mesh and configured its dimensions to match and placed its center point in the proper center of the points. So far so good, however I cannot figure out how to rotate the box so that it’s top face is parallel with the face of the rectangle.

https://playground.babylonjs.com/#SN5K8L#2

var createScene = function () {
    // This creates a basic Babylon Scene object (non-mesh)
    var scene = new BABYLON.Scene(engine);
    // This creates and positions a free camera (non-mesh)
    var target = new BABYLON.Vector3(1.5, 4, 0)
    var camera = new BABYLON.ArcRotateCamera("camera1", Math.PI / 2 + Math.PI, Math.PI / 4, 10, target, scene)
    // This attaches the camera to the canvas
    camera.attachControl(canvas, true);
    // This creates a light, aiming 0,1,0 - to the sky (non-mesh)
    var light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
    // Default intensity is 1. Let's dim the light a small amount
    light.intensity = 0.7;

    const axes = new BABYLON.AxesViewer(scene)

    const points = [
        new BABYLON.Vector3(1, 5, 1),
        new BABYLON.Vector3(2, 5, 1),
        new BABYLON.Vector3(2, 3, -1),
        new BABYLON.Vector3(1, 3, -1)
    ]

    const lines = BABYLON.MeshBuilder.CreateLines("lines", {
        points: [...points, points[0]] // add a duplicate of first point to close polygon
    }, scene)

    const centerPoint = new BABYLON.Vector3(
        (points[0].x + points[1].x + points[2].x + points[3].x) / 4,
        (points[0].y + points[1].y + points[2].y + points[3].y) / 4,
        (points[0].z + points[1].z + points[2].z + points[3].z) / 4
    )

    const width = Math.sqrt(
      Math.pow(points[1].x - points[0].x, 2) +
      Math.pow(points[1].y - points[0].y, 2) +
      Math.pow(points[1].z - points[0].z, 2)
    )

    const depth = Math.sqrt(
      Math.pow(points[2].x - points[1].x, 2) +
      Math.pow(points[2].y - points[1].y, 2) +
      Math.pow(points[2].z - points[1].z, 2)
    )

    const height = 0.15

    const box = BABYLON.CreateBox("box", { width, height, depth}, scene)
    box.position = centerPoint
    //box.rotation = ???

    return scene;
};

Solution

  • You can use Vector3.RotationFromAxis to compute the required rotation in Euler angles:

    const rotationAxisX = points[1].subtract(points[0])
    const rotationAxisZ = points[1].subtract(points[2])
    const rotationAxisY = rotationAxisZ.cross(rotationAxisX)
    
    // RotationFromAxis has the side effect of normalising the input vectors
    // so retrieve the box dimensions here
    const width = rotationAxisX.length()
    const depth = rotationAxisZ.length()
    const height = 0.15
    
    const rotationEuler = BABYLON.Vector3.RotationFromAxis(
        rotationAxisX, 
        rotationAxisY, 
        rotationAxisZ
    )
    
    const box = BABYLON.CreateBox("box", { width, height, depth}, scene)
    box.position = centerPoint
    box.rotation = rotationEuler
    

    enter image description here