owl-project / NVISII

Apache License 2.0
319 stars 27 forks source link

AABB misaligned if entity is rotated #147

Open kevinfoley opened 2 years ago

kevinfoley commented 2 years ago

The axis-aligned bounding box (AABB) for an entity is not aligned correctly and does not fully encompass the entity if the entity is rotated.

Here, I have a gray cube that is rotated 45 degrees on each axis, and a transparent red cube used to visualize the AABB. Note that the AABB does not fully encompass the gray cube: aabb misaligned

Script to generate the above image:

"""Tests for axis-aligned bounding boxes (aabb)"""

import nvisii
from nvisii import entity, transform, mat3, mat4, camera, mesh, texture, material, light, vec3, quat
import asyncio
import math

def init():
    nvisii.initialize(headless=False, verbose=True)

    cam : entity = entity.create(
        name = "camera",
        transform = nvisii.transform.create("camera"),
        camera = nvisii.camera.create(
            name = "camera",  
            aspect = 1,
            field_of_view = 120
        )
    )

    cam.get_transform().set_position((6, -6, 0))
    nvisii.set_camera_entity(cam)
    rot = (0.6532814824381883, 0.2705980500730985, 0.2705980500730985, 0.6532814824381883)
    cam.get_transform().set_rotation(rot)

def add_cube(name:str, pos : vec3) -> entity:
    m : mesh = nvisii.mesh.create_box(name)
    e : entity = nvisii.entity.create(
        name= name,
        mesh = m,
        transform = nvisii.transform.create(name),
    )
    e.get_transform().set_position(pos)
    return e

async def run_tests():
    cube = add_cube("cube", (0,0,0))
    mat : material = nvisii.material.create("material")
    cube.set_material(mat)

    #rotated 45 degrees on each axis
    rot = (0.1913417161825449, 0.46193976625564337, 0.1913417161825449, 0.8446231986207332)
    cube.get_transform().set_rotation(rot)

    aabb_min = cube.get_min_aabb_corner()
    aabb_max = cube.get_max_aabb_corner()
    size = (aabb_max.x - aabb_min.x, aabb_max.y - aabb_min.y, aabb_max.z - aabb_min.z)

    mat2 : material = nvisii.material.create("bounding_box_vis")
    mat2.set_base_color((1, 0, 0))
    mat2.set_alpha(.5)
    bounding_box_vis =  add_cube("bounding_box_vis", cube.get_aabb_center())
    bounding_box_vis.set_material(mat2)
    #cube at default scale is 2x2x2m, so we divide by 2
    bounding_box_vis.get_transform().set_scale((size[0]/2, size[1]/2, size[2]/2))

    await asyncio.sleep(10)

init()
asyncio.run(run_tests())
kevinfoley commented 2 years ago

Note: this code produces exactly the same bounding box. This was the first approach that popped into my head, but it's not the correct way to calculate AABB for a rotated entity.

m : mesh = e.get_mesh()
mesh_min = m.get_min_aabb_corner()
mesh_max = m.get_max_aabb_corner()

tf : transform  = e.get_transform()
aabb_min = tf.transform_point(mesh_min)
aabb_max = tf.transform_point(mesh_max)
center = ((aabb_max.x + aabb_min.x) /2, (aabb_max.y + aabb_min.y) /2,(aabb_max.z + aabb_min.z) /2)

I think the correct method is to calculate all 8 corners of the mesh bounding box, transform all 8, then get the min and max X, Y, and Z from those 8 transformed points.

samueleruffino99 commented 1 year ago

Hello, can I ask you the correct procedure to get the correct 3d vertices in world coordinates and the projecting them back onto the image?