mathrustlinear-algebragame-developmentbevy

Trying to rotate a gizmo to be flush with one of the sides of a cube


I am making a small block builder with physics in Bevy, just for fun and for learning purposes. I guess my issue boils down to basically a math problem. I am trying to rotate the block placement indicator to be flush with the side of the cube I am raycasting against, but as the cube rotates the indicator (gizmo) doesnt rotate with the cube properly (obviously).

When creating a Quaternion like this:

if let Ok(xf) = block_q.get(*entity) {
        let face_center = xf.translation() + (hit.normal * 0.5);

        gizmos.rect(
            Transform::from_translation(face_center + hit.normal * 0.01)
                .with_rotation(Quat::from_rotation_arc(Vec3::Z, hit.normal.normalize()))
                .to_isometry(),
            Vec2::new(1.0, 1.0),
            Color::WHITE,
        );

        pos = Some(face_center);
    }

It rotates and is pointing with the normal, but not with the block itself. Example 1

And if i just use the rotation from the block itself, it rotates properly with the cube, but isnt pointing to the normal (obviously).

    if let Ok(xf) = block_q.get(*entity) {
        let face_center = xf.translation() + (hit.normal * 0.5);

        gizmos.rect(
            Transform::from_translation(face_center + hit.normal * 0.01)
                .with_rotation(xf.rotation())
                .to_isometry(),
            Vec2::new(1.0, 1.0),
            Color::WHITE,
        );

        pos = Some(face_center);
    }

Example 2

So my question is, is it possible to mathematically combine these quaternions to achieve my desired result, or is there a better approach to my problem? Multiplying the quaternions together did not work...

Desired result


Solution

  • I have to multiply the cubes rotation with the normal, but first repoint the starting direction of the gizmo (Z) with the cubes rotation by multiplying it:

        if let Ok(xf) = block_q.get(*entity) {
            let face_center = xf.translation() + (hit.normal * 0.5);
    
            let block_rotation = xf.rotation();
            let align_to_normal = Quat::from_rotation_arc(block_rotation * Vec3::Z, hit.normal.normalize());
            let final_rotation = align_to_normal * block_rotation;
    
            gizmos.rect(
                Transform::from_translation(face_center + hit.normal * 0.01) // lil offset for style
                    .with_rotation(final_rotation)
                    .to_isometry(),
                Vec2::new(1.0, 1.0),
                Color::WHITE,
            );
    
            pos = Some(face_center);
        }
    

    The GlobalTransform actually has a method that returns its own Z direction in global space called .back(). So block_rotation * Vec3::Z can be replaced with *xf.back().