Path: blob/master/thirdparty/openxr/src/common/xr_linear.h
9903 views
// Copyright (c) 2017-2025 The Khronos Group Inc.1// Copyright (c) 2016, Oculus VR, LLC.2//3// SPDX-License-Identifier: Apache-2.04//5// Licensed under the Apache License, Version 2.0 (the "License");6// you may not use this file except in compliance with the License.7// You may obtain a copy of the License at8//9// http://www.apache.org/licenses/LICENSE-2.010//11// Unless required by applicable law or agreed to in writing, software12// distributed under the License is distributed on an "AS IS" BASIS,13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.14// See the License for the specific language governing permissions and15// limitations under the License.16//17// Author: J.M.P. van Waveren18//1920#ifndef XR_LINEAR_H_21#define XR_LINEAR_H_2223#include <openxr/openxr.h>2425/* REUSE-IgnoreStart */26/* The following has copyright notices that duplicate the header above */2728/*29================================================================================================3031Description : Vector, matrix and quaternion math.32Orig. Author : J.M.P. van Waveren33Orig. Date : 12/10/201634Language : C9935Copyright : Copyright (c) 2016 Oculus VR, LLC. All Rights reserved.363738DESCRIPTION39===========4041All matrices are column-major.4243INTERFACE44=========4546XrVector2f47XrVector3f48XrVector4f49XrQuaternionf50XrPosef51XrMatrix4x4f5253inline static void XrVector3f_Set(XrVector3f* v, const float value);54inline static void XrVector3f_Add(XrVector3f* result, const XrVector3f* a, const XrVector3f* b);55inline static void XrVector3f_Sub(XrVector3f* result, const XrVector3f* a, const XrVector3f* b);56inline static void XrVector3f_Min(XrVector3f* result, const XrVector3f* a, const XrVector3f* b);57inline static void XrVector3f_Max(XrVector3f* result, const XrVector3f* a, const XrVector3f* b);58inline static void XrVector3f_Decay(XrVector3f* result, const XrVector3f* a, const float value);59inline static void XrVector3f_Lerp(XrVector3f* result, const XrVector3f* a, const XrVector3f* b, const float fraction);60inline static void XrVector3f_Scale(XrVector3f* result, const XrVector3f* a, const float scaleFactor);61inline static void XrVector3f_Normalize(XrVector3f* v);62inline static float XrVector3f_Length(const XrVector3f* v);6364inline static void XrQuaternionf_CreateIdentity(XrQuaternionf* q);65inline static void XrQuaternionf_CreateFromAxisAngle(XrQuaternionf* result, const XrVector3f* axis, const float angleInRadians);66inline static void XrQuaternionf_Lerp(XrQuaternionf* result, const XrQuaternionf* a, const XrQuaternionf* b, const float fraction);67inline static void XrQuaternionf_Multiply(XrQuaternionf* result, const XrQuaternionf* a, const XrQuaternionf* b);68inline static void XrQuaternionf_Invert(XrQuaternionf* result, const XrQuaternionf* q);69inline static void XrQuaternionf_Normalize(XrQuaternionf* q);70inline static void XrQuaternionf_RotateVector3f(XrVector3f* result, const XrQuaternionf* a, const XrVector3f* v);7172inline static void XrPosef_CreateIdentity(XrPosef* result);73inline static void XrPosef_TransformVector3f(XrVector3f* result, const XrPosef* a, const XrVector3f* v);74inline static void XrPosef_Multiply(XrPosef* result, const XrPosef* a, const XrPosef* b);75inline static void XrPosef_Invert(XrPosef* result, const XrPosef* a);7677inline static void XrMatrix4x4f_CreateIdentity(XrMatrix4x4f* result);78inline static void XrMatrix4x4f_CreateTranslation(XrMatrix4x4f* result, const float x, const float y, const float z);79inline static void XrMatrix4x4f_CreateRotation(XrMatrix4x4f* result, const float degreesX, const float degreesY,80const float degreesZ);81inline static void XrMatrix4x4f_CreateScale(XrMatrix4x4f* result, const float x, const float y, const float z);82inline static void XrMatrix4x4f_CreateTranslationRotationScale(XrMatrix4x4f* result, const XrVector3f* translation,83const XrQuaternionf* rotation, const XrVector3f* scale);84inline static void XrMatrix4x4f_CreateFromRigidTransform(XrMatrix4x4f* result, const XrPosef* s);85inline static void XrMatrix4x4f_CreateProjection(XrMatrix4x4f* result, GraphicsAPI graphicsApi, const float tanAngleLeft,86const float tanAngleRight, const float tanAngleUp, float const tanAngleDown,87const float nearZ, const float farZ);88inline static void XrMatrix4x4f_CreateProjectionFov(XrMatrix4x4f* result, GraphicsAPI graphicsApi, const XrFovf fov,89const float nearZ, const float farZ);90inline static void XrMatrix4x4f_CreateFromQuaternion(XrMatrix4x4f* result, const XrQuaternionf* quat);91inline static void XrMatrix4x4f_CreateOffsetScaleForBounds(XrMatrix4x4f* result, const XrMatrix4x4f* matrix, const XrVector3f* mins,92const XrVector3f* maxs);9394inline static bool XrMatrix4x4f_IsAffine(const XrMatrix4x4f* matrix, const float epsilon);95inline static bool XrMatrix4x4f_IsOrthogonal(const XrMatrix4x4f* matrix, const float epsilon);96inline static bool XrMatrix4x4f_IsOrthonormal(const XrMatrix4x4f* matrix, const float epsilon);97inline static bool XrMatrix4x4f_IsRigidBody(const XrMatrix4x4f* matrix, const float epsilon);9899inline static void XrMatrix4x4f_GetTranslation(XrVector3f* result, const XrMatrix4x4f* src);100inline static void XrMatrix4x4f_GetRotation(XrQuaternionf* result, const XrMatrix4x4f* src);101inline static void XrMatrix4x4f_GetScale(XrVector3f* result, const XrMatrix4x4f* src);102103inline static void XrMatrix4x4f_Multiply(XrMatrix4x4f* result, const XrMatrix4x4f* a, const XrMatrix4x4f* b);104inline static void XrMatrix4x4f_Transpose(XrMatrix4x4f* result, const XrMatrix4x4f* src);105inline static void XrMatrix4x4f_Invert(XrMatrix4x4f* result, const XrMatrix4x4f* src);106inline static void XrMatrix4x4f_InvertRigidBody(XrMatrix4x4f* result, const XrMatrix4x4f* src);107108inline static void XrMatrix4x4f_TransformVector3f(XrVector3f* result, const XrMatrix4x4f* m, const XrVector3f* v);109inline static void XrMatrix4x4f_TransformVector4f(XrVector4f* result, const XrMatrix4x4f* m, const XrVector4f* v);110111inline static void XrMatrix4x4f_TransformBounds(XrVector3f* resultMins, XrVector3f* resultMaxs, const XrMatrix4x4f* matrix,112const XrVector3f* mins, const XrVector3f* maxs);113inline static bool XrMatrix4x4f_CullBounds(const XrMatrix4x4f* mvp, const XrVector3f* mins, const XrVector3f* maxs);114115================================================================================================116*/117118#include <assert.h>119#include <math.h>120#include <stdbool.h>121122#define MATH_PI 3.14159265358979323846f123124#define DEFAULT_NEAR_Z 0.015625f // exact floating point representation125#define INFINITE_FAR_Z 0.0f126127static const XrColor4f XrColorRed = {1.0f, 0.0f, 0.0f, 1.0f};128static const XrColor4f XrColorGreen = {0.0f, 1.0f, 0.0f, 1.0f};129static const XrColor4f XrColorBlue = {0.0f, 0.0f, 1.0f, 1.0f};130static const XrColor4f XrColorYellow = {1.0f, 1.0f, 0.0f, 1.0f};131static const XrColor4f XrColorPurple = {1.0f, 0.0f, 1.0f, 1.0f};132static const XrColor4f XrColorCyan = {0.0f, 1.0f, 1.0f, 1.0f};133static const XrColor4f XrColorLightGrey = {0.7f, 0.7f, 0.7f, 1.0f};134static const XrColor4f XrColorDarkGrey = {0.3f, 0.3f, 0.3f, 1.0f};135136typedef enum GraphicsAPI { GRAPHICS_VULKAN, GRAPHICS_OPENGL, GRAPHICS_OPENGL_ES, GRAPHICS_D3D, GRAPHICS_METAL } GraphicsAPI;137138// Column-major, pre-multiplied. This type does not exist in the OpenXR API and is provided for convenience.139typedef struct XrMatrix4x4f {140float m[16];141} XrMatrix4x4f;142143inline static float XrRcpSqrt(const float x) {144const float SMALLEST_NON_DENORMAL = 1.1754943508222875e-038f; // ( 1U << 23 )145const float rcp = (x >= SMALLEST_NON_DENORMAL) ? 1.0f / sqrtf(x) : 1.0f;146return rcp;147}148149inline static float XrVector2f_Length(const XrVector2f* v) { return sqrtf(v->x * v->x + v->y * v->y); }150151inline static void XrVector3f_Set(XrVector3f* v, const float value) {152v->x = value;153v->y = value;154v->z = value;155}156157inline static void XrVector3f_Add(XrVector3f* result, const XrVector3f* a, const XrVector3f* b) {158result->x = a->x + b->x;159result->y = a->y + b->y;160result->z = a->z + b->z;161}162163inline static void XrVector3f_Sub(XrVector3f* result, const XrVector3f* a, const XrVector3f* b) {164result->x = a->x - b->x;165result->y = a->y - b->y;166result->z = a->z - b->z;167}168169inline static void XrVector3f_Min(XrVector3f* result, const XrVector3f* a, const XrVector3f* b) {170result->x = (a->x < b->x) ? a->x : b->x;171result->y = (a->y < b->y) ? a->y : b->y;172result->z = (a->z < b->z) ? a->z : b->z;173}174175inline static void XrVector3f_Max(XrVector3f* result, const XrVector3f* a, const XrVector3f* b) {176result->x = (a->x > b->x) ? a->x : b->x;177result->y = (a->y > b->y) ? a->y : b->y;178result->z = (a->z > b->z) ? a->z : b->z;179}180181inline static void XrVector3f_Decay(XrVector3f* result, const XrVector3f* a, const float value) {182result->x = (fabsf(a->x) > value) ? ((a->x > 0.0f) ? (a->x - value) : (a->x + value)) : 0.0f;183result->y = (fabsf(a->y) > value) ? ((a->y > 0.0f) ? (a->y - value) : (a->y + value)) : 0.0f;184result->z = (fabsf(a->z) > value) ? ((a->z > 0.0f) ? (a->z - value) : (a->z + value)) : 0.0f;185}186187inline static void XrVector3f_Lerp(XrVector3f* result, const XrVector3f* a, const XrVector3f* b, const float fraction) {188result->x = a->x + fraction * (b->x - a->x);189result->y = a->y + fraction * (b->y - a->y);190result->z = a->z + fraction * (b->z - a->z);191}192193inline static void XrVector3f_Scale(XrVector3f* result, const XrVector3f* a, const float scaleFactor) {194result->x = a->x * scaleFactor;195result->y = a->y * scaleFactor;196result->z = a->z * scaleFactor;197}198199inline static float XrVector3f_Dot(const XrVector3f* a, const XrVector3f* b) { return a->x * b->x + a->y * b->y + a->z * b->z; }200201// Compute cross product, which generates a normal vector.202// Direction vector can be determined by right-hand rule: Pointing index finder in203// direction a and middle finger in direction b, thumb will point in Cross(a, b).204inline static void XrVector3f_Cross(XrVector3f* result, const XrVector3f* a, const XrVector3f* b) {205result->x = a->y * b->z - a->z * b->y;206result->y = a->z * b->x - a->x * b->z;207result->z = a->x * b->y - a->y * b->x;208}209210inline static void XrVector3f_Normalize(XrVector3f* v) {211const float lengthRcp = XrRcpSqrt(v->x * v->x + v->y * v->y + v->z * v->z);212v->x *= lengthRcp;213v->y *= lengthRcp;214v->z *= lengthRcp;215}216217inline static float XrVector3f_Length(const XrVector3f* v) { return sqrtf(v->x * v->x + v->y * v->y + v->z * v->z); }218219inline static void XrQuaternionf_CreateIdentity(XrQuaternionf* q) {220q->x = 0.0f;221q->y = 0.0f;222q->z = 0.0f;223q->w = 1.0f;224}225226inline static void XrQuaternionf_CreateFromAxisAngle(XrQuaternionf* result, const XrVector3f* axis, const float angleInRadians) {227float s = sinf(angleInRadians / 2.0f);228float lengthRcp = XrRcpSqrt(axis->x * axis->x + axis->y * axis->y + axis->z * axis->z);229result->x = s * axis->x * lengthRcp;230result->y = s * axis->y * lengthRcp;231result->z = s * axis->z * lengthRcp;232result->w = cosf(angleInRadians / 2.0f);233}234235inline static void XrQuaternionf_Lerp(XrQuaternionf* result, const XrQuaternionf* a, const XrQuaternionf* b, const float fraction) {236const float s = a->x * b->x + a->y * b->y + a->z * b->z + a->w * b->w;237const float fa = 1.0f - fraction;238const float fb = (s < 0.0f) ? -fraction : fraction;239const float x = a->x * fa + b->x * fb;240const float y = a->y * fa + b->y * fb;241const float z = a->z * fa + b->z * fb;242const float w = a->w * fa + b->w * fb;243const float lengthRcp = XrRcpSqrt(x * x + y * y + z * z + w * w);244result->x = x * lengthRcp;245result->y = y * lengthRcp;246result->z = z * lengthRcp;247result->w = w * lengthRcp;248}249250inline static void XrQuaternionf_Multiply(XrQuaternionf* result, const XrQuaternionf* a, const XrQuaternionf* b) {251result->x = (b->w * a->x) + (b->x * a->w) + (b->y * a->z) - (b->z * a->y);252result->y = (b->w * a->y) - (b->x * a->z) + (b->y * a->w) + (b->z * a->x);253result->z = (b->w * a->z) + (b->x * a->y) - (b->y * a->x) + (b->z * a->w);254result->w = (b->w * a->w) - (b->x * a->x) - (b->y * a->y) - (b->z * a->z);255}256257inline static void XrQuaternionf_Invert(XrQuaternionf* result, const XrQuaternionf* q) {258result->x = -q->x;259result->y = -q->y;260result->z = -q->z;261result->w = q->w;262}263264inline static void XrQuaternionf_Normalize(XrQuaternionf* q) {265const float lengthRcp = XrRcpSqrt(q->x * q->x + q->y * q->y + q->z * q->z + q->w * q->w);266q->x *= lengthRcp;267q->y *= lengthRcp;268q->z *= lengthRcp;269q->w *= lengthRcp;270}271272inline static void XrQuaternionf_RotateVector3f(XrVector3f* result, const XrQuaternionf* a, const XrVector3f* v) {273XrQuaternionf q = {v->x, v->y, v->z, 0.0f};274XrQuaternionf aq;275XrQuaternionf_Multiply(&aq, &q, a);276XrQuaternionf aInv;277XrQuaternionf_Invert(&aInv, a);278XrQuaternionf aqaInv;279XrQuaternionf_Multiply(&aqaInv, &aInv, &aq);280281result->x = aqaInv.x;282result->y = aqaInv.y;283result->z = aqaInv.z;284}285286inline static void XrPosef_CreateIdentity(XrPosef* result) {287XrQuaternionf_CreateIdentity(&result->orientation);288XrVector3f_Set(&result->position, 0);289}290291inline static void XrPosef_TransformVector3f(XrVector3f* result, const XrPosef* a, const XrVector3f* v) {292XrVector3f r0;293XrQuaternionf_RotateVector3f(&r0, &a->orientation, v);294XrVector3f_Add(result, &r0, &a->position);295}296297inline static void XrPosef_Multiply(XrPosef* result, const XrPosef* a, const XrPosef* b) {298XrQuaternionf_Multiply(&result->orientation, &b->orientation, &a->orientation);299XrPosef_TransformVector3f(&result->position, a, &b->position);300}301302inline static void XrPosef_Invert(XrPosef* result, const XrPosef* a) {303XrQuaternionf_Invert(&result->orientation, &a->orientation);304XrVector3f aPosNeg;305XrVector3f_Scale(&aPosNeg, &a->position, -1.0f);306XrQuaternionf_RotateVector3f(&result->position, &result->orientation, &aPosNeg);307}308309// Use left-multiplication to accumulate transformations.310inline static void XrMatrix4x4f_Multiply(XrMatrix4x4f* result, const XrMatrix4x4f* a, const XrMatrix4x4f* b) {311result->m[0] = a->m[0] * b->m[0] + a->m[4] * b->m[1] + a->m[8] * b->m[2] + a->m[12] * b->m[3];312result->m[1] = a->m[1] * b->m[0] + a->m[5] * b->m[1] + a->m[9] * b->m[2] + a->m[13] * b->m[3];313result->m[2] = a->m[2] * b->m[0] + a->m[6] * b->m[1] + a->m[10] * b->m[2] + a->m[14] * b->m[3];314result->m[3] = a->m[3] * b->m[0] + a->m[7] * b->m[1] + a->m[11] * b->m[2] + a->m[15] * b->m[3];315316result->m[4] = a->m[0] * b->m[4] + a->m[4] * b->m[5] + a->m[8] * b->m[6] + a->m[12] * b->m[7];317result->m[5] = a->m[1] * b->m[4] + a->m[5] * b->m[5] + a->m[9] * b->m[6] + a->m[13] * b->m[7];318result->m[6] = a->m[2] * b->m[4] + a->m[6] * b->m[5] + a->m[10] * b->m[6] + a->m[14] * b->m[7];319result->m[7] = a->m[3] * b->m[4] + a->m[7] * b->m[5] + a->m[11] * b->m[6] + a->m[15] * b->m[7];320321result->m[8] = a->m[0] * b->m[8] + a->m[4] * b->m[9] + a->m[8] * b->m[10] + a->m[12] * b->m[11];322result->m[9] = a->m[1] * b->m[8] + a->m[5] * b->m[9] + a->m[9] * b->m[10] + a->m[13] * b->m[11];323result->m[10] = a->m[2] * b->m[8] + a->m[6] * b->m[9] + a->m[10] * b->m[10] + a->m[14] * b->m[11];324result->m[11] = a->m[3] * b->m[8] + a->m[7] * b->m[9] + a->m[11] * b->m[10] + a->m[15] * b->m[11];325326result->m[12] = a->m[0] * b->m[12] + a->m[4] * b->m[13] + a->m[8] * b->m[14] + a->m[12] * b->m[15];327result->m[13] = a->m[1] * b->m[12] + a->m[5] * b->m[13] + a->m[9] * b->m[14] + a->m[13] * b->m[15];328result->m[14] = a->m[2] * b->m[12] + a->m[6] * b->m[13] + a->m[10] * b->m[14] + a->m[14] * b->m[15];329result->m[15] = a->m[3] * b->m[12] + a->m[7] * b->m[13] + a->m[11] * b->m[14] + a->m[15] * b->m[15];330}331332// Creates the transpose of the given matrix.333inline static void XrMatrix4x4f_Transpose(XrMatrix4x4f* result, const XrMatrix4x4f* src) {334result->m[0] = src->m[0];335result->m[1] = src->m[4];336result->m[2] = src->m[8];337result->m[3] = src->m[12];338339result->m[4] = src->m[1];340result->m[5] = src->m[5];341result->m[6] = src->m[9];342result->m[7] = src->m[13];343344result->m[8] = src->m[2];345result->m[9] = src->m[6];346result->m[10] = src->m[10];347result->m[11] = src->m[14];348349result->m[12] = src->m[3];350result->m[13] = src->m[7];351result->m[14] = src->m[11];352result->m[15] = src->m[15];353}354355// Returns a 3x3 minor of a 4x4 matrix.356inline static float XrMatrix4x4f_Minor(const XrMatrix4x4f* matrix, int r0, int r1, int r2, int c0, int c1, int c2) {357return matrix->m[4 * r0 + c0] *358(matrix->m[4 * r1 + c1] * matrix->m[4 * r2 + c2] - matrix->m[4 * r2 + c1] * matrix->m[4 * r1 + c2]) -359matrix->m[4 * r0 + c1] *360(matrix->m[4 * r1 + c0] * matrix->m[4 * r2 + c2] - matrix->m[4 * r2 + c0] * matrix->m[4 * r1 + c2]) +361matrix->m[4 * r0 + c2] *362(matrix->m[4 * r1 + c0] * matrix->m[4 * r2 + c1] - matrix->m[4 * r2 + c0] * matrix->m[4 * r1 + c1]);363}364365// Calculates the inverse of a 4x4 matrix.366inline static void XrMatrix4x4f_Invert(XrMatrix4x4f* result, const XrMatrix4x4f* src) {367const float rcpDet =3681.0f / (src->m[0] * XrMatrix4x4f_Minor(src, 1, 2, 3, 1, 2, 3) - src->m[1] * XrMatrix4x4f_Minor(src, 1, 2, 3, 0, 2, 3) +369src->m[2] * XrMatrix4x4f_Minor(src, 1, 2, 3, 0, 1, 3) - src->m[3] * XrMatrix4x4f_Minor(src, 1, 2, 3, 0, 1, 2));370371result->m[0] = XrMatrix4x4f_Minor(src, 1, 2, 3, 1, 2, 3) * rcpDet;372result->m[1] = -XrMatrix4x4f_Minor(src, 0, 2, 3, 1, 2, 3) * rcpDet;373result->m[2] = XrMatrix4x4f_Minor(src, 0, 1, 3, 1, 2, 3) * rcpDet;374result->m[3] = -XrMatrix4x4f_Minor(src, 0, 1, 2, 1, 2, 3) * rcpDet;375result->m[4] = -XrMatrix4x4f_Minor(src, 1, 2, 3, 0, 2, 3) * rcpDet;376result->m[5] = XrMatrix4x4f_Minor(src, 0, 2, 3, 0, 2, 3) * rcpDet;377result->m[6] = -XrMatrix4x4f_Minor(src, 0, 1, 3, 0, 2, 3) * rcpDet;378result->m[7] = XrMatrix4x4f_Minor(src, 0, 1, 2, 0, 2, 3) * rcpDet;379result->m[8] = XrMatrix4x4f_Minor(src, 1, 2, 3, 0, 1, 3) * rcpDet;380result->m[9] = -XrMatrix4x4f_Minor(src, 0, 2, 3, 0, 1, 3) * rcpDet;381result->m[10] = XrMatrix4x4f_Minor(src, 0, 1, 3, 0, 1, 3) * rcpDet;382result->m[11] = -XrMatrix4x4f_Minor(src, 0, 1, 2, 0, 1, 3) * rcpDet;383result->m[12] = -XrMatrix4x4f_Minor(src, 1, 2, 3, 0, 1, 2) * rcpDet;384result->m[13] = XrMatrix4x4f_Minor(src, 0, 2, 3, 0, 1, 2) * rcpDet;385result->m[14] = -XrMatrix4x4f_Minor(src, 0, 1, 3, 0, 1, 2) * rcpDet;386result->m[15] = XrMatrix4x4f_Minor(src, 0, 1, 2, 0, 1, 2) * rcpDet;387}388389// Calculates the inverse of a rigid body transform.390inline static void XrMatrix4x4f_InvertRigidBody(XrMatrix4x4f* result, const XrMatrix4x4f* src) {391result->m[0] = src->m[0];392result->m[1] = src->m[4];393result->m[2] = src->m[8];394result->m[3] = 0.0f;395result->m[4] = src->m[1];396result->m[5] = src->m[5];397result->m[6] = src->m[9];398result->m[7] = 0.0f;399result->m[8] = src->m[2];400result->m[9] = src->m[6];401result->m[10] = src->m[10];402result->m[11] = 0.0f;403result->m[12] = -(src->m[0] * src->m[12] + src->m[1] * src->m[13] + src->m[2] * src->m[14]);404result->m[13] = -(src->m[4] * src->m[12] + src->m[5] * src->m[13] + src->m[6] * src->m[14]);405result->m[14] = -(src->m[8] * src->m[12] + src->m[9] * src->m[13] + src->m[10] * src->m[14]);406result->m[15] = 1.0f;407}408409// Creates an identity matrix.410inline static void XrMatrix4x4f_CreateIdentity(XrMatrix4x4f* result) {411result->m[0] = 1.0f;412result->m[1] = 0.0f;413result->m[2] = 0.0f;414result->m[3] = 0.0f;415result->m[4] = 0.0f;416result->m[5] = 1.0f;417result->m[6] = 0.0f;418result->m[7] = 0.0f;419result->m[8] = 0.0f;420result->m[9] = 0.0f;421result->m[10] = 1.0f;422result->m[11] = 0.0f;423result->m[12] = 0.0f;424result->m[13] = 0.0f;425result->m[14] = 0.0f;426result->m[15] = 1.0f;427}428429// Creates a translation matrix.430inline static void XrMatrix4x4f_CreateTranslation(XrMatrix4x4f* result, const float x, const float y, const float z) {431result->m[0] = 1.0f;432result->m[1] = 0.0f;433result->m[2] = 0.0f;434result->m[3] = 0.0f;435result->m[4] = 0.0f;436result->m[5] = 1.0f;437result->m[6] = 0.0f;438result->m[7] = 0.0f;439result->m[8] = 0.0f;440result->m[9] = 0.0f;441result->m[10] = 1.0f;442result->m[11] = 0.0f;443result->m[12] = x;444result->m[13] = y;445result->m[14] = z;446result->m[15] = 1.0f;447}448449// Creates a rotation matrix.450// If -Z=forward, +Y=up, +X=right, then radiansX=pitch, radiansY=yaw, radiansZ=roll.451inline static void XrMatrix4x4f_CreateRotationRadians(XrMatrix4x4f* result, const float radiansX, const float radiansY,452const float radiansZ) {453const float sinX = sinf(radiansX);454const float cosX = cosf(radiansX);455const XrMatrix4x4f rotationX = {{1, 0, 0, 0, 0, cosX, sinX, 0, 0, -sinX, cosX, 0, 0, 0, 0, 1}};456const float sinY = sinf(radiansY);457const float cosY = cosf(radiansY);458const XrMatrix4x4f rotationY = {{cosY, 0, -sinY, 0, 0, 1, 0, 0, sinY, 0, cosY, 0, 0, 0, 0, 1}};459const float sinZ = sinf(radiansZ);460const float cosZ = cosf(radiansZ);461const XrMatrix4x4f rotationZ = {{cosZ, sinZ, 0, 0, -sinZ, cosZ, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}};462XrMatrix4x4f rotationXY;463XrMatrix4x4f_Multiply(&rotationXY, &rotationY, &rotationX);464XrMatrix4x4f_Multiply(result, &rotationZ, &rotationXY);465}466467// Creates a rotation matrix.468// If -Z=forward, +Y=up, +X=right, then degreesX=pitch, degreesY=yaw, degreesZ=roll.469inline static void XrMatrix4x4f_CreateRotation(XrMatrix4x4f* result, const float degreesX, const float degreesY,470const float degreesZ) {471XrMatrix4x4f_CreateRotationRadians(result, degreesX * (MATH_PI / 180.0f), degreesY * (MATH_PI / 180.0f),472degreesZ * (MATH_PI / 180.0f));473}474475// Creates a scale matrix.476inline static void XrMatrix4x4f_CreateScale(XrMatrix4x4f* result, const float x, const float y, const float z) {477result->m[0] = x;478result->m[1] = 0.0f;479result->m[2] = 0.0f;480result->m[3] = 0.0f;481result->m[4] = 0.0f;482result->m[5] = y;483result->m[6] = 0.0f;484result->m[7] = 0.0f;485result->m[8] = 0.0f;486result->m[9] = 0.0f;487result->m[10] = z;488result->m[11] = 0.0f;489result->m[12] = 0.0f;490result->m[13] = 0.0f;491result->m[14] = 0.0f;492result->m[15] = 1.0f;493}494495// Creates a matrix from a quaternion.496inline static void XrMatrix4x4f_CreateFromQuaternion(XrMatrix4x4f* result, const XrQuaternionf* quat) {497const float x2 = quat->x + quat->x;498const float y2 = quat->y + quat->y;499const float z2 = quat->z + quat->z;500501const float xx2 = quat->x * x2;502const float yy2 = quat->y * y2;503const float zz2 = quat->z * z2;504505const float yz2 = quat->y * z2;506const float wx2 = quat->w * x2;507const float xy2 = quat->x * y2;508const float wz2 = quat->w * z2;509const float xz2 = quat->x * z2;510const float wy2 = quat->w * y2;511512result->m[0] = 1.0f - yy2 - zz2;513result->m[1] = xy2 + wz2;514result->m[2] = xz2 - wy2;515result->m[3] = 0.0f;516517result->m[4] = xy2 - wz2;518result->m[5] = 1.0f - xx2 - zz2;519result->m[6] = yz2 + wx2;520result->m[7] = 0.0f;521522result->m[8] = xz2 + wy2;523result->m[9] = yz2 - wx2;524result->m[10] = 1.0f - xx2 - yy2;525result->m[11] = 0.0f;526527result->m[12] = 0.0f;528result->m[13] = 0.0f;529result->m[14] = 0.0f;530result->m[15] = 1.0f;531}532533// Creates a combined translation(rotation(scale(object))) matrix.534inline static void XrMatrix4x4f_CreateTranslationRotationScale(XrMatrix4x4f* result, const XrVector3f* translation,535const XrQuaternionf* rotation, const XrVector3f* scale) {536XrMatrix4x4f scaleMatrix;537XrMatrix4x4f_CreateScale(&scaleMatrix, scale->x, scale->y, scale->z);538539XrMatrix4x4f rotationMatrix;540XrMatrix4x4f_CreateFromQuaternion(&rotationMatrix, rotation);541542XrMatrix4x4f translationMatrix;543XrMatrix4x4f_CreateTranslation(&translationMatrix, translation->x, translation->y, translation->z);544545XrMatrix4x4f combinedMatrix;546XrMatrix4x4f_Multiply(&combinedMatrix, &rotationMatrix, &scaleMatrix);547XrMatrix4x4f_Multiply(result, &translationMatrix, &combinedMatrix);548}549550inline static void XrMatrix4x4f_CreateFromRigidTransform(XrMatrix4x4f* result, const XrPosef* s) {551const XrVector3f identityScale = {1.0f, 1.0f, 1.0f};552XrMatrix4x4f_CreateTranslationRotationScale(result, &s->position, &s->orientation, &identityScale);553}554555// Creates a projection matrix based on the specified dimensions.556// The projection matrix transforms -Z=forward, +Y=up, +X=right to the appropriate clip space for the graphics API.557// The far plane is placed at infinity if farZ <= nearZ.558// An infinite projection matrix is preferred for rasterization because, except for559// things *right* up against the near plane, it always provides better precision:560// "Tightening the Precision of Perspective Rendering"561// Paul Upchurch, Mathieu Desbrun562// Journal of Graphics Tools, Volume 16, Issue 1, 2012563inline static void XrMatrix4x4f_CreateProjection(XrMatrix4x4f* result, GraphicsAPI graphicsApi, const float tanAngleLeft,564const float tanAngleRight, const float tanAngleUp, float const tanAngleDown,565const float nearZ, const float farZ) {566const float tanAngleWidth = tanAngleRight - tanAngleLeft;567568// Set to tanAngleDown - tanAngleUp for a clip space with positive Y down (Vulkan).569// Set to tanAngleUp - tanAngleDown for a clip space with positive Y up (OpenGL / D3D / Metal).570const float tanAngleHeight = graphicsApi == GRAPHICS_VULKAN ? (tanAngleDown - tanAngleUp) : (tanAngleUp - tanAngleDown);571572// Set to nearZ for a [-1,1] Z clip space (OpenGL / OpenGL ES).573// Set to zero for a [0,1] Z clip space (Vulkan / D3D / Metal).574const float offsetZ = (graphicsApi == GRAPHICS_OPENGL || graphicsApi == GRAPHICS_OPENGL_ES) ? nearZ : 0;575576if (farZ <= nearZ) {577// place the far plane at infinity578result->m[0] = 2.0f / tanAngleWidth;579result->m[4] = 0.0f;580result->m[8] = (tanAngleRight + tanAngleLeft) / tanAngleWidth;581result->m[12] = 0.0f;582583result->m[1] = 0.0f;584result->m[5] = 2.0f / tanAngleHeight;585result->m[9] = (tanAngleUp + tanAngleDown) / tanAngleHeight;586result->m[13] = 0.0f;587588result->m[2] = 0.0f;589result->m[6] = 0.0f;590result->m[10] = -1.0f;591result->m[14] = -(nearZ + offsetZ);592593result->m[3] = 0.0f;594result->m[7] = 0.0f;595result->m[11] = -1.0f;596result->m[15] = 0.0f;597} else {598// normal projection599result->m[0] = 2.0f / tanAngleWidth;600result->m[4] = 0.0f;601result->m[8] = (tanAngleRight + tanAngleLeft) / tanAngleWidth;602result->m[12] = 0.0f;603604result->m[1] = 0.0f;605result->m[5] = 2.0f / tanAngleHeight;606result->m[9] = (tanAngleUp + tanAngleDown) / tanAngleHeight;607result->m[13] = 0.0f;608609result->m[2] = 0.0f;610result->m[6] = 0.0f;611result->m[10] = -(farZ + offsetZ) / (farZ - nearZ);612result->m[14] = -(farZ * (nearZ + offsetZ)) / (farZ - nearZ);613614result->m[3] = 0.0f;615result->m[7] = 0.0f;616result->m[11] = -1.0f;617result->m[15] = 0.0f;618}619}620621// Creates a projection matrix based on the specified FOV.622inline static void XrMatrix4x4f_CreateProjectionFov(XrMatrix4x4f* result, GraphicsAPI graphicsApi, const XrFovf fov,623const float nearZ, const float farZ) {624const float tanLeft = tanf(fov.angleLeft);625const float tanRight = tanf(fov.angleRight);626627const float tanDown = tanf(fov.angleDown);628const float tanUp = tanf(fov.angleUp);629630XrMatrix4x4f_CreateProjection(result, graphicsApi, tanLeft, tanRight, tanUp, tanDown, nearZ, farZ);631}632633// Creates a matrix that transforms the -1 to 1 cube to cover the given 'mins' and 'maxs' transformed with the given 'matrix'.634inline static void XrMatrix4x4f_CreateOffsetScaleForBounds(XrMatrix4x4f* result, const XrMatrix4x4f* matrix, const XrVector3f* mins,635const XrVector3f* maxs) {636const XrVector3f offset = {(maxs->x + mins->x) * 0.5f, (maxs->y + mins->y) * 0.5f, (maxs->z + mins->z) * 0.5f};637const XrVector3f scale = {(maxs->x - mins->x) * 0.5f, (maxs->y - mins->y) * 0.5f, (maxs->z - mins->z) * 0.5f};638639result->m[0] = matrix->m[0] * scale.x;640result->m[1] = matrix->m[1] * scale.x;641result->m[2] = matrix->m[2] * scale.x;642result->m[3] = matrix->m[3] * scale.x;643644result->m[4] = matrix->m[4] * scale.y;645result->m[5] = matrix->m[5] * scale.y;646result->m[6] = matrix->m[6] * scale.y;647result->m[7] = matrix->m[7] * scale.y;648649result->m[8] = matrix->m[8] * scale.z;650result->m[9] = matrix->m[9] * scale.z;651result->m[10] = matrix->m[10] * scale.z;652result->m[11] = matrix->m[11] * scale.z;653654result->m[12] = matrix->m[12] + matrix->m[0] * offset.x + matrix->m[4] * offset.y + matrix->m[8] * offset.z;655result->m[13] = matrix->m[13] + matrix->m[1] * offset.x + matrix->m[5] * offset.y + matrix->m[9] * offset.z;656result->m[14] = matrix->m[14] + matrix->m[2] * offset.x + matrix->m[6] * offset.y + matrix->m[10] * offset.z;657result->m[15] = matrix->m[15] + matrix->m[3] * offset.x + matrix->m[7] * offset.y + matrix->m[11] * offset.z;658}659660// Returns true if the given matrix is affine.661inline static bool XrMatrix4x4f_IsAffine(const XrMatrix4x4f* matrix, const float epsilon) {662return fabsf(matrix->m[3]) <= epsilon && fabsf(matrix->m[7]) <= epsilon && fabsf(matrix->m[11]) <= epsilon &&663fabsf(matrix->m[15] - 1.0f) <= epsilon;664}665666// Returns true if the given matrix is orthogonal.667inline static bool XrMatrix4x4f_IsOrthogonal(const XrMatrix4x4f* matrix, const float epsilon) {668for (int i = 0; i < 3; i++) {669for (int j = 0; j < 3; j++) {670if (i != j) {671if (fabsf(matrix->m[4 * i + 0] * matrix->m[4 * j + 0] + matrix->m[4 * i + 1] * matrix->m[4 * j + 1] +672matrix->m[4 * i + 2] * matrix->m[4 * j + 2]) > epsilon) {673return false;674}675if (fabsf(matrix->m[4 * 0 + i] * matrix->m[4 * 0 + j] + matrix->m[4 * 1 + i] * matrix->m[4 * 1 + j] +676matrix->m[4 * 2 + i] * matrix->m[4 * 2 + j]) > epsilon) {677return false;678}679}680}681}682return true;683}684685// Returns true if the given matrix is orthonormal.686inline static bool XrMatrix4x4f_IsOrthonormal(const XrMatrix4x4f* matrix, const float epsilon) {687for (int i = 0; i < 3; i++) {688for (int j = 0; j < 3; j++) {689const float kd = (i == j) ? 1.0f : 0.0f; // Kronecker delta690if (fabsf(kd - (matrix->m[4 * i + 0] * matrix->m[4 * j + 0] + matrix->m[4 * i + 1] * matrix->m[4 * j + 1] +691matrix->m[4 * i + 2] * matrix->m[4 * j + 2])) > epsilon) {692return false;693}694if (fabsf(kd - (matrix->m[4 * 0 + i] * matrix->m[4 * 0 + j] + matrix->m[4 * 1 + i] * matrix->m[4 * 1 + j] +695matrix->m[4 * 2 + i] * matrix->m[4 * 2 + j])) > epsilon) {696return false;697}698}699}700return true;701}702703// Returns true if the given matrix is a rigid body transform.704inline static bool XrMatrix4x4f_IsRigidBody(const XrMatrix4x4f* matrix, const float epsilon) {705return XrMatrix4x4f_IsAffine(matrix, epsilon) && XrMatrix4x4f_IsOrthonormal(matrix, epsilon);706}707708// Get the translation from a combined translation(rotation(scale(object))) matrix.709inline static void XrMatrix4x4f_GetTranslation(XrVector3f* result, const XrMatrix4x4f* src) {710assert(XrMatrix4x4f_IsAffine(src, 1e-4f));711assert(XrMatrix4x4f_IsOrthogonal(src, 1e-4f));712713result->x = src->m[12];714result->y = src->m[13];715result->z = src->m[14];716}717718// Get the rotation from a combined translation(rotation(scale(object))) matrix.719inline static void XrMatrix4x4f_GetRotation(XrQuaternionf* result, const XrMatrix4x4f* src) {720assert(XrMatrix4x4f_IsAffine(src, 1e-4f));721assert(XrMatrix4x4f_IsOrthogonal(src, 1e-4f));722723const float rcpScaleX = XrRcpSqrt(src->m[0] * src->m[0] + src->m[1] * src->m[1] + src->m[2] * src->m[2]);724const float rcpScaleY = XrRcpSqrt(src->m[4] * src->m[4] + src->m[5] * src->m[5] + src->m[6] * src->m[6]);725const float rcpScaleZ = XrRcpSqrt(src->m[8] * src->m[8] + src->m[9] * src->m[9] + src->m[10] * src->m[10]);726const float m[9] = {src->m[0] * rcpScaleX, src->m[1] * rcpScaleX, src->m[2] * rcpScaleX,727src->m[4] * rcpScaleY, src->m[5] * rcpScaleY, src->m[6] * rcpScaleY,728src->m[8] * rcpScaleZ, src->m[9] * rcpScaleZ, src->m[10] * rcpScaleZ};729if (m[0 * 3 + 0] + m[1 * 3 + 1] + m[2 * 3 + 2] > 0.0f) {730float t = +m[0 * 3 + 0] + m[1 * 3 + 1] + m[2 * 3 + 2] + 1.0f;731float s = XrRcpSqrt(t) * 0.5f;732result->w = s * t;733result->z = (m[0 * 3 + 1] - m[1 * 3 + 0]) * s;734result->y = (m[2 * 3 + 0] - m[0 * 3 + 2]) * s;735result->x = (m[1 * 3 + 2] - m[2 * 3 + 1]) * s;736} else if (m[0 * 3 + 0] > m[1 * 3 + 1] && m[0 * 3 + 0] > m[2 * 3 + 2]) {737float t = +m[0 * 3 + 0] - m[1 * 3 + 1] - m[2 * 3 + 2] + 1.0f;738float s = XrRcpSqrt(t) * 0.5f;739result->x = s * t;740result->y = (m[0 * 3 + 1] + m[1 * 3 + 0]) * s;741result->z = (m[2 * 3 + 0] + m[0 * 3 + 2]) * s;742result->w = (m[1 * 3 + 2] - m[2 * 3 + 1]) * s;743} else if (m[1 * 3 + 1] > m[2 * 3 + 2]) {744float t = -m[0 * 3 + 0] + m[1 * 3 + 1] - m[2 * 3 + 2] + 1.0f;745float s = XrRcpSqrt(t) * 0.5f;746result->y = s * t;747result->x = (m[0 * 3 + 1] + m[1 * 3 + 0]) * s;748result->w = (m[2 * 3 + 0] - m[0 * 3 + 2]) * s;749result->z = (m[1 * 3 + 2] + m[2 * 3 + 1]) * s;750} else {751float t = -m[0 * 3 + 0] - m[1 * 3 + 1] + m[2 * 3 + 2] + 1.0f;752float s = XrRcpSqrt(t) * 0.5f;753result->z = s * t;754result->w = (m[0 * 3 + 1] - m[1 * 3 + 0]) * s;755result->x = (m[2 * 3 + 0] + m[0 * 3 + 2]) * s;756result->y = (m[1 * 3 + 2] + m[2 * 3 + 1]) * s;757}758}759760// Get the scale from a combined translation(rotation(scale(object))) matrix.761inline static void XrMatrix4x4f_GetScale(XrVector3f* result, const XrMatrix4x4f* src) {762assert(XrMatrix4x4f_IsAffine(src, 1e-4f));763assert(XrMatrix4x4f_IsOrthogonal(src, 1e-4f));764765result->x = sqrtf(src->m[0] * src->m[0] + src->m[1] * src->m[1] + src->m[2] * src->m[2]);766result->y = sqrtf(src->m[4] * src->m[4] + src->m[5] * src->m[5] + src->m[6] * src->m[6]);767result->z = sqrtf(src->m[8] * src->m[8] + src->m[9] * src->m[9] + src->m[10] * src->m[10]);768}769770// Transforms a 3D vector.771inline static void XrMatrix4x4f_TransformVector3f(XrVector3f* result, const XrMatrix4x4f* m, const XrVector3f* v) {772const float w = m->m[3] * v->x + m->m[7] * v->y + m->m[11] * v->z + m->m[15];773const float rcpW = 1.0f / w;774result->x = (m->m[0] * v->x + m->m[4] * v->y + m->m[8] * v->z + m->m[12]) * rcpW;775result->y = (m->m[1] * v->x + m->m[5] * v->y + m->m[9] * v->z + m->m[13]) * rcpW;776result->z = (m->m[2] * v->x + m->m[6] * v->y + m->m[10] * v->z + m->m[14]) * rcpW;777}778779// Transforms a 4D vector.780inline static void XrMatrix4x4f_TransformVector4f(XrVector4f* result, const XrMatrix4x4f* m, const XrVector4f* v) {781result->x = m->m[0] * v->x + m->m[4] * v->y + m->m[8] * v->z + m->m[12] * v->w;782result->y = m->m[1] * v->x + m->m[5] * v->y + m->m[9] * v->z + m->m[13] * v->w;783result->z = m->m[2] * v->x + m->m[6] * v->y + m->m[10] * v->z + m->m[14] * v->w;784result->w = m->m[3] * v->x + m->m[7] * v->y + m->m[11] * v->z + m->m[15] * v->w;785}786787// Transforms the 'mins' and 'maxs' bounds with the given 'matrix'.788inline static void XrMatrix4x4f_TransformBounds(XrVector3f* resultMins, XrVector3f* resultMaxs, const XrMatrix4x4f* matrix,789const XrVector3f* mins, const XrVector3f* maxs) {790assert(XrMatrix4x4f_IsAffine(matrix, 1e-4f));791792const XrVector3f center = {(mins->x + maxs->x) * 0.5f, (mins->y + maxs->y) * 0.5f, (mins->z + maxs->z) * 0.5f};793const XrVector3f extents = {maxs->x - center.x, maxs->y - center.y, maxs->z - center.z};794const XrVector3f newCenter = {matrix->m[0] * center.x + matrix->m[4] * center.y + matrix->m[8] * center.z + matrix->m[12],795matrix->m[1] * center.x + matrix->m[5] * center.y + matrix->m[9] * center.z + matrix->m[13],796matrix->m[2] * center.x + matrix->m[6] * center.y + matrix->m[10] * center.z + matrix->m[14]};797const XrVector3f newExtents = {798fabsf(extents.x * matrix->m[0]) + fabsf(extents.y * matrix->m[4]) + fabsf(extents.z * matrix->m[8]),799fabsf(extents.x * matrix->m[1]) + fabsf(extents.y * matrix->m[5]) + fabsf(extents.z * matrix->m[9]),800fabsf(extents.x * matrix->m[2]) + fabsf(extents.y * matrix->m[6]) + fabsf(extents.z * matrix->m[10])};801XrVector3f_Sub(resultMins, &newCenter, &newExtents);802XrVector3f_Add(resultMaxs, &newCenter, &newExtents);803}804805// Returns true if the 'mins' and 'maxs' bounds is completely off to one side of the projection matrix.806inline static bool XrMatrix4x4f_CullBounds(const XrMatrix4x4f* mvp, const XrVector3f* mins, const XrVector3f* maxs) {807if (maxs->x <= mins->x && maxs->y <= mins->y && maxs->z <= mins->z) {808return false;809}810811XrVector4f c[8];812for (int i = 0; i < 8; i++) {813const XrVector4f corner = {(i & 1) != 0 ? maxs->x : mins->x, (i & 2) != 0 ? maxs->y : mins->y,814(i & 4) != 0 ? maxs->z : mins->z, 1.0f};815XrMatrix4x4f_TransformVector4f(&c[i], mvp, &corner);816}817818int i;819for (i = 0; i < 8; i++) {820if (c[i].x > -c[i].w) {821break;822}823}824if (i == 8) {825return true;826}827for (i = 0; i < 8; i++) {828if (c[i].x < c[i].w) {829break;830}831}832if (i == 8) {833return true;834}835836for (i = 0; i < 8; i++) {837if (c[i].y > -c[i].w) {838break;839}840}841if (i == 8) {842return true;843}844for (i = 0; i < 8; i++) {845if (c[i].y < c[i].w) {846break;847}848}849if (i == 8) {850return true;851}852for (i = 0; i < 8; i++) {853if (c[i].z > -c[i].w) {854break;855}856}857if (i == 8) {858return true;859}860for (i = 0; i < 8; i++) {861if (c[i].z < c[i].w) {862break;863}864}865return i == 8;866}867868#endif // XR_LINEAR_H_869870871