blender/intern/cycles/util/math_quaternion.h
Sergey Sharybin 0636c92036 Cycles: Add Quaternion attribute type
The main goal is to bring a bit of structure to the use of quaternions
in the Cycles kernel.

Prior to this change there was no dedicated quaternion type and float4
was used instead, and quaternions are stored as (x, y, z, w) matching
naming between the quaternion and float4 fields. However, Blender uses
(w, x, y, z) quaternion order for attributes, which is different from
what Cycles uses. Without dedicated type this either leads to different
quaternion orders depending whether it comes from attribute, or makes
it intrinsically not possible to use implicit sharing.

This change introduces Cycles Quaternion type which is compatible with
Blender attribute math::Quaternion in both layout and alignment, making
it possible to benefit from implicit sharing and use a nice structure
in the kernel.

This change does not modify the existing quaternion usage in the kernel
which is currently used for transform decomposition and interpolation.

There is currently no SIMD for the Quaternion type, which allows to
avoid any special alignment requirement and share attributes with
Blender, but performance might not be ideal.

This attribute type will be used for representing gaussian splat
rotation.

The attribute access and interpolation matches behavior prior to this
change. Added some basic tests for quaternion attribute access for RGB
and alpha, mesh and volume attributes.

Ref #159470

Pull Request: https://projects.blender.org/blender/blender/pulls/163342
2026-09-02 09:08:34 +02:00

108 lines
3.1 KiB
C

/* SPDX-FileCopyrightText: 2011-2026 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "util/math_base.h"
#include "util/types_float3.h"
#include "util/types_float4.h"
#include "util/types_quaternion.h"
CCL_NAMESPACE_BEGIN
ccl_device_inline Quaternion operator-(const Quaternion q)
{
return make_quaternion(-q.w, -q.x, -q.y, -q.z);
}
ccl_device_inline Quaternion operator*(const float a, const Quaternion q)
{
return make_quaternion(a * q.w, a * q.x, a * q.y, a * q.z);
}
ccl_device_inline Quaternion operator*(const Quaternion q, const float a)
{
return make_quaternion(q.w * a, q.x * a, q.y * a, q.z * a);
}
ccl_device_inline Quaternion operator+(const Quaternion a, const Quaternion b)
{
return make_quaternion(a.w + b.w, a.x + b.x, a.y + b.y, a.z + b.z);
}
ccl_device_inline Quaternion operator-(const Quaternion a, const Quaternion b)
{
return make_quaternion(a.w - b.w, a.x - b.x, a.y - b.y, a.z - b.z);
}
ccl_device_inline ccl_private Quaternion &operator+=(ccl_private Quaternion &a, const Quaternion b)
{
a = a + b;
return a;
}
ccl_device_inline ccl_private Quaternion &operator-=(ccl_private Quaternion &a, const Quaternion b)
{
a = a - b;
return a;
}
ccl_device_inline float dot(const Quaternion a, const Quaternion b)
{
return a.w * b.w + a.x * b.x + a.y * b.y + a.z * b.z;
}
ccl_device_inline Quaternion normalized(const Quaternion q)
{
const float len = sqrtf(dot(q, q));
if (len != 0.0f) {
const float len_inv = 1.0f / len;
return make_quaternion(q.w * len_inv, q.x * len_inv, q.y * len_inv, q.z * len_inv);
}
return identity_quaternion();
}
/* Linear interpolation around the shortest angle.
* The caller is responsible for ensuring the input quaternions are normalized. */
ccl_device_inline Quaternion lerp(const Quaternion q1, const Quaternion q2, const float t)
{
/* TODO(sergey): Add assert that the quaternions are normalized? */
/* Ensure rotation around shortest angle, negated quaternions are the same. */
const Quaternion q2_shortest_angle = dot(q1, q2) >= 0.0f ? q2 : -q2;
return normalized((1.0f - t) * q1 + t * q2_shortest_angle);
}
ccl_device_template_spec Quaternion make_zero()
{
return make_quaternion(0.0f, 0.0f, 0.0f, 0.0f);
}
ccl_device_inline float3 make_float3(const Quaternion q)
{
/* Matches legacy storage and attribute access: make_float3(make_float4(q.w, q.x, q.y, q.z)) */
return make_float3(q.w, q.x, q.y);
}
ccl_device_inline float4 make_float4(const Quaternion q)
{
/* Matches legacy storage and attribute access: make_float4(q.w, q.x, q.y, q.z) */
return make_float4(q.w, q.x, q.y, q.z);
}
/* mix() on quaternions is used from generic attribute sampling and interpolation.
* Define it as a float4 mix for the compatibility until we can change the way quaternion
* attributes behave. */
ccl_device_inline Quaternion mix(const Quaternion a, const Quaternion b, const float t)
{
return a + t * (b - a);
}
ccl_device_inline void copy_v4_qt(ccl_private float *r, const Quaternion val)
{
r[0] = val.w;
r[1] = val.x;
r[2] = val.y;
r[3] = val.z;
}
CCL_NAMESPACE_END