Guides Transforms 3D Use Quaternions for 3D Rotation

Transforms 3D 3 min read Updated Apr 2026

Use Quaternions for 3D Rotation

Use this workflow when you want a 3D object or camera to rotate cleanly in code, especially when the object has a parent or when you want to combine several rotations together.

You do not need to abandon Euler angles completely. In Lenga, the normal workflow is:

  1. use Euler angles in the Inspector when that is the easiest way to author a starting orientation
  2. use Quaternion in code when you want safer 3D rotation math

What a Quaternion Is in Practice

In day-to-day Lenga use, a quaternion is simply a rotation value that is better suited to combining and applying 3D rotations than adding Euler angles together.

You will usually touch it through:

  • Transform::$localRotation
  • Transform::$rotation
  • Quaternion::fromEulerAngles(...)

When To Use Euler Angles

Euler angles are still useful when you want:

  • a readable starting rotation in the Inspector
  • a simple one-off rotation in degrees
  • to quickly see X, Y, and Z values while authoring

When To Use Quaternions

Use quaternions when you want:

  • to combine multiple 3D rotations safely
  • to rotate an object relative to its current orientation
  • to work with parented 3D objects
  • camera and aiming workflows that should not depend on Euler-angle addition

Step-by-Step Workflow

1. Start with a normal 3D object

Create or select a 3D object that already has a Transform.

You can still set its starting rotation in the Inspector using normal Euler fields.

2. Convert a familiar Euler rotation into a quaternion

use Lenga\Engine\Core\Quaternion;
use Lenga\Engine\Core\Vector3;

$yaw = Quaternion::fromEulerAngles(new Vector3(0.0, 90.0, 0.0));

This is the easiest way to begin if you are thinking in degrees.

3. Apply the quaternion to the transform

For a world rotation:

$this->transform->rotation = $yaw;

For a local rotation relative to the parent:

$this->transform->localRotation = $yaw;

4. Combine rotations

If you want to combine rotations, multiply them together.

$yaw = Quaternion::fromEulerAngles(new Vector3(0.0, 90.0, 0.0));
$pitch = Quaternion::fromEulerAngles(new Vector3(-15.0, 0.0, 0.0));

$this->transform->localRotation = $yaw->multiply($pitch);

This is the common pattern for things like turret aiming, camera pitch/yaw, and parented 3D props.

5. Use the transform directions

Once the transform has a rotation, its direction vectors follow that rotation.

$forward = $this->transform->forward;
$right = $this->transform->right;
$up = $this->transform->up;

These are useful for movement, aiming, and spawning projectiles in front of an object.

Example: Simple Camera Orbit Step

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Quaternion;
use Lenga\Engine\Core\Time;
use Lenga\Engine\Core\Vector3;

class OrbitCamera extends Behaviour
{
    public function update(): void
    {
        $yawDelta = Quaternion::fromEulerAngles(
            new Vector3(0.0, 45.0 * Time::deltaTime(), 0.0)
        );

        $this->transform->rotation = $yawDelta->multiply($this->transform->rotation);
    }
}

Inspector and Scene Interop

Lenga's Inspector remains mainly Euler-facing in this release.

That means:

  • you still edit the visible rotation fields as Euler angles
  • Transform composes 3D rotations internally using quaternion-backed math
  • scene data remains Inspector-friendly, while runtime rotation workflows can still use quaternions in code

Common Gotchas

  • rotation is world rotation. localRotation is rotation relative to the parent.
  • eulerAngles and localEulerAngles are still valid, but they are not the best way to combine complex 3D rotations in code.
  • Parented objects are exactly where quaternion-backed rotation becomes most useful. If a parent rotates, the child rotation workflow stays safer than plain Euler-angle addition.
  • forward in Lenga's 3D transform points along the object's rotated forward direction, so use that instead of guessing which axis to move along.