969 lines
28 KiB
C++
969 lines
28 KiB
C++
/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
* Copyright 2022 Blender Foundation. */
|
|
|
|
#pragma once
|
|
|
|
/** \file
|
|
* \ingroup bli
|
|
*
|
|
* Template for matrix types.
|
|
*
|
|
* The `blender::MatBase<T, NumCol, NumRow>` is a Row x Col matrix (in mathematical notation) laid
|
|
* out as column major in memory.
|
|
*
|
|
* This class overloads `+, -, *` and `+=, -=, *=` mathematical operators.
|
|
* They are all using per component operation, except for a few:
|
|
* `MatBase<C,R> * Vector<C>` the vector product with the matrix.
|
|
* `Vector<R> * MatBase<C,R>` the vector product with the **transposed** matrix.
|
|
* `MatBase<C,R> * MatBase<R,C>` and `MatBase<C,R> *= MatBase<R,C>` the matrix multiplication.
|
|
*
|
|
* The `blender::MatView` allows working on a subset of a matrix without having to move the data
|
|
* around. It can be obtained using the `MatBase.view<NumCol, NumRow>()`. It is const by default if
|
|
* the matrix type is. Otherwise, a `blender::MutableMatView` is returned.
|
|
*
|
|
* A `blender::MutableMatView`. It is mostly the same as `blender::MatView`, but can to be
|
|
* modified.
|
|
*
|
|
* This allow working with any number type `T` (float, double, mpq, ...) and to use these types in
|
|
* shared shader files (code compiled in both C++ and Shader language). To this end, only low level
|
|
* constructors are defined inside the class itself and every function working on matrices are
|
|
* defined outside of the class in the `blender::math` namespace.
|
|
*/
|
|
|
|
#include <array>
|
|
#include <cmath>
|
|
#include <iostream>
|
|
#include <type_traits>
|
|
|
|
#include "BLI_math_vector_types.hh"
|
|
#include "BLI_utildefines.h"
|
|
#include "BLI_utility_mixins.hh"
|
|
|
|
namespace blender {
|
|
|
|
template<typename T,
|
|
int NumCol,
|
|
int NumRow,
|
|
int SrcNumCol,
|
|
int SrcNumRow,
|
|
int SrcStartCol,
|
|
int SrcStartRow,
|
|
int Alignment>
|
|
struct MatView;
|
|
|
|
template<typename T,
|
|
int NumCol,
|
|
int NumRow,
|
|
int SrcNumCol,
|
|
int SrcNumRow,
|
|
int SrcStartCol,
|
|
int SrcStartRow,
|
|
int Alignment>
|
|
struct MutableMatView;
|
|
|
|
template<
|
|
/* Number type. */
|
|
typename T,
|
|
/* Number of column in the matrix. */
|
|
int NumCol,
|
|
/* Number of row in the matrix. */
|
|
int NumRow,
|
|
/* Alignment in bytes. Do not align matrices whose size is not a multiple of 4 component.
|
|
* This is in order to avoid padding when using arrays of matrices. */
|
|
int Alignment = (((NumCol * NumRow) % 4 == 0) ? 4 : 1) * sizeof(T)>
|
|
struct alignas(Alignment) MatBase : public vec_struct_base<VecBase<T, NumRow>, NumCol> {
|
|
|
|
using base_type = T;
|
|
using vec3_type = VecBase<T, 3>;
|
|
using col_type = VecBase<T, NumRow>;
|
|
using row_type = VecBase<T, NumCol>;
|
|
using loc_type = VecBase<T, (NumRow < NumCol) ? NumRow : (NumRow - 1)>;
|
|
static constexpr int min_dim = (NumRow < NumCol) ? NumRow : NumCol;
|
|
static constexpr int col_len = NumCol;
|
|
static constexpr int row_len = NumRow;
|
|
|
|
MatBase() = default;
|
|
|
|
/* Workaround issue with template BLI_ENABLE_IF((Size == 2)) not working. */
|
|
#define BLI_ENABLE_IF_MAT(_size, _test) int S = _size, BLI_ENABLE_IF((S _test))
|
|
|
|
template<BLI_ENABLE_IF_MAT(NumCol, == 2)> MatBase(col_type _x, col_type _y)
|
|
{
|
|
(*this)[0] = _x;
|
|
(*this)[1] = _y;
|
|
}
|
|
|
|
template<BLI_ENABLE_IF_MAT(NumCol, == 3)> MatBase(col_type _x, col_type _y, col_type _z)
|
|
{
|
|
(*this)[0] = _x;
|
|
(*this)[1] = _y;
|
|
(*this)[2] = _z;
|
|
}
|
|
|
|
template<BLI_ENABLE_IF_MAT(NumCol, == 4)>
|
|
MatBase(col_type _x, col_type _y, col_type _z, col_type _w)
|
|
{
|
|
(*this)[0] = _x;
|
|
(*this)[1] = _y;
|
|
(*this)[2] = _z;
|
|
(*this)[3] = _w;
|
|
}
|
|
|
|
/** Masking. */
|
|
|
|
template<typename U, int OtherNumCol, int OtherNumRow>
|
|
explicit MatBase(const MatBase<U, OtherNumCol, OtherNumRow> &other)
|
|
{
|
|
if constexpr ((OtherNumRow >= NumRow) && (OtherNumCol >= NumCol)) {
|
|
unroll<NumCol>([&](auto i) { (*this)[i] = col_type(other[i]); });
|
|
}
|
|
else {
|
|
/* Allow enlarging following GLSL standard (i.e: mat4x4(mat3x3())). */
|
|
unroll<NumCol>([&](auto i) {
|
|
unroll<NumRow>([&](auto j) {
|
|
if (i < OtherNumCol && j < OtherNumRow) {
|
|
(*this)[i][j] = other[i][j];
|
|
}
|
|
else if (i == j) {
|
|
(*this)[i][j] = T(1);
|
|
}
|
|
else {
|
|
(*this)[i][j] = T(0);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
#undef BLI_ENABLE_IF_MAT
|
|
|
|
/** Conversion from pointers (from C-style vectors). */
|
|
|
|
explicit MatBase(const T *ptr)
|
|
{
|
|
unroll<NumCol>([&](auto i) { (*this)[i] = reinterpret_cast<const col_type *>(ptr)[i]; });
|
|
}
|
|
|
|
template<typename U, BLI_ENABLE_IF((std::is_convertible_v<U, T>))> explicit MatBase(const U *ptr)
|
|
{
|
|
unroll<NumCol>([&](auto i) { (*this)[i] = ptr[i]; });
|
|
}
|
|
|
|
explicit MatBase(const T (*ptr)[NumCol]) : MatBase(static_cast<const T *>(ptr[0])) {}
|
|
|
|
/** Conversion from other matrix types. */
|
|
|
|
template<typename U> explicit MatBase(const MatBase<U, NumRow, NumCol> &vec)
|
|
{
|
|
unroll<NumCol>([&](auto i) { (*this)[i] = col_type(vec[i]); });
|
|
}
|
|
|
|
/** C-style pointer dereference. */
|
|
|
|
using c_style_mat = T[NumCol][NumRow];
|
|
|
|
/** \note Prevent implicit cast to types that could fit other pointer constructor. */
|
|
const c_style_mat &ptr() const
|
|
{
|
|
return *reinterpret_cast<const c_style_mat *>(this);
|
|
}
|
|
|
|
/** \note Prevent implicit cast to types that could fit other pointer constructor. */
|
|
c_style_mat &ptr()
|
|
{
|
|
return *reinterpret_cast<c_style_mat *>(this);
|
|
}
|
|
|
|
/** \note Prevent implicit cast to types that could fit other pointer constructor. */
|
|
const T *base_ptr() const
|
|
{
|
|
return reinterpret_cast<const T *>(this);
|
|
}
|
|
|
|
/** \note Prevent implicit cast to types that could fit other pointer constructor. */
|
|
T *base_ptr()
|
|
{
|
|
return reinterpret_cast<T *>(this);
|
|
}
|
|
|
|
/** View creation. */
|
|
|
|
template<int ViewNumCol = NumCol,
|
|
int ViewNumRow = NumRow,
|
|
int SrcStartCol = 0,
|
|
int SrcStartRow = 0>
|
|
const MatView<T, ViewNumCol, ViewNumRow, NumCol, NumRow, SrcStartCol, SrcStartRow, Alignment>
|
|
view() const
|
|
{
|
|
return MatView<T, ViewNumCol, ViewNumRow, NumCol, NumRow, SrcStartCol, SrcStartRow, Alignment>(
|
|
const_cast<MatBase &>(*this));
|
|
}
|
|
|
|
template<int ViewNumCol = NumCol,
|
|
int ViewNumRow = NumRow,
|
|
int SrcStartCol = 0,
|
|
int SrcStartRow = 0>
|
|
MutableMatView<T, ViewNumCol, ViewNumRow, NumCol, NumRow, SrcStartCol, SrcStartRow, Alignment>
|
|
view()
|
|
{
|
|
return MutableMatView<T,
|
|
ViewNumCol,
|
|
ViewNumRow,
|
|
NumCol,
|
|
NumRow,
|
|
SrcStartCol,
|
|
SrcStartRow,
|
|
Alignment>(*this);
|
|
}
|
|
|
|
/** Array access. */
|
|
|
|
const col_type &operator[](int index) const
|
|
{
|
|
BLI_assert(index >= 0);
|
|
BLI_assert(index < NumCol);
|
|
return reinterpret_cast<const col_type *>(this)[index];
|
|
}
|
|
|
|
col_type &operator[](int index)
|
|
{
|
|
BLI_assert(index >= 0);
|
|
BLI_assert(index < NumCol);
|
|
return reinterpret_cast<col_type *>(this)[index];
|
|
}
|
|
|
|
/** Access helpers. Using Blender coordinate system. */
|
|
|
|
vec3_type &x_axis()
|
|
{
|
|
BLI_STATIC_ASSERT(NumCol >= 1, "Wrong Matrix dimension");
|
|
BLI_STATIC_ASSERT(NumRow >= 3, "Wrong Matrix dimension");
|
|
return *reinterpret_cast<vec3_type *>(&(*this)[0]);
|
|
}
|
|
|
|
vec3_type &y_axis()
|
|
{
|
|
BLI_STATIC_ASSERT(NumCol >= 2, "Wrong Matrix dimension");
|
|
BLI_STATIC_ASSERT(NumRow >= 3, "Wrong Matrix dimension");
|
|
return *reinterpret_cast<vec3_type *>(&(*this)[1]);
|
|
}
|
|
|
|
vec3_type &z_axis()
|
|
{
|
|
BLI_STATIC_ASSERT(NumCol >= 3, "Wrong Matrix dimension");
|
|
BLI_STATIC_ASSERT(NumRow >= 3, "Wrong Matrix dimension");
|
|
return *reinterpret_cast<vec3_type *>(&(*this)[2]);
|
|
}
|
|
|
|
loc_type &location()
|
|
{
|
|
BLI_STATIC_ASSERT(NumCol >= 3, "Wrong Matrix dimension");
|
|
BLI_STATIC_ASSERT(NumRow >= 2, "Wrong Matrix dimension");
|
|
return *reinterpret_cast<loc_type *>(&(*this)[NumCol - 1]);
|
|
}
|
|
|
|
const vec3_type &x_axis() const
|
|
{
|
|
BLI_STATIC_ASSERT(NumCol >= 1, "Wrong Matrix dimension");
|
|
BLI_STATIC_ASSERT(NumRow >= 3, "Wrong Matrix dimension");
|
|
return *reinterpret_cast<const vec3_type *>(&(*this)[0]);
|
|
}
|
|
|
|
const vec3_type &y_axis() const
|
|
{
|
|
BLI_STATIC_ASSERT(NumCol >= 2, "Wrong Matrix dimension");
|
|
BLI_STATIC_ASSERT(NumRow >= 3, "Wrong Matrix dimension");
|
|
return *reinterpret_cast<const vec3_type *>(&(*this)[1]);
|
|
}
|
|
|
|
const vec3_type &z_axis() const
|
|
{
|
|
BLI_STATIC_ASSERT(NumCol >= 3, "Wrong Matrix dimension");
|
|
BLI_STATIC_ASSERT(NumRow >= 3, "Wrong Matrix dimension");
|
|
return *reinterpret_cast<const vec3_type *>(&(*this)[2]);
|
|
}
|
|
|
|
const loc_type &location() const
|
|
{
|
|
BLI_STATIC_ASSERT(NumCol >= 3, "Wrong Matrix dimension");
|
|
BLI_STATIC_ASSERT(NumRow >= 2, "Wrong Matrix dimension");
|
|
return *reinterpret_cast<const loc_type *>(&(*this)[NumCol - 1]);
|
|
}
|
|
|
|
/** Matrix operators. */
|
|
|
|
friend MatBase operator+(const MatBase &a, const MatBase &b)
|
|
{
|
|
MatBase result;
|
|
unroll<NumCol>([&](auto i) { result[i] = a[i] + b[i]; });
|
|
return result;
|
|
}
|
|
|
|
friend MatBase operator+(const MatBase &a, T b)
|
|
{
|
|
MatBase result;
|
|
unroll<NumCol>([&](auto i) { result[i] = a[i] + b; });
|
|
return result;
|
|
}
|
|
|
|
friend MatBase operator+(T a, const MatBase &b)
|
|
{
|
|
return b + a;
|
|
}
|
|
|
|
MatBase &operator+=(const MatBase &b)
|
|
{
|
|
unroll<NumCol>([&](auto i) { (*this)[i] += b[i]; });
|
|
return *this;
|
|
}
|
|
|
|
MatBase &operator+=(T b)
|
|
{
|
|
unroll<NumCol>([&](auto i) { (*this)[i] += b; });
|
|
return *this;
|
|
}
|
|
|
|
friend MatBase operator-(const MatBase &a)
|
|
{
|
|
MatBase result;
|
|
unroll<NumCol>([&](auto i) { result[i] = -a[i]; });
|
|
return result;
|
|
}
|
|
|
|
friend MatBase operator-(const MatBase &a, const MatBase &b)
|
|
{
|
|
MatBase result;
|
|
unroll<NumCol>([&](auto i) { result[i] = a[i] - b[i]; });
|
|
return result;
|
|
}
|
|
|
|
friend MatBase operator-(const MatBase &a, T b)
|
|
{
|
|
MatBase result;
|
|
unroll<NumCol>([&](auto i) { result[i] = a[i] - b; });
|
|
return result;
|
|
}
|
|
|
|
friend MatBase operator-(T a, const MatBase &b)
|
|
{
|
|
MatBase result;
|
|
unroll<NumCol>([&](auto i) { result[i] = a - b[i]; });
|
|
return result;
|
|
}
|
|
|
|
MatBase &operator-=(const MatBase &b)
|
|
{
|
|
unroll<NumCol>([&](auto i) { (*this)[i] -= b[i]; });
|
|
return *this;
|
|
}
|
|
|
|
MatBase &operator-=(T b)
|
|
{
|
|
unroll<NumCol>([&](auto i) { (*this)[i] -= b; });
|
|
return *this;
|
|
}
|
|
|
|
/** Multiply two matrices using matrix multiplication. */
|
|
MatBase<T, NumRow, NumRow> operator*(const MatBase<T, NumRow, NumCol> &b) const
|
|
{
|
|
const MatBase &a = *this;
|
|
/* This is the reference implementation.
|
|
* Might be overloaded with vectorized / optimized code. */
|
|
/* TODO(fclem): It should be possible to return non-square matrices when multiplying against
|
|
* MatBase<T, NumRow, OtherNumRow>. */
|
|
MatBase<T, NumRow, NumRow> result{};
|
|
unroll<NumRow>([&](auto j) {
|
|
unroll<NumRow>([&](auto i) {
|
|
/* Same as dot product, but avoid dependency on vector math. */
|
|
unroll<NumCol>([&](auto k) { result[j][i] += a[k][i] * b[j][k]; });
|
|
});
|
|
});
|
|
return result;
|
|
}
|
|
|
|
/** Multiply each component by a scalar. */
|
|
friend MatBase operator*(const MatBase &a, T b)
|
|
{
|
|
MatBase result;
|
|
unroll<NumCol>([&](auto i) { result[i] = a[i] * b; });
|
|
return result;
|
|
}
|
|
|
|
/** Multiply each component by a scalar. */
|
|
friend MatBase operator*(T a, const MatBase &b)
|
|
{
|
|
return b * a;
|
|
}
|
|
|
|
/** Multiply two matrices using matrix multiplication. */
|
|
MatBase &operator*=(const MatBase &b)
|
|
{
|
|
const MatBase &a = *this;
|
|
*this = a * b;
|
|
return *this;
|
|
}
|
|
|
|
/** Multiply each component by a scalar. */
|
|
MatBase &operator*=(T b)
|
|
{
|
|
unroll<NumCol>([&](auto i) { (*this)[i] *= b; });
|
|
return *this;
|
|
}
|
|
|
|
/** Vector operators. */
|
|
|
|
friend col_type operator*(const MatBase &a, const row_type &b)
|
|
{
|
|
/* This is the reference implementation.
|
|
* Might be overloaded with vectorized / optimized code. */
|
|
col_type result(0);
|
|
unroll<NumCol>([&](auto c) { result += b[c] * a[c]; });
|
|
return result;
|
|
}
|
|
|
|
/** Multiply by the transposed. */
|
|
friend row_type operator*(const col_type &a, const MatBase &b)
|
|
{
|
|
/* This is the reference implementation.
|
|
* Might be overloaded with vectorized / optimized code. */
|
|
row_type result(0);
|
|
unroll<NumCol>([&](auto c) { unroll<NumRow>([&](auto r) { result[c] += b[c][r] * a[r]; }); });
|
|
return result;
|
|
}
|
|
|
|
/** Compare. */
|
|
|
|
friend bool operator==(const MatBase &a, const MatBase &b)
|
|
{
|
|
for (int i = 0; i < NumCol; i++) {
|
|
if (a[i] != b[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
friend bool operator!=(const MatBase &a, const MatBase &b)
|
|
{
|
|
return !(a == b);
|
|
}
|
|
|
|
/** Miscellaneous. */
|
|
|
|
static MatBase diagonal(T value)
|
|
{
|
|
MatBase result{};
|
|
unroll<min_dim>([&](auto i) { result[i][i] = value; });
|
|
return result;
|
|
}
|
|
|
|
static MatBase all(T value)
|
|
{
|
|
MatBase result;
|
|
unroll<NumCol>([&](auto i) { result[i] = col_type(value); });
|
|
return result;
|
|
}
|
|
|
|
static MatBase identity()
|
|
{
|
|
return diagonal(1);
|
|
}
|
|
|
|
static MatBase zero()
|
|
{
|
|
return all(0);
|
|
}
|
|
|
|
uint64_t hash() const
|
|
{
|
|
uint64_t h = 435109;
|
|
unroll<NumCol * NumRow>([&](auto i) {
|
|
T value = (reinterpret_cast<const T *>(this))[i];
|
|
h = h * 33 + *reinterpret_cast<const as_uint_type<T> *>(&value);
|
|
});
|
|
return h;
|
|
}
|
|
|
|
friend std::ostream &operator<<(std::ostream &stream, const MatBase &mat)
|
|
{
|
|
stream << "(\n";
|
|
unroll<NumCol>([&](auto i) {
|
|
stream << "(";
|
|
unroll<NumRow>([&](auto j) {
|
|
/** NOTE: j and i are swapped to follow mathematical convention. */
|
|
stream << mat[j][i];
|
|
if (j < NumRow - 1) {
|
|
stream << ", ";
|
|
}
|
|
});
|
|
stream << ")";
|
|
if (i < NumCol - 1) {
|
|
stream << ",";
|
|
}
|
|
stream << "\n";
|
|
});
|
|
stream << ")\n";
|
|
return stream;
|
|
}
|
|
};
|
|
|
|
template<typename T,
|
|
/** The view dimensions. */
|
|
int NumCol,
|
|
int NumRow,
|
|
/** The source matrix dimensions. */
|
|
int SrcNumCol,
|
|
int SrcNumRow,
|
|
/** The base offset inside the source matrix. */
|
|
int SrcStartCol,
|
|
int SrcStartRow,
|
|
/** The source matrix alignment. */
|
|
int SrcAlignment>
|
|
struct MatView : NonCopyable, NonMovable {
|
|
using MatT = MatBase<T, NumCol, NumRow>;
|
|
using SrcMatT = MatBase<T, SrcNumCol, SrcNumRow, SrcAlignment>;
|
|
using col_type = VecBase<T, NumRow>;
|
|
using row_type = VecBase<T, NumCol>;
|
|
|
|
const SrcMatT &mat;
|
|
|
|
MatView() = delete;
|
|
|
|
MatView(const SrcMatT &src) : mat(src)
|
|
{
|
|
BLI_STATIC_ASSERT(SrcStartCol >= 0, "View does not fit source matrix dimensions");
|
|
BLI_STATIC_ASSERT(SrcStartRow >= 0, "View does not fit source matrix dimensions");
|
|
BLI_STATIC_ASSERT(SrcStartCol + NumCol <= SrcNumCol,
|
|
"View does not fit source matrix dimensions");
|
|
BLI_STATIC_ASSERT(SrcStartRow + NumRow <= SrcNumRow,
|
|
"View does not fit source matrix dimensions");
|
|
}
|
|
|
|
/** Allow wrapping C-style matrices using view. IMPORTANT: Alignment of src needs to match. */
|
|
explicit MatView(const float (*src)[SrcNumRow])
|
|
: MatView(*reinterpret_cast<const SrcMatT *>(&src[0][0])){};
|
|
|
|
/** Array access. */
|
|
|
|
const col_type &operator[](int index) const
|
|
{
|
|
BLI_assert(index >= 0);
|
|
BLI_assert(index < NumCol);
|
|
return *reinterpret_cast<const col_type *>(&mat[index + SrcStartCol][SrcStartRow]);
|
|
}
|
|
|
|
/** Conversion back to matrix. */
|
|
|
|
operator MatT() const
|
|
{
|
|
MatT mat;
|
|
unroll<NumCol>([&](auto c) { mat[c] = (*this)[c]; });
|
|
return mat;
|
|
}
|
|
|
|
/** Matrix operators. */
|
|
|
|
friend MatT operator+(const MatView &a, T b)
|
|
{
|
|
MatT result;
|
|
unroll<NumCol>([&](auto i) { result[i] = a[i] + b; });
|
|
return result;
|
|
}
|
|
|
|
friend MatT operator+(T a, const MatView &b)
|
|
{
|
|
return b + a;
|
|
}
|
|
|
|
friend MatT operator-(const MatView &a)
|
|
{
|
|
MatT result;
|
|
unroll<NumCol>([&](auto i) { result[i] = -a[i]; });
|
|
return result;
|
|
}
|
|
|
|
template<int OtherSrcNumCol,
|
|
int OtherSrcNumRow,
|
|
int OtherSrcStartCol,
|
|
int OtherSrcStartRow,
|
|
int OtherSrcAlignment>
|
|
friend MatT operator-(const MatView &a,
|
|
const MatView<T,
|
|
NumCol,
|
|
NumRow,
|
|
OtherSrcNumCol,
|
|
OtherSrcNumRow,
|
|
OtherSrcStartCol,
|
|
OtherSrcStartRow,
|
|
OtherSrcAlignment> &b)
|
|
{
|
|
MatT result;
|
|
unroll<NumCol>([&](auto i) { result[i] = a[i] - b[i]; });
|
|
return result;
|
|
}
|
|
|
|
friend MatT operator-(const MatView &a, const MatT &b)
|
|
{
|
|
return a - b.view();
|
|
}
|
|
|
|
template<int OtherSrcNumCol,
|
|
int OtherSrcNumRow,
|
|
int OtherSrcStartCol,
|
|
int OtherSrcStartRow,
|
|
int OtherSrcAlignment>
|
|
friend MatT operator-(const MatView<T,
|
|
NumCol,
|
|
NumRow,
|
|
OtherSrcNumCol,
|
|
OtherSrcNumRow,
|
|
OtherSrcStartCol,
|
|
OtherSrcStartRow,
|
|
OtherSrcAlignment> &a,
|
|
const MatView &b)
|
|
{
|
|
MatT result;
|
|
unroll<NumCol>([&](auto i) { result[i] = a[i] - b[i]; });
|
|
return result;
|
|
}
|
|
|
|
friend MatT operator-(const MatT &a, const MatView &b)
|
|
{
|
|
return a.view() - b;
|
|
}
|
|
|
|
friend MatT operator-(const MatView &a, T b)
|
|
{
|
|
MatT result;
|
|
unroll<NumCol>([&](auto i) { result[i] = a[i] - b; });
|
|
return result;
|
|
}
|
|
|
|
friend MatView operator-(T a, const MatView &b)
|
|
{
|
|
MatView result;
|
|
unroll<NumCol>([&](auto i) { result[i] = a - b[i]; });
|
|
return result;
|
|
}
|
|
|
|
/** Multiply two matrices using matrix multiplication. */
|
|
template<int OtherSrcNumCol,
|
|
int OtherSrcNumRow,
|
|
int OtherSrcStartCol,
|
|
int OtherSrcStartRow,
|
|
int OtherSrcAlignment>
|
|
MatBase<T, NumRow, NumRow> operator*(const MatView<T,
|
|
NumRow,
|
|
NumCol,
|
|
OtherSrcNumCol,
|
|
OtherSrcNumRow,
|
|
OtherSrcStartCol,
|
|
OtherSrcStartRow,
|
|
OtherSrcAlignment> &b) const
|
|
{
|
|
const MatView &a = *this;
|
|
/* This is the reference implementation.
|
|
* Might be overloaded with vectorized / optimized code. */
|
|
MatBase<T, NumRow, NumRow> result{};
|
|
unroll<NumRow>([&](auto j) {
|
|
unroll<NumRow>([&](auto i) {
|
|
/* Same as dot product, but avoid dependency on vector math. */
|
|
unroll<NumCol>([&](auto k) { result[j][i] += a[k][i] * b[j][k]; });
|
|
});
|
|
});
|
|
return result;
|
|
}
|
|
|
|
MatT operator*(const MatT &b) const
|
|
{
|
|
return *this * b.view();
|
|
}
|
|
|
|
/** Multiply each component by a scalar. */
|
|
friend MatT operator*(const MatView &a, T b)
|
|
{
|
|
MatT result;
|
|
unroll<NumCol>([&](auto i) { result[i] = a[i] * b; });
|
|
return result;
|
|
}
|
|
|
|
/** Multiply each component by a scalar. */
|
|
friend MatT operator*(T a, const MatView &b)
|
|
{
|
|
return b * a;
|
|
}
|
|
|
|
/** Vector operators. */
|
|
|
|
friend col_type operator*(const MatView &a, const row_type &b)
|
|
{
|
|
/* This is the reference implementation.
|
|
* Might be overloaded with vectorized / optimized code. */
|
|
col_type result(0);
|
|
unroll<NumCol>([&](auto c) { result += b[c] * a[c]; });
|
|
return result;
|
|
}
|
|
|
|
/** Multiply by the transposed. */
|
|
friend row_type operator*(const col_type &a, const MatView &b)
|
|
{
|
|
/* This is the reference implementation.
|
|
* Might be overloaded with vectorized / optimized code. */
|
|
row_type result(0);
|
|
unroll<NumCol>([&](auto c) { unroll<NumRow>([&](auto r) { result[c] += b[c][r] * a[r]; }); });
|
|
return result;
|
|
}
|
|
|
|
/** Compare. */
|
|
|
|
friend bool operator==(const MatView &a, const MatView &b)
|
|
{
|
|
for (int i = 0; i < NumCol; i++) {
|
|
if (a[i] != b[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
friend bool operator!=(const MatView &a, const MatView &b)
|
|
{
|
|
return !(a == b);
|
|
}
|
|
|
|
/** Miscellaneous. */
|
|
|
|
friend std::ostream &operator<<(std::ostream &stream, const MatView &mat)
|
|
{
|
|
return stream << mat->mat;
|
|
}
|
|
};
|
|
|
|
template<typename T,
|
|
/** The view dimensions. */
|
|
int NumCol,
|
|
int NumRow,
|
|
/** The source matrix dimensions. */
|
|
int SrcNumCol,
|
|
int SrcNumRow,
|
|
/** The base offset inside the source matrix. */
|
|
int SrcStartCol,
|
|
int SrcStartRow,
|
|
/** The source matrix alignment. */
|
|
int SrcAlignment>
|
|
struct MutableMatView
|
|
: MatView<T, NumCol, NumRow, SrcNumCol, SrcNumRow, SrcStartCol, SrcStartRow, SrcAlignment> {
|
|
|
|
using MatT = MatBase<T, NumCol, NumRow>;
|
|
using MatViewT =
|
|
MatView<T, NumCol, NumRow, SrcNumCol, SrcNumRow, SrcStartCol, SrcStartRow, SrcAlignment>;
|
|
using SrcMatT = MatBase<T, SrcNumCol, SrcNumRow, SrcAlignment>;
|
|
using col_type = VecBase<T, NumRow>;
|
|
using row_type = VecBase<T, NumCol>;
|
|
|
|
public:
|
|
MutableMatView() = delete;
|
|
|
|
MutableMatView(SrcMatT &src) : MatViewT(const_cast<const SrcMatT &>(src)){};
|
|
|
|
/** Allow wrapping C-style matrices using view. IMPORTANT: Alignment of src needs to match. */
|
|
explicit MutableMatView(float src[SrcNumCol][SrcNumRow])
|
|
: MutableMatView(*reinterpret_cast<SrcMatT *>(&src[0][0])){};
|
|
|
|
/** Array access. */
|
|
|
|
col_type &operator[](int index)
|
|
{
|
|
return const_cast<col_type &>(static_cast<MatViewT &>(*this)[index]);
|
|
}
|
|
|
|
/** Conversion to immutable view. */
|
|
|
|
operator MatViewT() const
|
|
{
|
|
return MatViewT(this->mat);
|
|
}
|
|
|
|
/** Copy Assignment. */
|
|
|
|
template<int OtherSrcNumCol,
|
|
int OtherSrcNumRow,
|
|
int OtherSrcStartCol,
|
|
int OtherSrcStartRow,
|
|
int OtherSrcAlignment>
|
|
MutableMatView &operator=(const MatView<T,
|
|
NumCol,
|
|
NumRow,
|
|
OtherSrcNumCol,
|
|
OtherSrcNumRow,
|
|
OtherSrcStartCol,
|
|
OtherSrcStartRow,
|
|
OtherSrcAlignment> &other)
|
|
{
|
|
BLI_assert_msg(
|
|
(reinterpret_cast<const void *>(&other.mat[0][0]) !=
|
|
reinterpret_cast<const void *>(&this->mat[0][0])) ||
|
|
/* Make sure assignment won't overwrite the source. OtherSrc* is the source. */
|
|
((OtherSrcStartCol > SrcStartCol) || (OtherSrcStartCol + NumCol <= SrcStartCol) ||
|
|
(OtherSrcStartRow > SrcStartRow + NumRow) ||
|
|
(OtherSrcStartRow + NumRow <= SrcStartRow)),
|
|
"Operation is undefined if views overlap.");
|
|
unroll<NumCol>([&](auto i) { (*this)[i] = other[i]; });
|
|
return *this;
|
|
}
|
|
|
|
MutableMatView &operator=(const MatT &other)
|
|
{
|
|
*this = other.view();
|
|
return *this;
|
|
}
|
|
|
|
/** Matrix operators. */
|
|
|
|
template<int OtherSrcNumCol,
|
|
int OtherSrcNumRow,
|
|
int OtherSrcStartCol,
|
|
int OtherSrcStartRow,
|
|
int OtherSrcAlignment>
|
|
MutableMatView &operator+=(const MatView<T,
|
|
NumCol,
|
|
NumRow,
|
|
OtherSrcNumCol,
|
|
OtherSrcNumRow,
|
|
OtherSrcStartCol,
|
|
OtherSrcStartRow,
|
|
OtherSrcAlignment> &b)
|
|
{
|
|
unroll<NumCol>([&](auto i) { (*this)[i] += b[i]; });
|
|
return *this;
|
|
}
|
|
|
|
MutableMatView &operator+=(const MatT &b)
|
|
{
|
|
return *this += b.view();
|
|
}
|
|
|
|
MutableMatView &operator+=(T b)
|
|
{
|
|
unroll<NumCol>([&](auto i) { (*this)[i] += b; });
|
|
return *this;
|
|
}
|
|
|
|
template<int OtherSrcNumCol,
|
|
int OtherSrcNumRow,
|
|
int OtherSrcStartCol,
|
|
int OtherSrcStartRow,
|
|
int OtherSrcAlignment>
|
|
MutableMatView &operator-=(const MatView<T,
|
|
NumCol,
|
|
NumRow,
|
|
OtherSrcNumCol,
|
|
OtherSrcNumRow,
|
|
OtherSrcStartCol,
|
|
OtherSrcStartRow,
|
|
OtherSrcAlignment> &b)
|
|
{
|
|
unroll<NumCol>([&](auto i) { (*this)[i] -= b[i]; });
|
|
return *this;
|
|
}
|
|
|
|
MutableMatView &operator-=(const MatT &b)
|
|
{
|
|
return *this -= b.view();
|
|
}
|
|
|
|
MutableMatView &operator-=(T b)
|
|
{
|
|
unroll<NumCol>([&](auto i) { (*this)[i] -= b; });
|
|
return *this;
|
|
}
|
|
|
|
/** Multiply two matrices using matrix multiplication. */
|
|
template<int OtherSrcNumCol,
|
|
int OtherSrcNumRow,
|
|
int OtherSrcStartCol,
|
|
int OtherSrcStartRow,
|
|
int OtherSrcAlignment>
|
|
MutableMatView &operator*=(const MatView<T,
|
|
NumCol,
|
|
NumRow,
|
|
OtherSrcNumCol,
|
|
OtherSrcNumRow,
|
|
OtherSrcStartCol,
|
|
OtherSrcStartRow,
|
|
OtherSrcAlignment> &b)
|
|
{
|
|
*this = *static_cast<MatViewT *>(this) * b;
|
|
return *this;
|
|
}
|
|
|
|
MutableMatView &operator*=(const MatT &b)
|
|
{
|
|
return *this *= b.view();
|
|
}
|
|
|
|
/** Multiply each component by a scalar. */
|
|
MutableMatView &operator*=(T b)
|
|
{
|
|
unroll<NumCol>([&](auto i) { (*this)[i] *= b; });
|
|
return *this;
|
|
}
|
|
|
|
/** Vector operators. Need to be redefined to avoid operator priority issue. */
|
|
|
|
friend col_type operator*(MutableMatView &a, const row_type &b)
|
|
{
|
|
/* This is the reference implementation.
|
|
* Might be overloaded with vectorized / optimized code. */
|
|
col_type result(0);
|
|
unroll<NumCol>([&](auto c) { result += b[c] * a[c]; });
|
|
return result;
|
|
}
|
|
|
|
/** Multiply by the transposed. */
|
|
friend row_type operator*(const col_type &a, MutableMatView &b)
|
|
{
|
|
/* This is the reference implementation.
|
|
* Might be overloaded with vectorized / optimized code. */
|
|
row_type result(0);
|
|
unroll<NumCol>([&](auto c) { unroll<NumRow>([&](auto r) { result[c] += b[c][r] * a[r]; }); });
|
|
return result;
|
|
}
|
|
};
|
|
|
|
using float2x2 = MatBase<float, 2, 2>;
|
|
using float2x3 = MatBase<float, 2, 3>;
|
|
using float2x4 = MatBase<float, 2, 4>;
|
|
using float3x2 = MatBase<float, 3, 2>;
|
|
using float3x3 = MatBase<float, 3, 3>;
|
|
using float3x4 = MatBase<float, 3, 4>;
|
|
using float4x2 = MatBase<float, 4, 2>;
|
|
using float4x3 = MatBase<float, 4, 3>;
|
|
using float4x4 = MatBase<float, 4, 4>;
|
|
|
|
/* These types are reserved to wrap C matrices without copy. Note the un-alignment. */
|
|
/* TODO: It would be preferable to align all C matrices inside DNA structs. */
|
|
using float4x4_view = MatView<float, 4, 4, 4, 4, 0, 0, alignof(float)>;
|
|
using float4x4_mutableview = MutableMatView<float, 4, 4, 4, 4, 0, 0, alignof(float)>;
|
|
|
|
using double2x2 = MatBase<double, 2, 2>;
|
|
using double2x3 = MatBase<double, 2, 3>;
|
|
using double2x4 = MatBase<double, 2, 4>;
|
|
using double3x2 = MatBase<double, 3, 2>;
|
|
using double3x3 = MatBase<double, 3, 3>;
|
|
using double3x4 = MatBase<double, 3, 4>;
|
|
using double4x2 = MatBase<double, 4, 2>;
|
|
using double4x3 = MatBase<double, 4, 3>;
|
|
using double4x4 = MatBase<double, 4, 4>;
|
|
|
|
/* Specialization for SSE optimization. */
|
|
template<> float4x4 float4x4::operator*(const float4x4 &b) const;
|
|
template<> float3x3 float3x3::operator*(const float3x3 &b) const;
|
|
|
|
extern template float2x2 float2x2::operator*(const float2x2 &b) const;
|
|
extern template double2x2 double2x2::operator*(const double2x2 &b) const;
|
|
extern template double3x3 double3x3::operator*(const double3x3 &b) const;
|
|
extern template double4x4 double4x4::operator*(const double4x4 &b) const;
|
|
|
|
} // namespace blender
|