Compare commits
49 Commits
alpha-0.0.
...
physics
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae3eb3276f | ||
|
|
615f68b48b | ||
|
|
c7547ffab5 | ||
|
|
14610c1f94 | ||
| 68dfc73ad2 | |||
| 98625523ca | |||
| ee84b290a6 | |||
|
|
6116ff3e59 | ||
|
|
7d8045918e | ||
| d5bda8ab5d | |||
| 8ff74d028f | |||
| 6089b144eb | |||
| ed3322f256 | |||
| d13030fbb0 | |||
| f78f36ffb2 | |||
|
|
8a35b2390c | ||
|
|
dc2c74fcb1 | ||
|
|
bae3d70b3c | ||
|
|
f3d5b4aeab | ||
|
|
1ab0d61890 | ||
|
|
2896dbeaf6 | ||
|
|
0175a1fed0 | ||
|
|
ce13f6f1ce | ||
|
|
69d5547d15 | ||
| 1d1a02a7b5 | |||
| cd7ca3edf0 | |||
|
|
6e6a21ce09 | ||
|
|
0adeba26e4 | ||
|
|
011d8a573c | ||
|
|
22406ad020 | ||
|
|
4db55a372b | ||
| 20d176ccb5 | |||
| 3863e7907f | |||
|
|
35b7d7bab0 | ||
| 6aca413b4f | |||
| ee322e3e7b | |||
| 864a15e4c8 | |||
| 7efd8218ea | |||
| 08db7f84b9 | |||
| 7119dea783 | |||
| 6e998fc368 | |||
| 441131a2f5 | |||
| c875fa1dee | |||
| 5e4b318d67 | |||
| 076fa7badc | |||
| 1091abd034 | |||
| dd9ea3ece8 | |||
| 6226161e31 | |||
| 6b32e8878e |
Binary file not shown.
BIN
assets/sol.glb
Normal file
BIN
assets/sol.glb
Normal file
Binary file not shown.
@@ -2,17 +2,38 @@
|
||||
|
||||
namespace blitz {
|
||||
|
||||
/**
|
||||
* \struct An exponential moving average smoother, or one pole lowpass filter
|
||||
* smoothes incoming data by attenutating sharp/sudden changes
|
||||
*/
|
||||
class EMASmoother {
|
||||
private:
|
||||
float Alpha;
|
||||
|
||||
public:
|
||||
float Current;
|
||||
EMASmoother();
|
||||
void TickUnitT(float target);
|
||||
void Tick(float target, float delta);
|
||||
float GetAlpha();
|
||||
void SetAlpha(float alpha);
|
||||
|
||||
public:
|
||||
/**
|
||||
* \brief the current output value of the smoother
|
||||
*/
|
||||
float Current;
|
||||
EMASmoother();
|
||||
/**
|
||||
* \brief behaves like `Tick(target, 1.0f)` but optimised
|
||||
*/
|
||||
void TickUnitT(float target);
|
||||
/**
|
||||
* \brief feed in the next value to the filter, and update `Current` accordingly
|
||||
* \param target the value in question
|
||||
* \param delta the time that has passed sinced the last value has been provided
|
||||
*/
|
||||
void Tick(float target, float delta);
|
||||
|
||||
/**
|
||||
* \brief set the amount of time it should take to reacha certain value, the higher this is
|
||||
* the "smoother the output signal"
|
||||
*/
|
||||
void SetSmoothingTime(float t);
|
||||
};
|
||||
|
||||
|
||||
@@ -7,9 +7,11 @@
|
||||
|
||||
#include "Player.h"
|
||||
#include "blitz/game/Listeners.h"
|
||||
#include "blitz/game/World.h"
|
||||
#include "blitz/misc/ObjectNotifier.h"
|
||||
#include "blitz/misc/Time.h"
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
namespace blitz {
|
||||
namespace game {
|
||||
@@ -45,6 +47,7 @@ class Game : public utils::ObjectNotifier<GameListener> {
|
||||
GameState m_GameState;
|
||||
utils::Timer m_GameTimer;
|
||||
GameConfig m_Config;
|
||||
std::unique_ptr<World> m_World;
|
||||
|
||||
public:
|
||||
/** \brief Default constructor */
|
||||
@@ -135,6 +138,13 @@ class Game : public utils::ObjectNotifier<GameListener> {
|
||||
std::uint64_t GetGameStateRemainingTime() const {
|
||||
return m_GameTimer.GetTimeRemaining();
|
||||
}
|
||||
|
||||
/**
|
||||
* \return The world associated with this game
|
||||
*/
|
||||
const World* GetWorld() const {
|
||||
return m_World.get();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "blitz/common/Defines.h"
|
||||
#include "blitz/maths/Vector.h"
|
||||
#include "blitz/maths/Physics.h"
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
@@ -42,6 +43,7 @@ struct PlayerStats {
|
||||
*/
|
||||
class Player {
|
||||
private:
|
||||
maths::AABB m_Hitbox;
|
||||
PlayerID m_ID;
|
||||
std::string m_Name;
|
||||
Vec3f m_Position;
|
||||
@@ -83,6 +85,20 @@ class Player {
|
||||
m_Name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the player's hitbox
|
||||
*/
|
||||
maths::AABB GetHitBox() const {
|
||||
return m_Hitbox + m_Position;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sets the player's hitbox
|
||||
*/
|
||||
void SetHitBoxSize(const maths::AABB& aabb) {
|
||||
m_Hitbox = aabb;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the position of the player
|
||||
* \return The position of the player
|
||||
|
||||
27
include/blitz/game/World.h
Normal file
27
include/blitz/game/World.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "blitz/game/Listeners.h"
|
||||
#include "blitz/maths/Physics.h"
|
||||
#include <vector>
|
||||
|
||||
namespace blitz {
|
||||
namespace game {
|
||||
|
||||
class Game;
|
||||
|
||||
class World {
|
||||
protected:
|
||||
Game* m_Game;
|
||||
std::vector<maths::AABB> m_AABBs;
|
||||
|
||||
public:
|
||||
World(Game* game);
|
||||
virtual ~World();
|
||||
|
||||
const std::vector<maths::AABB>& GetAABBs() const {
|
||||
return m_AABBs;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
} // namespace blitz
|
||||
@@ -128,18 +128,32 @@ T ReduceMax(const Vec3<T>& vect) {
|
||||
* \param v a vector
|
||||
* \return the (signed) minimal coordinate of the vector
|
||||
*/
|
||||
template <>
|
||||
inline float ReduceMin<float>(const Vec3f& v) {
|
||||
inline float ReduceFMinF(const Vec3f& v) {
|
||||
return std::fminf(std::fminf(v.x, v.y), v.z);
|
||||
}
|
||||
|
||||
inline Vec3f FmaF(const Vec3f& v, const Vec3f& a, const Vec3f& b) {
|
||||
return {
|
||||
std::fmaf(v.x, a.x, b.x),
|
||||
std::fmaf(v.y, a.y, b.y),
|
||||
std::fmaf(v.z, a.z, b.z),
|
||||
};
|
||||
}
|
||||
|
||||
inline Vec3d Fma(const Vec3d& v, const Vec3d& a, const Vec3d& b) {
|
||||
return {
|
||||
std::fma(v.x, a.x, b.x),
|
||||
std::fma(v.y, a.y, b.y),
|
||||
std::fma(v.z, a.z, b.z),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Returns the (signed) maximal coordinate of the vector
|
||||
* \param v a vector
|
||||
* \return the (signed) maximal coordinate of the vector
|
||||
*/
|
||||
template <>
|
||||
inline float ReduceMax<float>(const Vec3f& v) {
|
||||
inline float ReduceFMaxF(const Vec3f& v) {
|
||||
return std::fmaxf(std::fmaxf(v.x, v.y), v.z);
|
||||
}
|
||||
|
||||
@@ -148,8 +162,7 @@ inline float ReduceMax<float>(const Vec3f& v) {
|
||||
* \param v a vector
|
||||
* \return the (signed) minimal coordinate of the vector
|
||||
*/
|
||||
template <>
|
||||
inline double ReduceMin<double>(const Vec3d& v) {
|
||||
inline double ReduceFMin(const Vec3d& v) {
|
||||
return std::fmin(std::fmin(v.x, v.y), v.z);
|
||||
}
|
||||
|
||||
@@ -158,8 +171,7 @@ inline double ReduceMin<double>(const Vec3d& v) {
|
||||
* \param v a vector
|
||||
* \return the (signed) maximal coordinate of the vector
|
||||
*/
|
||||
template <>
|
||||
inline double ReduceMax<double>(const Vec3d& v) {
|
||||
inline double ReduceFMax(const Vec3d& v) {
|
||||
return std::fmax(std::fmax(v.x, v.y), v.z);
|
||||
}
|
||||
|
||||
@@ -182,13 +194,30 @@ constexpr Vec3<T> Min(const Vec3<T>& self, const Vec3<T>& other) {
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief returns the coordinate-wise maximum of the given vectors,
|
||||
* where a coordinate in the returned vector is NAN iff any of the two compared ones are NAN
|
||||
* \tparam T
|
||||
* \param self a vector
|
||||
* \param other an other vector
|
||||
* \return returns the coordinate-wise maximum of the given vectors,
|
||||
* @brief returns the coordinate-wise minimum of the given vectors,
|
||||
* where a coordinate in the returned vector is NAN iff both of the two compared ones are NAN
|
||||
*
|
||||
* @tparam T
|
||||
* @param self
|
||||
* @param other
|
||||
* @return constexpr Vec3f
|
||||
*/
|
||||
constexpr Vec3f FMinF(const Vec3f& self, const Vec3f& other) {
|
||||
return {
|
||||
std::fminf(self.x, other.x),
|
||||
std::fminf(self.y, other.y),
|
||||
std::fminf(self.z, other.z),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief returns the coordinate-wise maximum of the given vectors,
|
||||
* where a coordinate in the returned vector is NAN iff any of the two compared ones are NAN
|
||||
*
|
||||
* @tparam T
|
||||
* @param self
|
||||
* @param other
|
||||
* @return constexpr Vec3
|
||||
*/
|
||||
template <typename T>
|
||||
constexpr Vec3<T> Max(const Vec3<T>& self, const Vec3<T>& other) {
|
||||
@@ -199,8 +228,47 @@ constexpr Vec3<T> Max(const Vec3<T>& self, const Vec3<T>& other) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief returns the coordinate-wise maximum of the given vectors,
|
||||
* where a coordinate in the returned vector is NAN iff both of the two compared ones are NAN
|
||||
*
|
||||
* @tparam T
|
||||
* @param self
|
||||
* @param other
|
||||
* @return constexpr Vec3
|
||||
*/
|
||||
constexpr Vec3f FMaxF(const Vec3f& self, const Vec3f& other) {
|
||||
return {
|
||||
std::fmaxf(self.x, other.x),
|
||||
std::fmaxf(self.y, other.y),
|
||||
std::fmaxf(self.z, other.z),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief lane-wise copysign
|
||||
*/
|
||||
constexpr Vec3f CopysignF(const Vec3f& v, const Vec3f& signs) {
|
||||
return {
|
||||
std::copysignf(v.x, signs.x),
|
||||
std::copysignf(v.y, signs.y),
|
||||
std::copysignf(v.z, signs.z),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief lane-wise copysign
|
||||
*/
|
||||
constexpr Vec3d Copysign(const Vec3d& v, const Vec3d& signs) {
|
||||
return {
|
||||
std::copysign(v.x, signs.x),
|
||||
std::copysign(v.y, signs.y),
|
||||
std::copysign(v.z, signs.z),
|
||||
};
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// Matricies //
|
||||
// Matrices //
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,30 +1,149 @@
|
||||
#pragma once
|
||||
|
||||
#include "blitz/maths/Vector.h"
|
||||
/**
|
||||
* \file Physics.h
|
||||
* \brief Physics logic
|
||||
*/
|
||||
|
||||
#include "blitz/maths/Maths.h"
|
||||
#include <optional>
|
||||
|
||||
namespace blitz {
|
||||
namespace maths {
|
||||
|
||||
struct Ray {
|
||||
Vec3f origin;
|
||||
Vec3f direction;
|
||||
/**
|
||||
* \enum Axis
|
||||
* \brief represents an axis in 3-dimensional space
|
||||
*/
|
||||
enum Axis : std::size_t { aX = 0, aY, aZ };
|
||||
|
||||
/**
|
||||
* \brief encodes the movement an entity must perform, in an environment containing other
|
||||
* entitis it may collide with
|
||||
*/
|
||||
struct CollisionData {
|
||||
/**
|
||||
* \brief the effective movement the entity can/should perform, immediately
|
||||
*/
|
||||
Vec3f delta;
|
||||
/**
|
||||
* \brief the direction of "slide" along a side of a bounding
|
||||
* box, this is 0 if no collisions occured
|
||||
*/
|
||||
Vec3f slide;
|
||||
};
|
||||
|
||||
/**
|
||||
* \struct IntersectionData
|
||||
* \brief Intermediate data encoding an intersection between a Ray/AABB and an AABB
|
||||
*/
|
||||
struct IntersectionData {
|
||||
/**
|
||||
* \brief by how much to translate before intersecting with the AABB
|
||||
*/
|
||||
float distance;
|
||||
/**
|
||||
* \brief the axis along which the collision happens, used to solve sliding requirements
|
||||
*/
|
||||
Axis axis;
|
||||
|
||||
/**
|
||||
* \brief Returns the movements that the entity must perform. It is advised to make sure
|
||||
* `0.0f <= distance <= 1.0f` as this would return incoherent data
|
||||
* \pre `0.0f <= distance <= 1.0f`
|
||||
* \param dir
|
||||
* \return
|
||||
*/
|
||||
CollisionData ToCollision(const Vec3f& dir) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* \struct AABB
|
||||
* \brief an Axis Aligned Bounding Box, see wikipedia for more
|
||||
*/
|
||||
struct AABB {
|
||||
Vec3f from;
|
||||
Vec3f to;
|
||||
|
||||
/**
|
||||
* \return A vector representing the center of mass of the box
|
||||
*/
|
||||
inline Vec3f Center() const;
|
||||
|
||||
/**
|
||||
* \return The same box but all the coordinates in `from` are less than their
|
||||
* counterparts in `to`.
|
||||
*/
|
||||
AABB Direct() const;
|
||||
|
||||
/**
|
||||
* \brief Enlarges this box along each axis by the length of the box along said axis
|
||||
* \param with
|
||||
* \return The new, big, box
|
||||
*
|
||||
*/
|
||||
AABB Enlarge(const AABB& with) const;
|
||||
|
||||
AABB operator+(const Vec3f& offset) const;
|
||||
|
||||
AABB operator+=(const Vec3f& offset);
|
||||
|
||||
/**
|
||||
* \brief checks if this box overlaps with another one
|
||||
* \param aabb
|
||||
* \return whether this box overlaps with another one
|
||||
*/
|
||||
bool OverlapsWith(const AABB& aabb) const;
|
||||
|
||||
/**
|
||||
* \brief Given this box is to be offset by some vector colinear to dir,
|
||||
* this function behave exactly like Ray::Intersection
|
||||
* \param aabb
|
||||
* \param dir
|
||||
* \return
|
||||
*/
|
||||
std::optional<IntersectionData> Intersection(const AABB& aabb, const Vec3f& dir) const;
|
||||
};
|
||||
|
||||
inline AABB operator+(const AABB& aabb, const Vec3f& offset) {
|
||||
AABB result = aabb;
|
||||
result.from += offset;
|
||||
result.to += offset;
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* \struct Ray
|
||||
* \brief represents a (half-)line
|
||||
*/
|
||||
struct Ray {
|
||||
/**
|
||||
* \brief the origin/start/initial point of the line
|
||||
*/
|
||||
Vec3f origin;
|
||||
/**
|
||||
* \brief the direction of the line
|
||||
*/
|
||||
Vec3f direction;
|
||||
|
||||
float Distance(const Ray& ray, const AABB& aabb);
|
||||
/**
|
||||
* \brief Note that t isn't necessarily positive
|
||||
* \param t
|
||||
* \return `origin` + `direction` * `t`
|
||||
*/
|
||||
Vec3f operator()(float t) const;
|
||||
|
||||
bool Intersects(const Ray& ray, const AABB& aabb);
|
||||
/**
|
||||
* \brief Is essentially Intersection(aabb).distance >= 0.0f
|
||||
* \returns `true` if the half-line `ray` _strictly_ intersects with `aabb`,
|
||||
* and `false` if it _strictly_ doesn't intersect.
|
||||
* Note that if it only intersects with corners, edges, or sides of the box,
|
||||
* or if any coordinate in `ray` is `NAN` or `inf` or if any coordinate in
|
||||
* `aabb` is `NAN` the result is unspecified.
|
||||
*/
|
||||
bool Intersects(const AABB& aabb) const;
|
||||
|
||||
/**
|
||||
* \brief The `distance` field of the returned value is smallest number t
|
||||
* (in absolute value), if it exists, such that `ray(t)` is in `aabb`. otherwise
|
||||
* a quiet `NaN` is returned. The `axis` field is the axis to along
|
||||
* which `ray` intersects/collides with `aabb`
|
||||
*/
|
||||
std::optional<IntersectionData> Intersection(const AABB& aabb) const;
|
||||
};
|
||||
|
||||
} // namespace maths
|
||||
} // namespace blitz
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
|
||||
namespace blitz {
|
||||
@@ -48,6 +47,34 @@ struct Vec3 {
|
||||
};
|
||||
|
||||
constexpr Vec3(T X = 0, T Y = 0, T Z = 0) : x(X), y(Y), z(Z) {}
|
||||
|
||||
static Vec3 splat(T val) {
|
||||
return Vec3(val, val, val);
|
||||
}
|
||||
|
||||
const T* data() const {
|
||||
return reinterpret_cast<const T*>(this);
|
||||
}
|
||||
|
||||
T* data() {
|
||||
return reinterpret_cast<T*>(this);
|
||||
}
|
||||
|
||||
const T& at(std::size_t index) const {
|
||||
return data()[index];
|
||||
}
|
||||
|
||||
T& at(std::size_t index) {
|
||||
return data()[index];
|
||||
}
|
||||
|
||||
const T& operator[](std::size_t index) const {
|
||||
return at(index);
|
||||
}
|
||||
|
||||
T& operator[](std::size_t index) {
|
||||
return at(index);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@@ -96,10 +123,6 @@ using Vec4d = Vec4<double>;
|
||||
|
||||
using Color = Vec3uc;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Vec2
|
||||
|
||||
template <typename T>
|
||||
@@ -144,10 +167,6 @@ constexpr Vec2<T> operator*(T mult, const Vec2<T>& vect) {
|
||||
return vect * mult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Vec3
|
||||
|
||||
template <typename T>
|
||||
@@ -226,11 +245,6 @@ Vec3<T>& operator/=(Vec3<T>& vect, const Vec3<T>& other) {
|
||||
return vect;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool operator<(const Vec3<T>& vec3, const Vec3<T>& other) {
|
||||
return vec3.x < other.x && vec3.y < other.y && vec3.z < other.z;
|
||||
}
|
||||
|
||||
// Vec4
|
||||
|
||||
template <typename T>
|
||||
|
||||
@@ -64,6 +64,8 @@ class PlayerController : public utils::ObjectNotifier<game::PlayerInputListener>
|
||||
private:
|
||||
void UpdateShootTimer(int bpm);
|
||||
void UpdatePosition(float delta);
|
||||
void ApplyForce(const Vec3f& f, float delta);
|
||||
void ApplyGravity(float delta);
|
||||
};
|
||||
|
||||
} // namespace input
|
||||
|
||||
20
include/client/game/ClientWorld.h
Normal file
20
include/client/game/ClientWorld.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "blitz/game/World.h"
|
||||
|
||||
namespace blitz {
|
||||
namespace client {
|
||||
|
||||
class ClientGame;
|
||||
|
||||
class ClientWorld : public game::World {
|
||||
public:
|
||||
ClientWorld(ClientGame* game);
|
||||
virtual ~ClientWorld() {}
|
||||
|
||||
private:
|
||||
void LoadAABBs();
|
||||
};
|
||||
|
||||
} // namespace client
|
||||
} // namespace blitz
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "blitz/maths/Physics.h"
|
||||
#include "client/render/loader/GLLoader.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -16,5 +17,7 @@ struct Model {
|
||||
|
||||
Model LoadModel(const std::string& fileName);
|
||||
|
||||
std::vector<maths::AABB> LoadModelAABBs(const std::string& fileName);
|
||||
|
||||
} // namespace ModelLoader
|
||||
} // namespace blitz
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include "imgui.h" // IMGUI_IMPL_API
|
||||
#ifndef IMGUI_DISABLE
|
||||
|
||||
class SDL_Window;
|
||||
struct SDL_Window;
|
||||
struct SDL_Renderer;
|
||||
typedef union SDL_Event SDL_Event;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "blitz/game/Game.h"
|
||||
#include <cmath>
|
||||
|
||||
namespace blitz {
|
||||
namespace game {
|
||||
@@ -25,6 +26,13 @@ void Game::AddPlayer(PlayerID player, const std::string& name) {
|
||||
static float MAX_HP = 100;
|
||||
|
||||
game::Player newPlayer{player};
|
||||
static const maths::AABB PLAYER_BASE_AABB = {Vec3f{-0.5f, -0.9f, -0.5f}, Vec3f{0.5f, 0.9f, 0.5f}};
|
||||
newPlayer.SetHitBoxSize(PLAYER_BASE_AABB);
|
||||
newPlayer.SetPosition({
|
||||
0.0f,
|
||||
std::fabs(PLAYER_BASE_AABB.to.y - PLAYER_BASE_AABB.from.y) * 10.0f,
|
||||
0.0f
|
||||
});
|
||||
newPlayer.SetName(name);
|
||||
newPlayer.SetHP(MAX_HP);
|
||||
|
||||
|
||||
13
src/blitz/game/World.cpp
Normal file
13
src/blitz/game/World.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#include "blitz/game/World.h"
|
||||
|
||||
#include "blitz/game/Game.h"
|
||||
|
||||
namespace blitz {
|
||||
namespace game {
|
||||
|
||||
World::World(Game* game) : m_Game(game) {}
|
||||
|
||||
World::~World() {}
|
||||
|
||||
} // namespace game
|
||||
} // namespace blitz
|
||||
@@ -5,15 +5,20 @@
|
||||
namespace blitz {
|
||||
namespace maths {
|
||||
|
||||
Mat4f Perspective(float fovY, float aspectRatio, float zNear, float zFar) {
|
||||
const float tanHalfFovy = std::tan(fovY / 2.0f);
|
||||
Mat4f Perspective(float fovY, float invAspectRatio, float zNear, float zFar) {
|
||||
|
||||
static const float FRAC_PI_2 = 1.5707963267948966;
|
||||
|
||||
const float cotanHalfFovy = -std::tan(std::fmaf(fovY, 0.5f, FRAC_PI_2));
|
||||
|
||||
float neg_i_z_diff = -1.0f / (zFar - zNear);
|
||||
|
||||
Mat4f result{};
|
||||
result.x0 = 1.0f / (aspectRatio * tanHalfFovy);
|
||||
result.y1 = 1.0f / (tanHalfFovy);
|
||||
result.z2 = -(zFar + zNear) / (zFar - zNear);
|
||||
result.x0 = cotanHalfFovy * invAspectRatio;
|
||||
result.y1 = cotanHalfFovy;
|
||||
result.z2 = (zFar + zNear) * neg_i_z_diff;
|
||||
result.z3 = -1.0f;
|
||||
result.w2 = -(2.0f * zFar * zNear) / (zFar - zNear);
|
||||
result.w2 = 2.0f * zFar * zNear * neg_i_z_diff;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
113
src/blitz/maths/Physics.cpp
Executable file → Normal file
113
src/blitz/maths/Physics.cpp
Executable file → Normal file
@@ -1,67 +1,112 @@
|
||||
#include "blitz/maths/Physics.h"
|
||||
#include "blitz/maths/Maths.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace blitz {
|
||||
namespace maths {
|
||||
|
||||
/**
|
||||
* @brief Returns `true` if the half-line `ray` _strictly_ intersects with `aabb`,
|
||||
* and `false` if it _strictly_ doesn't intersect.
|
||||
* Note that if it only intersects with corners, edges, or sides of the box,
|
||||
* or if any coordinate in `ray` is `NAN` or `inf` or if any coordinate in
|
||||
* `aabb` is `NAN` the result is unspecified.
|
||||
* */
|
||||
float Distance(const Ray& ray, const AABB& aabb) {
|
||||
AABB AABB::Enlarge(const AABB& with) const {
|
||||
const Vec3f dir = 0.5f * (with.to - with.from);
|
||||
|
||||
// This function calculates smallest interval I = ] a, b [, for strictly positive a and b
|
||||
// such that for all t in I, for all l, r, o, d corresponding
|
||||
// coordinates in aabb.from, aabb.to, ray.origin, ray.direction, respectively
|
||||
return AABB{
|
||||
from - dir,
|
||||
to + dir,
|
||||
};
|
||||
}
|
||||
|
||||
AABB AABB::Direct() const {
|
||||
return AABB{Min(from, to), Max(from, to)};
|
||||
}
|
||||
|
||||
Vec3f AABB::Center() const {
|
||||
return (from + to) * 0.5f;
|
||||
}
|
||||
|
||||
AABB AABB::operator+(const Vec3f& offset) const {
|
||||
return AABB{from + offset, to + offset};
|
||||
}
|
||||
|
||||
AABB AABB::operator+=(const Vec3f& offset) {
|
||||
*this = *this + offset;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool AABB::OverlapsWith(const AABB& aabb) const {
|
||||
|
||||
return RangesOverlapping(from.x, to.x, aabb.from.x, aabb.to.x) &&
|
||||
RangesOverlapping(from.y, to.y, aabb.from.y, aabb.to.y) &&
|
||||
RangesOverlapping(from.z, to.z, aabb.from.z, aabb.to.z);
|
||||
}
|
||||
|
||||
std::optional<IntersectionData> AABB::Intersection(const AABB& aabb, const Vec3f& dir) const {
|
||||
return Ray { Center(), dir }.Intersection(aabb.Enlarge(*this));
|
||||
}
|
||||
|
||||
Vec3f Ray::operator()(float t) const {
|
||||
return FmaF(Vec3f::splat(t), direction, origin);
|
||||
}
|
||||
|
||||
std::optional<IntersectionData> Ray::Intersection(const AABB& aabb) const {
|
||||
|
||||
// This function calculates smallest interval I = ] tmin, tmax [,
|
||||
// such that for all t in I ray(t) is in the AABB, now, if tmin > tmax
|
||||
// there is no such t, i.e. the ray never intersects with the AABB
|
||||
//
|
||||
// min(l, r) < o + t * d < max(l, r)
|
||||
//
|
||||
// and returns true if it's non-empty i.e. a < b
|
||||
// and returns true if it's non-empty i.e. a < b
|
||||
|
||||
// m = min(l, r), M = max(l, r)
|
||||
// m < o + t * d < M
|
||||
Vec3f l = Min(aabb.from, aabb.to);
|
||||
Vec3f r = Max(aabb.to, aabb.from);
|
||||
AABB directed = aabb.Direct();
|
||||
Vec3f& l = directed.from;
|
||||
Vec3f& r = directed.to;
|
||||
|
||||
// m - o < t * d < M - o
|
||||
l -= ray.origin;
|
||||
r -= ray.origin;
|
||||
l -= origin;
|
||||
r -= origin;
|
||||
|
||||
// (m - o) / d < t < (M - o) / d
|
||||
l /= ray.direction;
|
||||
r /= ray.direction;
|
||||
l /= direction;
|
||||
r /= direction;
|
||||
|
||||
// but if d is negative the inequality is flipped
|
||||
Vec3f u = Min(l, r);
|
||||
r = Max(l, r);
|
||||
l = u;
|
||||
|
||||
float tmin = ReduceMax(l);
|
||||
float tmax = ReduceMin(r);
|
||||
const float tmin = ReduceFMaxF(l);
|
||||
const float tmax = ReduceFMinF(r);
|
||||
|
||||
// Since Min propagates NANs and ReduceMin doesn't, and since NAN !< <any float>
|
||||
// the inequality becomes ignored for coordinates where a NAN is involved
|
||||
// Since Min propagates NANs and ReduceFMinF doesn't, and since NAN != <any float>
|
||||
// the inequality becomes ignored for coordinates where a NaN is involved
|
||||
// (as a result of 0.0 / 0.0). If all coordinates are NAN, this means
|
||||
// that the box is reduced to a point and the ray has direction 0,
|
||||
// in which case this returns -1
|
||||
if (tmax >= 0.0f && tmin <= tmax) {
|
||||
return std::fmaxf(tmin, 0.0f);
|
||||
// in which case this returns 0.0, which is technically the correct result
|
||||
if (tmin <= tmax && tmax > 0.0f) {
|
||||
|
||||
Axis axis = Axis::aZ;
|
||||
for (std::size_t i = 2; i >= 0; i--) {
|
||||
Axis this_axis = Axis(i);
|
||||
if (l[this_axis] == tmin) {
|
||||
axis = this_axis;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const float dist = std::fmaxf(0.0f, tmin);
|
||||
return std::make_optional(IntersectionData { dist, axis });
|
||||
}
|
||||
|
||||
return -1.0f;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool Intersects(const AABB& aabb1, const AABB& aabb2) {
|
||||
return false;
|
||||
bool Ray::Intersects(const AABB& aabb) const {
|
||||
auto data = Intersection(aabb);
|
||||
return data.has_value() && data->distance >= 0.0f;
|
||||
}
|
||||
|
||||
bool Intersects(const Ray& ray, const AABB& aabb) {
|
||||
return Distance(ray, aabb) >= 0.0f;
|
||||
CollisionData IntersectionData::ToCollision(const Vec3f& dir) const {
|
||||
Vec3f new_dir = dir;
|
||||
new_dir[axis] = 0.0f;
|
||||
return CollisionData{dir * (distance - 0.07f), new_dir * (1.0f - distance)};
|
||||
}
|
||||
|
||||
} // namespace maths
|
||||
|
||||
@@ -32,7 +32,7 @@ float EaseOutCubic(float x) {
|
||||
}
|
||||
|
||||
float EaseInOutCubic(float x) {
|
||||
return static_cast<float>(x < 0.5 ? 4 * EaseInCubic(x) : 1 - std::pow(-2 * x + 2, 3) / 2.0);
|
||||
return x < 0.5f ? 4.0f * EaseInCubic(x) : 1.0f - std::pow(-2.0f * x + 2.0f, 3.0f) / 2.0f;
|
||||
}
|
||||
|
||||
/* Quint functions */
|
||||
@@ -60,8 +60,8 @@ float EaseOutCirc(float x) {
|
||||
}
|
||||
|
||||
float EaseInOutCirc(float x) {
|
||||
return static_cast<float>(
|
||||
x < 0.5 ? (1 - std::sqrt(1 - std::pow(2 * x, 2))) / 2.0 : (std::sqrt(1 - std::pow(-2 * x + 2, 2)) + 1) / 2.0);
|
||||
return x < 0.5f ? (1.0f - std::sqrt(1.0f - std::pow(2.0f * x, 2.0f))) / 2.0f
|
||||
: (std::sqrt(1.0f - std::pow(-2.0f * x + 2.0f, 2.0f)) + 1.0f) / 2.0f;
|
||||
}
|
||||
|
||||
/* Elastic functions */
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "blitz/game/Player.h"
|
||||
#include "blitz/maths/Maths.h"
|
||||
#include "blitz/maths/Physics.h"
|
||||
#include "blitz/misc/Log.h"
|
||||
#include "client/Client.h"
|
||||
#include "client/config/BlitzConfig.h"
|
||||
@@ -25,7 +26,7 @@ PlayerController::PlayerController(Client* client, input::InputManager& inputMan
|
||||
m_Client(client),
|
||||
m_InputManager(inputManager),
|
||||
m_Velocity({}),
|
||||
m_MaxVelocity(DEFAULT_MAX_LR_SPEED, DEFAULT_MAX_FB_SPEED, DEFAULT_JUMP_VEL),
|
||||
m_MaxVelocity(DEFAULT_MAX_LR_SPEED, DEFAULT_JUMP_VEL, DEFAULT_MAX_FB_SPEED),
|
||||
m_OnGround(true) {
|
||||
m_DxSmoother.Current = 0.0f;
|
||||
m_DySmoother.Current = 0.0f;
|
||||
@@ -80,13 +81,19 @@ void PlayerController::Update(float delta) {
|
||||
static_cast<float>(ImGui::IsKeyDown((ImGuiKey(keys[kaDroite]))));
|
||||
|
||||
// scale values in such a way that clamps ||(lr, fb)|| to 1.0
|
||||
float scale = 1.0f / std::max(sqrt(lr * lr + fb * fb), 1.0f);
|
||||
float scale = 1.0f / std::fmaxf(sqrt(lr * lr + fb * fb), 1.0f);
|
||||
|
||||
static constexpr float AIR_MOVEMENT_DAMP = 3.0f;
|
||||
float no = std::fmaf(static_cast<float>(!m_OnGround), AIR_MOVEMENT_DAMP, 1.0f);
|
||||
|
||||
m_DxSmoother.SetSmoothingTime(DEFAULT_LR_SPEED_SMOOTHING_TIME * no);
|
||||
m_DySmoother.SetSmoothingTime(DEFAULT_FB_SPEED_SMOOTHING_TIME * no);
|
||||
|
||||
m_Velocity.x = lr * m_MaxVelocity.x * scale;
|
||||
m_Velocity.y = fb * m_MaxVelocity.y * scale;
|
||||
m_Velocity.z = fb * m_MaxVelocity.z * scale;
|
||||
|
||||
if (ImGui::IsKeyDown(ImGuiKey::ImGuiKey_Space) && m_OnGround) {
|
||||
m_Velocity.z = m_MaxVelocity.z;
|
||||
m_Velocity.y = m_MaxVelocity.y;
|
||||
NotifyListeners(&game::PlayerInputListener::OnLocalPlayerJump);
|
||||
}
|
||||
|
||||
@@ -97,12 +104,22 @@ void PlayerController::Update(float delta) {
|
||||
m_ShootTimer.ApplyCooldown();
|
||||
}
|
||||
} else {
|
||||
m_Velocity.x = m_Velocity.y = 0.0f;
|
||||
m_Velocity.x = m_Velocity.z = 0.0f;
|
||||
}
|
||||
|
||||
ApplyGravity(delta);
|
||||
|
||||
UpdatePosition(delta);
|
||||
}
|
||||
|
||||
void PlayerController::ApplyForce(const Vec3f& f, float delta) {
|
||||
m_Velocity += f * delta;
|
||||
}
|
||||
|
||||
void PlayerController::ApplyGravity(float delta) {
|
||||
ApplyForce(Vec3f { 0.0f, -m_Client->GetGame()->GetGameConfig().Gravity, 0.0f }, delta);
|
||||
}
|
||||
|
||||
void PlayerController::UpdatePosition(const float delta) {
|
||||
|
||||
float yaw = m_Player->GetYaw();
|
||||
@@ -110,28 +127,54 @@ void PlayerController::UpdatePosition(const float delta) {
|
||||
float cosine = std::cos(yaw);
|
||||
|
||||
m_DxSmoother.Tick(m_Velocity.x, delta);
|
||||
m_DySmoother.Tick(m_Velocity.y, delta);
|
||||
m_DySmoother.Tick(m_Velocity.z, delta);
|
||||
|
||||
float dx_smooth = m_DxSmoother.Current;
|
||||
float dy_smooth = m_DySmoother.Current;
|
||||
|
||||
float dx = (dx_smooth * cosine + dy_smooth * sine) * delta;
|
||||
float dy = (dx_smooth * sine - dy_smooth * cosine) * delta;
|
||||
Vec3f vel = Vec3f((dx_smooth * cosine + dy_smooth * sine), m_Velocity.y, (dx_smooth * sine - dy_smooth * cosine)) * delta;
|
||||
maths::CollisionData collision { Vec3f::splat(0.0f), vel };
|
||||
|
||||
float dz = m_Velocity.z * delta;
|
||||
const auto& aabbs = m_Client->GetGame()->GetWorld()->GetAABBs();
|
||||
maths::AABB hitbox = m_Player->GetHitBox();
|
||||
|
||||
// the floor here is y-level zero, once downwards collision lands it will be dynmicallly calculated
|
||||
// assumed to be a negative number
|
||||
const float floor_dist = 0.0f - m_Player->GetPosition().y;
|
||||
bool on_ground = false;
|
||||
|
||||
if ((m_OnGround = (dz <= floor_dist))) {
|
||||
dz = floor_dist;
|
||||
m_Velocity.z = 0.0f;
|
||||
} else {
|
||||
m_Velocity.z -= m_Client->GetGame()->GetGameConfig().Gravity * 1.0f * delta;
|
||||
}
|
||||
do {
|
||||
|
||||
m_Player->AddPosition({dx, dz, dy});
|
||||
maths::IntersectionData closest { 1.0f, maths::Axis::aY };
|
||||
|
||||
for (const maths::AABB& aabb : aabbs) {
|
||||
|
||||
auto intersection = hitbox.Intersection(aabb, collision.slide);
|
||||
if (intersection.has_value()) {
|
||||
|
||||
float d = intersection->distance;
|
||||
if (d < closest.distance) {
|
||||
|
||||
maths::Axis axis = intersection->axis;
|
||||
on_ground |= (axis == maths::Axis::aY);
|
||||
|
||||
closest.distance = d;
|
||||
closest.axis = axis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (closest.distance < 0.99f) {
|
||||
m_Velocity[closest.axis] = 0.0f;
|
||||
}
|
||||
|
||||
collision = closest.ToCollision(collision.slide);
|
||||
|
||||
Vec3f v = collision.delta;
|
||||
|
||||
m_Player->AddPosition(v);
|
||||
m_Player->SetVelocity(v);
|
||||
|
||||
} while (maths::Length(collision.slide) > 1e-6);
|
||||
|
||||
m_OnGround = on_ground;
|
||||
}
|
||||
|
||||
} // namespace input
|
||||
|
||||
@@ -13,12 +13,14 @@
|
||||
#include "blitz/protocol/packets/UpdateGameStatePacket.h"
|
||||
#include "blitz/protocol/packets/UpdateHealthPacket.h"
|
||||
#include "client/Client.h"
|
||||
#include "client/game/ClientWorld.h"
|
||||
|
||||
namespace blitz {
|
||||
namespace client {
|
||||
|
||||
ClientGame::ClientGame(Client* client, protocol::PacketDispatcher* dispatcher) :
|
||||
protocol::PacketHandler(dispatcher), m_Client(client) {
|
||||
m_World = std::make_unique<ClientWorld>(this);
|
||||
RegisterHandlers();
|
||||
}
|
||||
|
||||
|
||||
18
src/client/game/ClientWorld.cpp
Normal file
18
src/client/game/ClientWorld.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "client/game/ClientWorld.h"
|
||||
|
||||
#include "client/game/ClientGame.h"
|
||||
#include "client/render/loader/ModelLoader.h"
|
||||
|
||||
namespace blitz {
|
||||
namespace client {
|
||||
|
||||
ClientWorld::ClientWorld(ClientGame* game) : game::World(game) {
|
||||
LoadAABBs();
|
||||
}
|
||||
|
||||
void ClientWorld::LoadAABBs() {
|
||||
m_AABBs = ModelLoader::LoadModelAABBs("sol.glb");
|
||||
}
|
||||
|
||||
} // namespace client
|
||||
} // namespace blitz
|
||||
@@ -7,7 +7,8 @@
|
||||
namespace blitz {
|
||||
namespace render {
|
||||
|
||||
static const float EyeHeight = 1.25f;
|
||||
/// \brief Eye height from center
|
||||
static const float EyeHeight = 0.5f;
|
||||
|
||||
void Camera::Update(float delta) {
|
||||
int windowWidth = static_cast<int>(ImGui::GetIO().DisplaySize.x);
|
||||
@@ -16,8 +17,12 @@ void Camera::Update(float delta) {
|
||||
if (windowWidth != m_LastWindowSize.x || windowHeight != m_LastWindowSize.y) {
|
||||
m_LastWindowSize = {windowWidth, windowHeight};
|
||||
|
||||
m_PerspectiveMatrix =
|
||||
maths::Perspective(80.0f / 180.0f * maths::PI, static_cast<float>(windowWidth) / windowHeight, 0.1f, 160.0f);
|
||||
m_PerspectiveMatrix = maths::Perspective(
|
||||
80.0f / 180.0f * maths::PI,
|
||||
static_cast<float>(windowHeight) / static_cast<float>(windowWidth),
|
||||
0.1f,
|
||||
160.0f
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ MainRenderer::~MainRenderer() {
|
||||
}
|
||||
|
||||
void MainRenderer::LoadModels() {
|
||||
m_WorldModel = ModelLoader::LoadModel("base_deambu.glb");
|
||||
m_WorldModel = ModelLoader::LoadModel("sol.glb");
|
||||
m_PlayerModel = ModelLoader::LoadModel("human.obj");
|
||||
m_GunModel = ModelLoader::LoadModel("fingergun.obj");
|
||||
|
||||
|
||||
@@ -101,14 +101,15 @@ Model LoadModel(const std::string& fileName) {
|
||||
|
||||
const aiScene* scene = importer.ReadFileFromMemory(fileData.data(), fileData.GetSize(),
|
||||
aiProcess_CalcTangentSpace | aiProcess_Triangulate | aiProcess_SortByPType | aiProcess_OptimizeGraph |
|
||||
aiProcess_OptimizeMeshes);
|
||||
aiProcess_OptimizeMeshes | aiProcess_GenBoundingBoxes);
|
||||
|
||||
|
||||
if (nullptr == scene) {
|
||||
utils::LOGE("[ModelLoader] Failed to load model !");
|
||||
return {};
|
||||
}
|
||||
|
||||
utils::LOGD(utils::Format("[ModelLoader]\tModel nodes : %i", scene->mRootNode->mNumMeshes));
|
||||
utils::LOGD(utils::Format("[ModelLoader]\tModel nodes : %i", scene->mRootNode->mNumChildren));
|
||||
|
||||
Model model;
|
||||
ProcessNode(scene->mRootNode, scene, model.mVaos, {});
|
||||
@@ -116,5 +117,47 @@ Model LoadModel(const std::string& fileName) {
|
||||
return model;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void ProcessAABBNode(aiNode* node, const aiScene* scene, std::vector<maths::AABB>& aabbs, const aiMatrix4x4& transform) {
|
||||
// recursive
|
||||
for (unsigned int i = 0; i < node->mNumChildren; i++) {
|
||||
ProcessAABBNode(node->mChildren[i], scene, aabbs, transform * node->mTransformation);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < node->mNumMeshes; i++) {
|
||||
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
|
||||
aiAABB& aabb = mesh->mAABB;
|
||||
|
||||
aiVector3D aabbMin = transform * node->mTransformation * aabb.mMin;
|
||||
aiVector3D aabbMax = transform * node->mTransformation * aabb.mMax;
|
||||
|
||||
aabbs.push_back({Vec3f{aabbMin.x, aabbMin.y, aabbMin.z}, {aabbMax.x, aabbMax.y, aabbMax.z}});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::vector<maths::AABB> LoadModelAABBs(const std::string& fileName) {
|
||||
DataBuffer fileData = utils::AssetsManager::GetAsset(fileName);
|
||||
|
||||
Assimp::Importer importer;
|
||||
|
||||
const aiScene* scene = importer.ReadFileFromMemory(fileData.data(), fileData.GetSize(), aiProcess_GenBoundingBoxes);
|
||||
|
||||
if (nullptr == scene) {
|
||||
utils::LOGE("[ModelLoader] Failed to load AABB model !");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<maths::AABB> aabbs;
|
||||
ProcessAABBNode(scene->mRootNode, scene, aabbs, {});
|
||||
|
||||
utils::LOGD(utils::Format("[ModelLoader]\tAABB count : %i", aabbs.size()));
|
||||
|
||||
return aabbs;
|
||||
}
|
||||
|
||||
|
||||
} // namespace ModelLoader
|
||||
} // namespace blitz
|
||||
|
||||
@@ -78,15 +78,13 @@ void ServerGame::SendPlayerPositions() {
|
||||
|
||||
void ServerGame::ProcessShoot(game::PlayerID shooter, Vec3f position, float yaw, float pitch) {
|
||||
maths::Ray shootRay;
|
||||
shootRay.origin = position + Vec3f{0.0, 1.75, 0.0};
|
||||
shootRay.origin = position + Vec3f{0.0, 0.85, 0.0};
|
||||
shootRay.direction = {
|
||||
std::cos(yaw) * std::cos(pitch),
|
||||
std::sin(pitch),
|
||||
std::sin(yaw) * std::cos(pitch),
|
||||
};
|
||||
|
||||
static const maths::AABB playerStaticAABB = {Vec3f{-0.5f, 0.0f, -0.5f}, Vec3f{0.5f, 1.8f, 0.5f}};
|
||||
|
||||
bool shootLanded = false;
|
||||
|
||||
game::Player* shooterPlayer = GetPlayerById(shooter);
|
||||
@@ -94,7 +92,7 @@ void ServerGame::ProcessShoot(game::PlayerID shooter, Vec3f position, float yaw,
|
||||
shooterPlayer->GetStats().m_ShootCount++;
|
||||
|
||||
for (auto& [playerId, player] : GetPlayers()) {
|
||||
if (playerId != shooter && maths::Intersects(shootRay, playerStaticAABB + player.GetPosition())) {
|
||||
if (playerId != shooter && shootRay.Intersects(player.GetHitBox())) {
|
||||
if (!shootLanded) {
|
||||
shootLanded = true;
|
||||
shooterPlayer->GetStats().m_ShootSuccessCount++;
|
||||
|
||||
96
test/test_intersects.cpp
Executable file → Normal file
96
test/test_intersects.cpp
Executable file → Normal file
@@ -1,105 +1,105 @@
|
||||
#include "blitz/misc/Test.h"
|
||||
|
||||
#include "blitz/maths/Physics.h"
|
||||
#include "blitz/misc/Log.h"
|
||||
#include "blitz/misc/Format.h"
|
||||
#include "blitz/misc/Log.h"
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
#include <limits>
|
||||
#include <ostream>
|
||||
|
||||
using namespace blitz;
|
||||
using namespace maths;
|
||||
|
||||
#define let auto // sexy boiiii
|
||||
#define let auto // sexy boiiii
|
||||
|
||||
static void test_left() {
|
||||
let box = AABB { {-1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f} };
|
||||
let ray = Ray { {-2.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 0.0f} };
|
||||
let box = AABB{{-1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}};
|
||||
let ray = Ray{{-2.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 0.0f}};
|
||||
|
||||
blitz_test_assert(Intersects(ray, box));
|
||||
blitz_test_assert(ray.Intersects(box));
|
||||
}
|
||||
|
||||
static void test_right() {
|
||||
let box = AABB { {-1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f} };
|
||||
let ray = Ray { {2.0f, 0.0f, 0.0f}, {-1.0f, 0.0f, 0.0f} };
|
||||
let box = AABB{{-1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}};
|
||||
let ray = Ray{{2.0f, 0.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}};
|
||||
|
||||
blitz_test_assert(Intersects(ray, box));
|
||||
blitz_test_assert(ray.Intersects(box));
|
||||
}
|
||||
|
||||
static void test_forward() {
|
||||
let box = AABB { {-1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f} };
|
||||
let ray = Ray { {0.0f, 2.0f, 0.0f}, {0.0f, -1.0f, 0.0f} };
|
||||
let box = AABB{{-1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}};
|
||||
let ray = Ray{{0.0f, 2.0f, 0.0f}, {0.0f, -1.0f, 0.0f}};
|
||||
|
||||
blitz_test_assert(Intersects(ray, box));
|
||||
blitz_test_assert(ray.Intersects(box));
|
||||
}
|
||||
|
||||
static void test_backward() {
|
||||
let box = AABB { {-1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f} };
|
||||
let ray = Ray { {0.0f, -2.0f, 0.0f}, {0.0f, 1.0f, 0.0f} };
|
||||
let box = AABB{{-1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}};
|
||||
let ray = Ray{{0.0f, -2.0f, 0.0f}, {0.0f, 1.0f, 0.0f}};
|
||||
|
||||
blitz_test_assert(Intersects(ray, box));
|
||||
blitz_test_assert(ray.Intersects(box));
|
||||
}
|
||||
|
||||
static void test_above() {
|
||||
let box = AABB { {-1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f} };
|
||||
let ray = Ray { {0.0f, 0.0f, 2.0f}, {0.0f, 0.0f, -1.0f} };
|
||||
let box = AABB{{-1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}};
|
||||
let ray = Ray{{0.0f, 0.0f, 2.0f}, {0.0f, 0.0f, -1.0f}};
|
||||
|
||||
blitz_test_assert(Intersects(ray, box));
|
||||
blitz_test_assert(ray.Intersects(box));
|
||||
}
|
||||
|
||||
static void test_below() {
|
||||
let box = AABB { {-1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f} };
|
||||
let ray = Ray { {0.0f, 0.0f, -2.0f}, {0.0f, 0.0f, 1.0f} };
|
||||
let box = AABB{{-1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}};
|
||||
let ray = Ray{{0.0f, 0.0f, -2.0f}, {0.0f, 0.0f, 1.0f}};
|
||||
|
||||
blitz_test_assert(Intersects(ray, box));
|
||||
blitz_test_assert(ray.Intersects(box));
|
||||
}
|
||||
|
||||
static void test_shifted() {
|
||||
let box = AABB { {-1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f} };
|
||||
let ray = Ray { {-3.0f, 0.0f, 100.0f}, {1.0f, 0.0f, 0.0f} };
|
||||
let box = AABB{{-1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}};
|
||||
let ray = Ray{{-3.0f, 0.0f, 100.0f}, {1.0f, 0.0f, 0.0f}};
|
||||
|
||||
blitz_test_assert(!Intersects(ray, box));
|
||||
blitz_test_assert(!ray.Intersects(box));
|
||||
}
|
||||
|
||||
static void test_corner_inside() {
|
||||
let box = AABB { {0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f} };
|
||||
let ray = Ray { {0.0f, 0.0f, 0.0f}, {0.5f, 0.5f, 0.5f} };
|
||||
let box = AABB{{0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f}};
|
||||
let ray = Ray{{0.0f, 0.0f, 0.0f}, {0.5f, 0.5f, 0.5f}};
|
||||
|
||||
blitz_test_assert(Intersects(ray, box));
|
||||
blitz_test_assert(ray.Intersects(box));
|
||||
}
|
||||
|
||||
static void test_corner_inside_weird() {
|
||||
let box = AABB { {0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f} };
|
||||
let ray = Ray { {0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f} };
|
||||
let box = AABB{{0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f}};
|
||||
let ray = Ray{{0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f}};
|
||||
|
||||
blitz_test_assert(Intersects(ray, box));
|
||||
blitz_test_assert(ray.Intersects(box));
|
||||
}
|
||||
|
||||
static void test_corner_inside_opposite_big() {
|
||||
let box = AABB { {0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f} };
|
||||
let ray = Ray { {0.0f, 0.0f, 0.0f}, {1.5f, 1.5f, 1.5f} };
|
||||
let box = AABB{{0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f}};
|
||||
let ray = Ray{{0.0f, 0.0f, 0.0f}, {1.5f, 1.5f, 1.5f}};
|
||||
|
||||
blitz_test_assert(Intersects(ray, box));
|
||||
blitz_test_assert(ray.Intersects(box));
|
||||
}
|
||||
|
||||
static void test_corner_inside_opposite_weird() {
|
||||
let box = AABB { {0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f} };
|
||||
let ray = Ray { {0.4f, 0.4f, 0.4f}, {-1.0f, -1.0f, -1.0f} };
|
||||
let box = AABB{{0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f}};
|
||||
let ray = Ray{{0.4f, 0.4f, 0.4f}, {-1.0f, -1.0f, -1.0f}};
|
||||
|
||||
blitz_test_assert(Intersects(ray, box));
|
||||
blitz_test_assert(ray.Intersects(box));
|
||||
}
|
||||
|
||||
int main(int argc, const char* args[]) {
|
||||
test_left();
|
||||
test_right();
|
||||
test_forward();
|
||||
test_backward();
|
||||
test_below();
|
||||
test_above();
|
||||
test_shifted();
|
||||
test_corner_inside();
|
||||
test_corner_inside_weird();
|
||||
test_corner_inside_opposite_big();
|
||||
test_corner_inside_opposite_weird();
|
||||
return BLITZ_TEST_SUCCESSFUL;
|
||||
test_left();
|
||||
test_right();
|
||||
test_forward();
|
||||
test_backward();
|
||||
test_below();
|
||||
test_above();
|
||||
test_shifted();
|
||||
test_corner_inside();
|
||||
test_corner_inside_weird();
|
||||
test_corner_inside_opposite_big();
|
||||
test_corner_inside_opposite_weird();
|
||||
return BLITZ_TEST_SUCCESSFUL;
|
||||
}
|
||||
Reference in New Issue
Block a user