123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549 |
- // Copyright (c) ZeniMax Media Inc.
- // Licensed under the GNU General Public License 2.0.
- #pragma once
- // q_vec3 - vec3 stuff
- #include <stdexcept>
- #include <type_traits>
- using nullptr_t = std::nullptr_t;
- struct vec3_t
- {
- float x, y, z;
- [[nodiscard]] constexpr const float &operator[](size_t i) const
- {
- if (i == 0)
- return x;
- else if (i == 1)
- return y;
- else if (i == 2)
- return z;
- throw std::out_of_range("i");
- }
- [[nodiscard]] constexpr float &operator[](size_t i)
- {
- if (i == 0)
- return x;
- else if (i == 1)
- return y;
- else if (i == 2)
- return z;
- throw std::out_of_range("i");
- }
- // comparison
- [[nodiscard]] constexpr bool equals(const vec3_t &v) const
- {
- return x == v.x && y == v.y && z == v.z;
- }
- [[nodiscard]] inline bool equals(const vec3_t &v, const float &epsilon) const
- {
- return fabsf(x - v.x) <= epsilon && fabsf(y - v.y) <= epsilon && fabsf(z - v.z) <= epsilon;
- }
- [[nodiscard]] constexpr bool operator==(const vec3_t &v) const
- {
- return equals(v);
- }
- [[nodiscard]] constexpr bool operator!=(const vec3_t &v) const
- {
- return !(*this == v);
- }
- [[nodiscard]] constexpr explicit operator bool() const
- {
- return x || y || z;
- }
- // dot
- [[nodiscard]] constexpr float dot(const vec3_t &v) const
- {
- return (x * v.x) + (y * v.y) + (z * v.z);
- }
- [[nodiscard]] constexpr vec3_t scaled(const vec3_t &v) const
- {
- return { x * v.x, y * v.y, z * v.z };
- }
- constexpr vec3_t &scale(const vec3_t &v)
- {
- *this = this->scaled(v);
- return *this;
- }
- // basic operators
- [[nodiscard]] constexpr vec3_t operator-(const vec3_t &v) const
- {
- return { x - v.x, y - v.y, z - v.z };
- }
- [[nodiscard]] constexpr vec3_t operator+(const vec3_t &v) const
- {
- return { x + v.x, y + v.y, z + v.z };
- }
- [[nodiscard]] constexpr vec3_t operator/(const vec3_t &v) const
- {
- return { x / v.x, y / v.y, z / v.z };
- }
- template<typename T, typename = std::enable_if_t<std::is_floating_point_v<T> || std::is_integral_v<T>>>
- [[nodiscard]] constexpr vec3_t operator/(const T &v) const
- {
- return { static_cast<float>(x / v), static_cast<float>(y / v), static_cast<float>(z / v) };
- }
- template<typename T, typename = std::enable_if_t<std::is_floating_point_v<T> || std::is_integral_v<T>>>
- [[nodiscard]] constexpr vec3_t operator*(const T &v) const
- {
- return { static_cast<float>(x * v), static_cast<float>(y * v), static_cast<float>(z * v) };
- }
- [[nodiscard]] constexpr vec3_t operator-() const
- {
- return { -x, -y, -z };
- }
- constexpr vec3_t &operator-=(const vec3_t &v)
- {
- *this = *this - v;
- return *this;
- }
- constexpr vec3_t &operator+=(const vec3_t &v)
- {
- *this = *this + v;
- return *this;
- }
- constexpr vec3_t &operator/=(const vec3_t &v)
- {
- *this = *this / v;
- return *this;
- }
- template<typename T, typename = std::enable_if_t<std::is_floating_point_v<T> || std::is_integral_v<T>>>
- constexpr vec3_t &operator/=(const T &v)
- {
- *this = *this / v;
- return *this;
- }
- template<typename T, typename = std::enable_if_t<std::is_floating_point_v<T> || std::is_integral_v<T>>>
- constexpr vec3_t &operator*=(const T &v)
- {
- *this = *this * v;
- return *this;
- }
- // operations
- [[nodiscard]] constexpr float lengthSquared() const
- {
- return this->dot(*this);
- }
- [[nodiscard]] inline float length() const
- {
- return sqrtf(lengthSquared());
- }
- [[nodiscard]] inline vec3_t normalized() const
- {
- float len = length();
- return len ? (*this * (1.f / len)) : *this;
- }
- [[nodiscard]] inline vec3_t normalized(float &len) const
- {
- len = length();
- return len ? (*this * (1.f / len)) : *this;
- }
- inline float normalize()
- {
- float len = length();
- if (len)
- *this *= (1.f / len);
- return len;
- }
- [[nodiscard]] constexpr vec3_t cross(const vec3_t &v) const
- {
- return {
- y * v.z - z * v.y,
- z * v.x - x * v.z,
- x * v.y - y * v.x
- };
- }
- };
- constexpr vec3_t vec3_origin{};
- inline void AngleVectors(const vec3_t &angles, vec3_t *forward, vec3_t *right, vec3_t *up)
- {
- float angle = angles[YAW] * (PIf * 2 / 360);
- float sy = sinf(angle);
- float cy = cosf(angle);
- angle = angles[PITCH] * (PIf * 2 / 360);
- float sp = sinf(angle);
- float cp = cosf(angle);
- angle = angles[ROLL] * (PIf * 2 / 360);
- float sr = sinf(angle);
- float cr = cosf(angle);
- if (forward)
- {
- forward->x = cp * cy;
- forward->y = cp * sy;
- forward->z = -sp;
- }
- if (right)
- {
- right->x = (-1 * sr * sp * cy + -1 * cr * -sy);
- right->y = (-1 * sr * sp * sy + -1 * cr * cy);
- right->z = -1 * sr * cp;
- }
- if (up)
- {
- up->x = (cr * sp * cy + -sr * -sy);
- up->y = (cr * sp * sy + -sr * cy);
- up->z = cr * cp;
- }
- }
- struct angle_vectors_t {
- vec3_t forward, right, up;
- };
- // for destructuring
- inline angle_vectors_t AngleVectors(const vec3_t &angles)
- {
- angle_vectors_t v;
- AngleVectors(angles, &v.forward, &v.right, &v.up);
- return v;
- }
- // silly wrappers to allow old C code to work
- inline void AngleVectors(const vec3_t &angles, vec3_t &forward, vec3_t &right, vec3_t &up)
- {
- AngleVectors(angles, &forward, &right, &up);
- }
- inline void AngleVectors(const vec3_t &angles, vec3_t &forward, vec3_t &right, nullptr_t)
- {
- AngleVectors(angles, &forward, &right, nullptr);
- }
- inline void AngleVectors(const vec3_t &angles, vec3_t &forward, nullptr_t, vec3_t &up)
- {
- AngleVectors(angles, &forward, nullptr, &up);
- }
- inline void AngleVectors(const vec3_t &angles, vec3_t &forward, nullptr_t, nullptr_t)
- {
- AngleVectors(angles, &forward, nullptr, nullptr);
- }
- inline void AngleVectors(const vec3_t &angles, nullptr_t, nullptr_t, vec3_t &up)
- {
- AngleVectors(angles, nullptr, nullptr, &up);
- }
- inline void AngleVectors(const vec3_t &angles, nullptr_t, vec3_t &right, nullptr_t)
- {
- AngleVectors(angles, nullptr, &right, nullptr);
- }
- inline void ClearBounds(vec3_t &mins, vec3_t &maxs)
- {
- mins[0] = mins[1] = mins[2] = std::numeric_limits<float>::infinity();
- maxs[0] = maxs[1] = maxs[2] = -std::numeric_limits<float>::infinity();
- }
- inline void AddPointToBounds(const vec3_t &v, vec3_t &mins, vec3_t &maxs)
- {
- for (int i = 0; i < 3; i++)
- {
- float val = v[i];
- if (val < mins[i])
- mins[i] = val;
- if (val > maxs[i])
- maxs[i] = val;
- }
- }
- [[nodiscard]] constexpr vec3_t ProjectPointOnPlane(const vec3_t &p, const vec3_t &normal)
- {
- float inv_denom = 1.0f / normal.dot(normal);
- float d = normal.dot(p) * inv_denom;
- return p - ((normal * inv_denom) * d);
- }
- /*
- ** assumes "src" is normalized
- */
- [[nodiscard]] inline vec3_t PerpendicularVector(const vec3_t &src)
- {
- int pos;
- int i;
- float minelem = 1.0F;
- vec3_t tempvec;
- /*
- ** find the smallest magnitude axially aligned vector
- */
- for (pos = 0, i = 0; i < 3; i++)
- {
- if (fabsf(src[i]) < minelem)
- {
- pos = i;
- minelem = fabsf(src[i]);
- }
- }
- tempvec[0] = tempvec[1] = tempvec[2] = 0.0F;
- tempvec[pos] = 1.0F;
- /*
- ** project the point onto the plane defined by src & normalize the result
- */
- return ProjectPointOnPlane(tempvec, src).normalized();
- }
- using mat3_t = std::array<std::array<float, 3>, 3>;
- /*
- ================
- R_ConcatRotations
- ================
- */
- [[nodiscard]] constexpr mat3_t R_ConcatRotations(const mat3_t &in1, const mat3_t &in2)
- {
- return {
- std::array<float, 3> {
- in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0],
- in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1],
- in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2]
- },
- {
- in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0],
- in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1],
- in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2]
- },
- {
- in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0],
- in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1],
- in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2]
- }
- };
- }
- [[nodiscard]] inline vec3_t RotatePointAroundVector(const vec3_t &dir, const vec3_t &point, float degrees)
- {
- mat3_t m;
- mat3_t im;
- mat3_t zrot;
- mat3_t rot;
- vec3_t vr, vup, vf;
- vf = dir;
- vr = PerpendicularVector(dir);
- vup = vr.cross(vf);
- m[0][0] = vr[0];
- m[1][0] = vr[1];
- m[2][0] = vr[2];
- m[0][1] = vup[0];
- m[1][1] = vup[1];
- m[2][1] = vup[2];
- m[0][2] = vf[0];
- m[1][2] = vf[1];
- m[2][2] = vf[2];
- im = m;
- im[0][1] = m[1][0];
- im[0][2] = m[2][0];
- im[1][0] = m[0][1];
- im[1][2] = m[2][1];
- im[2][0] = m[0][2];
- im[2][1] = m[1][2];
- zrot = {};
- zrot[0][0] = zrot[1][1] = zrot[2][2] = 1.0F;
- zrot[0][0] = cosf(DEG2RAD(degrees));
- zrot[0][1] = sinf(DEG2RAD(degrees));
- zrot[1][0] = -sinf(DEG2RAD(degrees));
- zrot[1][1] = cosf(DEG2RAD(degrees));
- rot = R_ConcatRotations(R_ConcatRotations(m, zrot), im);
- return {
- rot[0][0] * point[0] + rot[0][1] * point[1] + rot[0][2] * point[2],
- rot[1][0] * point[0] + rot[1][1] * point[1] + rot[1][2] * point[2],
- rot[2][0] * point[0] + rot[2][1] * point[1] + rot[2][2] * point[2]
- };
- }
- [[nodiscard]] constexpr vec3_t closest_point_to_box(const vec3_t &from, const vec3_t &absmins, const vec3_t &absmaxs)
- {
- return {
- (from[0] < absmins[0]) ? absmins[0] : (from[0] > absmaxs[0]) ? absmaxs[0] : from[0],
- (from[1] < absmins[1]) ? absmins[1] : (from[1] > absmaxs[1]) ? absmaxs[1] : from[1],
- (from[2] < absmins[2]) ? absmins[2] : (from[2] > absmaxs[2]) ? absmaxs[2] : from[2]
- };
- }
- [[nodiscard]] inline float distance_between_boxes(const vec3_t &absminsa, const vec3_t &absmaxsa, const vec3_t &absminsb, const vec3_t &absmaxsb)
- {
- float len = 0;
- for (size_t i = 0; i < 3; i++)
- {
- if (absmaxsa[i] < absminsb[i])
- {
- float d = absmaxsa[i] - absminsb[i];
- len += d * d;
- }
- else if (absminsa[i] > absmaxsb[i])
- {
- float d = absminsa[i] - absmaxsb[i];
- len += d * d;
- }
- }
-
- return sqrt(len);
- }
- [[nodiscard]] constexpr bool boxes_intersect(const vec3_t &amins, const vec3_t &amaxs, const vec3_t &bmins, const vec3_t &bmaxs)
- {
- return amins.x <= bmaxs.x &&
- amaxs.x >= bmins.x &&
- amins.y <= bmaxs.y &&
- amaxs.y >= bmins.y &&
- amins.z <= bmaxs.z &&
- amaxs.z >= bmins.z;
- }
- /*
- ==================
- ClipVelocity
- Slide off of the impacting object
- ==================
- */
- constexpr float STOP_EPSILON = 0.1f;
- [[nodiscard]] constexpr vec3_t ClipVelocity(const vec3_t &in, const vec3_t &normal, float overbounce)
- {
- float dot = in.dot(normal);
- vec3_t out = in + (normal * (-2 * dot));
- out *= overbounce - 1.f;
- if (out.lengthSquared() < STOP_EPSILON * STOP_EPSILON)
- out = {};
- return out;
- }
- [[nodiscard]] constexpr vec3_t SlideClipVelocity(const vec3_t &in, const vec3_t &normal, float overbounce)
- {
- float backoff = in.dot(normal) * overbounce;
- vec3_t out = in - (normal * backoff);
- for (int i = 0; i < 3; i++)
- if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
- out[i] = 0;
- return out;
- }
- [[nodiscard]] inline float vectoyaw(const vec3_t &vec)
- {
- // PMM - fixed to correct for pitch of 0
- if (vec[PITCH] == 0)
- {
- if (vec[YAW] == 0)
- return 0.f;
- else if (vec[YAW] > 0)
- return 90.f;
- else
- return 270.f;
- }
- float yaw = (atan2(vec[YAW], vec[PITCH]) * (180.f / PIf));
- if (yaw < 0)
- yaw += 360;
- return yaw;
- }
- [[nodiscard]] inline vec3_t vectoangles(const vec3_t &vec)
- {
- float forward;
- float yaw, pitch;
- if (vec[1] == 0 && vec[0] == 0)
- {
- if (vec[2] > 0)
- return { -90.f, 0.f, 0.f };
- else
- return { -270.f, 0.f, 0.f };
- }
- // PMM - fixed to correct for pitch of 0
- if (vec[0])
- yaw = (atan2(vec[1], vec[0]) * (180.f / PIf));
- else if (vec[1] > 0)
- yaw = 90;
- else
- yaw = 270;
- if (yaw < 0)
- yaw += 360;
- forward = sqrt(vec[0] * vec[0] + vec[1] * vec[1]);
- pitch = (atan2(vec[2], forward) * (180.f / PIf));
- if (pitch < 0)
- pitch += 360;
- return { -pitch, yaw, 0 };
- }
- [[nodiscard]] constexpr vec3_t G_ProjectSource(const vec3_t &point, const vec3_t &distance, const vec3_t &forward, const vec3_t &right)
- {
- return point + (forward * distance[0]) + (right * distance[1]) + vec3_t{0.f, 0.f, distance[2]};
- }
- [[nodiscard]] constexpr vec3_t G_ProjectSource2(const vec3_t &point, const vec3_t &distance, const vec3_t &forward, const vec3_t &right, const vec3_t &up)
- {
- return point + (forward * distance[0]) + (right * distance[1]) + (up * distance[2]);
- }
- [[nodiscard]] inline vec3_t slerp(const vec3_t &from, const vec3_t &to, float t)
- {
- float dot = from.dot(to);
- float aFactor;
- float bFactor;
- if (fabsf(dot) > 0.9995f)
- {
- aFactor = 1.0f - t;
- bFactor = t;
- }
- else
- {
- float ang = acos(dot);
- float sinOmega = sin(ang);
- float sinAOmega = sin((1.0f - t) * ang);
- float sinBOmega = sin(t * ang);
- aFactor = sinAOmega / sinOmega;
- bFactor = sinBOmega / sinOmega;
- }
- return from * aFactor + to * bFactor;
- }
- // Fmt support
- template<>
- struct fmt::formatter<vec3_t> : fmt::formatter<float>
- {
- template<typename FormatContext>
- auto format(const vec3_t &p, FormatContext &ctx) -> decltype(ctx.out())
- {
- auto out = fmt::formatter<float>::format(p.x, ctx);
- out = fmt::format_to(out, " ");
- ctx.advance_to(out);
- out = fmt::formatter<float>::format(p.y, ctx);
- out = fmt::format_to(out, " ");
- ctx.advance_to(out);
- return fmt::formatter<float>::format(p.z, ctx);
- }
- };
|