#ifndef VEC3_HPP #define VEC3_HPP #include #include #include #include #include template class Vec3 { public: struct{ T x, y, z; }; Vec3() : x(0), y(0), z(0) {} Vec3(T x, T y, T z) : x(x), y(y), z(z) {} Vec3(T scalar) : x(scalar), y(scalar), z(scalar) {} Vec3(float acd[3]) : x(acd[0]), y(acd[1]), z(acd[2]) {} Vec3(const class Vec2& vec2, T z = 0); Vec3& move(const Vec3& newpos) { x = newpos.x; y = newpos.y; z = newpos.z; return *this; } // Arithmetic operations template Vec3 operator+(const Vec3& other) const { return Vec3(x + other.x, y + other.y, z + other.z); } template Vec3 operator-(const Vec3& other) const { return Vec3(x - other.x, y - other.y, z - other.z); } template Vec3 operator*(const Vec3& other) const { return Vec3(x * other.x, y * other.y, z * other.z); } template Vec3 operator/(const Vec3& other) const { return Vec3(x / other.x, y / other.y, z / other.z); } Vec3 operator+(T scalar) const { return Vec3(x + scalar, y + scalar, z + scalar); } Vec3 operator-(T scalar) const { return Vec3(x - scalar, y - scalar, z - scalar); } Vec3 operator-() const { return Vec3(-x, -y, -z); } Vec3 operator*(T scalar) const { return Vec3(x * scalar, y * scalar, z * scalar); } Vec3 operator/(T scalar) const { return Vec3(x / scalar, y / scalar, z / scalar); } Vec3& operator=(T scalar) { x = y = z = scalar; return *this; } Vec3& operator+=(const Vec3& other) { x += other.x; y += other.y; z += other.z; return *this; } Vec3& operator-=(const Vec3& other) { x -= other.x; y -= other.y; z -= other.z; return *this; } Vec3& operator*=(const Vec3& other) { x *= other.x; y *= other.y; z *= other.z; return *this; } Vec3& operator/=(const Vec3& other) { x /= other.x; y /= other.y; z /= other.z; return *this; } Vec3& operator+=(T scalar) { x += scalar; y += scalar; z += scalar; return *this; } Vec3& operator-=(T scalar) { x -= scalar; y -= scalar; z -= scalar; return *this; } Vec3& operator*=(T scalar) { x *= scalar; y *= scalar; z *= scalar; return *this; } Vec3& operator/=(T scalar) { x /= scalar; y /= scalar; z /= scalar; return *this; } float dot(const Vec3& other) const { return x * other.x + y * other.y + z * other.z; } Vec3 cross(const Vec3& other) const { return Vec3( y * other.z - z * other.y, z * other.x - x * other.z, x * other.y - y * other.x ); } T length() const { return static_cast(std::sqrt(static_cast(x * x + y * y + z * z))); } T lengthSquared() const { return x * x + y * y + z * z; } T distance(const Vec3& other) const { return (*this - other).length(); } T distanceSquared(const Vec3& other) const { Vec3 diff = *this - other; return diff.x * diff.x + diff.y * diff.y + diff.z * diff.z; } Vec3 normalized() const { T len = length(); if (len > 0) { return *this / len; } return *this; } bool operator==(const Vec3& other) const { return x == other.x && y == other.y && z == other.z; } bool operator!=(const Vec3& other) const { return x != other.x || y != other.y || z != other.z; } bool operator<(const Vec3& other) const { return (lengthSquared() < other.lengthSquared()); } bool operator<(T scalar) const { return (x < scalar && y < scalar && z < scalar); } bool operator<=(const Vec3& other) const { return (lengthSquared() <= other.lengthSquared()); } bool operator<=(T scalar) const { return (x <= scalar && y <= scalar && z <= scalar); } bool operator>(const Vec3& other) const { return (lengthSquared() > other.lengthSquared()); } bool operator>(T scalar) const { return (x > scalar && y > scalar && z > scalar); } bool operator>=(const Vec3& other) const { return (lengthSquared() >= other.lengthSquared()); } bool operator>=(T scalar) const { return (x >= scalar && y >= scalar && z >= scalar); } bool AllLT(const Vec3& other) { return x < other.x && y < other.y && z < other.z; } bool AllGT(const Vec3& other) { return x > other.x && y > other.y && z > other.z; } bool AllLTE(const Vec3& other) { return x <= other.x && y <= other.y && z <= other.z; } bool AllGTE(const Vec3& other) { return x >= other.x && y >= other.y && z >= other.z; } bool AnyLT(const Vec3& other) { return x < other.x || y < other.y || z < other.z; } bool AnyGT(const Vec3& other) { return x > other.x || y > other.y || z > other.z; } bool AnyLTE(const Vec3& other) { return x <= other.x || y <= other.y || z <= other.z; } bool AnyGTE(const Vec3& other) { return x >= other.x || y >= other.y || z >= other.z; } template Vec3 mask(CompareFunc comp, T value) const { return Vec3(comp(x, value), comp(y, value), comp(z, value)); } template Vec3 mask(CompareFunc comp, const Vec3& other) const { return Vec3(comp(x, other.x), comp(y, other.y), comp(z, other.z)); } Vec3 abs() const { return Vec3(std::abs(x), std::abs(y), std::abs(z)); } Vec3 floor() const { return Vec3(std::floor(x), std::floor(y), std::floor(z)); } Vec3 floorToI() const { return Vec3(static_cast(std::floor(x)), static_cast(std::floor(x)), static_cast(std::floor(z))); } Vec3 floorToT() const { return Vec3(static_cast(std::floor(x)), static_cast(std::floor(x)), static_cast(std::floor(z))); } Vec3 toFloat() const { return Vec3(static_cast(x), static_cast(y), static_cast(z)); } Vec3 toDouble() const { return Vec3(static_cast(x), static_cast(y), static_cast(z)); } Vec3 ceil() const { return Vec3(std::ceil(x), std::ceil(y), std::ceil(z)); } Vec3 round() const { return Vec3(std::round(x), std::round(y), std::round(z)); } Vec3 min(const Vec3& other) const { return Vec3(std::min(x, other.x), std::min(y, other.y), std::min(z, other.z)); } Vec3 max(const Vec3& other) const { return Vec3(std::max(x, other.x), std::max(y, other.y), std::max(z, other.z)); } Vec3 clamp(const Vec3& minVal, const Vec3& maxVal) const { return Vec3( std::clamp(x, minVal.x, maxVal.x), std::clamp(y, minVal.y, maxVal.y), std::clamp(z, minVal.z, maxVal.z) ); } Vec3 clamp(T minVal, T maxVal) const { return Vec3( std::clamp(x, minVal, maxVal), std::clamp(y, minVal, maxVal), std::clamp(z, minVal, maxVal) ); } bool isZero(float epsilon = 1e-10f) const { return std::abs(x) < epsilon && std::abs(y) < epsilon && std::abs(z) < epsilon; } bool equals(const Vec3& other, float epsilon = 1e-10f) const { return std::abs(x - other.x) < epsilon && std::abs(y - other.y) < epsilon && std::abs(z - other.z) < epsilon; } // Template friend operators to allow different scalar types // template // friend Vec3 operator+(S scalar, const Vec3& vec) { // return Vec3(static_cast(scalar + vec.x), // static_cast(scalar + vec.y), // static_cast(scalar + vec.z)); // } // template // friend Vec3 operator-(S scalar, const Vec3& vec) { // return Vec3(static_cast(scalar - static_cast(vec.x)), // static_cast(scalar - static_cast(vec.y)), // static_cast(scalar - static_cast(vec.z))); // } // template // friend Vec3 operator*(S scalar, const Vec3& vec) { // return Vec3(static_cast(scalar * vec.x), // static_cast(scalar * vec.y), // static_cast(scalar * vec.z)); // } // template // friend Vec3 operator/(S scalar, const Vec3& vec) { // return Vec3(static_cast(scalar / vec.x), // static_cast(scalar / vec.y), // static_cast(scalar / vec.z)); // } Vec3 reflect(const Vec3& normal) const { return *this - 2.0f * this->dot(normal) * normal; } Vec3 lerp(const Vec3& other, T t) const { t = std::clamp(t, 0.0f, 1.0f); return *this + (other - *this) * t; } Vec3 slerp(const Vec3& other, T t) const { t = std::clamp(t, 0.0f, 1.0f); T dot = this->dot(other); dot = std::clamp(dot, -1.0f, 1.0f); T theta = std::acos(dot) * t; Vec3 relative = other - *this * dot; relative = relative.normalized(); return (*this * std::cos(theta)) + (relative * std::sin(theta)); } Vec3 rotateX(float angle) const { float cosA = std::cos(angle); float sinA = std::sin(angle); return Vec3(x, y * cosA - z * sinA, y * sinA + z * cosA); } Vec3 rotateY(float angle) const { float cosA = std::cos(angle); float sinA = std::sin(angle); return Vec3(x * cosA + z * sinA, y, -x * sinA + z * cosA); } Vec3 rotateZ(float angle) const { float cosA = std::cos(angle); float sinA = std::sin(angle); return Vec3(x * cosA - y * sinA, x * sinA + y * cosA, z); } float angle() const { float r = length(); if (r == 0) return 0; float θ = std::acos(z / r); return θ; } float azimuth() const { float φ = std::atan2(y, x); return φ; } std::pair sphericalAngles() const { float r = length(); if (r == 0) return {0, 0}; float θ = std::acos(z / r); float φ = std::atan2(y, x); return {θ, φ}; } float angleTo(const Vec3& other) const { return std::acos(this->dot(other) / (this->length() * other.length())); } float directionTo(const Vec3& other) const { Vec3 direction = other - *this; return direction.angleTo(other); } T& operator[](int index) { return (&x)[index]; } const T& operator[](int index) const { return (&x)[index]; } Vec3 safeInverse(float epsilon = 1e-10f) const { return Vec3( 1 / (std::abs(x) < epsilon ? std::copysign(epsilon, x) : x), 1 / (std::abs(y) < epsilon ? std::copysign(epsilon, y) : y), 1 / (std::abs(z) < epsilon ? std::copysign(epsilon, z) : z) ); } uint8_t calculateOctantMask() const { uint8_t mask = 0; if (x > 0.0f) mask |= 1; if (y > 0.0f) mask |= 2; if (z > 0.0f) mask |= 4; return mask; } float maxComp() const { return std::max({x, y, z}); } float minComp() const { return std::min({x, y, z}); } std::string toString() const { return "(" + std::to_string(x) + ", " + std::to_string(y) + ", " + std::to_string(z) + ")"; } struct Hash { std::size_t operator()(const Vec3& v) const { return std::hash()(v.x) ^ (std::hash()(v.y) << 1) ^ (std::hash()(v.z) << 2); } }; }; using Vec3f = Vec3; using Vec3d = Vec3; using Vec3i = Vec3; using Vec3i8 = Vec3; using Vec3ui8 = Vec3; using Vec3T = Vec3; using Vec3b = Vec3; template inline std::ostream& operator<<(std::ostream& os, const Vec3& vec) { os << vec.toString(); return os; } namespace std { template struct hash> { size_t operator()(const Vec3& v) const { return hash()(v.x) ^ (hash()(v.y) << 1) ^ (hash()(v.z) << 2); } }; } template Vec3 max(Vec3 a, Vec3 b) { return a.max(b); } template Vec3 min(Vec3 a, Vec3 b) { return a.min(b); } template Vec3 mix(const Vec3& a, const Vec3& b, const Vec3& mask) { return Vec3( mask.x ? b.x : a.x, mask.y ? b.y : a.y, mask.z ? b.z : a.z ); } #endif