feat(host): vendor PyroWave + minimal Granite subset as crates/pyrowave-sys
Phase 0 of design/pyrowave-codec-plan.md — the opt-in wired-LAN ultra-low- latency codec. Vendored at upstream 509e4f88 (API 0.4.0, Granite 44362775, volk + vulkan-headers pins in PUNKTFUNK-VENDOR.txt), pruned to the 6.6 MB the standalone no-renderer build needs; scripts/vendor-pyrowave.sh reproduces the tree (a pin bump is protocol-affecting, plan §4.2). build.rs drives the wrapper CMakeLists (static archives incl. a static C-API lib upstream only ships shared) + bindgen over pyrowave.h; Linux and Windows only, empty stub elsewhere (Apple gets a native Metal port, §4.7). Offline-safe by construction: no network, no system lib, vendored Vulkan headers — same model as the opus dep (flatpak builder has no network). Phase-0 validation on .21 (RTX 5070 Ti, driver 610.43.03): - upstream pyrowave-c-test + interop test (incl. dmabuf/DRM-modifier Vulkan<->Vulkan) pass, from the pristine AND the pruned tree - GPU kernel times at ~1.6 bpp noise: encode/decode 0.090/0.042 ms @800p, 0.146/0.067 @1080p, 0.226/0.103 @1440p, 0.477/0.201 @4K — order of magnitude under NVENC's 1-2 ms retrieve, CBR lands within ~100 B of target - cargo test -p pyrowave-sys green (static link + API-version pin check) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
add_granite_internal_lib(granite-math
|
||||
math.hpp math.cpp
|
||||
frustum.hpp frustum.cpp
|
||||
aabb.cpp aabb.hpp
|
||||
render_parameters.hpp
|
||||
interpolation.cpp interpolation.hpp
|
||||
muglm/muglm.cpp muglm/muglm.hpp
|
||||
muglm/muglm_impl.hpp muglm/matrix_helper.hpp
|
||||
transforms.cpp transforms.hpp
|
||||
simd.hpp simd_headers.hpp)
|
||||
|
||||
target_include_directories(granite-math PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
@@ -0,0 +1,56 @@
|
||||
/* Copyright (c) 2017-2026 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "aabb.hpp"
|
||||
#include <float.h>
|
||||
|
||||
namespace Granite
|
||||
{
|
||||
AABB AABB::transform(const mat4 &m) const
|
||||
{
|
||||
vec3 m0 = vec3(FLT_MAX);
|
||||
vec3 m1 = vec3(-FLT_MAX);
|
||||
|
||||
for (unsigned i = 0; i < 8; i++)
|
||||
{
|
||||
vec3 c = get_corner(i);
|
||||
vec4 t = m * vec4(c, 1.0f);
|
||||
vec3 v = t.xyz();
|
||||
m0 = min(v, m0);
|
||||
m1 = max(v, m1);
|
||||
}
|
||||
|
||||
return AABB(m0, m1);
|
||||
}
|
||||
|
||||
vec3 AABB::get_coord(float dx, float dy, float dz) const
|
||||
{
|
||||
return mix(minimum.v3, maximum.v3, vec3(dx, dy, dz));
|
||||
}
|
||||
|
||||
void AABB::expand(const AABB &aabb)
|
||||
{
|
||||
minimum.v3 = min(minimum.v3, aabb.minimum.v3);
|
||||
maximum.v3 = max(maximum.v3, aabb.maximum.v3);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/* Copyright (c) 2017-2026 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "math.hpp"
|
||||
#include "muglm/muglm_impl.hpp"
|
||||
|
||||
namespace Granite
|
||||
{
|
||||
class AABB
|
||||
{
|
||||
public:
|
||||
AABB(vec3 minimum_, vec3 maximum_)
|
||||
{
|
||||
minimum.v4 = vec4(minimum_, 1.0f);
|
||||
maximum.v4 = vec4(maximum_, 1.0f);
|
||||
}
|
||||
|
||||
AABB() = default;
|
||||
|
||||
vec3 get_coord(float dx, float dy, float dz) const;
|
||||
AABB transform(const mat4 &m) const;
|
||||
|
||||
void expand(const AABB &aabb);
|
||||
|
||||
const vec3 &get_minimum() const
|
||||
{
|
||||
return minimum.v3;
|
||||
}
|
||||
|
||||
const vec3 &get_maximum() const
|
||||
{
|
||||
return maximum.v3;
|
||||
}
|
||||
|
||||
const vec4 &get_minimum4() const
|
||||
{
|
||||
return minimum.v4;
|
||||
}
|
||||
|
||||
const vec4 &get_maximum4() const
|
||||
{
|
||||
return maximum.v4;
|
||||
}
|
||||
|
||||
vec4 &get_minimum4()
|
||||
{
|
||||
return minimum.v4;
|
||||
}
|
||||
|
||||
vec4 &get_maximum4()
|
||||
{
|
||||
return maximum.v4;
|
||||
}
|
||||
|
||||
vec3 get_corner(unsigned i) const
|
||||
{
|
||||
float x = i & 1 ? maximum.v3.x : minimum.v3.x;
|
||||
float y = i & 2 ? maximum.v3.y : minimum.v3.y;
|
||||
float z = i & 4 ? maximum.v3.z : minimum.v3.z;
|
||||
return vec3(x, y, z);
|
||||
}
|
||||
|
||||
vec3 get_center() const
|
||||
{
|
||||
return minimum.v3 + (maximum.v3 - minimum.v3) * vec3(0.5f);
|
||||
}
|
||||
|
||||
float get_radius() const
|
||||
{
|
||||
return 0.5f * distance(minimum.v3, maximum.v3);
|
||||
}
|
||||
|
||||
private:
|
||||
union
|
||||
{
|
||||
vec3 v3;
|
||||
vec4 v4;
|
||||
} minimum, maximum;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,423 @@
|
||||
# Modified SQUAD for non-uniform timestamps
|
||||
|
||||
The SQUAD algorithm is a well-known algorithm for smooth interpolation of rotations.
|
||||
|
||||
The standard and simple algorithm for rotation is SLERP,
|
||||
which ensures constant angular velocity over a given interpolation segment.
|
||||
However, the flaw of SLERP for camera interpolation is that the angular velocity
|
||||
is not continuous, and it will abruptly change on a new segment.
|
||||
This problem is solved by SQUAD, but in its naive implementation, the length of
|
||||
each segment must be uniform, otherwise the derivation fails.
|
||||
|
||||
I spent some time studying the underlying math and derived a formula that works for
|
||||
non-uniform timestamps as well.
|
||||
|
||||
## The standard runtime algorithm
|
||||
|
||||
In SQUAD, each key-frame point is represented as
|
||||
a quaternion q<sub>k</sub> at timestamp t<sub>k</sub>.
|
||||
At each timestamp, we also pre-compute a
|
||||
helper control point q<sup>c</sup><sub>k</sub>,
|
||||
which derivation will be explored further below.
|
||||
|
||||
We are given the implementation:
|
||||
|
||||
squad<sub>k</sub>(t) = slerp(slerp(q<sub>k</sub>, q<sub>k+1</sub>, t),
|
||||
slerp(q<sup>c</sup><sub>k</sub>, q<sup>c</sup><sub>k+1</sub>, t),
|
||||
2t(1 - t))
|
||||
|
||||
t is given here in the range [0, 1), and is computed by:
|
||||
|
||||
t = (T - t<sub>k</sub>) / (t<sub>k+1</sub> - t<sub>k</sub>)
|
||||
|
||||
where T is the global time for which to evaluate.
|
||||
An animation clip is stitched together by many such splines, one for each k.
|
||||
|
||||
### Analyze the expression
|
||||
|
||||
To perform further calculus on the squad(t) function, we can simplify to scalars.
|
||||
If we assume for the purposes of analysis that all
|
||||
the rotations have the same axis of rotation, we can
|
||||
rewrite squad(t) to a linear interpolation of rotation angle θ, which
|
||||
each key-frame now represents:
|
||||
|
||||
squad<sub>k</sub>(t) = lerp(lerp(θ<sub>k</sub>, θ<sub>k+1</sub>, t),
|
||||
lerp(θ<sup>c</sup><sub>k</sub>, θ<sup>c</sup><sub>k+1</sub>, t),
|
||||
2t(1 - t))
|
||||
|
||||
All these lerps are trivial expressions:
|
||||
|
||||
lerp(a, b, t) = (1 - t)a + tb
|
||||
|
||||
We can expand the expression and compute their first and second order derivatives.
|
||||
|
||||
v<sub>k</sub>(t) =
|
||||
(-3 + 8t - 6t<sup>2</sup>) θ<sub>k</sub> +
|
||||
(-1 - 4t + 6t<sup>2</sup>) θ<sub>k+1</sub> +
|
||||
(2 - 8t + 6t<sup>2</sup>) θ<sup>c</sup><sub>k</sub> +
|
||||
(4t - 6t<sup>2</sup>) θ<sup>c</sup><sub>k+1</sub>
|
||||
|
||||
a<sub>k</sub>(t) =
|
||||
(8 - 12t) θ<sub>k</sub> +
|
||||
(-4 + 12t) θ<sub>k+1</sub> +
|
||||
(-8 + 12t) θ<sup>c</sup><sub>k</sub> +
|
||||
(4 - 12t) θ<sup>c</sup><sub>k+1</sub>
|
||||
|
||||
It is important to note here that we derive with respect to the spline local parameter t.
|
||||
To obtain the absolute angular velocity and acceleration at time t, we need to apply chain rules:
|
||||
|
||||
V<sub>k</sub>(t) = v<sub>k</sub>(t) (dt / dT) = v<sub>k</sub>(t) / d<sub>k</sub>
|
||||
|
||||
where d<sub>k</sub> = t<sub>k+1</sub> - t<sub>k</sub>,
|
||||
and V<sub>k</sub>(t) is absolute angular velocity, dθ / dT.
|
||||
|
||||
Similarly, A<sub>k</sub>(t) = a<sub>k</sub>(t) / d<sub>k</sub><sup>2</sup> is
|
||||
absolute angular acceleration, d<sup>2</sup>θ / (dT)<sup>2</sup>. When d<sub>k</sub> is constant,
|
||||
all uses of d<sub>k</sub> cancel out,
|
||||
and this is the assumption various algorithms online make.
|
||||
To ensure first order continuity we must satisfy
|
||||
V<sub>k</sub>(1) = V<sub>k+1</sub>(0), or alternatively if d<sub>k</sub> is constant,
|
||||
v<sub>k</sub>(1) = v<sub>k+1</sub>(0). Similarly, if we want to ensure continuous second order derivative, we must
|
||||
satisfy A<sub>k</sub>(1) = A<sub>k+1</sub>(0).
|
||||
|
||||
**v<sub>k+1</sub>(0)** =
|
||||
-3θ<sub>k+1</sub> +
|
||||
1θ<sub>k+2</sub> +
|
||||
2θ<sup>c</sup><sub>k+1</sub> +
|
||||
0θ<sup>c</sup><sub>k+2</sub> =\
|
||||
**(θ<sub>k+2</sub> - θ<sub>k+1</sub>) -
|
||||
2(θ<sub>k+1</sub> - θ<sup>c</sup><sub>k+1</sub>)**
|
||||
|
||||
**v<sub>k</sub>(1)** =
|
||||
-1θ<sub>k</sub> +
|
||||
3θ<sub>k+1</sub> +
|
||||
0θ<sup>c</sup><sub>k</sub> -
|
||||
2θ<sup>c</sup><sub>k+1</sub> =\
|
||||
**(θ<sub>k+1</sub> - θ<sub>k</sub>) +
|
||||
2(θ<sub>k+1</sub> - θ<sup>c</sup><sub>k+1</sub>)**
|
||||
|
||||
**a<sub>k+1</sub>(0)** =
|
||||
8θ<sub>k+1</sub> -
|
||||
4θ<sub>k+2</sub> -
|
||||
8θ<sup>c</sup><sub>k+1</sub> +
|
||||
4θ<sup>c</sup><sub>k+2</sub> =\
|
||||
**8(θ<sub>k+1</sub> - θ<sup>c</sup><sub>k+1</sub>) -
|
||||
4(θ<sub>k+2</sub> - θ<sup>c</sup><sub>k+2</sub>)**
|
||||
|
||||
**a<sub>k</sub>(1)** =
|
||||
-4θ<sub>k</sub> +
|
||||
8θ<sub>k+1</sub> +
|
||||
4θ<sup>c</sup><sub>k</sub> -
|
||||
8θ<sup>c</sup><sub>k+1</sub> =\
|
||||
**8(θ<sub>k+1</sub> - θ<sup>c</sup><sub>k+1</sub>) -
|
||||
4(θ<sub>k</sub> - θ<sup>c</sup><sub>k</sub>)**
|
||||
|
||||
Based on these expressions, we can already intuit what the relationship
|
||||
between the control points and the key-frame points are. The difference expresses
|
||||
acceleration. With positive acceleration, the control points lags behind the key-frame, and vice versa.
|
||||
Looking at the velocity expressions, with positive acceleration, we also get larger velocity at t = 1 compared to t = 0,
|
||||
as expected.
|
||||
|
||||
To satisfy the velocity equations, we need to choose v<sub>k+1</sub>(0) = v<sub>k</sub>(1), so\
|
||||
(θ<sub>k+2</sub> - θ<sub>k+1</sub>) -
|
||||
2(θ<sub>k+1</sub> - θ<sup>c</sup><sub>k+1</sub>) =
|
||||
(θ<sub>k+1</sub> - θ<sub>k</sub>) +
|
||||
2(θ<sub>k+1</sub> - θ<sup>c</sup><sub>k+1</sub>)\
|
||||
(θ<sub>k+2</sub> - θ<sub>k+1</sub>) -
|
||||
(θ<sub>k+1</sub> - θ<sub>k</sub>) =
|
||||
4(θ<sub>k+1</sub> - θ<sup>c</sup><sub>k+1</sub>)\
|
||||
((θ<sub>k+2</sub> - θ<sub>k+1</sub>) -
|
||||
(θ<sub>k+1</sub> - θ<sub>k</sub>)) / 4 =
|
||||
θ<sub>k+1</sub> - θ<sup>c</sup><sub>k+1</sub>\
|
||||
θ<sub>k+1</sub> - ((θ<sub>k+2</sub> - θ<sub>k+1</sub>) -
|
||||
(θ<sub>k+1</sub> - θ<sub>k</sub>)) / 4 =
|
||||
**θ<sup>c</sup><sub>k+1</sub>**
|
||||
|
||||
(θ<sub>k+2</sub> - θ<sub>k+1</sub>) - (θ<sub>k+1</sub> - θ<sub>k</sub>)
|
||||
is quite recognizable and intuitive.
|
||||
This is the discrete measurement of acceleration at t<sub>k+1</sub>.
|
||||
|
||||
For simplicity of notation, we introduce the local delta,
|
||||
Δ<sub>k</sub> = θ<sub>k</sub> - θ<sup>c</sup><sub>k</sub>.
|
||||
We can now rewrite the equations in a more digestable form:
|
||||
|
||||
v<sub>k+1</sub>(0) =
|
||||
(θ<sub>k+2</sub> - θ<sub>k+1</sub>) - 2Δ<sub>k+1</sub>
|
||||
|
||||
v<sub>k</sub>(1) =
|
||||
(θ<sub>k+1</sub> - θ<sub>k</sub>) + 2Δ<sub>k+1</sub>
|
||||
|
||||
a<sub>k+1</sub>(0) =
|
||||
8Δ<sub>k+1</sub> - 4Δ<sub>k+2</sub>
|
||||
|
||||
a<sub>k</sub>(1) =
|
||||
8Δ<sub>k+1</sub> - 4Δ<sub>k</sub>
|
||||
|
||||
This equation will only yield a continuous acceleration if
|
||||
Δ<sub>k</sub> = Δ<sub>k+2</sub>, which is not guaranteed.
|
||||
However, we have the nice property that a constant acceleration will
|
||||
yield a constant a<sub>k</sub>(t) for any k equal to 4Δ<sub>k</sub>.
|
||||
As we deduced earlier, Δ<sub>k</sub> is 1/4th the measured discrete acceleration,
|
||||
so everything checks out. Continuous acceleration is a nice property,
|
||||
but not required for smooth camera motion.
|
||||
|
||||
## Going back to the quaternion domain
|
||||
|
||||
We have found expressions for the control points, but the derivation
|
||||
has been happening in the angular domain, we need to work with quaternions.
|
||||
|
||||
Here, articles online will usually begin talking about logarithms and exponential functions of quaternions
|
||||
which at first glance is pure non-sense, but it is actually fairly intuitive.
|
||||
It took me a while to understand what the hell the article authors were smoking at first.
|
||||
|
||||
The insight is that **multiplying** two quaternions **adds** their rotational angles.
|
||||
This is exactly the same as complex numbers, where multiplying two complex numbers
|
||||
add their angles.
|
||||
For logarithms of quaternions to work, we need to convert them to a number where
|
||||
adding the results will function similarly to angular addition. Taking the exponent
|
||||
should give us back the result.
|
||||
|
||||
q<sub>a</sub>q<sub>b</sub> = exp(ln(q<sub>a</sub>q<sub>b</sub>)) =
|
||||
exp(ln(q<sub>a</sub>) + ln(q<sub>b</sub>))
|
||||
|
||||
As an aside, this extension also allows us to reason about powers of quaternions, since
|
||||
ln(q<sub>a</sub><sup>c</sup>) = c⋅ln(q<sub>a</sub>).
|
||||
|
||||
The logarithm for a unit quaternion is computed as:
|
||||
|
||||
```
|
||||
// vec3 quat_log(q)
|
||||
|
||||
if (abs(q.w) > 0.9999f)
|
||||
return vec3(0.0f);
|
||||
else
|
||||
return normalize(q.as_vec4().xyz()) * acos(q.w);
|
||||
```
|
||||
|
||||
The main confusion for me here is that quaternion multiplication does not commute,
|
||||
but here the logarithm additions do. Subtraction might make more sense ...
|
||||
|
||||
```c++
|
||||
// compute_inner_control_point_delta(q, delta)
|
||||
|
||||
quat inv_q1 = conjugate(q1);
|
||||
quat delta_k = inv_q1 * q2; // q2 - q1
|
||||
quat delta_k_minus1 = inv_q1 * q0; // q0 - q1 = -(q1 - q0)
|
||||
vec3 delta_k_log = quat_log(delta_k);
|
||||
vec3 delta_k_minus1_log = quat_log(delta_k_minus1);
|
||||
vec3 delta = 0.25f * (delta_k_log + delta_k_minus1_log);
|
||||
return delta;
|
||||
```
|
||||
|
||||
The multiplication order seems somewhat arbitrary,
|
||||
and I cannot prove exactly why we have to do it like this, but I cribbed
|
||||
this part from the web. This document so far is trying to justify how it works.
|
||||
At the very least, we can see similarities with the original derivation.
|
||||
Here, `delta_k` and `delta_k_minus1` measure the velocities between key-frames.
|
||||
Multiplying is "addition", but multiplying by conjugate is "subtraction".
|
||||
By subtracting in the log-domain we can get an angular differential and measure acceleration.
|
||||
As expected, we also take 1/4th since the delta is 1/4th measured acceleration.
|
||||
|
||||
This delta is later used to construct the control point q<sup>c</sup><sub>k</sub>.
|
||||
|
||||
```c++
|
||||
// compute_inner_control_point(q, delta)
|
||||
|
||||
// Subtraction in angular domain.
|
||||
return q * quat_exp(-delta);
|
||||
```
|
||||
|
||||
which maps to the definition we made:
|
||||
|
||||
θ<sub>k</sub> - θ<sup>c</sup><sub>k</sub> =
|
||||
Δ<sub>k</sub> \
|
||||
**θ<sup>c</sup><sub>k</sub> =
|
||||
θ<sub>k</sub> - Δ<sub>k</sub>**
|
||||
|
||||
To my great surprise, this is actually delightfully simple.
|
||||
The logarithm is a vec3 where the direction is axis of rotation,
|
||||
and length is the angle θ. This is what allows us to add/subtract the rotations
|
||||
together. This style of expressing rotation is basically how we would
|
||||
express torque in physics, nice!
|
||||
|
||||
The exponent just inverts what the log did.
|
||||
We recover the angle by taking length of vector
|
||||
and rebuilding the quaternion from that.
|
||||
|
||||
```
|
||||
// quat quat_exp(q)
|
||||
|
||||
float l = dot(q, q);
|
||||
if (l < 0.000001f)
|
||||
{
|
||||
return quat(1.0f, 0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
float vlen = length(q);
|
||||
vec3 v = normalize(q) * sin(vlen);
|
||||
return quat(cos(vlen), v);
|
||||
}
|
||||
```
|
||||
|
||||
## Non-uniform time deltas d<sub>k</sub>
|
||||
|
||||
This is where it gets spicy and where I was initially stumped when
|
||||
attempting to implement SQUAD. When constructing arbitrary camera paths,
|
||||
it is helpful to be able to place key-frames at any timestamp.
|
||||
What unfortunately happens now is that velocities are no longer
|
||||
continuous over a spline boundary, because the splines are now swept at varying
|
||||
rates. We will need to re-derive the control points,
|
||||
based on V<sub>k</sub>(t), not v<sub>k</sub>(t), in angular domain.
|
||||
|
||||
V<sub>k+1</sub>(0) =
|
||||
((θ<sub>k+2</sub> - θ<sub>k+1</sub>) - 2Δ<sub>k+1</sub>) / d<sub>k+1</sub>
|
||||
|
||||
V<sub>k</sub>(1) =
|
||||
((θ<sub>k+1</sub> - θ<sub>k</sub>) + 2Δ<sub>k+1</sub>) / d<sub>k</sub>
|
||||
|
||||
To be able to solve this, we need to consider that Δ<sub>k+1</sub> need
|
||||
not be a single value.
|
||||
When evaluating spline k and k + 1, it can take different values as needed.
|
||||
This gives rise to the "incoming" and "outgoing" control points.
|
||||
The incoming control point delta is used when evaluating the previous spline.
|
||||
|
||||
V<sub>k+1</sub>(0) =
|
||||
((θ<sub>k+2</sub> - θ<sub>k+1</sub>) - 2Δ<sup>o</sup><sub>k+1</sub>) / d<sub>k+1</sub>
|
||||
|
||||
V<sub>k</sub>(1) =
|
||||
((θ<sub>k+1</sub> - θ<sub>k</sub>) + 2Δ<sup>i</sup><sub>k+1</sub>) / d<sub>k</sub>
|
||||
|
||||
The superscript o and i denote outgoing and incoming respectively.
|
||||
We can now solve this equation directly, similar to the derivation we did for constant d<sub>k</sub> earlier.
|
||||
|
||||
((θ<sub>k+2</sub> - θ<sub>k+1</sub>) - 2Δ<sup>o</sup><sub>k+1</sub>) / d<sub>k+1</sub> =
|
||||
((θ<sub>k+1</sub> - θ<sub>k</sub>) + 2Δ<sup>i</sup><sub>k+1</sub>) / d<sub>k</sub>
|
||||
|
||||
(θ<sub>k+2</sub> - θ<sub>k+1</sub>) / d<sub>k+1</sub> -
|
||||
2Δ<sup>o</sup><sub>k+1</sub> / d<sub>k+1</sub> =
|
||||
(θ<sub>k+1</sub> - θ<sub>k</sub>) / d<sub>k</sub> +
|
||||
2Δ<sup>i</sup><sub>k+1</sub> / d<sub>k</sub>
|
||||
|
||||
(θ<sub>k+2</sub> - θ<sub>k+1</sub>) / d<sub>k+1</sub> -
|
||||
(θ<sub>k+1</sub> - θ<sub>k</sub>) / d<sub>k</sub> -
|
||||
2Δ<sup>o</sup><sub>k+1</sub> / d<sub>k+1</sub> =
|
||||
2Δ<sup>i</sup><sub>k+1</sub> / d<sub>k</sub>
|
||||
|
||||
If we let the ratio r be d<sub>k</sub> / d<sub>k+1</sub>, we get
|
||||
|
||||
Δ<sup>i</sup><sub>k+1</sub> =
|
||||
((θ<sub>k+2</sub> - θ<sub>k+1</sub>)(d<sub>k</sub> / d<sub>k+1</sub>) -
|
||||
(θ<sub>k+1</sub> - θ<sub>k</sub>)) / 2 -
|
||||
Δ<sup>o</sup><sub>k+1</sub>(d<sub>k</sub> / d<sub>k+1</sub>)
|
||||
|
||||
Which shows that we can actually select the outgoing control point rather freely,
|
||||
and we can then use this formula to compensate the difference in
|
||||
d<sub>k</sub> in the incoming control point.
|
||||
|
||||
For the outgoing control point, we should modify the acceleration
|
||||
computation to be aware of different step rates. Basically,
|
||||
we normalize the discrete velocities in terms of global time T.
|
||||
There might be better ways of computing this, but, meh.
|
||||
From empiric testing, the result is pretty accurate.
|
||||
|
||||
```
|
||||
quat inv_q1 = conjugate(q1);
|
||||
quat delta_k = inv_q1 * q2; // q2 - q1
|
||||
quat delta_k_minus1 = inv_q1 * q0; // q0 - q1 = -(q1 - q0)
|
||||
vec3 delta_k_log = quat_log(delta_k);
|
||||
vec3 delta_k_minus1_log = quat_log(delta_k_minus1);
|
||||
|
||||
// We sample velocity at the center of the segment when taking the difference.
|
||||
// Future sample is at t = +1/2 dt
|
||||
// Past sample is at t = -1/2 dt
|
||||
float segment_time = 0.5f * (dt0 + dt1);
|
||||
vec3 absolute_accel = (delta_k_log / dt1 + delta_k_minus1_log / dt0) / segment_time;
|
||||
vec3 delta = (0.25f * dt1 * dt1) * absolute_accel;
|
||||
```
|
||||
|
||||
```
|
||||
// Computed from snippet above
|
||||
vec3 outgoing = tmp_spline_deltas[i];
|
||||
|
||||
float dt0 = new_linear_timestamps[i] - new_linear_timestamps[i - 1];
|
||||
float dt1 = i + 1 < n ? (new_linear_timestamps[i + 1] - new_linear_timestamps[i]) : dt0;
|
||||
float t_ratio = dt0 / dt1;
|
||||
|
||||
const quat &q0 = new_linear_values[i - 1];
|
||||
const quat &q1 = new_linear_values[i];
|
||||
const quat &q2 = i + 1 < n ? new_linear_values[i + 1] : q1;
|
||||
|
||||
quat q12 = conjugate(q1) * q2;
|
||||
quat q10 = conjugate(q1) * q0; // This is implicitly negated.
|
||||
vec3 delta_q12 = quat_log(q12);
|
||||
vec3 delta_q10 = quat_log(q10);
|
||||
|
||||
vec3 incoming = 0.5f * (t_ratio * delta_q12 + delta_q10) - t_ratio * outgoing;
|
||||
|
||||
spline_data[3 * spline + 0] = q1 * quat_exp(-incoming);
|
||||
spline_data[3 * spline + 1] = q1;
|
||||
spline_data[3 * spline + 2] = q1 * quat_exp(-outgoing);
|
||||
```
|
||||
|
||||
Each key-frame gets 3 values. This is very similar to the
|
||||
CUBICSPLINE formulation used in glTF.
|
||||
When evalulating the spline in runtime we look at indices
|
||||
3 * k + {1, 2, 3, 4}.
|
||||
|
||||
### Modified SQUAD function
|
||||
|
||||
squad<sub>k</sub>(t) = slerp(slerp(q<sub>k</sub>, q<sub>k+1</sub>, t),
|
||||
slerp(q<sub>k</sub>·quatExp(-Δ<sup>o</sup><sub>k</sub>),
|
||||
q<sub>k+1</sub>·quatExp(-Δ<sup>i</sup><sub>k+1</sub>), t),
|
||||
2t(1 - t))
|
||||
|
||||
Δ is in the log domain as we computed above with outgoing and incoming deltas.
|
||||
|
||||
## Verification
|
||||
|
||||
While doing this work, I also made a test bench of sorts to evaluate the results.
|
||||
I tested 4 different scenarios with scalars.
|
||||
|
||||
- Interpolate quadratic function with even timestamps. Should be 100% exact.
|
||||
- Interpolate quadratic function with uneven timestamps. Will have some error.
|
||||
- Interpolate cubic function with even timestamps. Expect some errors due to non-constant acceleration.
|
||||
- Interpolate cubic function with uneven timestamps. Expect some errors due to non-constant acceleration.
|
||||
|
||||
We want to validate:
|
||||
- Average error of reference function f(t) and interpolated result.
|
||||
- Continuity of measured first derivative (and second derivative).
|
||||
|
||||
### Quadratic
|
||||
|
||||
f(t) = 0.5t - 0.25t<sup>2</sup>
|
||||
|
||||
#### Even timestamps
|
||||
|
||||
Key frames placed at t = {0, 0.5, 1.0, 2.0, 2.5, 3.0}.\
|
||||
Perfect result. As expected.
|
||||
|
||||
#### Uneven timestamps
|
||||
Key frames placed at t = {0, 1.0, 1.8, 2.1, 2.9, 3.0, 4.2, 4.3, 5.0, 6.0}.\
|
||||
Average error: 0.008141\
|
||||
Continuous first derivative, discontinuous second derivative.
|
||||
|
||||
### Cubic
|
||||
|
||||
f(t) = 0.5t - 0.25t<sup>2</sup> + 0.25t<sup>3</sup>
|
||||
|
||||
#### Even timestamps
|
||||
Key frames placed at t = {0, 0.5, 1.0, 2.0, 2.5, 3.0}.\
|
||||
Average error: 0.00195\
|
||||
Continuous first derivative, discontinuous second derivative.
|
||||
|
||||
#### Uneven timestamps
|
||||
Key frames placed at t = {0, 0.5, 0.9, 1.1, 1.4, 1.5, 2.1, 2.2, 2.5, 3.0}.\
|
||||
Average error: 0.008285\
|
||||
Continuous first derivative, discontinuous second derivative.
|
||||
|
||||
### Summary
|
||||
|
||||
The more even timestamps we have, the more accurate the spline becomes.
|
||||
The error is also quite acceptable, and we see continuous first derivative,
|
||||
which is the critical part to get right.
|
||||
@@ -0,0 +1,155 @@
|
||||
/* Copyright (c) 2017-2026 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "frustum.hpp"
|
||||
|
||||
namespace Granite
|
||||
{
|
||||
|
||||
// For reference, should always use SIMD-version.
|
||||
bool Frustum::intersects_slow(const AABB &aabb) const
|
||||
{
|
||||
for (auto &plane : planes)
|
||||
{
|
||||
bool intersects_plane = false;
|
||||
for (unsigned i = 0; i < 8; i++)
|
||||
{
|
||||
if (dot(vec4(aabb.get_corner(i), 1.0f), plane) >= 0.0f)
|
||||
{
|
||||
intersects_plane = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!intersects_plane)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Frustum::intersects_sphere(const AABB &aabb) const
|
||||
{
|
||||
vec4 center(aabb.get_center(), 1.0f);
|
||||
float radius = aabb.get_radius();
|
||||
|
||||
for (auto &plane : planes)
|
||||
if (dot(plane, center) < -radius)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static constexpr float FarClipInfiniteClamp = 1e-10f;
|
||||
|
||||
vec3 Frustum::get_coord(float dx, float dy, float dz) const
|
||||
{
|
||||
dz = 1.0f - dz;
|
||||
|
||||
bool infinite_z = inv_view_projection[3][3] == 0.0f;
|
||||
if (infinite_z)
|
||||
dz = muglm::max<float>(dz, FarClipInfiniteClamp);
|
||||
|
||||
vec4 clip = vec4(2.0f * dx - 1.0f, 2.0f * dy - 1.0f, dz, 1.0f);
|
||||
clip = inv_view_projection * clip;
|
||||
return clip.xyz() / clip.w;
|
||||
}
|
||||
|
||||
vec4 Frustum::get_bounding_sphere(const mat4 &inv_projection, const mat4 &inv_view)
|
||||
{
|
||||
// Make sure that radius is numerically stable throughout, since we use that as a snapping factor potentially.
|
||||
// Use the inverse projection to create the radius.
|
||||
|
||||
const auto get_coord = [&](float x, float y, float z) -> vec3 {
|
||||
vec4 clip = vec4(x, y, z, 1.0f);
|
||||
clip = inv_projection * clip;
|
||||
return clip.xyz() / clip.w;
|
||||
};
|
||||
|
||||
vec3 center_near = get_coord(0.0f, 0.0f, 0.0f);
|
||||
vec3 center_far = get_coord(0.0f, 0.0f, 1.0f);
|
||||
|
||||
vec3 near_pos = get_coord(-1.0f, -1.0f, 0.0f);
|
||||
vec3 far_pos = get_coord(+1.0f, +1.0f, 1.0f);
|
||||
|
||||
float C = length(center_far - center_near);
|
||||
float N = dot(near_pos - center_near, near_pos - center_near);
|
||||
float F = dot(far_pos - center_far, far_pos - center_far);
|
||||
|
||||
// Solve the equation:
|
||||
// n^2 + x^2 == f^2 + (C - x)^2 =>
|
||||
// N + x^2 == F + C^2 - 2Cx + x^2.
|
||||
// x = (F - N + C^2) / 2C
|
||||
float center_distance = (F - N + C * C) / (2.0f * C);
|
||||
float radius = muglm::sqrt(center_distance * center_distance + N);
|
||||
vec3 view_space_center = center_near + center_distance * normalize(center_far - center_near);
|
||||
vec3 center = (inv_view * vec4(view_space_center, 1.0f)).xyz();
|
||||
return vec4(center, radius);
|
||||
}
|
||||
|
||||
void Frustum::build_planes(const mat4 &inv_view_projection_)
|
||||
{
|
||||
inv_view_projection = inv_view_projection_;
|
||||
bool infinite_z = inv_view_projection[3][3] == 0.0f;
|
||||
float far_clip_z = infinite_z ? FarClipInfiniteClamp : 0.0f;
|
||||
const vec4 tln(-1.0f, -1.0f, 1.0f, 1.0f);
|
||||
const vec4 bln(-1.0f, +1.0f, 1.0f, 1.0f);
|
||||
const vec4 blf(-1.0f, +1.0f, far_clip_z, 1.0f);
|
||||
const vec4 trn(+1.0f, -1.0f, 1.0f, 1.0f);
|
||||
const vec4 trf(+1.0f, -1.0f, far_clip_z, 1.0f);
|
||||
const vec4 brn(+1.0f, +1.0f, 1.0f, 1.0f);
|
||||
const vec4 brf(+1.0f, +1.0f, far_clip_z, 1.0f);
|
||||
const vec4 c(0.0f, 0.0f, 0.5f, 1.0f);
|
||||
|
||||
const auto project = [](const vec4 &v) {
|
||||
return v.xyz() / vec3(v.w);
|
||||
};
|
||||
|
||||
vec3 TLN = project(inv_view_projection * tln);
|
||||
vec3 BLN = project(inv_view_projection * bln);
|
||||
vec3 BLF = project(inv_view_projection * blf);
|
||||
vec3 TRN = project(inv_view_projection * trn);
|
||||
vec3 TRF = project(inv_view_projection * trf);
|
||||
vec3 BRN = project(inv_view_projection * brn);
|
||||
vec3 BRF = project(inv_view_projection * brf);
|
||||
vec4 center = inv_view_projection * c;
|
||||
|
||||
vec3 l = normalize(cross(BLF - BLN, TLN - BLN));
|
||||
vec3 r = normalize(cross(TRF - TRN, BRN - TRN));
|
||||
vec3 n = normalize(cross(BLN - BRN, TRN - BRN));
|
||||
vec3 f = normalize(cross(TRF - BRF, BLF - BRF));
|
||||
vec3 t = normalize(cross(TLN - TRN, TRF - TRN));
|
||||
vec3 b = normalize(cross(BRF - BRN, BLN - BRN));
|
||||
|
||||
planes[0] = vec4(l, -dot(l, BLN));
|
||||
planes[1] = vec4(r, -dot(r, TRN));
|
||||
planes[2] = vec4(n, -dot(n, BRN));
|
||||
planes[3] = infinite_z ? vec4(0.0f) : vec4(f, -dot(f, BRF));
|
||||
planes[4] = vec4(t, -dot(t, TRN));
|
||||
planes[5] = vec4(b, -dot(b, BRN));
|
||||
|
||||
// Winding order checks.
|
||||
for (auto &p : planes)
|
||||
if (dot(center, p) < 0.0f)
|
||||
p = -p;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/* Copyright (c) 2017-2026 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "math.hpp"
|
||||
#include "aabb.hpp"
|
||||
|
||||
namespace Granite
|
||||
{
|
||||
class Frustum
|
||||
{
|
||||
public:
|
||||
void build_planes(const mat4& inv_view_projection);
|
||||
bool intersects_sphere(const AABB &aabb) const;
|
||||
bool intersects_slow(const AABB &aabb) const;
|
||||
|
||||
vec3 get_coord(float dx, float dy, float dz) const;
|
||||
|
||||
static vec4 get_bounding_sphere(const mat4 &inv_projection, const mat4 &inv_view);
|
||||
|
||||
const vec4 *get_planes() const
|
||||
{
|
||||
return planes;
|
||||
}
|
||||
|
||||
private:
|
||||
vec4 planes[6];
|
||||
mat4 inv_view_projection;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/* Copyright (c) 2017-2026 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "interpolation.hpp"
|
||||
|
||||
namespace Granite
|
||||
{
|
||||
float catmull_rom_spline(float c0, float c1, float c2, float c3, float phase)
|
||||
{
|
||||
float phase2 = phase * phase;
|
||||
float phase3 = phase2 * phase;
|
||||
return c1 +
|
||||
(c2 - c0) * 0.5f * phase +
|
||||
(c0 - (2.5f * c1) + (2.0f * c2) - (0.5f * c3)) * phase2 +
|
||||
((-0.5f * c0) + (1.5f * c1) - (1.5f * c2) + (0.5f * c3)) * phase3;
|
||||
}
|
||||
|
||||
// Computes the analytic derivative of the spline dFd(phase).
|
||||
float catmull_rom_spline_gradient(float c0, float c1, float c2, float c3, float phase)
|
||||
{
|
||||
float phase2 = phase * phase;
|
||||
return (c2 - c0) * 0.5f +
|
||||
(c0 - (2.5f * c1) + (2.0f * c2) - (0.5f * c3)) * 2.0f * phase +
|
||||
((-0.5f * c0) + (1.5f * c1) - (1.5f * c2) + (0.5f * c3)) * 3.0f * phase2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/* Copyright (c) 2017-2026 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Granite
|
||||
{
|
||||
float catmull_rom_spline(float c0, float c1, float c2, float c3, float phase);
|
||||
float catmull_rom_spline_gradient(float c0, float c1, float c2, float c3, float phase);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/* Copyright (c) 2017-2026 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "math.hpp"
|
||||
#include "muglm/muglm_impl.hpp"
|
||||
|
||||
namespace Granite
|
||||
{
|
||||
void quantize_color(uint8_t *v, const vec4 &color)
|
||||
{
|
||||
for (unsigned i = 0; i < 4; i++)
|
||||
v[i] = uint8_t(muglm::round(muglm::clamp(color[i] * 255.0f, 0.0f, 255.0f)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/* Copyright (c) 2017-2026 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "muglm/muglm.hpp"
|
||||
|
||||
namespace Granite
|
||||
{
|
||||
using namespace muglm;
|
||||
void quantize_color(uint8_t *v, const vec4 &color);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/* Copyright (c) 2017-2026 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "muglm.hpp"
|
||||
#include <limits>
|
||||
|
||||
namespace muglm
|
||||
{
|
||||
mat4 mat4_cast(const quat &q);
|
||||
mat_affine mat_affine_cast(const quat &q);
|
||||
mat3 mat3_cast(const quat &q);
|
||||
mat4 translate(const vec3 &v);
|
||||
mat4 scale(const vec3 &v);
|
||||
mat_affine translate_affine(const vec3 &v);
|
||||
mat_affine scale_affine(const vec3 &v);
|
||||
mat2 inverse(const mat2 &m);
|
||||
mat3 inverse(const mat3 &m);
|
||||
mat4 inverse(const mat4 &m);
|
||||
|
||||
float determinant(const mat2 &m);
|
||||
float determinant(const mat3 &m);
|
||||
|
||||
constexpr float InfiniteFarPlane = std::numeric_limits<float>::max();
|
||||
mat4 perspective(float fovy, float aspect, float near, float far);
|
||||
mat4 frustum(float left, float right, float bottom, float top, float near, float far);
|
||||
|
||||
// Orthogonal projection cannot have infinite far-plane.
|
||||
mat4 ortho(float left, float right, float bottom, float top, float near, float far);
|
||||
|
||||
void decompose(const mat4 &m, vec3 &scale, quat &rot, vec3 &trans);
|
||||
}
|
||||
@@ -0,0 +1,460 @@
|
||||
/* Copyright (c) 2017-2026 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "matrix_helper.hpp"
|
||||
#include "muglm_impl.hpp"
|
||||
#include "simd_headers.hpp"
|
||||
|
||||
namespace muglm
|
||||
{
|
||||
mat3 mat3_cast(const quat &q_)
|
||||
{
|
||||
auto &q = q_.as_vec4();
|
||||
|
||||
mat3 res(1.0f);
|
||||
float qxx = q.x * q.x;
|
||||
float qyy = q.y * q.y;
|
||||
float qzz = q.z * q.z;
|
||||
float qxz = q.x * q.z;
|
||||
float qxy = q.x * q.y;
|
||||
float qyz = q.y * q.z;
|
||||
float qwx = q.w * q.x;
|
||||
float qwy = q.w * q.y;
|
||||
float qwz = q.w * q.z;
|
||||
|
||||
res[0][0] = 1.0f - 2.0f * (qyy + qzz);
|
||||
res[0][1] = 2.0f * (qxy + qwz);
|
||||
res[0][2] = 2.0f * (qxz - qwy);
|
||||
|
||||
res[1][0] = 2.0f * (qxy - qwz);
|
||||
res[1][1] = 1.0f - 2.0f * (qxx + qzz);
|
||||
res[1][2] = 2.0f * (qyz + qwx);
|
||||
|
||||
res[2][0] = 2.0f * (qxz + qwy);
|
||||
res[2][1] = 2.0f * (qyz - qwx);
|
||||
res[2][2] = 1.0f - 2.0f * (qxx + qyy);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
mat4 mat4_cast(const quat &q)
|
||||
{
|
||||
return mat4(mat3_cast(q));
|
||||
}
|
||||
|
||||
mat_affine mat_affine_cast(const quat &q)
|
||||
{
|
||||
return mat_affine(mat3_cast(q));
|
||||
}
|
||||
|
||||
mat4 translate(const vec3 &v)
|
||||
{
|
||||
return mat4(
|
||||
vec4(1.0f, 0.0f, 0.0f, 0.0f),
|
||||
vec4(0.0f, 1.0f, 0.0f, 0.0f),
|
||||
vec4(0.0f, 0.0f, 1.0f, 0.0f),
|
||||
vec4(v, 1.0f));
|
||||
}
|
||||
|
||||
mat4 scale(const vec3 &v)
|
||||
{
|
||||
return mat4(
|
||||
vec4(v.x, 0.0f, 0.0f, 0.0f),
|
||||
vec4(0.0f, v.y, 0.0f, 0.0f),
|
||||
vec4(0.0f, 0.0f, v.z, 0.0f),
|
||||
vec4(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
}
|
||||
|
||||
mat_affine translate_affine(const vec3 &v)
|
||||
{
|
||||
return mat_affine(
|
||||
vec4(1.0f, 0.0f, 0.0f, v.x),
|
||||
vec4(0.0f, 1.0f, 0.0f, v.y),
|
||||
vec4(0.0f, 0.0f, 1.0f, v.z));
|
||||
}
|
||||
|
||||
mat_affine scale_affine(const vec3 &v)
|
||||
{
|
||||
return mat_affine(
|
||||
vec4(v.x, 0.0f, 0.0f, 0.0f),
|
||||
vec4(0.0f, v.y, 0.0f, 0.0f),
|
||||
vec4(0.0f, 0.0f, v.z, 0.0f));
|
||||
}
|
||||
|
||||
float determinant(const mat2 &m)
|
||||
{
|
||||
return m[0][0] * m[1][1] - m[1][0] * m[0][1];
|
||||
}
|
||||
|
||||
mat2 inverse(const mat2 &m)
|
||||
{
|
||||
float OneOverDeterminant = 1.0f / determinant(m);
|
||||
|
||||
mat2 Inverse(
|
||||
vec2(m[1][1] * OneOverDeterminant,
|
||||
-m[0][1] * OneOverDeterminant),
|
||||
vec2(-m[1][0] * OneOverDeterminant,
|
||||
m[0][0] * OneOverDeterminant));
|
||||
|
||||
return Inverse;
|
||||
}
|
||||
|
||||
float determinant(const mat3 &m)
|
||||
{
|
||||
return m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2])
|
||||
- m[1][0] * (m[0][1] * m[2][2] - m[2][1] * m[0][2])
|
||||
+ m[2][0] * (m[0][1] * m[1][2] - m[1][1] * m[0][2]);
|
||||
}
|
||||
|
||||
mat3 inverse(const mat3 &m)
|
||||
{
|
||||
float OneOverDeterminant = 1.0f / determinant(m);
|
||||
|
||||
mat3 Inverse;
|
||||
Inverse[0][0] = +(m[1][1] * m[2][2] - m[2][1] * m[1][2]) * OneOverDeterminant;
|
||||
Inverse[1][0] = -(m[1][0] * m[2][2] - m[2][0] * m[1][2]) * OneOverDeterminant;
|
||||
Inverse[2][0] = +(m[1][0] * m[2][1] - m[2][0] * m[1][1]) * OneOverDeterminant;
|
||||
Inverse[0][1] = -(m[0][1] * m[2][2] - m[2][1] * m[0][2]) * OneOverDeterminant;
|
||||
Inverse[1][1] = +(m[0][0] * m[2][2] - m[2][0] * m[0][2]) * OneOverDeterminant;
|
||||
Inverse[2][1] = -(m[0][0] * m[2][1] - m[2][0] * m[0][1]) * OneOverDeterminant;
|
||||
Inverse[0][2] = +(m[0][1] * m[1][2] - m[1][1] * m[0][2]) * OneOverDeterminant;
|
||||
Inverse[1][2] = -(m[0][0] * m[1][2] - m[1][0] * m[0][2]) * OneOverDeterminant;
|
||||
Inverse[2][2] = +(m[0][0] * m[1][1] - m[1][0] * m[0][1]) * OneOverDeterminant;
|
||||
|
||||
return Inverse;
|
||||
}
|
||||
|
||||
mat4 inverse(const mat4 &m)
|
||||
{
|
||||
float Coef00 = m[2][2] * m[3][3] - m[3][2] * m[2][3];
|
||||
float Coef02 = m[1][2] * m[3][3] - m[3][2] * m[1][3];
|
||||
float Coef03 = m[1][2] * m[2][3] - m[2][2] * m[1][3];
|
||||
|
||||
float Coef04 = m[2][1] * m[3][3] - m[3][1] * m[2][3];
|
||||
float Coef06 = m[1][1] * m[3][3] - m[3][1] * m[1][3];
|
||||
float Coef07 = m[1][1] * m[2][3] - m[2][1] * m[1][3];
|
||||
|
||||
float Coef08 = m[2][1] * m[3][2] - m[3][1] * m[2][2];
|
||||
float Coef10 = m[1][1] * m[3][2] - m[3][1] * m[1][2];
|
||||
float Coef11 = m[1][1] * m[2][2] - m[2][1] * m[1][2];
|
||||
|
||||
float Coef12 = m[2][0] * m[3][3] - m[3][0] * m[2][3];
|
||||
float Coef14 = m[1][0] * m[3][3] - m[3][0] * m[1][3];
|
||||
float Coef15 = m[1][0] * m[2][3] - m[2][0] * m[1][3];
|
||||
|
||||
float Coef16 = m[2][0] * m[3][2] - m[3][0] * m[2][2];
|
||||
float Coef18 = m[1][0] * m[3][2] - m[3][0] * m[1][2];
|
||||
float Coef19 = m[1][0] * m[2][2] - m[2][0] * m[1][2];
|
||||
|
||||
float Coef20 = m[2][0] * m[3][1] - m[3][0] * m[2][1];
|
||||
float Coef22 = m[1][0] * m[3][1] - m[3][0] * m[1][1];
|
||||
float Coef23 = m[1][0] * m[2][1] - m[2][0] * m[1][1];
|
||||
|
||||
vec4 Fac0(Coef00, Coef00, Coef02, Coef03);
|
||||
vec4 Fac1(Coef04, Coef04, Coef06, Coef07);
|
||||
vec4 Fac2(Coef08, Coef08, Coef10, Coef11);
|
||||
vec4 Fac3(Coef12, Coef12, Coef14, Coef15);
|
||||
vec4 Fac4(Coef16, Coef16, Coef18, Coef19);
|
||||
vec4 Fac5(Coef20, Coef20, Coef22, Coef23);
|
||||
|
||||
vec4 Vec0(m[1][0], m[0][0], m[0][0], m[0][0]);
|
||||
vec4 Vec1(m[1][1], m[0][1], m[0][1], m[0][1]);
|
||||
vec4 Vec2(m[1][2], m[0][2], m[0][2], m[0][2]);
|
||||
vec4 Vec3(m[1][3], m[0][3], m[0][3], m[0][3]);
|
||||
|
||||
vec4 Inv0(Vec1 * Fac0 - Vec2 * Fac1 + Vec3 * Fac2);
|
||||
vec4 Inv1(Vec0 * Fac0 - Vec2 * Fac3 + Vec3 * Fac4);
|
||||
vec4 Inv2(Vec0 * Fac1 - Vec1 * Fac3 + Vec3 * Fac5);
|
||||
vec4 Inv3(Vec0 * Fac2 - Vec1 * Fac4 + Vec2 * Fac5);
|
||||
|
||||
vec4 SignA(+1, -1, +1, -1);
|
||||
vec4 SignB(-1, +1, -1, +1);
|
||||
mat4 Inverse(Inv0 * SignA, Inv1 * SignB, Inv2 * SignA, Inv3 * SignB);
|
||||
|
||||
vec4 Row0(Inverse[0][0], Inverse[1][0], Inverse[2][0], Inverse[3][0]);
|
||||
|
||||
vec4 Dot0(m[0] * Row0);
|
||||
float Dot1 = (Dot0.x + Dot0.y) + (Dot0.z + Dot0.w);
|
||||
|
||||
float OneOverDeterminant = 1.0f / Dot1;
|
||||
|
||||
return Inverse * OneOverDeterminant;
|
||||
}
|
||||
|
||||
void decompose(const mat4 &m, vec3 &scale, quat &rotation, vec3 &trans)
|
||||
{
|
||||
vec4 rot;
|
||||
|
||||
// Make a lot of assumptions.
|
||||
// We don't need skew, nor perspective.
|
||||
|
||||
// Isolate translation.
|
||||
trans = m[3].xyz();
|
||||
|
||||
vec3 cols[3];
|
||||
cols[0] = m[0].xyz();
|
||||
cols[1] = m[1].xyz();
|
||||
cols[2] = m[2].xyz();
|
||||
|
||||
scale.x = length(cols[0]);
|
||||
scale.y = length(cols[1]);
|
||||
scale.z = length(cols[2]);
|
||||
|
||||
// Isolate scale.
|
||||
cols[0] /= scale.x;
|
||||
cols[1] /= scale.y;
|
||||
cols[2] /= scale.z;
|
||||
|
||||
vec3 pdum3 = cross(cols[1], cols[2]);
|
||||
if (dot(cols[0], pdum3) < 0.0f)
|
||||
{
|
||||
scale = -scale;
|
||||
cols[0] = -cols[0];
|
||||
cols[1] = -cols[1];
|
||||
cols[2] = -cols[2];
|
||||
}
|
||||
|
||||
int i, j, k = 0;
|
||||
float root, trace = cols[0].x + cols[1].y + cols[2].z;
|
||||
if (trace > 0.0f)
|
||||
{
|
||||
root = sqrt(trace + 1.0f);
|
||||
rot.w = 0.5f * root;
|
||||
root = 0.5f / root;
|
||||
rot.x = root * (cols[1].z - cols[2].y);
|
||||
rot.y = root * (cols[2].x - cols[0].z);
|
||||
rot.z = root * (cols[0].y - cols[1].x);
|
||||
}
|
||||
else
|
||||
{
|
||||
static const int Next[3] = {1, 2, 0};
|
||||
|
||||
i = 0;
|
||||
if (cols[1].y > cols[0].x) i = 1;
|
||||
if (cols[2].z > cols[i][i]) i = 2;
|
||||
|
||||
j = Next[i];
|
||||
k = Next[j];
|
||||
|
||||
root = sqrt(cols[i][i] - cols[j][j] - cols[k][k] + 1.0f);
|
||||
|
||||
rot[i] = 0.5f * root;
|
||||
root = 0.5f / root;
|
||||
rot[j] = root * (cols[i][j] + cols[j][i]);
|
||||
rot[k] = root * (cols[i][k] + cols[k][i]);
|
||||
rot.w = root * (cols[j][k] - cols[k][j]);
|
||||
}
|
||||
|
||||
rotation = quat(rot);
|
||||
}
|
||||
|
||||
mat4 ortho(float left, float right, float bottom, float top, float near, float far)
|
||||
{
|
||||
mat4 result(1.0f);
|
||||
result[0][0] = 2.0f / (right - left);
|
||||
result[1][1] = 2.0f / (top - bottom);
|
||||
result[3][0] = -(right + left) / (right - left);
|
||||
result[3][1] = -(top + bottom) / (top - bottom);
|
||||
|
||||
result[2][2] = 1.0f / (far - near);
|
||||
result[3][2] = 1.0f + near / (far - near);
|
||||
|
||||
result[0].y *= -1.0f;
|
||||
result[1].y *= -1.0f;
|
||||
result[2].y *= -1.0f;
|
||||
result[3].y *= -1.0f;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
mat4 frustum(float left, float right, float bottom, float top, float near, float far)
|
||||
{
|
||||
mat4 result(0.0f);
|
||||
result[0][0] = (2.0f * near) / (right - left);
|
||||
result[1][1] = (2.0f * near) / (top - bottom);
|
||||
result[2][0] = (right + left) / (right - left);
|
||||
result[2][1] = (top + bottom) / (top - bottom);
|
||||
|
||||
// Inverse Z
|
||||
if (far == InfiniteFarPlane)
|
||||
{
|
||||
result[3][2] = -near;
|
||||
}
|
||||
else
|
||||
{
|
||||
result[2][2] = -1.0f - far / (near - far);
|
||||
result[3][2] = -(far * near) / (near - far);
|
||||
}
|
||||
|
||||
result[2][3] = -1.0f;
|
||||
|
||||
// Y-flip so we don't have to bother with negative viewport heights.
|
||||
result[0].y *= -1.0f;
|
||||
result[1].y *= -1.0f;
|
||||
result[2].y *= -1.0f;
|
||||
result[3].y *= -1.0f;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
mat4 perspective(float fovy, float aspect, float near, float far)
|
||||
{
|
||||
float tanHalfFovy = tan(fovy / 2.0f);
|
||||
|
||||
mat4 result(0.0f);
|
||||
result[0][0] = 1.0f / (aspect * tanHalfFovy);
|
||||
result[1][1] = 1.0f / (tanHalfFovy);
|
||||
|
||||
// Inverse Z
|
||||
if (far == InfiniteFarPlane)
|
||||
{
|
||||
result[3][2] = near;
|
||||
}
|
||||
else
|
||||
{
|
||||
result[2][2] = -1.0f - far / (near - far);
|
||||
result[3][2] = -(far * near) / (near - far);
|
||||
}
|
||||
|
||||
result[2][3] = -1.0f;
|
||||
|
||||
// Y-flip so we don't have to bother with negative viewport heights.
|
||||
result[0].y *= -1.0f;
|
||||
result[1].y *= -1.0f;
|
||||
result[2].y *= -1.0f;
|
||||
result[3].y *= -1.0f;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void transpose(mat4 &dst, const mat4 &src)
|
||||
{
|
||||
#if __SSE__
|
||||
__m128 r0 = _mm_loadu_ps(src[0].data);
|
||||
__m128 r1 = _mm_loadu_ps(src[1].data);
|
||||
__m128 r2 = _mm_loadu_ps(src[2].data);
|
||||
__m128 r3 = _mm_loadu_ps(src[3].data);
|
||||
_MM_TRANSPOSE4_PS(r0, r1, r2, r3);
|
||||
_mm_storeu_ps(dst[0].data, r0);
|
||||
_mm_storeu_ps(dst[1].data, r1);
|
||||
_mm_storeu_ps(dst[2].data, r2);
|
||||
_mm_storeu_ps(dst[3].data, r3);
|
||||
#elif defined(__ARM_NEON)
|
||||
float32x4x4_t a = vld4q_f32(src[0].data);
|
||||
vst1q_f32(dst[0].data, a.val[0]);
|
||||
vst1q_f32(dst[1].data, a.val[1]);
|
||||
vst1q_f32(dst[2].data, a.val[2]);
|
||||
vst1q_f32(dst[3].data, a.val[3]);
|
||||
#else
|
||||
dst = transpose(src);
|
||||
#endif
|
||||
}
|
||||
|
||||
void transpose_to_affine(vec4 dst[3], const mat4 &src)
|
||||
{
|
||||
#if __SSE__
|
||||
__m128 r0 = _mm_loadu_ps(src[0].data);
|
||||
__m128 r1 = _mm_loadu_ps(src[1].data);
|
||||
__m128 r2 = _mm_loadu_ps(src[2].data);
|
||||
__m128 r3 = _mm_loadu_ps(src[3].data);
|
||||
_MM_TRANSPOSE4_PS(r0, r1, r2, r3);
|
||||
_mm_storeu_ps(dst[0].data, r0);
|
||||
_mm_storeu_ps(dst[1].data, r1);
|
||||
_mm_storeu_ps(dst[2].data, r2);
|
||||
#elif defined(__ARM_NEON)
|
||||
float32x4x4_t a = vld4q_f32(src[0].data);
|
||||
vst1q_f32(dst[0].data, a.val[0]);
|
||||
vst1q_f32(dst[1].data, a.val[1]);
|
||||
vst1q_f32(dst[2].data, a.val[2]);
|
||||
#else
|
||||
mat4 m = transpose(src);
|
||||
for (int i = 0; i < 3; i++)
|
||||
dst[i] = m[i];
|
||||
#endif
|
||||
}
|
||||
|
||||
void transpose_from_affine(mat4 &dst, const vec4 src[3])
|
||||
{
|
||||
#if __SSE__
|
||||
__m128 r0 = _mm_loadu_ps(src[0].data);
|
||||
__m128 r1 = _mm_loadu_ps(src[1].data);
|
||||
__m128 r2 = _mm_loadu_ps(src[2].data);
|
||||
__m128 r3 = _mm_set_ps(1, 0, 0, 0);
|
||||
_MM_TRANSPOSE4_PS(r0, r1, r2, r3);
|
||||
_mm_storeu_ps(dst[0].data, r0);
|
||||
_mm_storeu_ps(dst[1].data, r1);
|
||||
_mm_storeu_ps(dst[2].data, r2);
|
||||
_mm_storeu_ps(dst[3].data, r3);
|
||||
#elif defined(__ARM_NEON)
|
||||
alignas(16) static const float r3_data[] = { 0, 0, 0, 1 };
|
||||
float32x4_t r0 = vld1q_f32(src[0].data);
|
||||
float32x4_t r1 = vld1q_f32(src[1].data);
|
||||
float32x4_t r2 = vld1q_f32(src[2].data);
|
||||
float32x4_t r3 = vld1q_f32(r3_data);
|
||||
float32x4x4_t r = { r0, r1, r2, r3 };
|
||||
vst4q_f32(dst[0].data, r);
|
||||
#else
|
||||
mat4 m = transpose(src);
|
||||
for (int i = 0; i < 3; i++)
|
||||
dst[i] = m[i];
|
||||
#endif
|
||||
}
|
||||
|
||||
void mat_affine::to_mat4(muglm::mat4 &m) const
|
||||
{
|
||||
transpose_from_affine(m, vec);
|
||||
}
|
||||
|
||||
mat4 mat_affine::to_mat4() const
|
||||
{
|
||||
mat4 m;
|
||||
to_mat4(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
float mat_affine::get_uniform_scale() const
|
||||
{
|
||||
return length(vec[0].xyz());
|
||||
}
|
||||
|
||||
vec3 mat_affine::get_translation() const
|
||||
{
|
||||
// this * vec4(0, 0, 0, 1)
|
||||
return { vec[0].w, vec[1].w, vec[2].w };
|
||||
}
|
||||
|
||||
vec3 mat_affine::get_forward() const
|
||||
{
|
||||
// this * vec4(0, 0, -1, 0).
|
||||
return { -vec[0].z, -vec[1].z, -vec[2].z };
|
||||
}
|
||||
|
||||
vec3 mat_affine::get_right() const
|
||||
{
|
||||
return { vec[0].x, vec[1].x, vec[2].x };
|
||||
}
|
||||
|
||||
vec3 mat_affine::get_up() const
|
||||
{
|
||||
return { vec[0].y, vec[1].y, vec[2].y };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,993 @@
|
||||
/* Copyright (c) 2017-2026 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace muglm
|
||||
{
|
||||
template <typename T> struct tvec2;
|
||||
template <typename T> struct tvec3;
|
||||
template <typename T> struct tvec4;
|
||||
template <typename T> struct tmat2;
|
||||
template <typename T> struct tmat3;
|
||||
template <typename T> struct tmat4;
|
||||
|
||||
template <typename T>
|
||||
struct tvec2
|
||||
{
|
||||
tvec2() = default;
|
||||
tvec2(const tvec2 &) = default;
|
||||
tvec2 &operator=(const tvec2 &) = default;
|
||||
|
||||
explicit inline tvec2(T v) noexcept
|
||||
{
|
||||
x = v;
|
||||
y = v;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
explicit inline tvec2(const tvec2<U> &u) noexcept
|
||||
{
|
||||
x = T(u.x);
|
||||
y = T(u.y);
|
||||
}
|
||||
|
||||
inline tvec2(T x_, T y_) noexcept
|
||||
{
|
||||
x = x_;
|
||||
y = y_;
|
||||
}
|
||||
|
||||
union
|
||||
{
|
||||
T data[2];
|
||||
struct
|
||||
{
|
||||
T x, y;
|
||||
};
|
||||
};
|
||||
|
||||
inline T &operator[](size_t index)
|
||||
{
|
||||
return data[index];
|
||||
}
|
||||
|
||||
inline const T &operator[](size_t index) const
|
||||
{
|
||||
return data[index];
|
||||
}
|
||||
|
||||
inline tvec2 xx() const;
|
||||
inline tvec2 xy() const;
|
||||
inline tvec2 yx() const;
|
||||
inline tvec2 yy() const;
|
||||
|
||||
inline tvec3<T> xxx() const;
|
||||
inline tvec3<T> xxy() const;
|
||||
inline tvec3<T> xyx() const;
|
||||
inline tvec3<T> xyy() const;
|
||||
inline tvec3<T> yxx() const;
|
||||
inline tvec3<T> yxy() const;
|
||||
inline tvec3<T> yyx() const;
|
||||
inline tvec3<T> yyy() const;
|
||||
|
||||
inline tvec4<T> xxxx() const;
|
||||
inline tvec4<T> xxxy() const;
|
||||
inline tvec4<T> xxyx() const;
|
||||
inline tvec4<T> xxyy() const;
|
||||
inline tvec4<T> xyxx() const;
|
||||
inline tvec4<T> xyxy() const;
|
||||
inline tvec4<T> xyyx() const;
|
||||
inline tvec4<T> xyyy() const;
|
||||
inline tvec4<T> yxxx() const;
|
||||
inline tvec4<T> yxxy() const;
|
||||
inline tvec4<T> yxyx() const;
|
||||
inline tvec4<T> yxyy() const;
|
||||
inline tvec4<T> yyxx() const;
|
||||
inline tvec4<T> yyxy() const;
|
||||
inline tvec4<T> yyyx() const;
|
||||
inline tvec4<T> yyyy() const;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct tvec3
|
||||
{
|
||||
tvec3() = default;
|
||||
tvec3(const tvec3 &) = default;
|
||||
tvec3 &operator=(const tvec3 &) = default;
|
||||
|
||||
template <typename U>
|
||||
explicit inline tvec3(const tvec3<U> &u) noexcept
|
||||
{
|
||||
x = T(u.x);
|
||||
y = T(u.y);
|
||||
z = T(u.z);
|
||||
}
|
||||
|
||||
inline tvec3(const tvec2<T> &a, T b) noexcept
|
||||
{
|
||||
x = a.x;
|
||||
y = a.y;
|
||||
z = b;
|
||||
}
|
||||
|
||||
inline tvec3(T a, const tvec2<T> &b) noexcept
|
||||
{
|
||||
x = a;
|
||||
y = b.x;
|
||||
z = b.y;
|
||||
}
|
||||
|
||||
explicit inline tvec3(T v) noexcept
|
||||
{
|
||||
x = v;
|
||||
y = v;
|
||||
z = v;
|
||||
}
|
||||
|
||||
inline tvec3(T x_, T y_, T z_) noexcept
|
||||
{
|
||||
x = x_;
|
||||
y = y_;
|
||||
z = z_;
|
||||
}
|
||||
|
||||
union
|
||||
{
|
||||
T data[3];
|
||||
struct
|
||||
{
|
||||
T x, y, z;
|
||||
};
|
||||
};
|
||||
|
||||
inline T &operator[](size_t index)
|
||||
{
|
||||
return data[index];
|
||||
}
|
||||
|
||||
inline const T &operator[](size_t index) const
|
||||
{
|
||||
return data[index];
|
||||
}
|
||||
|
||||
inline tvec2<T> xx() const;
|
||||
inline tvec2<T> xy() const;
|
||||
inline tvec2<T> xz() const;
|
||||
inline tvec2<T> yx() const;
|
||||
inline tvec2<T> yy() const;
|
||||
inline tvec2<T> yz() const;
|
||||
inline tvec2<T> zx() const;
|
||||
inline tvec2<T> zy() const;
|
||||
inline tvec2<T> zz() const;
|
||||
|
||||
inline tvec3<T> xxx() const;
|
||||
inline tvec3<T> xxy() const;
|
||||
inline tvec3<T> xxz() const;
|
||||
inline tvec3<T> xyx() const;
|
||||
inline tvec3<T> xyy() const;
|
||||
inline tvec3<T> xyz() const;
|
||||
inline tvec3<T> xzx() const;
|
||||
inline tvec3<T> xzy() const;
|
||||
inline tvec3<T> xzz() const;
|
||||
inline tvec3<T> yxx() const;
|
||||
inline tvec3<T> yxy() const;
|
||||
inline tvec3<T> yxz() const;
|
||||
inline tvec3<T> yyx() const;
|
||||
inline tvec3<T> yyy() const;
|
||||
inline tvec3<T> yyz() const;
|
||||
inline tvec3<T> yzx() const;
|
||||
inline tvec3<T> yzy() const;
|
||||
inline tvec3<T> yzz() const;
|
||||
inline tvec3<T> zxx() const;
|
||||
inline tvec3<T> zxy() const;
|
||||
inline tvec3<T> zxz() const;
|
||||
inline tvec3<T> zyx() const;
|
||||
inline tvec3<T> zyy() const;
|
||||
inline tvec3<T> zyz() const;
|
||||
inline tvec3<T> zzx() const;
|
||||
inline tvec3<T> zzy() const;
|
||||
inline tvec3<T> zzz() const;
|
||||
|
||||
inline tvec4<T> xxxx() const;
|
||||
inline tvec4<T> xxxy() const;
|
||||
inline tvec4<T> xxxz() const;
|
||||
inline tvec4<T> xxyx() const;
|
||||
inline tvec4<T> xxyy() const;
|
||||
inline tvec4<T> xxyz() const;
|
||||
inline tvec4<T> xxzx() const;
|
||||
inline tvec4<T> xxzy() const;
|
||||
inline tvec4<T> xxzz() const;
|
||||
inline tvec4<T> xyxx() const;
|
||||
inline tvec4<T> xyxy() const;
|
||||
inline tvec4<T> xyxz() const;
|
||||
inline tvec4<T> xyyx() const;
|
||||
inline tvec4<T> xyyy() const;
|
||||
inline tvec4<T> xyyz() const;
|
||||
inline tvec4<T> xyzx() const;
|
||||
inline tvec4<T> xyzy() const;
|
||||
inline tvec4<T> xyzz() const;
|
||||
inline tvec4<T> xzxx() const;
|
||||
inline tvec4<T> xzxy() const;
|
||||
inline tvec4<T> xzxz() const;
|
||||
inline tvec4<T> xzyx() const;
|
||||
inline tvec4<T> xzyy() const;
|
||||
inline tvec4<T> xzyz() const;
|
||||
inline tvec4<T> xzzx() const;
|
||||
inline tvec4<T> xzzy() const;
|
||||
inline tvec4<T> xzzz() const;
|
||||
inline tvec4<T> yxxx() const;
|
||||
inline tvec4<T> yxxy() const;
|
||||
inline tvec4<T> yxxz() const;
|
||||
inline tvec4<T> yxyx() const;
|
||||
inline tvec4<T> yxyy() const;
|
||||
inline tvec4<T> yxyz() const;
|
||||
inline tvec4<T> yxzx() const;
|
||||
inline tvec4<T> yxzy() const;
|
||||
inline tvec4<T> yxzz() const;
|
||||
inline tvec4<T> yyxx() const;
|
||||
inline tvec4<T> yyxy() const;
|
||||
inline tvec4<T> yyxz() const;
|
||||
inline tvec4<T> yyyx() const;
|
||||
inline tvec4<T> yyyy() const;
|
||||
inline tvec4<T> yyyz() const;
|
||||
inline tvec4<T> yyzx() const;
|
||||
inline tvec4<T> yyzy() const;
|
||||
inline tvec4<T> yyzz() const;
|
||||
inline tvec4<T> yzxx() const;
|
||||
inline tvec4<T> yzxy() const;
|
||||
inline tvec4<T> yzxz() const;
|
||||
inline tvec4<T> yzyx() const;
|
||||
inline tvec4<T> yzyy() const;
|
||||
inline tvec4<T> yzyz() const;
|
||||
inline tvec4<T> yzzx() const;
|
||||
inline tvec4<T> yzzy() const;
|
||||
inline tvec4<T> yzzz() const;
|
||||
inline tvec4<T> zxxx() const;
|
||||
inline tvec4<T> zxxy() const;
|
||||
inline tvec4<T> zxxz() const;
|
||||
inline tvec4<T> zxyx() const;
|
||||
inline tvec4<T> zxyy() const;
|
||||
inline tvec4<T> zxyz() const;
|
||||
inline tvec4<T> zxzx() const;
|
||||
inline tvec4<T> zxzy() const;
|
||||
inline tvec4<T> zxzz() const;
|
||||
inline tvec4<T> zyxx() const;
|
||||
inline tvec4<T> zyxy() const;
|
||||
inline tvec4<T> zyxz() const;
|
||||
inline tvec4<T> zyyx() const;
|
||||
inline tvec4<T> zyyy() const;
|
||||
inline tvec4<T> zyyz() const;
|
||||
inline tvec4<T> zyzx() const;
|
||||
inline tvec4<T> zyzy() const;
|
||||
inline tvec4<T> zyzz() const;
|
||||
inline tvec4<T> zzxx() const;
|
||||
inline tvec4<T> zzxy() const;
|
||||
inline tvec4<T> zzxz() const;
|
||||
inline tvec4<T> zzyx() const;
|
||||
inline tvec4<T> zzyy() const;
|
||||
inline tvec4<T> zzyz() const;
|
||||
inline tvec4<T> zzzx() const;
|
||||
inline tvec4<T> zzzy() const;
|
||||
inline tvec4<T> zzzz() const;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct tvec4
|
||||
{
|
||||
tvec4() = default;
|
||||
tvec4(const tvec4 &) = default;
|
||||
tvec4 &operator=(const tvec4 &) = default;
|
||||
|
||||
template <typename U>
|
||||
explicit inline tvec4(const tvec4<U> &u) noexcept
|
||||
{
|
||||
x = T(u.x);
|
||||
y = T(u.y);
|
||||
z = T(u.z);
|
||||
w = T(u.w);
|
||||
}
|
||||
|
||||
inline tvec4(const tvec2<T> &a, const tvec2<T> &b) noexcept
|
||||
{
|
||||
x = a.x;
|
||||
y = a.y;
|
||||
z = b.x;
|
||||
w = b.y;
|
||||
}
|
||||
|
||||
inline tvec4(const tvec3<T> &a, T b) noexcept
|
||||
{
|
||||
x = a.x;
|
||||
y = a.y;
|
||||
z = a.z;
|
||||
w = b;
|
||||
}
|
||||
|
||||
inline tvec4(T a, const tvec3<T> &b) noexcept
|
||||
{
|
||||
x = a;
|
||||
y = b.x;
|
||||
z = b.y;
|
||||
w = b.z;
|
||||
}
|
||||
|
||||
inline tvec4(const tvec2<T> &a, T b, T c) noexcept
|
||||
{
|
||||
x = a.x;
|
||||
y = a.y;
|
||||
z = b;
|
||||
w = c;
|
||||
}
|
||||
|
||||
inline tvec4(T a, const tvec2<T> &b, T c) noexcept
|
||||
{
|
||||
x = a;
|
||||
y = b.x;
|
||||
z = b.y;
|
||||
w = c;
|
||||
}
|
||||
|
||||
inline tvec4(T a, T b, const tvec2<T> &c) noexcept
|
||||
{
|
||||
x = a;
|
||||
y = b;
|
||||
z = c.x;
|
||||
w = c.y;
|
||||
}
|
||||
|
||||
explicit inline tvec4(T v) noexcept
|
||||
{
|
||||
x = v;
|
||||
y = v;
|
||||
z = v;
|
||||
w = v;
|
||||
}
|
||||
|
||||
inline tvec4(T x_, T y_, T z_, T w_) noexcept
|
||||
{
|
||||
x = x_;
|
||||
y = y_;
|
||||
z = z_;
|
||||
w = w_;
|
||||
}
|
||||
|
||||
inline T &operator[](size_t index)
|
||||
{
|
||||
return data[index];
|
||||
}
|
||||
|
||||
inline const T &operator[](size_t index) const
|
||||
{
|
||||
return data[index];
|
||||
}
|
||||
|
||||
union
|
||||
{
|
||||
T data[4];
|
||||
struct
|
||||
{
|
||||
T x, y, z, w;
|
||||
};
|
||||
};
|
||||
|
||||
inline tvec2<T> xx() const;
|
||||
inline tvec2<T> xy() const;
|
||||
inline tvec2<T> xz() const;
|
||||
inline tvec2<T> xw() const;
|
||||
inline tvec2<T> yx() const;
|
||||
inline tvec2<T> yy() const;
|
||||
inline tvec2<T> yz() const;
|
||||
inline tvec2<T> yw() const;
|
||||
inline tvec2<T> zx() const;
|
||||
inline tvec2<T> zy() const;
|
||||
inline tvec2<T> zz() const;
|
||||
inline tvec2<T> zw() const;
|
||||
inline tvec2<T> wx() const;
|
||||
inline tvec2<T> wy() const;
|
||||
inline tvec2<T> wz() const;
|
||||
inline tvec2<T> ww() const;
|
||||
|
||||
inline tvec3<T> xxx() const;
|
||||
inline tvec3<T> xxy() const;
|
||||
inline tvec3<T> xxz() const;
|
||||
inline tvec3<T> xxw() const;
|
||||
inline tvec3<T> xyx() const;
|
||||
inline tvec3<T> xyy() const;
|
||||
inline tvec3<T> xyz() const;
|
||||
inline tvec3<T> xyw() const;
|
||||
inline tvec3<T> xzx() const;
|
||||
inline tvec3<T> xzy() const;
|
||||
inline tvec3<T> xzz() const;
|
||||
inline tvec3<T> xzw() const;
|
||||
inline tvec3<T> xwx() const;
|
||||
inline tvec3<T> xwy() const;
|
||||
inline tvec3<T> xwz() const;
|
||||
inline tvec3<T> xww() const;
|
||||
inline tvec3<T> yxx() const;
|
||||
inline tvec3<T> yxy() const;
|
||||
inline tvec3<T> yxz() const;
|
||||
inline tvec3<T> yxw() const;
|
||||
inline tvec3<T> yyx() const;
|
||||
inline tvec3<T> yyy() const;
|
||||
inline tvec3<T> yyz() const;
|
||||
inline tvec3<T> yyw() const;
|
||||
inline tvec3<T> yzx() const;
|
||||
inline tvec3<T> yzy() const;
|
||||
inline tvec3<T> yzz() const;
|
||||
inline tvec3<T> yzw() const;
|
||||
inline tvec3<T> ywx() const;
|
||||
inline tvec3<T> ywy() const;
|
||||
inline tvec3<T> ywz() const;
|
||||
inline tvec3<T> yww() const;
|
||||
inline tvec3<T> zxx() const;
|
||||
inline tvec3<T> zxy() const;
|
||||
inline tvec3<T> zxz() const;
|
||||
inline tvec3<T> zxw() const;
|
||||
inline tvec3<T> zyx() const;
|
||||
inline tvec3<T> zyy() const;
|
||||
inline tvec3<T> zyz() const;
|
||||
inline tvec3<T> zyw() const;
|
||||
inline tvec3<T> zzx() const;
|
||||
inline tvec3<T> zzy() const;
|
||||
inline tvec3<T> zzz() const;
|
||||
inline tvec3<T> zzw() const;
|
||||
inline tvec3<T> zwx() const;
|
||||
inline tvec3<T> zwy() const;
|
||||
inline tvec3<T> zwz() const;
|
||||
inline tvec3<T> zww() const;
|
||||
inline tvec3<T> wxx() const;
|
||||
inline tvec3<T> wxy() const;
|
||||
inline tvec3<T> wxz() const;
|
||||
inline tvec3<T> wxw() const;
|
||||
inline tvec3<T> wyx() const;
|
||||
inline tvec3<T> wyy() const;
|
||||
inline tvec3<T> wyz() const;
|
||||
inline tvec3<T> wyw() const;
|
||||
inline tvec3<T> wzx() const;
|
||||
inline tvec3<T> wzy() const;
|
||||
inline tvec3<T> wzz() const;
|
||||
inline tvec3<T> wzw() const;
|
||||
inline tvec3<T> wwx() const;
|
||||
inline tvec3<T> wwy() const;
|
||||
inline tvec3<T> wwz() const;
|
||||
inline tvec3<T> www() const;
|
||||
|
||||
inline tvec4 xxxx() const;
|
||||
inline tvec4 xxxy() const;
|
||||
inline tvec4 xxxz() const;
|
||||
inline tvec4 xxxw() const;
|
||||
inline tvec4 xxyx() const;
|
||||
inline tvec4 xxyy() const;
|
||||
inline tvec4 xxyz() const;
|
||||
inline tvec4 xxyw() const;
|
||||
inline tvec4 xxzx() const;
|
||||
inline tvec4 xxzy() const;
|
||||
inline tvec4 xxzz() const;
|
||||
inline tvec4 xxzw() const;
|
||||
inline tvec4 xxwx() const;
|
||||
inline tvec4 xxwy() const;
|
||||
inline tvec4 xxwz() const;
|
||||
inline tvec4 xxww() const;
|
||||
inline tvec4 xyxx() const;
|
||||
inline tvec4 xyxy() const;
|
||||
inline tvec4 xyxz() const;
|
||||
inline tvec4 xyxw() const;
|
||||
inline tvec4 xyyx() const;
|
||||
inline tvec4 xyyy() const;
|
||||
inline tvec4 xyyz() const;
|
||||
inline tvec4 xyyw() const;
|
||||
inline tvec4 xyzx() const;
|
||||
inline tvec4 xyzy() const;
|
||||
inline tvec4 xyzz() const;
|
||||
inline tvec4 xyzw() const;
|
||||
inline tvec4 xywx() const;
|
||||
inline tvec4 xywy() const;
|
||||
inline tvec4 xywz() const;
|
||||
inline tvec4 xyww() const;
|
||||
inline tvec4 xzxx() const;
|
||||
inline tvec4 xzxy() const;
|
||||
inline tvec4 xzxz() const;
|
||||
inline tvec4 xzxw() const;
|
||||
inline tvec4 xzyx() const;
|
||||
inline tvec4 xzyy() const;
|
||||
inline tvec4 xzyz() const;
|
||||
inline tvec4 xzyw() const;
|
||||
inline tvec4 xzzx() const;
|
||||
inline tvec4 xzzy() const;
|
||||
inline tvec4 xzzz() const;
|
||||
inline tvec4 xzzw() const;
|
||||
inline tvec4 xzwx() const;
|
||||
inline tvec4 xzwy() const;
|
||||
inline tvec4 xzwz() const;
|
||||
inline tvec4 xzww() const;
|
||||
inline tvec4 xwxx() const;
|
||||
inline tvec4 xwxy() const;
|
||||
inline tvec4 xwxz() const;
|
||||
inline tvec4 xwxw() const;
|
||||
inline tvec4 xwyx() const;
|
||||
inline tvec4 xwyy() const;
|
||||
inline tvec4 xwyz() const;
|
||||
inline tvec4 xwyw() const;
|
||||
inline tvec4 xwzx() const;
|
||||
inline tvec4 xwzy() const;
|
||||
inline tvec4 xwzz() const;
|
||||
inline tvec4 xwzw() const;
|
||||
inline tvec4 xwwx() const;
|
||||
inline tvec4 xwwy() const;
|
||||
inline tvec4 xwwz() const;
|
||||
inline tvec4 xwww() const;
|
||||
inline tvec4 yxxx() const;
|
||||
inline tvec4 yxxy() const;
|
||||
inline tvec4 yxxz() const;
|
||||
inline tvec4 yxxw() const;
|
||||
inline tvec4 yxyx() const;
|
||||
inline tvec4 yxyy() const;
|
||||
inline tvec4 yxyz() const;
|
||||
inline tvec4 yxyw() const;
|
||||
inline tvec4 yxzx() const;
|
||||
inline tvec4 yxzy() const;
|
||||
inline tvec4 yxzz() const;
|
||||
inline tvec4 yxzw() const;
|
||||
inline tvec4 yxwx() const;
|
||||
inline tvec4 yxwy() const;
|
||||
inline tvec4 yxwz() const;
|
||||
inline tvec4 yxww() const;
|
||||
inline tvec4 yyxx() const;
|
||||
inline tvec4 yyxy() const;
|
||||
inline tvec4 yyxz() const;
|
||||
inline tvec4 yyxw() const;
|
||||
inline tvec4 yyyx() const;
|
||||
inline tvec4 yyyy() const;
|
||||
inline tvec4 yyyz() const;
|
||||
inline tvec4 yyyw() const;
|
||||
inline tvec4 yyzx() const;
|
||||
inline tvec4 yyzy() const;
|
||||
inline tvec4 yyzz() const;
|
||||
inline tvec4 yyzw() const;
|
||||
inline tvec4 yywx() const;
|
||||
inline tvec4 yywy() const;
|
||||
inline tvec4 yywz() const;
|
||||
inline tvec4 yyww() const;
|
||||
inline tvec4 yzxx() const;
|
||||
inline tvec4 yzxy() const;
|
||||
inline tvec4 yzxz() const;
|
||||
inline tvec4 yzxw() const;
|
||||
inline tvec4 yzyx() const;
|
||||
inline tvec4 yzyy() const;
|
||||
inline tvec4 yzyz() const;
|
||||
inline tvec4 yzyw() const;
|
||||
inline tvec4 yzzx() const;
|
||||
inline tvec4 yzzy() const;
|
||||
inline tvec4 yzzz() const;
|
||||
inline tvec4 yzzw() const;
|
||||
inline tvec4 yzwx() const;
|
||||
inline tvec4 yzwy() const;
|
||||
inline tvec4 yzwz() const;
|
||||
inline tvec4 yzww() const;
|
||||
inline tvec4 ywxx() const;
|
||||
inline tvec4 ywxy() const;
|
||||
inline tvec4 ywxz() const;
|
||||
inline tvec4 ywxw() const;
|
||||
inline tvec4 ywyx() const;
|
||||
inline tvec4 ywyy() const;
|
||||
inline tvec4 ywyz() const;
|
||||
inline tvec4 ywyw() const;
|
||||
inline tvec4 ywzx() const;
|
||||
inline tvec4 ywzy() const;
|
||||
inline tvec4 ywzz() const;
|
||||
inline tvec4 ywzw() const;
|
||||
inline tvec4 ywwx() const;
|
||||
inline tvec4 ywwy() const;
|
||||
inline tvec4 ywwz() const;
|
||||
inline tvec4 ywww() const;
|
||||
inline tvec4 zxxx() const;
|
||||
inline tvec4 zxxy() const;
|
||||
inline tvec4 zxxz() const;
|
||||
inline tvec4 zxxw() const;
|
||||
inline tvec4 zxyx() const;
|
||||
inline tvec4 zxyy() const;
|
||||
inline tvec4 zxyz() const;
|
||||
inline tvec4 zxyw() const;
|
||||
inline tvec4 zxzx() const;
|
||||
inline tvec4 zxzy() const;
|
||||
inline tvec4 zxzz() const;
|
||||
inline tvec4 zxzw() const;
|
||||
inline tvec4 zxwx() const;
|
||||
inline tvec4 zxwy() const;
|
||||
inline tvec4 zxwz() const;
|
||||
inline tvec4 zxww() const;
|
||||
inline tvec4 zyxx() const;
|
||||
inline tvec4 zyxy() const;
|
||||
inline tvec4 zyxz() const;
|
||||
inline tvec4 zyxw() const;
|
||||
inline tvec4 zyyx() const;
|
||||
inline tvec4 zyyy() const;
|
||||
inline tvec4 zyyz() const;
|
||||
inline tvec4 zyyw() const;
|
||||
inline tvec4 zyzx() const;
|
||||
inline tvec4 zyzy() const;
|
||||
inline tvec4 zyzz() const;
|
||||
inline tvec4 zyzw() const;
|
||||
inline tvec4 zywx() const;
|
||||
inline tvec4 zywy() const;
|
||||
inline tvec4 zywz() const;
|
||||
inline tvec4 zyww() const;
|
||||
inline tvec4 zzxx() const;
|
||||
inline tvec4 zzxy() const;
|
||||
inline tvec4 zzxz() const;
|
||||
inline tvec4 zzxw() const;
|
||||
inline tvec4 zzyx() const;
|
||||
inline tvec4 zzyy() const;
|
||||
inline tvec4 zzyz() const;
|
||||
inline tvec4 zzyw() const;
|
||||
inline tvec4 zzzx() const;
|
||||
inline tvec4 zzzy() const;
|
||||
inline tvec4 zzzz() const;
|
||||
inline tvec4 zzzw() const;
|
||||
inline tvec4 zzwx() const;
|
||||
inline tvec4 zzwy() const;
|
||||
inline tvec4 zzwz() const;
|
||||
inline tvec4 zzww() const;
|
||||
inline tvec4 zwxx() const;
|
||||
inline tvec4 zwxy() const;
|
||||
inline tvec4 zwxz() const;
|
||||
inline tvec4 zwxw() const;
|
||||
inline tvec4 zwyx() const;
|
||||
inline tvec4 zwyy() const;
|
||||
inline tvec4 zwyz() const;
|
||||
inline tvec4 zwyw() const;
|
||||
inline tvec4 zwzx() const;
|
||||
inline tvec4 zwzy() const;
|
||||
inline tvec4 zwzz() const;
|
||||
inline tvec4 zwzw() const;
|
||||
inline tvec4 zwwx() const;
|
||||
inline tvec4 zwwy() const;
|
||||
inline tvec4 zwwz() const;
|
||||
inline tvec4 zwww() const;
|
||||
inline tvec4 wxxx() const;
|
||||
inline tvec4 wxxy() const;
|
||||
inline tvec4 wxxz() const;
|
||||
inline tvec4 wxxw() const;
|
||||
inline tvec4 wxyx() const;
|
||||
inline tvec4 wxyy() const;
|
||||
inline tvec4 wxyz() const;
|
||||
inline tvec4 wxyw() const;
|
||||
inline tvec4 wxzx() const;
|
||||
inline tvec4 wxzy() const;
|
||||
inline tvec4 wxzz() const;
|
||||
inline tvec4 wxzw() const;
|
||||
inline tvec4 wxwx() const;
|
||||
inline tvec4 wxwy() const;
|
||||
inline tvec4 wxwz() const;
|
||||
inline tvec4 wxww() const;
|
||||
inline tvec4 wyxx() const;
|
||||
inline tvec4 wyxy() const;
|
||||
inline tvec4 wyxz() const;
|
||||
inline tvec4 wyxw() const;
|
||||
inline tvec4 wyyx() const;
|
||||
inline tvec4 wyyy() const;
|
||||
inline tvec4 wyyz() const;
|
||||
inline tvec4 wyyw() const;
|
||||
inline tvec4 wyzx() const;
|
||||
inline tvec4 wyzy() const;
|
||||
inline tvec4 wyzz() const;
|
||||
inline tvec4 wyzw() const;
|
||||
inline tvec4 wywx() const;
|
||||
inline tvec4 wywy() const;
|
||||
inline tvec4 wywz() const;
|
||||
inline tvec4 wyww() const;
|
||||
inline tvec4 wzxx() const;
|
||||
inline tvec4 wzxy() const;
|
||||
inline tvec4 wzxz() const;
|
||||
inline tvec4 wzxw() const;
|
||||
inline tvec4 wzyx() const;
|
||||
inline tvec4 wzyy() const;
|
||||
inline tvec4 wzyz() const;
|
||||
inline tvec4 wzyw() const;
|
||||
inline tvec4 wzzx() const;
|
||||
inline tvec4 wzzy() const;
|
||||
inline tvec4 wzzz() const;
|
||||
inline tvec4 wzzw() const;
|
||||
inline tvec4 wzwx() const;
|
||||
inline tvec4 wzwy() const;
|
||||
inline tvec4 wzwz() const;
|
||||
inline tvec4 wzww() const;
|
||||
inline tvec4 wwxx() const;
|
||||
inline tvec4 wwxy() const;
|
||||
inline tvec4 wwxz() const;
|
||||
inline tvec4 wwxw() const;
|
||||
inline tvec4 wwyx() const;
|
||||
inline tvec4 wwyy() const;
|
||||
inline tvec4 wwyz() const;
|
||||
inline tvec4 wwyw() const;
|
||||
inline tvec4 wwzx() const;
|
||||
inline tvec4 wwzy() const;
|
||||
inline tvec4 wwzz() const;
|
||||
inline tvec4 wwzw() const;
|
||||
inline tvec4 wwwx() const;
|
||||
inline tvec4 wwwy() const;
|
||||
inline tvec4 wwwz() const;
|
||||
inline tvec4 wwww() const;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct tmat2
|
||||
{
|
||||
tmat2() = default;
|
||||
tmat2(const tmat2 &) = default;
|
||||
tmat2 &operator=(const tmat2 &) = default;
|
||||
|
||||
explicit inline tmat2(T v) noexcept
|
||||
{
|
||||
vec[0] = tvec2<T>(v, T(0));
|
||||
vec[1] = tvec2<T>(T(0), v);
|
||||
}
|
||||
|
||||
inline tmat2(const tvec2<T> &a, const tvec2<T> &b) noexcept
|
||||
{
|
||||
vec[0] = a;
|
||||
vec[1] = b;
|
||||
}
|
||||
|
||||
inline tvec2<T> &operator[](size_t index)
|
||||
{
|
||||
return vec[index];
|
||||
}
|
||||
|
||||
inline const tvec2<T> &operator[](size_t index) const
|
||||
{
|
||||
return vec[index];
|
||||
}
|
||||
|
||||
private:
|
||||
tvec2<T> vec[2];
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct tmat3
|
||||
{
|
||||
tmat3() = default;
|
||||
tmat3(const tmat3 &) = default;
|
||||
tmat3 &operator=(const tmat3 &) = default;
|
||||
|
||||
explicit inline tmat3(T v) noexcept
|
||||
{
|
||||
vec[0] = tvec3<T>(v, T(0), T(0));
|
||||
vec[1] = tvec3<T>(T(0), v, T(0));
|
||||
vec[2] = tvec3<T>(T(0), T(0), v);
|
||||
}
|
||||
|
||||
inline tmat3(const tvec3<T> &a, const tvec3<T> &b, const tvec3<T> &c) noexcept
|
||||
{
|
||||
vec[0] = a;
|
||||
vec[1] = b;
|
||||
vec[2] = c;
|
||||
}
|
||||
|
||||
explicit inline tmat3(const tmat4<T> &m) noexcept
|
||||
{
|
||||
for (int col = 0; col < 3; col++)
|
||||
for (int row = 0; row < 3; row++)
|
||||
vec[col][row] = m[col][row];
|
||||
}
|
||||
|
||||
inline tvec3<T> &operator[](size_t index)
|
||||
{
|
||||
return vec[index];
|
||||
}
|
||||
|
||||
inline const tvec3<T> &operator[](size_t index) const
|
||||
{
|
||||
return vec[index];
|
||||
}
|
||||
|
||||
private:
|
||||
tvec3<T> vec[3];
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct tmat4
|
||||
{
|
||||
tmat4() = default;
|
||||
tmat4(const tmat4 &) = default;
|
||||
tmat4 &operator=(const tmat4 &) = default;
|
||||
|
||||
explicit inline tmat4(T v) noexcept
|
||||
{
|
||||
vec[0] = tvec4<T>(v, T(0), T(0), T(0));
|
||||
vec[1] = tvec4<T>(T(0), v, T(0), T(0));
|
||||
vec[2] = tvec4<T>(T(0), T(0), v, T(0));
|
||||
vec[3] = tvec4<T>(T(0), T(0), T(0), v);
|
||||
}
|
||||
|
||||
explicit inline tmat4(const tmat3<T> &m) noexcept
|
||||
{
|
||||
vec[0] = tvec4<T>(m[0], T(0));
|
||||
vec[1] = tvec4<T>(m[1], T(0));
|
||||
vec[2] = tvec4<T>(m[2], T(0));
|
||||
vec[3] = tvec4<T>(T(0), T(0), T(0), T(1));
|
||||
}
|
||||
|
||||
inline tmat4(const tvec4<T> &a, const tvec4<T> &b, const tvec4<T> &c, const tvec4<T> &d) noexcept
|
||||
{
|
||||
vec[0] = a;
|
||||
vec[1] = b;
|
||||
vec[2] = c;
|
||||
vec[3] = d;
|
||||
}
|
||||
|
||||
inline tvec4<T> &operator[](size_t index)
|
||||
{
|
||||
return vec[index];
|
||||
}
|
||||
|
||||
inline const tvec4<T> &operator[](size_t index) const
|
||||
{
|
||||
return vec[index];
|
||||
}
|
||||
|
||||
private:
|
||||
tvec4<T> vec[4];
|
||||
};
|
||||
|
||||
using uint = uint32_t;
|
||||
using vec2 = tvec2<float>;
|
||||
using vec3 = tvec3<float>;
|
||||
using vec4 = tvec4<float>;
|
||||
using mat2 = tmat2<float>;
|
||||
using mat3 = tmat3<float>;
|
||||
using mat4 = tmat4<float>;
|
||||
|
||||
using dvec2 = tvec2<double>;
|
||||
using dvec3 = tvec3<double>;
|
||||
using dvec4 = tvec4<double>;
|
||||
using dmat2 = tmat2<double>;
|
||||
using dmat3 = tmat3<double>;
|
||||
using dmat4 = tmat4<double>;
|
||||
|
||||
using ivec2 = tvec2<int32_t>;
|
||||
using ivec3 = tvec3<int32_t>;
|
||||
using ivec4 = tvec4<int32_t>;
|
||||
using uvec2 = tvec2<uint32_t>;
|
||||
using uvec3 = tvec3<uint32_t>;
|
||||
using uvec4 = tvec4<uint32_t>;
|
||||
|
||||
using u16vec2 = tvec2<uint16_t>;
|
||||
using u16vec3 = tvec3<uint16_t>;
|
||||
using u16vec4 = tvec4<uint16_t>;
|
||||
using i16vec2 = tvec2<int16_t>;
|
||||
using i16vec3 = tvec3<int16_t>;
|
||||
using i16vec4 = tvec4<int16_t>;
|
||||
|
||||
using u8vec2 = tvec2<uint8_t>;
|
||||
using u8vec3 = tvec3<uint8_t>;
|
||||
using u8vec4 = tvec4<uint8_t>;
|
||||
using i8vec2 = tvec2<int8_t>;
|
||||
using i8vec3 = tvec3<int8_t>;
|
||||
using i8vec4 = tvec4<int8_t>;
|
||||
|
||||
using bvec2 = tvec2<bool>;
|
||||
using bvec3 = tvec3<bool>;
|
||||
using bvec4 = tvec4<bool>;
|
||||
|
||||
void transpose(mat4 &dst, const mat4 &src);
|
||||
void transpose_to_affine(vec4 dst[3], const mat4 &src);
|
||||
|
||||
struct mat_affine
|
||||
{
|
||||
mat_affine() = default;
|
||||
mat_affine(const mat_affine &) = default;
|
||||
mat_affine &operator=(const mat_affine &) = default;
|
||||
|
||||
explicit inline mat_affine(const mat4 &m)
|
||||
{
|
||||
transpose_to_affine(vec, m);
|
||||
}
|
||||
|
||||
explicit inline mat_affine(const mat3 &m)
|
||||
: vec{{m[0][0], m[1][0], m[2][0], 0.0f},
|
||||
{m[0][1], m[1][1], m[2][1], 0.0f},
|
||||
{m[0][2], m[1][2], m[2][2], 0.0f}}
|
||||
{
|
||||
}
|
||||
|
||||
explicit inline mat_affine(float v)
|
||||
: vec{{v, 0, 0, 0}, {0, v, 0, 0}, {0, 0, v, 0}}
|
||||
{
|
||||
}
|
||||
|
||||
inline mat_affine(const vec4 &r0, const vec4 &r1, const vec4 &r2)
|
||||
: vec{r0, r1, r2}
|
||||
{
|
||||
}
|
||||
|
||||
inline vec4 &operator[](size_t index)
|
||||
{
|
||||
return vec[index];
|
||||
}
|
||||
|
||||
inline const vec4 &operator[](size_t index) const
|
||||
{
|
||||
return vec[index];
|
||||
}
|
||||
|
||||
float get_uniform_scale() const;
|
||||
vec3 get_translation() const;
|
||||
// Based on identity view pointing to -Z axis, Y up.
|
||||
vec3 get_forward() const;
|
||||
vec3 get_right() const;
|
||||
vec3 get_up() const;
|
||||
|
||||
mat4 to_mat4() const;
|
||||
void to_mat4(mat4 &m) const;
|
||||
|
||||
inline mat3 to_mat3() const
|
||||
{
|
||||
return {
|
||||
vec3(vec[0][0], vec[1][0], vec[2][0]),
|
||||
vec3(vec[0][1], vec[1][1], vec[2][1]),
|
||||
vec3(vec[0][2], vec[1][2], vec[2][2]),
|
||||
};
|
||||
}
|
||||
|
||||
private:
|
||||
vec4 vec[3];
|
||||
};
|
||||
|
||||
struct quat : private vec4
|
||||
{
|
||||
quat() = default;
|
||||
quat(const quat &) = default;
|
||||
quat(float w_, float x_, float y_, float z_)
|
||||
: vec4(x_, y_, z_, w_)
|
||||
{}
|
||||
|
||||
explicit inline quat(const vec4 &v)
|
||||
: vec4(v)
|
||||
{}
|
||||
|
||||
inline quat(float w_, const vec3 &v_)
|
||||
: vec4(v_, w_)
|
||||
{}
|
||||
|
||||
inline const vec4 &as_vec4() const
|
||||
{
|
||||
return *static_cast<const vec4 *>(this);
|
||||
}
|
||||
|
||||
quat &operator=(const quat &) = default;
|
||||
|
||||
using vec4::x;
|
||||
using vec4::y;
|
||||
using vec4::z;
|
||||
using vec4::w;
|
||||
};
|
||||
|
||||
template <typename T> constexpr inline T pi() { return T(3.1415926535897932384626433832795028841971); }
|
||||
template <typename T> constexpr inline T half_pi() { return T(0.5) * pi<T>(); }
|
||||
template <typename T> constexpr inline T one_over_root_two() { return T(0.7071067811865476); }
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,251 @@
|
||||
/* Copyright (c) 2017-2026 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "muglm_impl.hpp"
|
||||
#include "matrix_helper.hpp"
|
||||
#include "simd.hpp"
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include "aabb.hpp"
|
||||
|
||||
using namespace muglm;
|
||||
|
||||
#define MATH_ASSERT(x) do { \
|
||||
if (!bool(x)) \
|
||||
abort(); \
|
||||
} while(0)
|
||||
|
||||
template <typename T>
|
||||
static void assert_equal_epsilon(const T &a, const T &b, float epsilon = 0.0001f)
|
||||
{
|
||||
MATH_ASSERT(all(lessThanEqual(abs(a - b), T(epsilon))));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void assert_equal(const T &a, const T &b)
|
||||
{
|
||||
MATH_ASSERT(all(equal(a, b)));
|
||||
}
|
||||
|
||||
static void assert_equal_epsilon(const mat2 &a, const mat2 &b, float epsilon = 0.0001f)
|
||||
{
|
||||
assert_equal_epsilon(a[0], b[0], epsilon);
|
||||
assert_equal_epsilon(a[1], b[1], epsilon);
|
||||
}
|
||||
|
||||
static void assert_equal_epsilon(const mat3 &a, const mat3 &b, float epsilon = 0.0001f)
|
||||
{
|
||||
assert_equal_epsilon(a[0], b[0], epsilon);
|
||||
assert_equal_epsilon(a[1], b[1], epsilon);
|
||||
assert_equal_epsilon(a[2], b[2], epsilon);
|
||||
}
|
||||
|
||||
static void assert_equal_epsilon(const mat4 &a, const mat4 &b, float epsilon = 0.0001f)
|
||||
{
|
||||
assert_equal_epsilon(a[0], b[0], epsilon);
|
||||
assert_equal_epsilon(a[1], b[1], epsilon);
|
||||
assert_equal_epsilon(a[2], b[2], epsilon);
|
||||
assert_equal_epsilon(a[3], b[3], epsilon);
|
||||
}
|
||||
|
||||
static void assert_equal_epsilon(const mat_affine &a, const mat_affine &b, float epsilon = 0.0001f)
|
||||
{
|
||||
assert_equal_epsilon(a[0], b[0], epsilon);
|
||||
assert_equal_epsilon(a[1], b[1], epsilon);
|
||||
assert_equal_epsilon(a[2], b[2], epsilon);
|
||||
}
|
||||
|
||||
static void assert_equal(const mat2 &a, const mat2 &b)
|
||||
{
|
||||
assert_equal_epsilon(a, b, 0.0f);
|
||||
}
|
||||
|
||||
static void assert_equal(const mat3 &a, const mat3 &b)
|
||||
{
|
||||
assert_equal_epsilon(a, b, 0.0f);
|
||||
}
|
||||
|
||||
static void assert_equal(const mat4 &a, const mat4 &b)
|
||||
{
|
||||
assert_equal_epsilon(a, b, 0.0f);
|
||||
}
|
||||
|
||||
static void test_mat2()
|
||||
{
|
||||
mat2 m(vec2(2.0f, 0.5f), vec2(8.0f, 4.0f));
|
||||
mat2 inv_m = inverse(m);
|
||||
mat2 mul0 = m * inv_m;
|
||||
mat2 mul1 = inv_m * m;
|
||||
assert_equal_epsilon(mul0, mat2(1.0f));
|
||||
assert_equal_epsilon(mul1, mat2(1.0f));
|
||||
assert_equal(transpose(transpose(m)), m);
|
||||
}
|
||||
|
||||
static void test_mat3()
|
||||
{
|
||||
mat3 m(vec3(2.0f, 0.5f, -3.0f), vec3(8.0f, 4.0f, 0.25f), vec3(-20.0f, 5.0f, 1.0f));
|
||||
mat3 inv_m = inverse(m);
|
||||
mat3 mul0 = m * inv_m;
|
||||
mat3 mul1 = inv_m * m;
|
||||
assert_equal_epsilon(mul0, mat3(1.0f));
|
||||
assert_equal_epsilon(mul1, mat3(1.0f));
|
||||
assert_equal(transpose(transpose(m)), m);
|
||||
}
|
||||
|
||||
static void test_mat4()
|
||||
{
|
||||
mat4 m(vec4(2.0f, 0.5f, -3.0f, 1.0f), vec4(8.0f, 4.0f, 0.25f, 8.0f), vec4(8.0f, -20.0f, 5.0f, 1.0f), vec4(0.0f, 1.0f, 2.0f, 3.0f));
|
||||
mat4 inv_m = inverse(m);
|
||||
mat4 mul0 = m * inv_m;
|
||||
mat4 mul1 = inv_m * m;
|
||||
assert_equal_epsilon(mul0, mat4(1.0f));
|
||||
assert_equal_epsilon(mul1, mat4(1.0f));
|
||||
assert_equal(transpose(transpose(m)), m);
|
||||
}
|
||||
|
||||
static void test_quat()
|
||||
{
|
||||
// X
|
||||
{
|
||||
quat q = angleAxis(half_pi<float>(), vec3(0.0f, 0.0f, 1.0f));
|
||||
vec3 y = q * vec3(1.0f, 0.0f, 0.0f);
|
||||
assert_equal_epsilon(y, vec3(0.0f, 1.0f, 0.0f));
|
||||
|
||||
quat half_q = angleAxis(0.5f * half_pi<float>(), vec3(0.0f, 0.0f, 1.0f));
|
||||
q = half_q * half_q;
|
||||
y = q * vec3(1.0f, 0.0f, 0.0f);
|
||||
assert_equal_epsilon(y, vec3(0.0f, 1.0f, 0.0f));
|
||||
}
|
||||
|
||||
// Y
|
||||
{
|
||||
quat q = angleAxis(half_pi<float>(), vec3(0.0f, 1.0f, 0.0f));
|
||||
vec3 y = q * vec3(1.0f, 0.0f, 0.0f);
|
||||
assert_equal_epsilon(y, vec3(0.0f, 0.0f, -1.0f));
|
||||
|
||||
quat half_q = angleAxis(0.5f * half_pi<float>(), vec3(0.0f, 1.0f, 0.0f));
|
||||
q = half_q * half_q;
|
||||
y = q * vec3(1.0f, 0.0f, 0.0f);
|
||||
assert_equal_epsilon(y, vec3(0.0f, 0.0f, -1.0f));
|
||||
}
|
||||
|
||||
assert_equal_epsilon(mat3_cast(quat(1.0f, 0.0f, 0.0f, 0.0f)), mat3(1.0f));
|
||||
assert_equal_epsilon(mat4_cast(quat(1.0f, 0.0f, 0.0f, 0.0f)), mat4(1.0f));
|
||||
}
|
||||
|
||||
static void test_decompose()
|
||||
{
|
||||
vec3 scaling(4.0f, -3.0f, 2.0f);
|
||||
quat rotate = angleAxis(0.543f, vec3(0.1f, 0.2f, -0.9f));
|
||||
vec3 trans(5.0f, 4.0f, 2.0f);
|
||||
|
||||
mat4 original = translate(trans) * mat4_cast(rotate) * scale(scaling);
|
||||
|
||||
vec3 s, t;
|
||||
quat r;
|
||||
decompose(original, s, r, t);
|
||||
|
||||
mat4 reconstructed = translate(t) * mat4_cast(r) * scale(s);
|
||||
assert_equal_epsilon(original, reconstructed);
|
||||
}
|
||||
|
||||
static void test_transpose()
|
||||
{
|
||||
mat4 original(vec4(1, 2, 3, 4), vec4(5, 6, 7, 8), vec4(9, 10, 11, 12), vec4(13, 14, 15, 16));
|
||||
mat4 transposed = transpose(original);
|
||||
mat_affine aff{original};
|
||||
|
||||
const mat4 expected_transposed(
|
||||
vec4(1, 5, 9, 13),
|
||||
vec4(2, 6, 10, 14),
|
||||
vec4(3, 7, 11, 15),
|
||||
vec4(4, 8, 12, 16));
|
||||
|
||||
mat_affine expected_aff;
|
||||
for (int i = 0; i < 3; i++)
|
||||
expected_aff[i] = expected_transposed[i];
|
||||
|
||||
assert_equal_epsilon(expected_transposed, transposed, 0.0f);
|
||||
assert_equal_epsilon(expected_aff, aff, 0.0f);
|
||||
}
|
||||
|
||||
static void test_affine()
|
||||
{
|
||||
mat4 a(vec4(1, 2, 3, 0), vec4(5, 6, 7, 0),
|
||||
vec4(9, 10, 11, 0), vec4(13, 14, 15, 1));
|
||||
mat4 b(vec4(17, 18, 19, 0), vec4(21, 22, 23, 0),
|
||||
vec4(25, 26, 27, 0), vec4(29, 30, 31, 1));
|
||||
|
||||
mat4 c = a * b;
|
||||
mat_affine aff_c(c);
|
||||
mat_affine aff_a(a);
|
||||
mat_affine aff_b(b);
|
||||
|
||||
mat_affine res;
|
||||
Granite::SIMD::mul(res, aff_a, aff_b);
|
||||
assert_equal_epsilon(res, aff_c, 0.0f);
|
||||
}
|
||||
|
||||
static void test_affine_mul()
|
||||
{
|
||||
mat4 a(vec4(1, 2, 3, 0), vec4(5, 6, 7, 0),
|
||||
vec4(9, 10, 11, 0), vec4(13, 14, 15, 1));
|
||||
vec4 b(1, 2, 3, 1);
|
||||
|
||||
mat_affine aff_a(a);
|
||||
|
||||
vec4 ref = a * b;
|
||||
vec4 res4, res_affine;
|
||||
Granite::SIMD::mul(res4, a, b);
|
||||
Granite::SIMD::mul(res_affine, aff_a, b);
|
||||
|
||||
assert_equal_epsilon(ref, res4, 0.0f);
|
||||
assert_equal_epsilon(ref, res_affine, 0.0f);
|
||||
}
|
||||
|
||||
static void test_aabb_transform()
|
||||
{
|
||||
mat4 m(vec4(1, 2, 3, 0), vec4(5, 6, 7, 0),
|
||||
vec4(9, 10, 11, 0), vec4(13, 14, 15, 1));
|
||||
|
||||
Granite::AABB aabb{vec3(-1, -2, -4), vec3(3, 7, 4)};
|
||||
|
||||
Granite::AABB reference_aabb, affine_aabb;
|
||||
Granite::SIMD::transform_aabb(reference_aabb, aabb, m);
|
||||
Granite::SIMD::transform_aabb(affine_aabb, aabb, mat_affine(m));
|
||||
|
||||
assert_equal_epsilon(reference_aabb.get_maximum4(), affine_aabb.get_maximum4(), 0.0f);
|
||||
assert_equal_epsilon(reference_aabb.get_minimum4(), affine_aabb.get_minimum4(), 0.0f);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_mat2();
|
||||
test_mat3();
|
||||
test_mat4();
|
||||
test_quat();
|
||||
test_decompose();
|
||||
test_transpose();
|
||||
test_affine();
|
||||
test_affine_mul();
|
||||
test_aabb_transform();
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
/* Copyright (c) 2017-2026 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "math.hpp"
|
||||
#include "image.hpp"
|
||||
#include "lights/light_info.hpp"
|
||||
#include "limits.hpp"
|
||||
|
||||
namespace Granite
|
||||
{
|
||||
class LightClusterer;
|
||||
class VolumetricFog;
|
||||
class VolumetricDiffuseLightManager;
|
||||
enum { NumShadowCascades = 4 };
|
||||
|
||||
struct RenderParameters
|
||||
{
|
||||
mat4 projection;
|
||||
mat4 view;
|
||||
mat4 view_projection;
|
||||
mat4 inv_projection;
|
||||
mat4 inv_view;
|
||||
mat4 inv_view_projection;
|
||||
mat4 local_view_projection;
|
||||
mat4 inv_local_view_projection;
|
||||
|
||||
mat4 unjittered_view_projection;
|
||||
mat4 unjittered_inv_view_projection;
|
||||
mat4 unjittered_prev_view_projection;
|
||||
|
||||
alignas(16) vec3 camera_position;
|
||||
alignas(16) vec3 camera_front;
|
||||
alignas(16) vec3 camera_right;
|
||||
alignas(16) vec3 camera_up;
|
||||
|
||||
float z_near;
|
||||
float z_far;
|
||||
};
|
||||
|
||||
struct ResolutionParameters
|
||||
{
|
||||
alignas(8) vec2 resolution;
|
||||
alignas(8) vec2 inv_resolution;
|
||||
};
|
||||
|
||||
struct VolumetricFogParameters
|
||||
{
|
||||
float slice_z_log2_scale;
|
||||
};
|
||||
|
||||
struct FogParameters
|
||||
{
|
||||
alignas(16) vec3 color;
|
||||
float falloff;
|
||||
};
|
||||
|
||||
struct DirectionalParameters
|
||||
{
|
||||
alignas(16) vec3 color;
|
||||
alignas(16) vec3 direction;
|
||||
};
|
||||
|
||||
struct ShadowParameters
|
||||
{
|
||||
alignas(16) mat4 transforms[NumShadowCascades];
|
||||
float cascade_log_bias;
|
||||
};
|
||||
|
||||
struct ClustererParametersBindless
|
||||
{
|
||||
alignas(16) mat4 transform;
|
||||
alignas(16) vec4 clip_scale;
|
||||
alignas(16) vec3 camera_base;
|
||||
alignas(16) vec3 camera_front;
|
||||
|
||||
alignas(8) vec2 xy_scale;
|
||||
alignas(8) ivec2 resolution_xy;
|
||||
alignas(8) vec2 inv_resolution_xy;
|
||||
|
||||
uint32_t num_lights;
|
||||
uint32_t num_lights_32;
|
||||
uint32_t num_decals;
|
||||
uint32_t num_decals_32;
|
||||
uint32_t decals_texture_offset;
|
||||
uint32_t z_max_index;
|
||||
float z_scale;
|
||||
};
|
||||
|
||||
struct DiffuseVolumeParameters
|
||||
{
|
||||
mat_affine world_to_texture;
|
||||
vec4 world_lo;
|
||||
vec4 world_hi;
|
||||
float lo_tex_coord_x;
|
||||
float hi_tex_coord_x;
|
||||
float guard_band_factor;
|
||||
float guard_band_sharpen;
|
||||
};
|
||||
|
||||
#define CLUSTERER_MAX_VOLUMES 128
|
||||
struct ClustererParametersVolumetric
|
||||
{
|
||||
alignas(16) muglm::vec3 sun_direction;
|
||||
uint32_t bindless_index_offset;
|
||||
alignas(16) muglm::vec3 sun_color;
|
||||
uint32_t num_volumes;
|
||||
alignas(16) DiffuseVolumeParameters volumes[CLUSTERER_MAX_VOLUMES];
|
||||
};
|
||||
|
||||
struct FogRegionParameters
|
||||
{
|
||||
mat_affine world_to_texture;
|
||||
vec4 world_lo;
|
||||
vec4 world_hi;
|
||||
};
|
||||
|
||||
#define CLUSTERER_MAX_FOG_REGIONS 128
|
||||
struct ClustererParametersFogRegions
|
||||
{
|
||||
uint32_t bindless_index_offset;
|
||||
uint32_t num_regions;
|
||||
alignas(16) FogRegionParameters regions[CLUSTERER_MAX_FOG_REGIONS];
|
||||
};
|
||||
|
||||
#define CLUSTERER_MAX_LIGHTS_BINDLESS 4096
|
||||
#define CLUSTERER_MAX_DECALS_BINDLESS 4096
|
||||
#define CLUSTERER_MAX_LIGHTS_GLOBAL 32
|
||||
|
||||
struct BindlessDecalTransform
|
||||
{
|
||||
mat_affine world_to_texture;
|
||||
};
|
||||
|
||||
struct ClustererBindlessTransforms
|
||||
{
|
||||
PositionalFragmentInfo lights[CLUSTERER_MAX_LIGHTS_BINDLESS];
|
||||
mat4 shadow[CLUSTERER_MAX_LIGHTS_BINDLESS];
|
||||
mat_affine model[CLUSTERER_MAX_LIGHTS_BINDLESS];
|
||||
uint32_t type_mask[CLUSTERER_MAX_LIGHTS_BINDLESS / 32];
|
||||
BindlessDecalTransform decals[CLUSTERER_MAX_DECALS_BINDLESS];
|
||||
};
|
||||
|
||||
struct ClustererGlobalTransforms
|
||||
{
|
||||
alignas(16) PositionalFragmentInfo lights[CLUSTERER_MAX_LIGHTS_GLOBAL];
|
||||
alignas(16) mat4 shadow[CLUSTERER_MAX_LIGHTS_GLOBAL];
|
||||
alignas(16) uint32_t type_mask[CLUSTERER_MAX_LIGHTS_GLOBAL / 32];
|
||||
uint32_t descriptor_offset;
|
||||
uint32_t num_lights;
|
||||
};
|
||||
static_assert(sizeof(ClustererGlobalTransforms) <= Vulkan::VULKAN_MAX_UBO_SIZE, "Global transforms is too large.");
|
||||
|
||||
struct CombinedRenderParameters
|
||||
{
|
||||
alignas(16) FogParameters fog;
|
||||
alignas(16) ShadowParameters shadow;
|
||||
alignas(16) VolumetricFogParameters volumetric_fog;
|
||||
alignas(16) DirectionalParameters directional;
|
||||
alignas(16) ResolutionParameters resolution;
|
||||
};
|
||||
static_assert(sizeof(CombinedRenderParameters) <= Vulkan::VULKAN_MAX_UBO_SIZE, "CombinedRenderParameters cannot fit in min-spec.");
|
||||
|
||||
struct LightingParameters
|
||||
{
|
||||
FogParameters fog = {};
|
||||
DirectionalParameters directional;
|
||||
ShadowParameters shadow;
|
||||
|
||||
Vulkan::ImageView *shadows = nullptr;
|
||||
Vulkan::ImageView *ambient_occlusion = nullptr;
|
||||
const LightClusterer *cluster = nullptr;
|
||||
const VolumetricFog *volumetric_fog = nullptr;
|
||||
const VolumetricDiffuseLightManager *volumetric_diffuse = nullptr;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,602 @@
|
||||
/* Copyright (c) 2017-2026 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "math.hpp"
|
||||
#include "aabb.hpp"
|
||||
#include "simd_headers.hpp"
|
||||
#include "muglm/matrix_helper.hpp"
|
||||
|
||||
namespace Granite
|
||||
{
|
||||
namespace SIMD
|
||||
{
|
||||
static inline bool frustum_cull(const AABB &aabb, const vec4 *planes)
|
||||
{
|
||||
#if defined(__SSE3__)
|
||||
__m128 lo = _mm_loadu_ps(aabb.get_minimum4().data);
|
||||
__m128 hi = _mm_loadu_ps(aabb.get_maximum4().data);
|
||||
|
||||
#define COMPUTE_PLANE(i) \
|
||||
__m128 p##i = _mm_loadu_ps(planes[i].data); \
|
||||
__m128 mask##i = _mm_cmpgt_ps(p##i, _mm_setzero_ps()); \
|
||||
__m128 major_axis##i = _mm_or_ps(_mm_and_ps(mask##i, hi), _mm_andnot_ps(mask##i, lo)); \
|
||||
__m128 dotted##i = _mm_mul_ps(p##i, major_axis##i)
|
||||
COMPUTE_PLANE(0);
|
||||
COMPUTE_PLANE(1);
|
||||
COMPUTE_PLANE(2);
|
||||
COMPUTE_PLANE(3);
|
||||
COMPUTE_PLANE(4);
|
||||
COMPUTE_PLANE(5);
|
||||
|
||||
__m128 merged01 = _mm_hadd_ps(dotted0, dotted1);
|
||||
__m128 merged23 = _mm_hadd_ps(dotted2, dotted3);
|
||||
__m128 merged45 = _mm_hadd_ps(dotted4, dotted5);
|
||||
__m128 merged0123 = _mm_hadd_ps(merged01, merged23);
|
||||
merged45 = _mm_hadd_ps(merged45, merged45);
|
||||
__m128 merged = _mm_or_ps(merged0123, merged45);
|
||||
// Sets bit if the sign bit is set.
|
||||
int mask = _mm_movemask_ps(merged);
|
||||
return mask == 0;
|
||||
#elif defined(__ARM_NEON)
|
||||
float32x4_t lo = vld1q_f32(aabb.get_minimum4().data);
|
||||
float32x4_t hi = vld1q_f32(aabb.get_maximum4().data);
|
||||
|
||||
#define COMPUTE_PLANE(i) \
|
||||
float32x4_t p##i = vld1q_f32(planes[i].data); \
|
||||
uint32x4_t mask##i = vcgtq_f32(p##i, vdupq_n_f32(0.0f)); \
|
||||
float32x4_t major_axis##i = vbslq_f32(mask##i, hi, lo); \
|
||||
float32x4_t dotted##i = vmulq_f32(p##i, major_axis##i)
|
||||
COMPUTE_PLANE(0);
|
||||
COMPUTE_PLANE(1);
|
||||
COMPUTE_PLANE(2);
|
||||
COMPUTE_PLANE(3);
|
||||
COMPUTE_PLANE(4);
|
||||
COMPUTE_PLANE(5);
|
||||
|
||||
#if defined(__aarch64__)
|
||||
float32x4_t merged01 = vpaddq_f32(dotted0, dotted1);
|
||||
float32x4_t merged23 = vpaddq_f32(dotted2, dotted3);
|
||||
float32x4_t merged45 = vpaddq_f32(dotted4, dotted5);
|
||||
float32x4_t merged0123 = vpaddq_f32(merged01, merged23);
|
||||
merged45 = vpaddq_f32(merged45, merged45);
|
||||
float32x4_t merged = vminq_f32(merged0123, merged45);
|
||||
float32x2_t merged_half = vmin_f32(vget_low_f32(merged), vget_high_f32(merged));
|
||||
merged_half = vpmin_f32(merged_half, merged_half);
|
||||
return vget_lane_f32(merged_half, 0) >= 0.0f;
|
||||
#else
|
||||
float32x2_t merged0 = vpadd_f32(vget_low_f32(dotted0), vget_high_f32(dotted0));
|
||||
float32x2_t merged1 = vpadd_f32(vget_low_f32(dotted1), vget_high_f32(dotted1));
|
||||
float32x2_t merged2 = vpadd_f32(vget_low_f32(dotted2), vget_high_f32(dotted2));
|
||||
float32x2_t merged3 = vpadd_f32(vget_low_f32(dotted3), vget_high_f32(dotted3));
|
||||
float32x2_t merged4 = vpadd_f32(vget_low_f32(dotted4), vget_high_f32(dotted4));
|
||||
float32x2_t merged5 = vpadd_f32(vget_low_f32(dotted5), vget_high_f32(dotted5));
|
||||
float32x2_t merged01 = vpadd_f32(merged0, merged1);
|
||||
float32x2_t merged23 = vpadd_f32(merged2, merged3);
|
||||
float32x2_t merged45 = vpadd_f32(merged4, merged5);
|
||||
float32x2_t merged = vmin_f32(merged01, merged23);
|
||||
merged = vmin_f32(merged, merged45);
|
||||
float32x2_t merged_half = vpmin_f32(merged, merged);
|
||||
return vget_lane_f32(merged_half, 0) >= 0.0f;
|
||||
#endif
|
||||
#else
|
||||
#error "Implement me."
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void mul(vec4 &c, const mat_affine &a, const vec4 &b)
|
||||
{
|
||||
#if defined(__SSE4_1__)
|
||||
__m128 a0 = _mm_loadu_ps(a[0].data);
|
||||
__m128 a1 = _mm_loadu_ps(a[1].data);
|
||||
__m128 a2 = _mm_loadu_ps(a[2].data);
|
||||
__m128 b0 = _mm_loadu_ps(b.data);
|
||||
__m128 r0 = _mm_dp_ps(a0, b0, 0xf1);
|
||||
__m128 r1 = _mm_dp_ps(a1, b0, 0xf2);
|
||||
__m128 r2 = _mm_dp_ps(a2, b0, 0xf4);
|
||||
__m128 r = _mm_or_ps(_mm_or_ps(r0, r1), r2);
|
||||
r = _mm_insert_ps(r, _mm_set_ss(1.0f), 0x30);
|
||||
_mm_storeu_ps(c.data, r);
|
||||
#elif defined(__SSE__)
|
||||
__m128 a0 = _mm_loadu_ps(a[0].data);
|
||||
__m128 a1 = _mm_loadu_ps(a[1].data);
|
||||
__m128 a2 = _mm_loadu_ps(a[2].data);
|
||||
__m128 a3 = _mm_set_ps(1, 0, 0, 0);
|
||||
_MM_TRANSPOSE4_PS(a0, a1, a2, a3);
|
||||
__m128 b0 = _mm_loadu_ps(b.data);
|
||||
|
||||
__m128 b00 = _mm_shuffle_ps(b0, b0, _MM_SHUFFLE(0, 0, 0, 0));
|
||||
__m128 b01 = _mm_shuffle_ps(b0, b0, _MM_SHUFFLE(1, 1, 1, 1));
|
||||
__m128 b02 = _mm_shuffle_ps(b0, b0, _MM_SHUFFLE(2, 2, 2, 2));
|
||||
__m128 b03 = _mm_shuffle_ps(b0, b0, _MM_SHUFFLE(3, 3, 3, 3));
|
||||
|
||||
__m128 col0 = _mm_mul_ps(a0, b00);
|
||||
col0 = _mm_add_ps(col0, _mm_mul_ps(a1, b01));
|
||||
col0 = _mm_add_ps(col0, _mm_mul_ps(a2, b02));
|
||||
col0 = _mm_add_ps(col0, _mm_mul_ps(a3, b03));
|
||||
|
||||
_mm_storeu_ps(c.data, col0);
|
||||
#elif defined(__aarch64__)
|
||||
alignas(16) static const float a3_data[] = { 0, 0, 0, 1 };
|
||||
float32x4_t a0 = vld1q_f32(a[0].data);
|
||||
float32x4_t a1 = vld1q_f32(a[1].data);
|
||||
float32x4_t a2 = vld1q_f32(a[2].data);
|
||||
float32x4_t a3 = vld1q_f32(a3_data);
|
||||
|
||||
// From sse2neon.h
|
||||
float64x2_t r0 = (float64x2_t)vtrn1q_f32(a0, a1);
|
||||
float64x2_t r1 = (float64x2_t)vtrn2q_f32(a0, a1);
|
||||
float64x2_t r2 = (float64x2_t)vtrn1q_f32(a2, a3);
|
||||
float64x2_t r3 = (float64x2_t)vtrn2q_f32(a2, a3);
|
||||
|
||||
a0 = (float32x4_t)vtrn1q_f64(r0, r2);
|
||||
a1 = (float32x4_t)vtrn1q_f64(r1, r3);
|
||||
a2 = (float32x4_t)vtrn2q_f64(r0, r2);
|
||||
a3 = (float32x4_t)vtrn2q_f64(r1, r3);
|
||||
|
||||
float32x4_t b0 = vld1q_f32(b.data);
|
||||
|
||||
float32x4_t col0 = vmulq_n_f32(a0, vgetq_lane_f32(b0, 0));
|
||||
col0 = vmlaq_n_f32(col0, a1, vgetq_lane_f32(b0, 1));
|
||||
col0 = vmlaq_n_f32(col0, a2, vgetq_lane_f32(b0, 2));
|
||||
col0 = vmlaq_n_f32(col0, a3, vgetq_lane_f32(b0, 3));
|
||||
|
||||
vst1q_f32(c.data, col0);
|
||||
#else
|
||||
c = transpose(mat4(a[0], a[1], a[2], vec4(0, 0, 0, 1))) * b;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void mul(vec4 &c, const mat4 &a, const vec4 &b)
|
||||
{
|
||||
#if defined(__SSE__)
|
||||
__m128 a0 = _mm_loadu_ps(a[0].data);
|
||||
__m128 a1 = _mm_loadu_ps(a[1].data);
|
||||
__m128 a2 = _mm_loadu_ps(a[2].data);
|
||||
__m128 a3 = _mm_loadu_ps(a[3].data);
|
||||
__m128 b0 = _mm_loadu_ps(b.data);
|
||||
|
||||
__m128 b00 = _mm_shuffle_ps(b0, b0, _MM_SHUFFLE(0, 0, 0, 0));
|
||||
__m128 b01 = _mm_shuffle_ps(b0, b0, _MM_SHUFFLE(1, 1, 1, 1));
|
||||
__m128 b02 = _mm_shuffle_ps(b0, b0, _MM_SHUFFLE(2, 2, 2, 2));
|
||||
__m128 b03 = _mm_shuffle_ps(b0, b0, _MM_SHUFFLE(3, 3, 3, 3));
|
||||
|
||||
__m128 col0 = _mm_mul_ps(a0, b00);
|
||||
col0 = _mm_add_ps(col0, _mm_mul_ps(a1, b01));
|
||||
col0 = _mm_add_ps(col0, _mm_mul_ps(a2, b02));
|
||||
col0 = _mm_add_ps(col0, _mm_mul_ps(a3, b03));
|
||||
|
||||
_mm_storeu_ps(c.data, col0);
|
||||
#elif defined(__ARM_NEON)
|
||||
float32x4_t a0 = vld1q_f32(a[0].data);
|
||||
float32x4_t a1 = vld1q_f32(a[1].data);
|
||||
float32x4_t a2 = vld1q_f32(a[2].data);
|
||||
float32x4_t a3 = vld1q_f32(a[3].data);
|
||||
float32x4_t b0 = vld1q_f32(b.data);
|
||||
|
||||
float32x4_t col0 = vmulq_n_f32(a0, vgetq_lane_f32(b0, 0));
|
||||
col0 = vmlaq_n_f32(col0, a1, vgetq_lane_f32(b0, 1));
|
||||
col0 = vmlaq_n_f32(col0, a2, vgetq_lane_f32(b0, 2));
|
||||
col0 = vmlaq_n_f32(col0, a3, vgetq_lane_f32(b0, 3));
|
||||
|
||||
vst1q_f32(c.data, col0);
|
||||
#else
|
||||
c = a * b;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void mul(mat_affine &c, const mat_affine &a, const mat_affine &b)
|
||||
{
|
||||
#if defined(__SSE__)
|
||||
// Swap the arguments to allow treating the multiplication as column-major.
|
||||
__m128 a0 = _mm_loadu_ps(b[0].data);
|
||||
__m128 a1 = _mm_loadu_ps(b[1].data);
|
||||
__m128 a2 = _mm_loadu_ps(b[2].data);
|
||||
__m128 b0 = _mm_loadu_ps(a[0].data);
|
||||
__m128 b1 = _mm_loadu_ps(a[1].data);
|
||||
__m128 b2 = _mm_loadu_ps(a[2].data);
|
||||
const __m128 a3 = _mm_set_ps(1, 0, 0, 0);
|
||||
|
||||
__m128 b00 = _mm_shuffle_ps(b0, b0, _MM_SHUFFLE(0, 0, 0, 0));
|
||||
__m128 b01 = _mm_shuffle_ps(b0, b0, _MM_SHUFFLE(1, 1, 1, 1));
|
||||
__m128 b02 = _mm_shuffle_ps(b0, b0, _MM_SHUFFLE(2, 2, 2, 2));
|
||||
__m128 b03 = _mm_shuffle_ps(b0, b0, _MM_SHUFFLE(3, 3, 3, 3));
|
||||
|
||||
__m128 col0 = _mm_mul_ps(a0, b00);
|
||||
col0 = _mm_add_ps(col0, _mm_mul_ps(a1, b01));
|
||||
col0 = _mm_add_ps(col0, _mm_mul_ps(a2, b02));
|
||||
col0 = _mm_add_ps(col0, _mm_mul_ps(a3, b03));
|
||||
__m128 b10 = _mm_shuffle_ps(b1, b1, _MM_SHUFFLE(0, 0, 0, 0));
|
||||
__m128 b11 = _mm_shuffle_ps(b1, b1, _MM_SHUFFLE(1, 1, 1, 1));
|
||||
__m128 b12 = _mm_shuffle_ps(b1, b1, _MM_SHUFFLE(2, 2, 2, 2));
|
||||
__m128 b13 = _mm_shuffle_ps(b1, b1, _MM_SHUFFLE(3, 3, 3, 3));
|
||||
|
||||
__m128 col1 = _mm_mul_ps(a0, b10);
|
||||
col1 = _mm_add_ps(col1, _mm_mul_ps(a1, b11));
|
||||
col1 = _mm_add_ps(col1, _mm_mul_ps(a2, b12));
|
||||
col1 = _mm_add_ps(col1, _mm_mul_ps(a3, b13));
|
||||
|
||||
__m128 b20 = _mm_shuffle_ps(b2, b2, _MM_SHUFFLE(0, 0, 0, 0));
|
||||
__m128 b21 = _mm_shuffle_ps(b2, b2, _MM_SHUFFLE(1, 1, 1, 1));
|
||||
__m128 b22 = _mm_shuffle_ps(b2, b2, _MM_SHUFFLE(2, 2, 2, 2));
|
||||
__m128 b23 = _mm_shuffle_ps(b2, b2, _MM_SHUFFLE(3, 3, 3, 3));
|
||||
|
||||
__m128 col2 = _mm_mul_ps(a0, b20);
|
||||
col2 = _mm_add_ps(col2, _mm_mul_ps(a1, b21));
|
||||
col2 = _mm_add_ps(col2, _mm_mul_ps(a2, b22));
|
||||
col2 = _mm_add_ps(col2, _mm_mul_ps(a3, b23));
|
||||
|
||||
_mm_storeu_ps(c[0].data, col0);
|
||||
_mm_storeu_ps(c[1].data, col1);
|
||||
_mm_storeu_ps(c[2].data, col2);
|
||||
|
||||
#elif defined(__ARM_NEON)
|
||||
alignas(16) static const float a3_data[] = { 0, 0, 0, 1 };
|
||||
float32x4_t a0 = vld1q_f32(b[0].data);
|
||||
float32x4_t a1 = vld1q_f32(b[1].data);
|
||||
float32x4_t a2 = vld1q_f32(b[2].data);
|
||||
float32x4_t a3 = vld1q_f32(a3_data);
|
||||
float32x4_t b0 = vld1q_f32(a[0].data);
|
||||
float32x4_t b1 = vld1q_f32(a[1].data);
|
||||
float32x4_t b2 = vld1q_f32(a[2].data);
|
||||
|
||||
float32x4_t col0 = vmulq_n_f32(a0, vgetq_lane_f32(b0, 0));
|
||||
float32x4_t col1 = vmulq_n_f32(a0, vgetq_lane_f32(b1, 0));
|
||||
float32x4_t col2 = vmulq_n_f32(a0, vgetq_lane_f32(b2, 0));
|
||||
|
||||
col0 = vmlaq_n_f32(col0, a1, vgetq_lane_f32(b0, 1));
|
||||
col1 = vmlaq_n_f32(col1, a1, vgetq_lane_f32(b1, 1));
|
||||
col2 = vmlaq_n_f32(col2, a1, vgetq_lane_f32(b2, 1));
|
||||
|
||||
col0 = vmlaq_n_f32(col0, a2, vgetq_lane_f32(b0, 2));
|
||||
col1 = vmlaq_n_f32(col1, a2, vgetq_lane_f32(b1, 2));
|
||||
col2 = vmlaq_n_f32(col2, a2, vgetq_lane_f32(b2, 2));
|
||||
|
||||
col0 = vmlaq_n_f32(col0, a3, vgetq_lane_f32(b0, 3));
|
||||
col1 = vmlaq_n_f32(col1, a3, vgetq_lane_f32(b1, 3));
|
||||
col2 = vmlaq_n_f32(col2, a3, vgetq_lane_f32(b2, 3));
|
||||
|
||||
vst1q_f32(c[0].data, col0);
|
||||
vst1q_f32(c[1].data, col1);
|
||||
vst1q_f32(c[2].data, col2);
|
||||
#else
|
||||
mat4 a4(a[0], a[1], a[2], vec4(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
mat4 b4(b[0], b[1], b[2], vec4(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
mat4 c4 = b4 * a4;
|
||||
for (int i = 0; i < 3; i++)
|
||||
c[i] = c4[i];
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void mul(mat4 &c, const mat4 &a, const mat4 &b)
|
||||
{
|
||||
#if defined(__SSE__)
|
||||
__m128 a0 = _mm_loadu_ps(a[0].data);
|
||||
__m128 a1 = _mm_loadu_ps(a[1].data);
|
||||
__m128 a2 = _mm_loadu_ps(a[2].data);
|
||||
__m128 a3 = _mm_loadu_ps(a[3].data);
|
||||
__m128 b0 = _mm_loadu_ps(b[0].data);
|
||||
__m128 b1 = _mm_loadu_ps(b[1].data);
|
||||
__m128 b2 = _mm_loadu_ps(b[2].data);
|
||||
__m128 b3 = _mm_loadu_ps(b[3].data);
|
||||
|
||||
__m128 b00 = _mm_shuffle_ps(b0, b0, _MM_SHUFFLE(0, 0, 0, 0));
|
||||
__m128 b01 = _mm_shuffle_ps(b0, b0, _MM_SHUFFLE(1, 1, 1, 1));
|
||||
__m128 b02 = _mm_shuffle_ps(b0, b0, _MM_SHUFFLE(2, 2, 2, 2));
|
||||
__m128 b03 = _mm_shuffle_ps(b0, b0, _MM_SHUFFLE(3, 3, 3, 3));
|
||||
|
||||
__m128 col0 = _mm_mul_ps(a0, b00);
|
||||
col0 = _mm_add_ps(col0, _mm_mul_ps(a1, b01));
|
||||
col0 = _mm_add_ps(col0, _mm_mul_ps(a2, b02));
|
||||
col0 = _mm_add_ps(col0, _mm_mul_ps(a3, b03));
|
||||
|
||||
__m128 b10 = _mm_shuffle_ps(b1, b1, _MM_SHUFFLE(0, 0, 0, 0));
|
||||
__m128 b11 = _mm_shuffle_ps(b1, b1, _MM_SHUFFLE(1, 1, 1, 1));
|
||||
__m128 b12 = _mm_shuffle_ps(b1, b1, _MM_SHUFFLE(2, 2, 2, 2));
|
||||
__m128 b13 = _mm_shuffle_ps(b1, b1, _MM_SHUFFLE(3, 3, 3, 3));
|
||||
|
||||
__m128 col1 = _mm_mul_ps(a0, b10);
|
||||
col1 = _mm_add_ps(col1, _mm_mul_ps(a1, b11));
|
||||
col1 = _mm_add_ps(col1, _mm_mul_ps(a2, b12));
|
||||
col1 = _mm_add_ps(col1, _mm_mul_ps(a3, b13));
|
||||
|
||||
__m128 b20 = _mm_shuffle_ps(b2, b2, _MM_SHUFFLE(0, 0, 0, 0));
|
||||
__m128 b21 = _mm_shuffle_ps(b2, b2, _MM_SHUFFLE(1, 1, 1, 1));
|
||||
__m128 b22 = _mm_shuffle_ps(b2, b2, _MM_SHUFFLE(2, 2, 2, 2));
|
||||
__m128 b23 = _mm_shuffle_ps(b2, b2, _MM_SHUFFLE(3, 3, 3, 3));
|
||||
|
||||
__m128 col2 = _mm_mul_ps(a0, b20);
|
||||
col2 = _mm_add_ps(col2, _mm_mul_ps(a1, b21));
|
||||
col2 = _mm_add_ps(col2, _mm_mul_ps(a2, b22));
|
||||
col2 = _mm_add_ps(col2, _mm_mul_ps(a3, b23));
|
||||
|
||||
__m128 b30 = _mm_shuffle_ps(b3, b3, _MM_SHUFFLE(0, 0, 0, 0));
|
||||
__m128 b31 = _mm_shuffle_ps(b3, b3, _MM_SHUFFLE(1, 1, 1, 1));
|
||||
__m128 b32 = _mm_shuffle_ps(b3, b3, _MM_SHUFFLE(2, 2, 2, 2));
|
||||
__m128 b33 = _mm_shuffle_ps(b3, b3, _MM_SHUFFLE(3, 3, 3, 3));
|
||||
|
||||
__m128 col3 = _mm_mul_ps(a0, b30);
|
||||
col3 = _mm_add_ps(col3, _mm_mul_ps(a1, b31));
|
||||
col3 = _mm_add_ps(col3, _mm_mul_ps(a2, b32));
|
||||
col3 = _mm_add_ps(col3, _mm_mul_ps(a3, b33));
|
||||
|
||||
_mm_storeu_ps(c[0].data, col0);
|
||||
_mm_storeu_ps(c[1].data, col1);
|
||||
_mm_storeu_ps(c[2].data, col2);
|
||||
_mm_storeu_ps(c[3].data, col3);
|
||||
#elif defined(__ARM_NEON)
|
||||
float32x4_t a0 = vld1q_f32(a[0].data);
|
||||
float32x4_t a1 = vld1q_f32(a[1].data);
|
||||
float32x4_t a2 = vld1q_f32(a[2].data);
|
||||
float32x4_t a3 = vld1q_f32(a[3].data);
|
||||
float32x4_t b0 = vld1q_f32(b[0].data);
|
||||
float32x4_t b1 = vld1q_f32(b[1].data);
|
||||
float32x4_t b2 = vld1q_f32(b[2].data);
|
||||
float32x4_t b3 = vld1q_f32(b[3].data);
|
||||
|
||||
float32x4_t col0 = vmulq_n_f32(a0, vgetq_lane_f32(b0, 0));
|
||||
float32x4_t col1 = vmulq_n_f32(a0, vgetq_lane_f32(b1, 0));
|
||||
float32x4_t col2 = vmulq_n_f32(a0, vgetq_lane_f32(b2, 0));
|
||||
float32x4_t col3 = vmulq_n_f32(a0, vgetq_lane_f32(b3, 0));
|
||||
|
||||
col0 = vmlaq_n_f32(col0, a1, vgetq_lane_f32(b0, 1));
|
||||
col1 = vmlaq_n_f32(col1, a1, vgetq_lane_f32(b1, 1));
|
||||
col2 = vmlaq_n_f32(col2, a1, vgetq_lane_f32(b2, 1));
|
||||
col3 = vmlaq_n_f32(col3, a1, vgetq_lane_f32(b3, 1));
|
||||
|
||||
col0 = vmlaq_n_f32(col0, a2, vgetq_lane_f32(b0, 2));
|
||||
col1 = vmlaq_n_f32(col1, a2, vgetq_lane_f32(b1, 2));
|
||||
col2 = vmlaq_n_f32(col2, a2, vgetq_lane_f32(b2, 2));
|
||||
col3 = vmlaq_n_f32(col3, a2, vgetq_lane_f32(b3, 2));
|
||||
|
||||
col0 = vmlaq_n_f32(col0, a3, vgetq_lane_f32(b0, 3));
|
||||
col1 = vmlaq_n_f32(col1, a3, vgetq_lane_f32(b1, 3));
|
||||
col2 = vmlaq_n_f32(col2, a3, vgetq_lane_f32(b2, 3));
|
||||
col3 = vmlaq_n_f32(col3, a3, vgetq_lane_f32(b3, 3));
|
||||
|
||||
vst1q_f32(c[0].data, col0);
|
||||
vst1q_f32(c[1].data, col1);
|
||||
vst1q_f32(c[2].data, col2);
|
||||
vst1q_f32(c[3].data, col3);
|
||||
#else
|
||||
c = a * b;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void transform_aabb(AABB &output, const AABB &aabb, const mat_affine &m)
|
||||
{
|
||||
#if defined(__SSE__)
|
||||
__m128 lo = _mm_loadu_ps(aabb.get_minimum4().data);
|
||||
__m128 hi = _mm_loadu_ps(aabb.get_maximum4().data);
|
||||
|
||||
__m128 m0 = _mm_loadu_ps(m[0].data);
|
||||
__m128 m1 = _mm_loadu_ps(m[1].data);
|
||||
__m128 m2 = _mm_loadu_ps(m[2].data);
|
||||
__m128 m3 = _mm_set_ps(1, 0, 0, 0);
|
||||
_MM_TRANSPOSE4_PS(m0, m1, m2, m3);
|
||||
|
||||
__m128 m0_pos = _mm_cmpgt_ps(m0, _mm_setzero_ps());
|
||||
__m128 m1_pos = _mm_cmpgt_ps(m1, _mm_setzero_ps());
|
||||
__m128 m2_pos = _mm_cmpgt_ps(m2, _mm_setzero_ps());
|
||||
|
||||
__m128 hi0 = _mm_shuffle_ps(hi, hi, _MM_SHUFFLE(0, 0, 0, 0));
|
||||
__m128 hi1 = _mm_shuffle_ps(hi, hi, _MM_SHUFFLE(1, 1, 1, 1));
|
||||
__m128 hi2 = _mm_shuffle_ps(hi, hi, _MM_SHUFFLE(2, 2, 2, 2));
|
||||
__m128 lo0 = _mm_shuffle_ps(lo, lo, _MM_SHUFFLE(0, 0, 0, 0));
|
||||
__m128 lo1 = _mm_shuffle_ps(lo, lo, _MM_SHUFFLE(1, 1, 1, 1));
|
||||
__m128 lo2 = _mm_shuffle_ps(lo, lo, _MM_SHUFFLE(2, 2, 2, 2));
|
||||
|
||||
__m128 hi_result = m3;
|
||||
hi_result = _mm_add_ps(hi_result, _mm_mul_ps(m0, _mm_or_ps(_mm_and_ps(m0_pos, hi0), _mm_andnot_ps(m0_pos, lo0))));
|
||||
hi_result = _mm_add_ps(hi_result, _mm_mul_ps(m1, _mm_or_ps(_mm_and_ps(m1_pos, hi1), _mm_andnot_ps(m1_pos, lo1))));
|
||||
hi_result = _mm_add_ps(hi_result, _mm_mul_ps(m2, _mm_or_ps(_mm_and_ps(m2_pos, hi2), _mm_andnot_ps(m2_pos, lo2))));
|
||||
|
||||
__m128 lo_result = m3;
|
||||
lo_result = _mm_add_ps(lo_result, _mm_mul_ps(m0, _mm_or_ps(_mm_andnot_ps(m0_pos, hi0), _mm_and_ps(m0_pos, lo0))));
|
||||
lo_result = _mm_add_ps(lo_result, _mm_mul_ps(m1, _mm_or_ps(_mm_andnot_ps(m1_pos, hi1), _mm_and_ps(m1_pos, lo1))));
|
||||
lo_result = _mm_add_ps(lo_result, _mm_mul_ps(m2, _mm_or_ps(_mm_andnot_ps(m2_pos, hi2), _mm_and_ps(m2_pos, lo2))));
|
||||
|
||||
_mm_storeu_ps(output.get_minimum4().data, lo_result);
|
||||
_mm_storeu_ps(output.get_maximum4().data, hi_result);
|
||||
#elif defined(__aarch64__)
|
||||
alignas(16) static const float m3_data[] = { 0, 0, 0, 1 };
|
||||
float32x4_t lo = vld1q_f32(aabb.get_minimum4().data);
|
||||
float32x4_t hi = vld1q_f32(aabb.get_maximum4().data);
|
||||
|
||||
float32x4_t m0 = vld1q_f32(m[0].data);
|
||||
float32x4_t m1 = vld1q_f32(m[1].data);
|
||||
float32x4_t m2 = vld1q_f32(m[2].data);
|
||||
float32x4_t m3 = vld1q_f32(m3_data);
|
||||
|
||||
// From sse2neon.h
|
||||
float64x2_t r0 = (float64x2_t)vtrn1q_f32(m0, m1);
|
||||
float64x2_t r1 = (float64x2_t)vtrn2q_f32(m0, m1);
|
||||
float64x2_t r2 = (float64x2_t)vtrn1q_f32(m2, m3);
|
||||
float64x2_t r3 = (float64x2_t)vtrn2q_f32(m2, m3);
|
||||
|
||||
m0 = (float32x4_t)vtrn1q_f64(r0, r2);
|
||||
m1 = (float32x4_t)vtrn1q_f64(r1, r3);
|
||||
m2 = (float32x4_t)vtrn2q_f64(r0, r2);
|
||||
m3 = (float32x4_t)vtrn2q_f64(r1, r3);
|
||||
|
||||
uint32x4_t m0_pos = vcgtq_f32(m0, vdupq_n_f32(0.0f));
|
||||
uint32x4_t m1_pos = vcgtq_f32(m1, vdupq_n_f32(0.0f));
|
||||
uint32x4_t m2_pos = vcgtq_f32(m2, vdupq_n_f32(0.0f));
|
||||
|
||||
float32x4_t lo0 = vdupq_lane_f32(vget_low_f32(lo), 0);
|
||||
float32x4_t lo1 = vdupq_lane_f32(vget_low_f32(lo), 1);
|
||||
float32x4_t lo2 = vdupq_lane_f32(vget_high_f32(lo), 0);
|
||||
float32x4_t hi0 = vdupq_lane_f32(vget_low_f32(hi), 0);
|
||||
float32x4_t hi1 = vdupq_lane_f32(vget_low_f32(hi), 1);
|
||||
float32x4_t hi2 = vdupq_lane_f32(vget_high_f32(hi), 0);
|
||||
|
||||
float32x4_t hi_result = m3;
|
||||
hi_result = vmlaq_f32(hi_result, m0, vbslq_f32(m0_pos, hi0, lo0));
|
||||
hi_result = vmlaq_f32(hi_result, m1, vbslq_f32(m1_pos, hi1, lo1));
|
||||
hi_result = vmlaq_f32(hi_result, m2, vbslq_f32(m2_pos, hi2, lo2));
|
||||
|
||||
float32x4_t lo_result = m3;
|
||||
lo_result = vmlaq_f32(lo_result, m0, vbslq_f32(m0_pos, lo0, hi0));
|
||||
lo_result = vmlaq_f32(lo_result, m1, vbslq_f32(m1_pos, lo1, hi1));
|
||||
lo_result = vmlaq_f32(lo_result, m2, vbslq_f32(m2_pos, lo2, hi2));
|
||||
|
||||
vst1q_f32(output.get_minimum4().data, lo_result);
|
||||
vst1q_f32(output.get_maximum4().data, hi_result);
|
||||
#else
|
||||
output = aabb.transform(transpose(mat4(m[0], m[1], m[2], vec4(0, 0, 0, 1))));
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void transform_aabb(AABB &output, const AABB &aabb, const mat4 &m)
|
||||
{
|
||||
#if defined(__SSE__)
|
||||
__m128 lo = _mm_loadu_ps(aabb.get_minimum4().data);
|
||||
__m128 hi = _mm_loadu_ps(aabb.get_maximum4().data);
|
||||
|
||||
__m128 m0 = _mm_loadu_ps(m[0].data);
|
||||
__m128 m1 = _mm_loadu_ps(m[1].data);
|
||||
__m128 m2 = _mm_loadu_ps(m[2].data);
|
||||
__m128 m3 = _mm_loadu_ps(m[3].data);
|
||||
|
||||
__m128 m0_pos = _mm_cmpgt_ps(m0, _mm_setzero_ps());
|
||||
__m128 m1_pos = _mm_cmpgt_ps(m1, _mm_setzero_ps());
|
||||
__m128 m2_pos = _mm_cmpgt_ps(m2, _mm_setzero_ps());
|
||||
|
||||
__m128 hi0 = _mm_shuffle_ps(hi, hi, _MM_SHUFFLE(0, 0, 0, 0));
|
||||
__m128 hi1 = _mm_shuffle_ps(hi, hi, _MM_SHUFFLE(1, 1, 1, 1));
|
||||
__m128 hi2 = _mm_shuffle_ps(hi, hi, _MM_SHUFFLE(2, 2, 2, 2));
|
||||
__m128 lo0 = _mm_shuffle_ps(lo, lo, _MM_SHUFFLE(0, 0, 0, 0));
|
||||
__m128 lo1 = _mm_shuffle_ps(lo, lo, _MM_SHUFFLE(1, 1, 1, 1));
|
||||
__m128 lo2 = _mm_shuffle_ps(lo, lo, _MM_SHUFFLE(2, 2, 2, 2));
|
||||
|
||||
__m128 hi_result = m3;
|
||||
hi_result = _mm_add_ps(hi_result, _mm_mul_ps(m0, _mm_or_ps(_mm_and_ps(m0_pos, hi0), _mm_andnot_ps(m0_pos, lo0))));
|
||||
hi_result = _mm_add_ps(hi_result, _mm_mul_ps(m1, _mm_or_ps(_mm_and_ps(m1_pos, hi1), _mm_andnot_ps(m1_pos, lo1))));
|
||||
hi_result = _mm_add_ps(hi_result, _mm_mul_ps(m2, _mm_or_ps(_mm_and_ps(m2_pos, hi2), _mm_andnot_ps(m2_pos, lo2))));
|
||||
|
||||
__m128 lo_result = m3;
|
||||
lo_result = _mm_add_ps(lo_result, _mm_mul_ps(m0, _mm_or_ps(_mm_andnot_ps(m0_pos, hi0), _mm_and_ps(m0_pos, lo0))));
|
||||
lo_result = _mm_add_ps(lo_result, _mm_mul_ps(m1, _mm_or_ps(_mm_andnot_ps(m1_pos, hi1), _mm_and_ps(m1_pos, lo1))));
|
||||
lo_result = _mm_add_ps(lo_result, _mm_mul_ps(m2, _mm_or_ps(_mm_andnot_ps(m2_pos, hi2), _mm_and_ps(m2_pos, lo2))));
|
||||
|
||||
_mm_storeu_ps(output.get_minimum4().data, lo_result);
|
||||
_mm_storeu_ps(output.get_maximum4().data, hi_result);
|
||||
#elif defined(__ARM_NEON)
|
||||
float32x4_t lo = vld1q_f32(aabb.get_minimum4().data);
|
||||
float32x4_t hi = vld1q_f32(aabb.get_maximum4().data);
|
||||
|
||||
float32x4_t m0 = vld1q_f32(m[0].data);
|
||||
float32x4_t m1 = vld1q_f32(m[1].data);
|
||||
float32x4_t m2 = vld1q_f32(m[2].data);
|
||||
float32x4_t m3 = vld1q_f32(m[3].data);
|
||||
|
||||
uint32x4_t m0_pos = vcgtq_f32(m0, vdupq_n_f32(0.0f));
|
||||
uint32x4_t m1_pos = vcgtq_f32(m1, vdupq_n_f32(0.0f));
|
||||
uint32x4_t m2_pos = vcgtq_f32(m2, vdupq_n_f32(0.0f));
|
||||
|
||||
float32x4_t lo0 = vdupq_lane_f32(vget_low_f32(lo), 0);
|
||||
float32x4_t lo1 = vdupq_lane_f32(vget_low_f32(lo), 1);
|
||||
float32x4_t lo2 = vdupq_lane_f32(vget_high_f32(lo), 0);
|
||||
float32x4_t hi0 = vdupq_lane_f32(vget_low_f32(hi), 0);
|
||||
float32x4_t hi1 = vdupq_lane_f32(vget_low_f32(hi), 1);
|
||||
float32x4_t hi2 = vdupq_lane_f32(vget_high_f32(hi), 0);
|
||||
|
||||
float32x4_t hi_result = m3;
|
||||
hi_result = vmlaq_f32(hi_result, m0, vbslq_f32(m0_pos, hi0, lo0));
|
||||
hi_result = vmlaq_f32(hi_result, m1, vbslq_f32(m1_pos, hi1, lo1));
|
||||
hi_result = vmlaq_f32(hi_result, m2, vbslq_f32(m2_pos, hi2, lo2));
|
||||
|
||||
float32x4_t lo_result = m3;
|
||||
lo_result = vmlaq_f32(lo_result, m0, vbslq_f32(m0_pos, lo0, hi0));
|
||||
lo_result = vmlaq_f32(lo_result, m1, vbslq_f32(m1_pos, lo1, hi1));
|
||||
lo_result = vmlaq_f32(lo_result, m2, vbslq_f32(m2_pos, lo2, hi2));
|
||||
|
||||
vst1q_f32(output.get_minimum4().data, lo_result);
|
||||
vst1q_f32(output.get_maximum4().data, hi_result);
|
||||
#else
|
||||
output = aabb.transform(m);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline void transform_and_expand_aabb(AABB &expandee, const AABB &aabb, const T &m)
|
||||
{
|
||||
alignas(16) AABB tmp;
|
||||
transform_aabb(tmp, aabb, m);
|
||||
#if defined(__SSE__)
|
||||
__m128 lo = _mm_min_ps(_mm_load_ps(tmp.get_minimum4().data), _mm_loadu_ps(expandee.get_minimum4().data));
|
||||
__m128 hi = _mm_max_ps(_mm_load_ps(tmp.get_maximum4().data), _mm_loadu_ps(expandee.get_maximum4().data));
|
||||
_mm_storeu_ps(expandee.get_minimum4().data, lo);
|
||||
_mm_storeu_ps(expandee.get_maximum4().data, hi);
|
||||
#elif defined(__ARM_NEON)
|
||||
float32x4_t lo = vminq_f32(vld1q_f32(tmp.get_minimum4().data), vld1q_f32(expandee.get_minimum4().data));
|
||||
float32x4_t hi = vmaxq_f32(vld1q_f32(tmp.get_maximum4().data), vld1q_f32(expandee.get_maximum4().data));
|
||||
vst1q_f32(expandee.get_minimum4().data, lo);
|
||||
vst1q_f32(expandee.get_maximum4().data, hi);
|
||||
#else
|
||||
auto &output_min = expandee.get_minimum4();
|
||||
auto &output_max = expandee.get_maximum4();
|
||||
output_min = min<vec4>(output_min, tmp.get_minimum4());
|
||||
output_max = max<vec4>(output_max, tmp.get_maximum4());
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void convert_quaternion_with_scale(vec4 *cols, const quat &q, const vec3 &scale)
|
||||
{
|
||||
#if defined(__SSE3__)
|
||||
__m128 quat = _mm_loadu_ps(q.as_vec4().data);
|
||||
|
||||
#define SHUF(x, y, z) _mm_shuffle_ps(quat, quat, _MM_SHUFFLE(z, y, x, 3))
|
||||
__m128 q_yy_xz_xy = _mm_mul_ps(SHUF(1, 0, 0), SHUF(1, 2, 1));
|
||||
__m128 q_zz_wy_wz = _mm_mul_ps(SHUF(2, 3, 3), SHUF(2, 1, 2));
|
||||
__m128 col0 = _mm_mul_ps(_mm_set_ps(+2.0f, +2.0f, -2.0f, 0.0f), _mm_addsub_ps(q_yy_xz_xy, q_zz_wy_wz));
|
||||
col0 = _mm_shuffle_ps(col0, col0, _MM_SHUFFLE(0, 2, 3, 1));
|
||||
col0 = _mm_add_ps(col0, _mm_set_ss(1.0f));
|
||||
col0 = _mm_mul_ps(col0, _mm_set1_ps(scale.x));
|
||||
_mm_storeu_ps(cols[0].data, col0);
|
||||
|
||||
__m128 q_xx_xy_yz = _mm_mul_ps(SHUF(0, 0, 1), SHUF(0, 1, 2));
|
||||
__m128 q_zz_wz_wx = _mm_mul_ps(SHUF(2, 3, 3), SHUF(2, 2, 0));
|
||||
__m128 col1 = _mm_mul_ps(_mm_set_ps(2.0f, 2.0f, -2.0f, 0.0f), _mm_addsub_ps(q_xx_xy_yz, q_zz_wz_wx));
|
||||
col1 = _mm_shuffle_ps(col1, col1, _MM_SHUFFLE(0, 3, 1, 2));
|
||||
col1 = _mm_add_ps(col1, _mm_set_ps(0.0f, 0.0f, 1.0f, 0.0f));
|
||||
col1 = _mm_mul_ps(col1, _mm_set1_ps(scale.y));
|
||||
_mm_storeu_ps(cols[1].data, col1);
|
||||
|
||||
__m128 q_xz_yz_xx = _mm_mul_ps(SHUF(0, 1, 0), SHUF(2, 2, 0));
|
||||
__m128 q_wy_wx_yy = _mm_mul_ps(SHUF(3, 3, 1), SHUF(1, 0, 1));
|
||||
__m128 col2 = _mm_mul_ps(_mm_set_ps(-2.0f, 2.0f, 2.0f, 0.0f), _mm_addsub_ps(q_xz_yz_xx, q_wy_wx_yy));
|
||||
col2 = _mm_shuffle_ps(col2, col2, _MM_SHUFFLE(0, 3, 2, 1));
|
||||
col2 = _mm_add_ps(col2, _mm_set_ps(0.0f, 1.0f, 0.0f, 0.0f));
|
||||
col2 = _mm_mul_ps(col2, _mm_set1_ps(scale.z));
|
||||
_mm_storeu_ps(cols[2].data, col2);
|
||||
#undef SHUF
|
||||
#else
|
||||
mat3 m = muglm::mat3_cast(q);
|
||||
cols[0] = vec4(m[0] * scale.x, 0.0f);
|
||||
cols[1] = vec4(m[1] * scale.y, 0.0f);
|
||||
cols[2] = vec4(m[2] * scale.z, 0.0f);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/* Copyright (c) 2017-2026 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
#include <intrin.h>
|
||||
#if defined(_INCLUDED_PMM) && !defined(__SSE3__)
|
||||
#define __SSE3__ 1
|
||||
#endif
|
||||
#if !defined(__SSE__)
|
||||
#define __SSE__ 1
|
||||
#endif
|
||||
#if defined(_INCLUDED_IMM) && !defined(__AVX__)
|
||||
#define __AVX__ 1
|
||||
#endif
|
||||
|
||||
#elif defined(__AVX__)
|
||||
#include <immintrin.h>
|
||||
#elif defined(__SSE4_1__)
|
||||
#include <smmintrin.h>
|
||||
#elif defined(__SSE3__)
|
||||
#include <pmmintrin.h>
|
||||
#elif defined(__SSE__)
|
||||
#include <xmmintrin.h>
|
||||
#elif defined(__ARM_NEON)
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
@@ -0,0 +1,371 @@
|
||||
/* Copyright (c) 2017-2026 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "transforms.hpp"
|
||||
#include "aabb.hpp"
|
||||
#include "simd.hpp"
|
||||
#include "muglm/matrix_helper.hpp"
|
||||
#include <assert.h>
|
||||
|
||||
namespace Granite
|
||||
{
|
||||
bool compute_plane_reflection(mat4 &projection, mat4 &view, vec3 camera_pos, vec3 center, vec3 normal, vec3 look_up,
|
||||
float radius_up, float radius_other, float &z_near, float z_far)
|
||||
{
|
||||
normal = normalize(normal);
|
||||
|
||||
// Reflect the camera position from the plane.
|
||||
float over_plane = dot(normal, camera_pos - center);
|
||||
if (over_plane <= 0.0f)
|
||||
return false;
|
||||
|
||||
camera_pos -= 2.0f * over_plane * normal;
|
||||
|
||||
// The look direction is up through the plane direction.
|
||||
// This way we avoid skewed near and far planes (i.e. oblique).
|
||||
// Make sure look_up is perpendicular to normal.
|
||||
vec3 look_pos_x = normalize(cross(normal, look_up));
|
||||
look_up = normalize(cross(look_pos_x, normal));
|
||||
|
||||
view = mat4_cast(look_at(normal, look_up)) * translate(-camera_pos);
|
||||
|
||||
float dist_x = dot(look_pos_x, center - camera_pos);
|
||||
float left = dist_x - radius_other;
|
||||
float right = dist_x + radius_other;
|
||||
|
||||
float dist_y = dot(look_up, center - camera_pos);
|
||||
float bottom = dist_y - radius_up;
|
||||
float top = dist_y + radius_up;
|
||||
|
||||
z_near = over_plane;
|
||||
projection = frustum(left, right, bottom, top, over_plane, z_far);
|
||||
if (z_near >= z_far)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool compute_plane_refraction(mat4 &projection, mat4 &view, vec3 camera_pos, vec3 center, vec3 normal, vec3 look_up,
|
||||
float radius_up, float radius_other, float &z_near, float z_far)
|
||||
{
|
||||
normal = normalize(normal);
|
||||
|
||||
// Reflect the camera position from the plane.
|
||||
float over_plane = dot(normal, camera_pos - center);
|
||||
if (over_plane <= 0.0f)
|
||||
return false;
|
||||
|
||||
normal = -normal;
|
||||
|
||||
// The look direction is up through the plane direction.
|
||||
// This way we avoid skewed near and far planes (i.e. oblique).
|
||||
// Make sure look_up is perpendicular to normal.
|
||||
vec3 look_pos_x = normalize(cross(normal, look_up));
|
||||
look_up = normalize(cross(look_pos_x, normal));
|
||||
|
||||
view = mat4_cast(look_at(normal, look_up)) * translate(-camera_pos);
|
||||
|
||||
float dist_x = dot(look_pos_x, center - camera_pos);
|
||||
float left = dist_x - radius_other;
|
||||
float right = dist_x + radius_other;
|
||||
|
||||
float dist_y = dot(look_up, center - camera_pos);
|
||||
float bottom = dist_y - radius_up;
|
||||
float top = dist_y + radius_up;
|
||||
|
||||
z_near = over_plane;
|
||||
projection = frustum(left, right, bottom, top, over_plane, z_far);
|
||||
if (z_near >= z_far)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void compute_model_transform(mat_affine &world, vec3 s, quat rot, vec3 trans, const mat_affine &parent)
|
||||
{
|
||||
// TODO: Make this more affine friendly.
|
||||
mat4 model;
|
||||
model[3] = vec4(trans, 1.0f);
|
||||
SIMD::convert_quaternion_with_scale(&model[0], rot, s);
|
||||
|
||||
SIMD::mul(world, parent, mat_affine(model));
|
||||
}
|
||||
|
||||
void compute_normal_transform(mat4 &normal, const mat4 &world)
|
||||
{
|
||||
normal = mat4(transpose(inverse(mat3(world))));
|
||||
}
|
||||
|
||||
void compute_normal_transform(mat_affine &normal, const mat_affine &world)
|
||||
{
|
||||
// Can be done better, but not important unless it gets used a lot.
|
||||
normal = mat_affine(mat4(transpose(inverse(world.to_mat3()))));
|
||||
}
|
||||
|
||||
quat rotate_vector(vec3 from, vec3 to)
|
||||
{
|
||||
from = normalize(from);
|
||||
to = normalize(to);
|
||||
|
||||
float cos_angle = dot(from, to);
|
||||
if (abs(cos_angle) > 0.9999f)
|
||||
{
|
||||
if (cos_angle > 0.9999f)
|
||||
return quat(1.0f, 0.0f, 0.0f, 0.0f);
|
||||
else
|
||||
{
|
||||
vec3 rotation = cross(vec3(1.0f, 0.0f, 0.0f), from);
|
||||
if (dot(rotation, rotation) > 0.001f)
|
||||
rotation = normalize(rotation);
|
||||
else
|
||||
rotation = normalize(cross(vec3(0.0f, 1.0f, 0.0f), from));
|
||||
return quat(0.0f, rotation);
|
||||
}
|
||||
}
|
||||
|
||||
vec3 rotation = normalize(cross(from, to));
|
||||
vec3 half_vector = normalize(from + to);
|
||||
float cos_half_range = clamp(dot(half_vector, from), 0.0f, 1.0f);
|
||||
float sin_half_angle = sqrtf(1.0f - cos_half_range * cos_half_range);
|
||||
return quat(cos_half_range, rotation * sin_half_angle);
|
||||
}
|
||||
|
||||
quat rotate_vector_axis(vec3 from, vec3 to, vec3 axis)
|
||||
{
|
||||
axis = normalize(axis);
|
||||
from = normalize(cross(axis, from));
|
||||
to = normalize(cross(axis, to));
|
||||
|
||||
if (dot(to, from) < -0.9999f)
|
||||
return quat(0.0f, axis);
|
||||
|
||||
// Rotate CCW or CW, we only find the angle of rotation below.
|
||||
float quat_sign = sign(dot(axis, cross(from, to)));
|
||||
|
||||
vec3 half_vector = normalize(from + to);
|
||||
float cos_half_range = clamp(dot(half_vector, from), 0.0f, 1.0f);
|
||||
float sin_half_angle = quat_sign * sqrtf(1.0f - cos_half_range * cos_half_range);
|
||||
return quat(cos_half_range, axis * sin_half_angle);
|
||||
}
|
||||
|
||||
quat look_at(vec3 direction, vec3 up)
|
||||
{
|
||||
static const vec3 z(0.0f, 0.0f, -1.0f);
|
||||
static const vec3 y(0.0f, 1.0f, 0.0f);
|
||||
direction = normalize(direction);
|
||||
vec3 right = cross(direction, up);
|
||||
vec3 actual_up = cross(right, direction);
|
||||
quat look_transform = rotate_vector(direction, z);
|
||||
quat up_transform = rotate_vector_axis(look_transform * actual_up, y, z);
|
||||
return up_transform * look_transform;
|
||||
}
|
||||
|
||||
quat look_at_arbitrary_up(vec3 direction)
|
||||
{
|
||||
return rotate_vector(normalize(direction), vec3(0.0f, 0.0f, -1.0f));
|
||||
}
|
||||
|
||||
mat4 projection(float fovy, float aspect, float znear, float zfar)
|
||||
{
|
||||
return perspective(fovy, aspect, znear, zfar);
|
||||
}
|
||||
|
||||
mat4 ortho(const AABB &aabb)
|
||||
{
|
||||
vec3 min = aabb.get_minimum();
|
||||
vec3 max = aabb.get_maximum();
|
||||
|
||||
// Flip Z for RH, ortho zNear/zFar is LH style.
|
||||
std::swap(max.z, min.z);
|
||||
max.z = -max.z;
|
||||
min.z = -min.z;
|
||||
|
||||
return muglm::ortho(min.x, max.x, min.y, max.y, min.z, max.z);
|
||||
}
|
||||
|
||||
void compute_cube_render_transform(vec3 center, unsigned face, mat4 &proj, mat4 &view, float znear, float zfar)
|
||||
{
|
||||
static const vec3 dirs[6] = {
|
||||
vec3(1.0f, 0.0f, 0.0f),
|
||||
vec3(-1.0f, 0.0f, 0.0f),
|
||||
vec3(0.0f, 1.0f, 0.0f),
|
||||
vec3(0.0f, -1.0f, 0.0f),
|
||||
vec3(0.0f, 0.0f, 1.0f),
|
||||
vec3(0.0f, 0.0f, -1.0f),
|
||||
};
|
||||
|
||||
static const vec3 ups[6] = {
|
||||
vec3(0.0f, 1.0f, 0.0f),
|
||||
vec3(0.0f, 1.0f, 0.0f),
|
||||
vec3(0.0f, 0.0f, -1.0f),
|
||||
vec3(0.0f, 0.0f, +1.0f),
|
||||
vec3(0.0f, 1.0f, 0.0f),
|
||||
vec3(0.0f, 1.0f, 0.0f),
|
||||
};
|
||||
|
||||
view = mat4_cast(look_at(dirs[face], ups[face])) * translate(-center);
|
||||
proj = scale(vec3(-1.0f, 1.0f, 1.0f)) * projection(0.5f * pi<float>(), 1.0f, znear, zfar);
|
||||
}
|
||||
|
||||
vec3 PositionalSampler::sample(unsigned index, float l) const
|
||||
{
|
||||
if (l == 0.0f)
|
||||
return values[index];
|
||||
else if (l == 1.0f)
|
||||
return values[index + 1];
|
||||
|
||||
assert(index + 1 < values.size());
|
||||
return mix(values[index], values[index + 1], l);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T compute_cubic_spline(const std::vector<T> &values, unsigned index, float t, float dt)
|
||||
{
|
||||
assert(3 * index + 4 < values.size());
|
||||
T p0 = values[3 * index + 1];
|
||||
T p1 = values[3 * index + 4];
|
||||
|
||||
// For t == 0.0f, the result must be exactly on the point as specified by glTF.
|
||||
if (t == 0.0f)
|
||||
return p0;
|
||||
else if (t == 1.0f)
|
||||
return p1;
|
||||
|
||||
T m0 = dt * values[3 * index + 2];
|
||||
T m1 = dt * values[3 * index + 3];
|
||||
|
||||
float t2 = t * t;
|
||||
float t3 = t2 * t;
|
||||
|
||||
return (2.0f * t3 - 3.0f * t2 + 1.0f) * p0 +
|
||||
(t3 - 2.0f * t2 + t) * m0 +
|
||||
(-2.0f * t3 + 3.0f * t2) * p1 +
|
||||
(t3 - t2) * m1;
|
||||
}
|
||||
|
||||
vec3 PositionalSampler::sample_spline(unsigned index, float t, float dt) const
|
||||
{
|
||||
return compute_cubic_spline(values, index, t, dt);
|
||||
}
|
||||
|
||||
quat SphericalSampler::sample(unsigned index, float l) const
|
||||
{
|
||||
if (l == 0.0f)
|
||||
return quat(values[index]);
|
||||
else if (l == 1.0f)
|
||||
return quat(values[index + 1]);
|
||||
|
||||
assert(index + 1 < values.size());
|
||||
return slerp(quat(values[index]), quat(values[index + 1]), l);
|
||||
}
|
||||
|
||||
quat SphericalSampler::sample_spline(unsigned index, float t, float dt) const
|
||||
{
|
||||
// CUBICSPLINE for quaternion is defined as simple vec4 interpolation with normalization.
|
||||
return normalize(quat(compute_cubic_spline(values, index, t, dt)));
|
||||
}
|
||||
|
||||
// See math/docs/squad.md for more detail and derivation.
|
||||
|
||||
quat SphericalSampler::sample_squad(unsigned index, float l) const
|
||||
{
|
||||
assert(3 * index + 4 < values.size());
|
||||
|
||||
if (l == 0.0f)
|
||||
return quat(values[3 * index + 1]);
|
||||
else if (l == 1.0f)
|
||||
return quat(values[3 * index + 4]);
|
||||
|
||||
quat q0 = quat(values[3 * index + 1]);
|
||||
quat cp0 = quat(values[3 * index + 2]);
|
||||
quat cp1 = quat(values[3 * index + 3]);
|
||||
quat q1 = quat(values[3 * index + 4]);
|
||||
|
||||
return slerp_no_invert(slerp_no_invert(q0, q1, l), slerp_no_invert(cp0, cp1, l), 2.0f * l * (1.0f - l));
|
||||
}
|
||||
|
||||
quat compute_inner_control_point(const quat &q, const vec3 &delta)
|
||||
{
|
||||
return q * quat_exp(-delta);
|
||||
}
|
||||
|
||||
vec3 compute_inner_control_point_delta(const quat &q0, const quat &q1, const quat &q2,
|
||||
float dt0, float dt1)
|
||||
{
|
||||
// This is almost gibberish, as this is just copy-pastaed from various implementations
|
||||
// found on the interwebs.
|
||||
// From studying it in greater detail,
|
||||
// the basic gist is that quaternion log and exp are used to
|
||||
// decompose what should be a series of multiplications (quat rotations) into additions, since
|
||||
// ln(a * b) = ln(a) + ln(b), and exp(ln(a)) = a.
|
||||
|
||||
// ln(q) means encoding a vec3 where the length encodes theta, and direction encodes direction.
|
||||
// Summing ln(a) + ln(b) will therefore "add" the addition together, similar to how one
|
||||
// would add torque vectors in physics. The exp must then re-encode the vector-magnitude encoding
|
||||
// back to normal quaternion form.
|
||||
|
||||
// In this domain we can average rotations, and go back again to a normal quaternion with exp.
|
||||
// inv_q1 * q2 and inv_q1 * q0 both do some form of "differential" of the rotations.
|
||||
// q12 and q10 estimate first derivative at the control points.
|
||||
// q12 and q10 have opposing signs,
|
||||
// so the sum of the logs is therefore seen as instantaneous acceleration at the q1.
|
||||
|
||||
// quat_log() breaks down if q.w goes negative it seems, so that explains some shenanigans
|
||||
// where some docs say that this only works for "normal" interpolation scenarios.
|
||||
// Probably more than good enough for us though.
|
||||
|
||||
// Weigh the deltas so that they compute absolute velocity and acceleration.
|
||||
// Rescale back to spline time domain after.
|
||||
|
||||
quat inv_q1 = conjugate(q1);
|
||||
quat delta_k = inv_q1 * q2; // q2 - q1
|
||||
quat delta_k_minus1 = inv_q1 * q0; // q0 - q1 = -(q1 - q0)
|
||||
vec3 delta_k_log = quat_log(delta_k);
|
||||
vec3 delta_k_minus1_log = quat_log(delta_k_minus1);
|
||||
|
||||
// We sample velocity at the center of the segment when taking the difference.
|
||||
// Future sample is at t = +1/2 dt
|
||||
// Past sample is at t = -1/2 dt
|
||||
float segment_time = 0.5f * (dt0 + dt1);
|
||||
vec3 absolute_accel = (delta_k_log / dt1 + delta_k_minus1_log / dt0) / segment_time;
|
||||
vec3 delta = (0.25f * dt1 * dt1) * absolute_accel;
|
||||
return delta;
|
||||
}
|
||||
|
||||
// From https://mina86.com/2019/srgb-xyz-matrix/
|
||||
static vec3 convert_primary(const vec2 &xy)
|
||||
{
|
||||
float X = xy.x / xy.y;
|
||||
float Y = 1.0f;
|
||||
float Z = (1.0f - xy.x - xy.y) / xy.y;
|
||||
return vec3(X, Y, Z);
|
||||
}
|
||||
|
||||
mat3 compute_xyz_matrix(const Primaries &primaries)
|
||||
{
|
||||
vec3 red = convert_primary(primaries.red);
|
||||
vec3 green = convert_primary(primaries.green);
|
||||
vec3 blue = convert_primary(primaries.blue);
|
||||
vec3 white = convert_primary(primaries.white_point);
|
||||
|
||||
vec3 component_scale = inverse(mat3(red, green, blue)) * white;
|
||||
return mat3(red * component_scale.x, green * component_scale.y, blue * component_scale.z);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/* Copyright (c) 2017-2026 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "math.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace Granite
|
||||
{
|
||||
class AABB;
|
||||
|
||||
bool compute_plane_reflection(mat4 &projection, mat4 &view, vec3 camera_pos, vec3 center, vec3 normal, vec3 look_up,
|
||||
float radius_up, float radius_other, float &z_near, float z_far);
|
||||
|
||||
bool compute_plane_refraction(mat4 &projection, mat4 &view, vec3 camera_pos, vec3 center, vec3 normal, vec3 look_up,
|
||||
float radius_up, float radius_other, float &z_near, float z_far);
|
||||
|
||||
void compute_model_transform(mat_affine &world, vec3 scale, quat rotation, vec3 translation, const mat_affine &parent);
|
||||
|
||||
void compute_normal_transform(mat4 &normal, const mat4 &world);
|
||||
void compute_normal_transform(mat_affine &normal, const mat_affine &world);
|
||||
|
||||
quat rotate_vector(vec3 from, vec3 to);
|
||||
|
||||
quat look_at(vec3 direction, vec3 up);
|
||||
|
||||
quat look_at_arbitrary_up(vec3 direction);
|
||||
|
||||
quat rotate_vector_axis(vec3 from, vec3 to, vec3 axis);
|
||||
|
||||
mat4 projection(float fovy, float aspect, float znear, float zfar);
|
||||
|
||||
mat4 ortho(const AABB &aabb);
|
||||
|
||||
void compute_cube_render_transform(vec3 center, unsigned face, mat4 &projection, mat4 &view, float znear, float zfar);
|
||||
|
||||
struct PositionalSampler
|
||||
{
|
||||
std::vector<vec3> values;
|
||||
vec3 sample(unsigned index, float l) const;
|
||||
vec3 sample_spline(unsigned index, float l, float dt) const;
|
||||
};
|
||||
|
||||
struct SphericalSampler
|
||||
{
|
||||
std::vector<vec4> values;
|
||||
quat sample(unsigned index, float l) const;
|
||||
quat sample_spline(unsigned index, float l, float dt) const;
|
||||
quat sample_squad(unsigned index, float l) const;
|
||||
};
|
||||
|
||||
// Compute control points for q1.
|
||||
// dt0 is delta time between q0 and q1.
|
||||
// dt1 is delta time between q1 and q2.
|
||||
vec3 compute_inner_control_point_delta(const quat &q0, const quat &q1, const quat &q2,
|
||||
float dt0, float dt1);
|
||||
quat compute_inner_control_point(const quat &q, const vec3 &delta);
|
||||
|
||||
struct Primaries
|
||||
{
|
||||
vec2 red, green, blue, white_point;
|
||||
};
|
||||
|
||||
mat3 compute_xyz_matrix(const Primaries &primaries);
|
||||
}
|
||||
Reference in New Issue
Block a user