Template vector types

master
Marc Gilleron 2022-07-17 18:34:05 +01:00
parent f8ce0b1e8c
commit 7254dcb7fc
9 changed files with 316 additions and 374 deletions

View File

@ -141,7 +141,7 @@ bool can_split(Vector3i node_origin, int node_size, const VoxelAccess &voxels, f
}
inline Vector3f get_center(const OctreeNode *node) {
return to_vec3f(node->origin) + 0.5 * Vector3f(node->size, node->size, node->size);
return to_vec3f(node->origin) + 0.5f * Vector3f(node->size, node->size, node->size);
}
class OctreeBuilderTopDown {

View File

@ -13,15 +13,19 @@ namespace zylann {
// Explicit conversion methods. Not in respective files because it would cause circular dependencies.
// Godot => Godot
// Note, in Godot this is an implicit conversion. But I dont like implicit
inline Vector3i to_vec3i(Vector3 v) {
return Vector3i(v.x, v.y, v.z);
}
inline Vector3i to_vec3i(Vector3f v) {
return Vector3i(v.x, v.y, v.z);
inline Vector3 to_vec3(const Vector3i v) {
return Vector3(v.x, v.y, v.z);
}
// Godot => ZN
inline Vector3f to_vec3f(Vector3i v) {
return Vector3f(v.x, v.y, v.z);
}
@ -30,22 +34,29 @@ inline Vector3f to_vec3f(Vector3 v) {
return Vector3f(v.x, v.y, v.z);
}
inline Vector3 to_vec3(const Vector3f v) {
return Vector3(v.x, v.y, v.z);
}
inline Vector3 to_vec3(const Vector3i v) {
return Vector3(v.x, v.y, v.z);
}
inline Vector3d to_vec3d(const Vector3f v) {
return Vector3d(v.x, v.y, v.z);
}
inline Vector3i16 to_vec3i16(const Vector3i v) {
return Vector3i16(v.x, v.y, v.z);
}
// ZN => Godot
template <typename T>
inline Vector3i to_vec3i(Vector3T<T> v) {
return Vector3i(v.x, v.y, v.z);
}
template <typename T>
inline Vector3 to_vec3(const Vector3T<T> v) {
return Vector3(v.x, v.y, v.z);
}
// ZN => ZN
template <typename T>
inline Vector3d to_vec3d(const Vector3T<T> v) {
return Vector3d(v.x, v.y, v.z);
}
inline bool can_convert_to_i16(Vector3i p) {
return p.x >= std::numeric_limits<int16_t>::min() && p.x <= std::numeric_limits<int16_t>::max() &&
p.y >= std::numeric_limits<int16_t>::min() && p.y <= std::numeric_limits<int16_t>::max() &&

View File

@ -2,78 +2,14 @@
#define ZYLANN_VECTOR2F_H
#include "../errors.h"
#include "vector2t.h"
namespace zylann {
// 32-bit float precision 2D vector.
// Because Godot's `Vector2` uses `real_t`, so when `real_t` is `double` it forces some things to use double-precision
// vectors while they dont need that amount of precision.
struct Vector2f {
static const unsigned int AXIS_X = 0;
static const unsigned int AXIS_Y = 1;
static const unsigned int AXIS_COUNT = 2;
union {
struct {
float x;
float y;
};
float coords[2];
};
Vector2f() : x(0), y(0) {}
Vector2f(float p_x, float p_y) : x(p_x), y(p_y) {}
float cross(const Vector2f &p_other) const {
return x * p_other.y - y * p_other.x;
}
inline const float &operator[](const unsigned int p_axis) const {
#ifdef DEBUG_ENABLED
ZN_ASSERT(p_axis < AXIS_COUNT);
#endif
return coords[p_axis];
}
inline float &operator[](const unsigned int p_axis) {
#ifdef DEBUG_ENABLED
ZN_ASSERT(p_axis < AXIS_COUNT);
#endif
return coords[p_axis];
}
inline Vector2f &operator+=(const Vector2f &p_v) {
x += p_v.x;
y += p_v.y;
return *this;
}
inline Vector2f operator+(const Vector2f &p_v) const {
return Vector2f(x + p_v.x, y + p_v.y);
}
inline Vector2f operator-(const Vector2f &p_v) const {
return Vector2f(x - p_v.x, y - p_v.y);
}
inline Vector2f operator*(const Vector2f &p_v) const {
return Vector2f(x * p_v.x, y * p_v.y);
}
inline Vector2f &operator*=(const float p_scalar) {
x *= p_scalar;
y *= p_scalar;
return *this;
}
inline Vector2f operator*(const float p_scalar) const {
return Vector2f(x * p_scalar, y * p_scalar);
}
};
inline Vector2f operator*(float p_scalar, const Vector2f &v) {
return v * p_scalar;
}
typedef Vector2T<float> Vector2f;
namespace math {
@ -83,13 +19,13 @@ inline bool is_point_in_triangle(const Vector2f &s, const Vector2f &a, const Vec
const Vector2f bn = b - s;
const Vector2f cn = c - s;
const bool orientation = an.cross(bn) > 0;
const bool orientation = cross(an, bn) > 0;
if ((bn.cross(cn) > 0) != orientation) {
if ((cross(bn, cn) > 0) != orientation) {
return false;
}
return (cn.cross(an) > 0) == orientation;
return (cross(cn, an) > 0) == orientation;
}
} // namespace math

81
util/math/vector2t.h Normal file
View File

@ -0,0 +1,81 @@
#ifndef ZN_VECTOR2T_H
#define ZN_VECTOR2T_H
namespace zylann {
template <typename T>
struct Vector2T {
static const unsigned int AXIS_X = 0;
static const unsigned int AXIS_Y = 1;
static const unsigned int AXIS_COUNT = 2;
union {
struct {
T x;
T y;
};
T coords[2];
};
Vector2T() : x(0), y(0) {}
Vector2T(T p_x, T p_y) : x(p_x), y(p_y) {}
inline const T &operator[](const unsigned int p_axis) const {
#ifdef DEBUG_ENABLED
ZN_ASSERT(p_axis < AXIS_COUNT);
#endif
return coords[p_axis];
}
inline T &operator[](const unsigned int p_axis) {
#ifdef DEBUG_ENABLED
ZN_ASSERT(p_axis < AXIS_COUNT);
#endif
return coords[p_axis];
}
inline Vector2T &operator+=(const Vector2T &p_v) {
x += p_v.x;
y += p_v.y;
return *this;
}
inline Vector2T operator+(const Vector2T &p_v) const {
return Vector2T(x + p_v.x, y + p_v.y);
}
inline Vector2T operator-(const Vector2T &p_v) const {
return Vector2T(x - p_v.x, y - p_v.y);
}
inline Vector2T operator*(const Vector2T &p_v) const {
return Vector2T(x * p_v.x, y * p_v.y);
}
inline Vector2T &operator*=(const T p_scalar) {
x *= p_scalar;
y *= p_scalar;
return *this;
}
inline Vector2T operator*(const T p_scalar) const {
return Vector2T(x * p_scalar, y * p_scalar);
}
};
template <typename T>
inline Vector2T<T> operator*(T p_scalar, const Vector2T<T> &v) {
return v * p_scalar;
}
namespace math {
template <typename T>
T cross(const Vector2T<T> &a, const Vector2T<T> &b) {
return a.x * b.y - a.y * b.x;
}
} // namespace math
} // namespace zylann
#endif // ZN_VECTOR2T_H

View File

@ -4,6 +4,9 @@
#include "funcs.h"
#include <core/math/vector3.h>
// 3-dimensional vector which components are either 32-bit float or 64-bit float depending on how Godot was compiled.
// This is the type to use for interoperating with Godot.
namespace zylann::math {
inline Vector3 fract(const Vector3 &p) {

View File

@ -1,117 +1,13 @@
#ifndef ZN_VECTOR3D_H
#define ZN_VECTOR3D_H
#include "vector3t.h"
namespace zylann {
struct Vector3d {
static const unsigned int AXIS_X = 0;
static const unsigned int AXIS_Y = 1;
static const unsigned int AXIS_Z = 2;
static const unsigned int AXIS_COUNT = 3;
// 3-dimensional vector using 64-bit floats, regardless on compiling options
typedef Vector3T<double> Vector3d;
union {
struct {
double x;
double y;
double z;
};
double coords[3];
};
Vector3d() : x(0), y(0), z(0) {}
// It is recommended to use `explicit` because otherwise it would open the door to plenty of implicit conversions
// which would make many cases ambiguous.
explicit Vector3d(double p_v) : x(p_v), y(p_v), z(p_v) {}
Vector3d(double p_x, double p_y, double p_z) : x(p_x), y(p_y), z(p_z) {}
inline Vector3d &operator+=(const Vector3d &p_v) {
x += p_v.x;
y += p_v.y;
z += p_v.z;
return *this;
}
inline Vector3d operator+(const Vector3d &p_v) const {
return Vector3d(x + p_v.x, y + p_v.y, z + p_v.z);
}
inline Vector3d &operator-=(const Vector3d &p_v) {
x -= p_v.x;
y -= p_v.y;
z -= p_v.z;
return *this;
}
inline Vector3d operator-(const Vector3d &p_v) const {
return Vector3d(x - p_v.x, y - p_v.y, z - p_v.z);
}
inline Vector3d &operator*=(const Vector3d &p_v) {
x *= p_v.x;
y *= p_v.y;
z *= p_v.z;
return *this;
}
inline Vector3d operator*(const Vector3d &p_v) const {
return Vector3d(x * p_v.x, y * p_v.y, z * p_v.z);
}
inline Vector3d &operator/=(const Vector3d &p_v) {
x /= p_v.x;
y /= p_v.y;
z /= p_v.z;
return *this;
}
inline Vector3d operator/(const Vector3d &p_v) const {
return Vector3d(x / p_v.x, y / p_v.y, z / p_v.z);
}
inline Vector3d &operator*=(const double p_scalar) {
x *= p_scalar;
y *= p_scalar;
z *= p_scalar;
return *this;
}
inline Vector3d operator*(const double p_scalar) const {
return Vector3d(x * p_scalar, y * p_scalar, z * p_scalar);
}
inline Vector3d &operator/=(const double p_scalar) {
x /= p_scalar;
y /= p_scalar;
z /= p_scalar;
return *this;
}
inline Vector3d operator/(const double p_scalar) const {
return Vector3d(x / p_scalar, y / p_scalar, z / p_scalar);
}
inline Vector3d operator-() const {
return Vector3d(-x, -y, -z);
}
};
namespace math {
inline Vector3d cross(const Vector3d &a, const Vector3d &b) {
const Vector3d ret( //
(a.y * b.z) - (a.z * b.y), //
(a.z * b.x) - (a.x * b.z), //
(a.x * b.y) - (a.y * b.x));
return ret;
}
inline double dot(const Vector3d &a, const Vector3d &b) {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
} // namespace math
} // namespace zylann
#endif // ZN_VECTOR3D_H

View File

@ -2,7 +2,7 @@
#define ZYLANN_VECTOR3F_H
#include "../errors.h"
#include "funcs.h"
#include "vector3t.h"
#include <iosfwd>
namespace zylann {
@ -11,146 +11,10 @@ namespace zylann {
// Because Godot's `Vector3` uses `real_t`, so when `real_t` is `double` it forces some things to use double-precision
// vectors while they dont need that amount of precision. This is also a problem for some third-party libraries
// that do not support `double` as a result.
struct Vector3f {
static const unsigned int AXIS_X = 0;
static const unsigned int AXIS_Y = 1;
static const unsigned int AXIS_Z = 2;
static const unsigned int AXIS_COUNT = 3;
union {
struct {
float x;
float y;
float z;
};
float coords[3];
};
Vector3f() : x(0), y(0), z(0) {}
// It is recommended to use `explicit` because otherwise it would open the door to plenty of implicit conversions
// which would make many cases ambiguous.
explicit Vector3f(float p_v) : x(p_v), y(p_v), z(p_v) {}
Vector3f(float p_x, float p_y, float p_z) : x(p_x), y(p_y), z(p_z) {}
inline const float &operator[](const unsigned int p_axis) const {
#ifdef DEBUG_ENABLED
ZN_ASSERT(p_axis < AXIS_COUNT);
#endif
return coords[p_axis];
}
inline float &operator[](const unsigned int p_axis) {
#ifdef DEBUG_ENABLED
ZN_ASSERT(p_axis < AXIS_COUNT);
#endif
return coords[p_axis];
}
inline Vector3f &operator+=(const Vector3f &p_v) {
x += p_v.x;
y += p_v.y;
z += p_v.z;
return *this;
}
inline Vector3f operator+(const Vector3f &p_v) const {
return Vector3f(x + p_v.x, y + p_v.y, z + p_v.z);
}
inline Vector3f &operator-=(const Vector3f &p_v) {
x -= p_v.x;
y -= p_v.y;
z -= p_v.z;
return *this;
}
inline Vector3f operator-(const Vector3f &p_v) const {
return Vector3f(x - p_v.x, y - p_v.y, z - p_v.z);
}
inline Vector3f &operator*=(const Vector3f &p_v) {
x *= p_v.x;
y *= p_v.y;
z *= p_v.z;
return *this;
}
inline Vector3f operator*(const Vector3f &p_v) const {
return Vector3f(x * p_v.x, y * p_v.y, z * p_v.z);
}
inline Vector3f &operator/=(const Vector3f &p_v) {
x /= p_v.x;
y /= p_v.y;
z /= p_v.z;
return *this;
}
inline Vector3f operator/(const Vector3f &p_v) const {
return Vector3f(x / p_v.x, y / p_v.y, z / p_v.z);
}
inline Vector3f &operator*=(const float p_scalar) {
x *= p_scalar;
y *= p_scalar;
z *= p_scalar;
return *this;
}
inline Vector3f operator*(const float p_scalar) const {
return Vector3f(x * p_scalar, y * p_scalar, z * p_scalar);
}
inline Vector3f &operator/=(const float p_scalar) {
x /= p_scalar;
y /= p_scalar;
z /= p_scalar;
return *this;
}
inline Vector3f operator/(const float p_scalar) const {
return Vector3f(x / p_scalar, y / p_scalar, z / p_scalar);
}
inline Vector3f operator-() const {
return Vector3f(-x, -y, -z);
}
inline bool operator==(const Vector3f &p_v) const {
return x == p_v.x && y == p_v.y && z == p_v.z;
}
inline bool operator!=(const Vector3f &p_v) const {
return x != p_v.x || y != p_v.y || z != p_v.z;
}
inline bool operator<(const Vector3f &p_v) const {
if (x == p_v.x) {
if (y == p_v.y) {
return z < p_v.z;
}
return y < p_v.y;
}
return x < p_v.x;
}
};
inline Vector3f operator*(float p_scalar, const Vector3f &v) {
return v * p_scalar;
}
typedef Vector3T<float> Vector3f;
namespace math {
inline Vector3f min(const Vector3f a, const Vector3f b) {
return Vector3f(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z));
}
inline Vector3f max(const Vector3f a, const Vector3f b) {
return Vector3f(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z));
}
inline Vector3f floor(const Vector3f a) {
return Vector3f(Math::floor(a.x), Math::floor(a.y), Math::floor(a.z));
}
@ -167,30 +31,6 @@ inline bool has_nan(const Vector3f &v) {
return Math::is_nan(v.x) || Math::is_nan(v.y) || Math::is_nan(v.z);
}
inline float length_squared(const Vector3f v) {
return v.x * v.x + v.y * v.y + v.z * v.z;
}
inline float length(const Vector3f &v) {
return Math::sqrt(length_squared(v));
}
inline float distance_squared(const Vector3f &a, const Vector3f &b) {
return length_squared(b - a);
}
inline Vector3f cross(const Vector3f &a, const Vector3f &b) {
const Vector3f ret( //
(a.y * b.z) - (a.z * b.y), //
(a.z * b.x) - (a.x * b.z), //
(a.x * b.y) - (a.y * b.x));
return ret;
}
inline float dot(const Vector3f &a, const Vector3f &b) {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
inline Vector3f normalized(const Vector3f &v) {
const float lengthsq = length_squared(v);
if (lengthsq == 0) {
@ -206,10 +46,6 @@ inline bool is_normalized(const Vector3f &v) {
return Math::is_equal_approx(length_squared(v), 1, float(UNIT_EPSILON));
}
inline float distance(const Vector3f &a, const Vector3f &b) {
return Math::sqrt(distance_squared(a, b));
}
} // namespace math
std::stringstream &operator<<(std::stringstream &ss, const Vector3f &v);

View File

@ -1,27 +1,13 @@
#ifndef ZN_VECTOR3I16_H
#define ZN_VECTOR3I16_H
#include "vector3t.h"
#include <cstdint>
#include <functional>
namespace zylann {
struct Vector3i16 {
int16_t x;
int16_t y;
int16_t z;
Vector3i16() : x(0), y(0), z(0) {}
Vector3i16(int16_t p_x, int16_t p_y, int16_t p_z) : x(p_x), y(p_y), z(p_z) {}
inline bool operator==(const Vector3i16 p_v) const {
return x == p_v.x && y == p_v.y && z == p_v.z;
}
inline bool operator!=(const Vector3i16 p_v) const {
return x != p_v.x || y != p_v.y || z != p_v.z;
}
};
typedef Vector3T<int16_t> Vector3i16;
inline size_t get_hash_st(const zylann::Vector3i16 &v) {
// TODO Optimization: benchmark this hash, I just wanted one that works

193
util/math/vector3t.h Normal file
View File

@ -0,0 +1,193 @@
#ifndef ZYLANN_VECTOR3T_H
#define ZYLANN_VECTOR3T_H
#include "../errors.h"
#include "funcs.h"
namespace zylann {
// Template 3-dimensional vector. Only fields and standard operators.
// Math functions are separate to allow more unified overloading, and similarity with other math libraries such as
// shaders.
template <typename T>
struct Vector3T {
static const unsigned int AXIS_X = 0;
static const unsigned int AXIS_Y = 1;
static const unsigned int AXIS_Z = 2;
static const unsigned int AXIS_COUNT = 3;
union {
struct {
T x;
T y;
T z;
};
T coords[3];
};
Vector3T() : x(0), y(0), z(0) {}
// It is recommended to use `explicit` because otherwise it would open the door to plenty of implicit conversions
// which would make many cases ambiguous.
explicit Vector3T(T p_v) : x(p_v), y(p_v), z(p_v) {}
Vector3T(T p_x, T p_y, T p_z) : x(p_x), y(p_y), z(p_z) {}
inline const T &operator[](const unsigned int p_axis) const {
#ifdef DEBUG_ENABLED
ZN_ASSERT(p_axis < AXIS_COUNT);
#endif
return coords[p_axis];
}
inline T &operator[](const unsigned int p_axis) {
#ifdef DEBUG_ENABLED
ZN_ASSERT(p_axis < AXIS_COUNT);
#endif
return coords[p_axis];
}
inline Vector3T &operator+=(const Vector3T &p_v) {
x += p_v.x;
y += p_v.y;
z += p_v.z;
return *this;
}
inline Vector3T operator+(const Vector3T &p_v) const {
return Vector3T(x + p_v.x, y + p_v.y, z + p_v.z);
}
inline Vector3T &operator-=(const Vector3T &p_v) {
x -= p_v.x;
y -= p_v.y;
z -= p_v.z;
return *this;
}
inline Vector3T operator-(const Vector3T &p_v) const {
return Vector3T(x - p_v.x, y - p_v.y, z - p_v.z);
}
inline Vector3T &operator*=(const Vector3T &p_v) {
x *= p_v.x;
y *= p_v.y;
z *= p_v.z;
return *this;
}
inline Vector3T operator*(const Vector3T &p_v) const {
return Vector3T(x * p_v.x, y * p_v.y, z * p_v.z);
}
inline Vector3T &operator/=(const Vector3T &p_v) {
x /= p_v.x;
y /= p_v.y;
z /= p_v.z;
return *this;
}
inline Vector3T operator/(const Vector3T &p_v) const {
return Vector3T(x / p_v.x, y / p_v.y, z / p_v.z);
}
inline Vector3T &operator*=(const T p_scalar) {
x *= p_scalar;
y *= p_scalar;
z *= p_scalar;
return *this;
}
inline Vector3T operator*(const T p_scalar) const {
return Vector3T(x * p_scalar, y * p_scalar, z * p_scalar);
}
inline Vector3T &operator/=(const T p_scalar) {
x /= p_scalar;
y /= p_scalar;
z /= p_scalar;
return *this;
}
inline Vector3T operator/(const T p_scalar) const {
return Vector3T(x / p_scalar, y / p_scalar, z / p_scalar);
}
inline Vector3T operator-() const {
return Vector3T(-x, -y, -z);
}
inline bool operator==(const Vector3T &p_v) const {
return x == p_v.x && y == p_v.y && z == p_v.z;
}
inline bool operator!=(const Vector3T &p_v) const {
return x != p_v.x || y != p_v.y || z != p_v.z;
}
inline bool operator<(const Vector3T &p_v) const {
if (x == p_v.x) {
if (y == p_v.y) {
return z < p_v.z;
}
return y < p_v.y;
}
return x < p_v.x;
}
};
template <typename T>
inline Vector3T<T> operator*(T p_scalar, const Vector3T<T> &v) {
return v * p_scalar;
}
namespace math {
template <typename T>
inline Vector3T<T> min(const Vector3T<T> a, const Vector3T<T> b) {
return Vector3f(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z));
}
template <typename T>
inline Vector3T<T> max(const Vector3T<T> a, const Vector3T<T> b) {
return Vector3f(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z));
}
template <typename T>
inline T length_squared(const Vector3T<T> v) {
return v.x * v.x + v.y * v.y + v.z * v.z;
}
template <typename T>
inline T length(const Vector3T<T> &v) {
return Math::sqrt(length_squared(v));
}
template <typename T>
inline T distance_squared(const Vector3T<T> &a, const Vector3T<T> &b) {
return length_squared(b - a);
}
template <typename T>
inline T distance(const Vector3T<T> &a, const Vector3T<T> &b) {
return Math::sqrt(distance_squared(a, b));
}
template <typename T>
inline Vector3T<T> cross(const Vector3T<T> &a, const Vector3T<T> &b) {
const Vector3T<T> ret( //
(a.y * b.z) - (a.z * b.y), //
(a.z * b.x) - (a.x * b.z), //
(a.x * b.y) - (a.y * b.x));
return ret;
}
template <typename T>
inline T dot(const Vector3T<T> &a, const Vector3T<T> &b) {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
} // namespace math
} // namespace zylann
#endif // ZYLANN_VECTOR3F_H