From c3058070e50d9f2873c87fc53f85a06191ab414a Mon Sep 17 00:00:00 2001 From: MarcEricMartel Date: Mon, 18 Sep 2023 15:56:17 -0400 Subject: [PATCH 01/19] Projet server. --- SQCSim-common/SQCSim-common.vcxproj | 154 ++ SQCSim-common/SQCSim-common.vcxproj.filters | 72 + SQCSim-common/array2d.h | 61 + SQCSim-common/array3d.h | 55 + SQCSim-common/blockinfo.cpp | 42 + SQCSim-common/blockinfo.h | 32 + SQCSim-common/bullet.cpp | 32 + SQCSim-common/bullet.h | 23 + SQCSim-common/chunk.cpp | 111 + SQCSim-common/chunk.h | 31 + SQCSim-common/define.h | 49 + SQCSim-common/matrix4.h | 571 +++++ SQCSim-common/opensimplex.cpp | 2542 +++++++++++++++++++ SQCSim-common/opensimplex.h | 51 + SQCSim-common/player.cpp | 160 ++ SQCSim-common/player.h | 34 + SQCSim-common/vector3.h | 219 ++ SQCSim-common/world.cpp | 109 + SQCSim-common/world.h | 45 + SQCSim-srv/SQCSim-srv.vcxproj | 138 + SQCSim-srv/SQCSim-srv.vcxproj.filters | 17 + SQCSim2021/SQCSim2021.vcxproj | 8 +- SQCSim2021/define.h | 21 +- SQCSim2021/world.cpp | 36 +- SQCSim2023.sln | 24 +- 25 files changed, 4600 insertions(+), 37 deletions(-) create mode 100644 SQCSim-common/SQCSim-common.vcxproj create mode 100644 SQCSim-common/SQCSim-common.vcxproj.filters create mode 100644 SQCSim-common/array2d.h create mode 100644 SQCSim-common/array3d.h create mode 100644 SQCSim-common/blockinfo.cpp create mode 100644 SQCSim-common/blockinfo.h create mode 100644 SQCSim-common/bullet.cpp create mode 100644 SQCSim-common/bullet.h create mode 100644 SQCSim-common/chunk.cpp create mode 100644 SQCSim-common/chunk.h create mode 100644 SQCSim-common/define.h create mode 100644 SQCSim-common/matrix4.h create mode 100644 SQCSim-common/opensimplex.cpp create mode 100644 SQCSim-common/opensimplex.h create mode 100644 SQCSim-common/player.cpp create mode 100644 SQCSim-common/player.h create mode 100644 SQCSim-common/vector3.h create mode 100644 SQCSim-common/world.cpp create mode 100644 SQCSim-common/world.h create mode 100644 SQCSim-srv/SQCSim-srv.vcxproj create mode 100644 SQCSim-srv/SQCSim-srv.vcxproj.filters diff --git a/SQCSim-common/SQCSim-common.vcxproj b/SQCSim-common/SQCSim-common.vcxproj new file mode 100644 index 0000000..5db21d0 --- /dev/null +++ b/SQCSim-common/SQCSim-common.vcxproj @@ -0,0 +1,154 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {ee91ab12-4225-4a4d-931d-69d72f6d91fb} + SQCSimcommon + 10.0 + + + + StaticLibrary + true + v143 + Unicode + + + StaticLibrary + false + v143 + true + Unicode + + + StaticLibrary + true + v143 + Unicode + + + StaticLibrary + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SQCSim-common/SQCSim-common.vcxproj.filters b/SQCSim-common/SQCSim-common.vcxproj.filters new file mode 100644 index 0000000..af8b625 --- /dev/null +++ b/SQCSim-common/SQCSim-common.vcxproj.filters @@ -0,0 +1,72 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Fichiers d%27en-tête + + + Fichiers d%27en-tête + + + Fichiers d%27en-tête + + + Fichiers d%27en-tête + + + Fichiers d%27en-tête + + + Fichiers d%27en-tête + + + Fichiers d%27en-tête + + + Fichiers d%27en-tête + + + Fichiers d%27en-tête + + + Fichiers d%27en-tête + + + Fichiers d%27en-tête + + + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + \ No newline at end of file diff --git a/SQCSim-common/array2d.h b/SQCSim-common/array2d.h new file mode 100644 index 0000000..7451365 --- /dev/null +++ b/SQCSim-common/array2d.h @@ -0,0 +1,61 @@ +#ifndef ARRAY2D_H__ +#define ARRAY2D_H__ + +#include "define.h" + +template +class Array2d { + public: + Array2d(int x, int y); + ~Array2d(); + Array2d(const Array2d& array); + + void Set(int x, int y, T type); + T Get(int x, int y) const; + T Remove(int x, int y); + + void Reset(T type); + + private: + int m_x, m_y; + T* m_array; + + int To1dIndex(int x, int y) const; +}; + +template +Array2d::Array2d(int x, int y) : m_x(x), m_y(y) { m_array = new T[m_x * m_y]; } + +template +Array2d::~Array2d() { delete[] m_array; } + +template +Array2d::Array2d(const Array2d& array) : m_x(array.m_x), m_y(array.m_y) { + m_array = new T[m_x * m_y]; + for (int i = 0; i < m_x * m_y; ++i) + m_array[i] = array.m_array[i]; +} + +template +void Array2d::Set(int x, int y, T type) { m_array[To1dIndex(x, y)] = type; } + +template +T Array2d::Get(int x, int y) const { return m_array[To1dIndex(x, y)]; } + +template +T Array2d::Remove(int x, int y) { + T thing = std::move(m_array[To1dIndex(x, y)]); + m_array[To1dIndex(x, y)] = nullptr; + return thing; +} + +template +void Array2d::Reset(T type) { + for (int i = 0; i < m_x * m_y; ++i) + m_array[i] = type; +} + +template +int Array2d::To1dIndex(int x, int y) const { return x + (y * m_x); } + +#endif // ARRAY2D_H__ diff --git a/SQCSim-common/array3d.h b/SQCSim-common/array3d.h new file mode 100644 index 0000000..97603ef --- /dev/null +++ b/SQCSim-common/array3d.h @@ -0,0 +1,55 @@ +#ifndef ARRAY3D_H__ +#define ARRAY3D_H__ + +#include "define.h" + +template +class Array3d { + public: + Array3d(int x, int y, int z); + ~Array3d(); + Array3d(const Array3d& array); + + void Set(int x, int y, int z, T type); + T Get(int x, int y, int z) const; + + void Reset(T type); + + private: + int m_x, m_y, m_z; + T* m_array; + + int To1dIndex(int x, int y, int z) const; +}; + +template +Array3d::Array3d(int x, int y, int z) : m_x(x), m_y(y), m_z(z) { m_array = new T[m_x * m_y * m_z]; } + +template +Array3d::~Array3d() { delete[] m_array; } + +template +Array3d::Array3d(const Array3d& array) : m_x(array.m_x), m_y(array.m_y), m_z(array.m_z) { + m_array = new T[m_x * m_y * m_z]; + for (int i = 0; i < m_x * m_y * m_z; ++i) + m_array[i] = array.m_array[i]; +} + +template +void Array3d::Set(int x, int y, int z, T type) { + m_array[To1dIndex(x, y, z)] = type; +} + +template +T Array3d::Get(int x, int y, int z) const { return m_array[To1dIndex(x, y, z)]; } + +template +void Array3d::Reset(T type) { + for (int i = 0; i < m_x * m_y * m_z; ++i) + m_array[i] = type; +} + +template +int Array3d::To1dIndex(int x, int y, int z) const { return x + (z * m_x) + (y * m_z * m_x); } + +#endif // ARRAY3D_H__ diff --git a/SQCSim-common/blockinfo.cpp b/SQCSim-common/blockinfo.cpp new file mode 100644 index 0000000..1e42825 --- /dev/null +++ b/SQCSim-common/blockinfo.cpp @@ -0,0 +1,42 @@ +#include "blockinfo.h" +#include + +BlockInfo::BlockInfo(BlockType type, const std::string& name, float u, float v, float s, int dur) : m_type(type), m_name(name), m_u(u), m_v(v), m_s(s), m_durability(dur) +{ +} + +BlockInfo::~BlockInfo() +{ +} + +BlockType BlockInfo::GetType() const +{ + return m_type; +} + +void BlockInfo::SetDurability(int durability) +{ + m_durability = durability; +} + +int BlockInfo::GetDurability() const +{ + return m_durability; +} + +void BlockInfo::GetTexture(float& u, float& v, float& s) +{ + u = m_u; + v = m_v; + s = m_s; +} + +void BlockInfo::Show() const +{ + std::cout << "Type: " << m_type << std::endl; + std::cout << "Nom: " << m_name << std::endl; + std::cout << "Durabilite: " << m_durability << std::endl; + std::cout << "Coordonnees Texture: " << m_u << ", " << m_v << ", " << m_s << std::endl; +} + + diff --git a/SQCSim-common/blockinfo.h b/SQCSim-common/blockinfo.h new file mode 100644 index 0000000..8a2afe7 --- /dev/null +++ b/SQCSim-common/blockinfo.h @@ -0,0 +1,32 @@ +#ifndef BLOCKINFO_H__ +#define BLOCKINFO_H__ + +#include +#include "define.h" + +class BlockInfo +{ + public: + BlockInfo(BlockType type, const std::string& name, float u, float v, float s, int dur); + ~BlockInfo(); + + BlockType GetType() const; + + void SetDurability(int durability); + int GetDurability() const; + + void GetTexture(float& u, float& v, float& s); + + void Show() const; + + private: + BlockType m_type; + float m_u; + float m_v; + float m_s; + std::string m_name; + int m_durability; + +}; + +#endif // BLOCKINFO_H__ diff --git a/SQCSim-common/bullet.cpp b/SQCSim-common/bullet.cpp new file mode 100644 index 0000000..c06c9e7 --- /dev/null +++ b/SQCSim-common/bullet.cpp @@ -0,0 +1,32 @@ +#include "bullet.h" +#include "world.h" + +Bullet::Bullet(Player& player) { + m_startpos = m_currentpos = player.GetPOV() + player.GetDirection(); + m_velocity = player.GetDirection(); +} + +Bullet::~Bullet() {} + +bool Bullet::Update(World* world, float elapsedtime) { + for (int x = 0; x < 1000; ++x) { + m_currentpos += m_velocity * elapsedtime; + + if (!world->ChunkAt(m_currentpos)) + return true; + else if (world->BlockAt(m_currentpos) != BTYPE_AIR) { + world->ChangeBlockAtPosition(BTYPE_AIR, m_currentpos); + return true; + } + else if ((m_currentpos - m_startpos).Length() > VIEW_DISTANCE) return true; + } + + return false; +} + +void Bullet::Transpose(int& x, int& z) { + m_currentpos.x -= x * CHUNK_SIZE_X; + m_currentpos.z -= z * CHUNK_SIZE_Z; + m_startpos.x -= x * CHUNK_SIZE_X; + m_startpos.z -= z * CHUNK_SIZE_Z; +} diff --git a/SQCSim-common/bullet.h b/SQCSim-common/bullet.h new file mode 100644 index 0000000..de2e14c --- /dev/null +++ b/SQCSim-common/bullet.h @@ -0,0 +1,23 @@ +#ifndef BULLET_H__ +#define BULLET_H__ + +#include "player.h" + +class World; + +class Bullet { +public: + Bullet(Player& player); + ~Bullet(); + + bool Update(World* world, float elapsedtime); + void Transpose(int& x, int& z); + +private: + Vector3f m_startpos; + Vector3f m_currentpos; + Vector3f m_velocity; +}; + +#endif // BULLET_H__ + diff --git a/SQCSim-common/chunk.cpp b/SQCSim-common/chunk.cpp new file mode 100644 index 0000000..7bfc690 --- /dev/null +++ b/SQCSim-common/chunk.cpp @@ -0,0 +1,111 @@ +#include "chunk.h" +#include "world.h" + +Chunk::Chunk(unsigned int x, unsigned int y) : m_posX(x), m_posY(y) { + //std::ostringstream pos; // Vérifie l'existence d'un fichier .chunk avec sa position. + //pos << CHUNK_PATH << x << '_' << y << ".chunk"; + //std::ifstream input(pos.str(), std::fstream::binary); + + //if (input.fail()) { + OpenSimplexNoise::Noise simplex = OpenSimplexNoise::Noise(SEED); + m_blocks.Reset(BTYPE_AIR); + + for (int ix = 0; ix < CHUNK_SIZE_X; ++ix) // Montagnes + for (int iz = 0; iz < CHUNK_SIZE_Z; ++iz) { + float xnoiz, ynoiz; + xnoiz = (double)(ix + x * CHUNK_SIZE_X) / 4096.; + ynoiz = (double)(iz + y * CHUNK_SIZE_Z) / 4096.; + double height = 0; + for (int x = 0; x < 39; ++x) { + height += simplex.eval(xnoiz, ynoiz); + height *= .79; + xnoiz *= 1.139; + ynoiz *= 1.139; + } + height = height * 2000. * simplex.eval((double)(ix + x * CHUNK_SIZE_X) / 512., (double)(iz + y * CHUNK_SIZE_Z) / 512.); + height /= (CHUNK_SIZE_Y / 1.9); + height += 15.; + for (int iy = 0; iy <= (int)height % CHUNK_SIZE_Y; ++iy) + SetBlock(ix, iy, iz, BTYPE_METAL, nullptr); + } + for (int ix = 0; ix < CHUNK_SIZE_X; ++ix) // Collines + for (int iz = 0; iz < CHUNK_SIZE_Z; ++iz) { + float xnoiz, ynoiz; + xnoiz = (double)(ix + x * CHUNK_SIZE_X) / 512.; + ynoiz = (double)(iz + y * CHUNK_SIZE_Z) / 512.; + float height = simplex.eval(xnoiz, ynoiz) * 50.f;// +1.f; + for (int iy = 0; iy <= (int)height % CHUNK_SIZE_Y; ++iy) { + if (GetBlock(ix, iy, iz) == BTYPE_AIR) + SetBlock(ix, iy, iz, BTYPE_GRASS, nullptr); + } + } + for (int ix = 0; ix < CHUNK_SIZE_X; ++ix) // "Lacs" + for (int iz = 0; iz < CHUNK_SIZE_Z; ++iz) { + for (int iy = 0; iy < 13; ++iy) { + if (GetBlock(ix, iy, iz) == BTYPE_AIR) + SetBlock(ix, iy, iz, BTYPE_ICE, nullptr); + } + } + //for (int ix = 0; ix < CHUNK_SIZE_X; ++ix) // "Arbres" + // for (int iz = 0; iz < CHUNK_SIZE_Z; ++iz) { + // float xnoiz, ynoiz; + // xnoiz = (double)(iz * CHUNK_SIZE_Y + x * CHUNK_SIZE_X) / 256.; + // ynoiz = (double)(ix * CHUNK_SIZE_Y + y * CHUNK_SIZE_Z) / 256.; + // bool tree = (int)(abs(simplex.eval(xnoiz, ynoiz)) * 17933.f) % CHUNK_SIZE_Y > 126 ? true : false; + // for (int iy = 0; iy < CHUNK_SIZE_Y - 10; ++iy) + // if (GetBlock(ix, iy, iz) == BTYPE_AIR) + // if (GetBlock(ix, iy - 1, iz) == BTYPE_GRASS) + // if (tree) { + // for (int i = 0; i < (int)(abs(simplex.eval(xnoiz, ynoiz) * 4)) % 42 + 1; ++i) + // SetBlock(ix, iy + i, iz, BTYPE_DIRT, nullptr); + // break; + // } + // } + /* } + else { + input.seekg(0, std::ios_base::end); + int size = input.tellg(); + input.seekg(0, std::ios_base::beg); + + char data[CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z]; + input.read(data, size); + input.close(); + + for (int ix = 0; ix < CHUNK_SIZE_X; ++ix) + for (int iz = 0; iz < CHUNK_SIZE_Z; ++iz) + for (int iy = 0; iy < CHUNK_SIZE_Y; ++iy) + m_blocks.Set(ix, iy, iz, data[ix + (iz * CHUNK_SIZE_X) + (iy * CHUNK_SIZE_Z * CHUNK_SIZE_X)]); + }*/ +} + +Chunk::~Chunk() { + /*if (m_isModified) { + char data[CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z]; + + for (int x = 0; x < CHUNK_SIZE_X; ++x) + for (int z = 0; z < CHUNK_SIZE_Z; ++z) + for (int y = 0; y < CHUNK_SIZE_Y; ++y) + data[x + (z * CHUNK_SIZE_X) + (y * CHUNK_SIZE_Z * CHUNK_SIZE_X)] = (char)GetBlock(x, y, z); + + std::ostringstream pos; + pos << CHUNK_PATH << m_posX << '_' << m_posY << ".chunk"; + + std::ofstream output(pos.str(), std::fstream::binary); + output.write(data, sizeof(data)); + output.close(); + }*/ +} + +void Chunk::RemoveBlock(int x, int y, int z, World* world) { + m_blocks.Set(x, y, z, BTYPE_AIR); +} + +void Chunk::SetBlock(int x, int y, int z, BlockType type, World* world) { + m_blocks.Set(x, y, z, type); +} + +BlockType Chunk::GetBlock(int x, int y, int z) { return m_blocks.Get(x, y, z); } + +void Chunk::GetPosition(unsigned int& x, unsigned int& y) const { x = m_posX; y = m_posY; } + +void Chunk::MakeModified() { m_isModified = true; } diff --git a/SQCSim-common/chunk.h b/SQCSim-common/chunk.h new file mode 100644 index 0000000..ec015ba --- /dev/null +++ b/SQCSim-common/chunk.h @@ -0,0 +1,31 @@ +#ifndef CHUNK_H__ +#define CHUNK_H__ +#include "define.h" +#include "array3d.h" +#include "array2d.h" +#include "blockinfo.h" +#include "opensimplex.h" + +class World; + +class Chunk { + protected: + Array3d m_blocks = Array3d(CHUNK_SIZE_X, CHUNK_SIZE_Y, CHUNK_SIZE_Z); + bool m_isModified = false; + + unsigned int m_posX; // Position du chunk dans l'array constituant le monde. + unsigned int m_posY; + + public: + Chunk(unsigned int x, unsigned int y); + ~Chunk(); + + void RemoveBlock(int x, int y, int z, World* world); + void SetBlock(int x, int y, int z, BlockType type, World* world); + BlockType GetBlock(int x, int y, int z); + void GetPosition(unsigned int& x, unsigned int& y) const; + + void MakeModified(); +}; + +#endif // CHUNK_H__ diff --git a/SQCSim-common/define.h b/SQCSim-common/define.h new file mode 100644 index 0000000..aea5ea2 --- /dev/null +++ b/SQCSim-common/define.h @@ -0,0 +1,49 @@ +#ifndef DEFINE_H__ +#define DEFINE_H__ + +#include + +#define CHUNK_SIZE_X 16 +#define CHUNK_SIZE_Y 128 +#define CHUNK_SIZE_Z 16 +#define MAX_SELECTION_DISTANCE 5 +#define SEED 12345 + +#ifdef _DEBUG +#define WORLD_SIZE_X 64 +#define WORLD_SIZE_Y 64 + +#define FRAMES_RENDER_CHUNKS 4 +#define FRAMES_UPDATE_CHUNKS 4 +#define FRAMES_DELETE_CHUNKS 4 + +#define THREADS_GENERATE_CHUNKS 1 +#define THREADS_UPDATE_CHUNKS 1 +#define THREADS_DELETE_CHUNKS 1 + +#define VIEW_DISTANCE 256 +#define TEXTURE_SIZE 128 +#define MAX_BULLETS 64 +#endif + +#ifdef NDEBUG +#define WORLD_SIZE_X 16 +#define WORLD_SIZE_Y 16 + +#define FRAMES_RENDER_CHUNKS 1 +#define FRAMES_UPDATE_CHUNKS 1 +#define FRAMES_DELETE_CHUNKS 1 + +#define THREADS_GENERATE_CHUNKS 12 +#define THREADS_UPDATE_CHUNKS 5 +#define THREADS_DELETE_CHUNKS 2 + +#define VIEW_DISTANCE 1024 +#define TEXTURE_SIZE 512 +#define MAX_BULLETS 512 +#endif + +typedef uint8_t BlockType; +enum BLOCK_TYPE { BTYPE_AIR, BTYPE_DIRT, BTYPE_GRASS, BTYPE_METAL, BTYPE_ICE, BTYPE_LAST }; + +#endif // DEFINE_H__ diff --git a/SQCSim-common/matrix4.h b/SQCSim-common/matrix4.h new file mode 100644 index 0000000..a0d911a --- /dev/null +++ b/SQCSim-common/matrix4.h @@ -0,0 +1,571 @@ +#ifndef MATRIX4_H__ +#define MATRIX4_H__ + +#include +#include +#include + +#include "define.h" +#include "vector3.h" + +#ifndef M_PI +#define M_PI 3.14159265f +#endif + +#define DEGTORAD(x) ((x * M_PI) / 180.f) +#define RADTODEG(x) ((180.f * x) / M_PI) + +template +class Matrix4 +{ + public: + typedef T Type; + + public: + static const Matrix4 ZERO; + static const Matrix4 IDENTITY; + + public: + Matrix4(); + Matrix4(const T& v); + Matrix4(const Matrix4& m); + Matrix4(const T& m_11, const T& m_12, const T& m_13, const T& m_14, + const T& m_21, const T& m_22, const T& m_23, const T& m_24, + const T& m_31, const T& m_32, const T& m_33, const T& m_34, + const T& m_41, const T& m_42, const T& m_43, const T& m_44); + + const T& Get11() const; + const T& Get12() const; + const T& Get13() const; + const T& Get14() const; + const T& Get21() const; + const T& Get22() const; + const T& Get23() const; + const T& Get24() const; + const T& Get31() const; + const T& Get32() const; + const T& Get33() const; + const T& Get34() const; + const T& Get41() const; + const T& Get42() const; + const T& Get43() const; + const T& Get44() const; + + Matrix4& operator=(const Matrix4& m); + + Matrix4 operator+(const Matrix4& m) const; + const Matrix4& operator+=(const Matrix4& m); + + Matrix4 operator-(const Matrix4& m) const; + Matrix4 operator-() const; + const Matrix4& operator-=(const Matrix4& m); + + Matrix4 operator*(const Matrix4& m) const; + Matrix4 operator*(const T& v) const; + const Matrix4& operator*=(const Matrix4& m); + const Matrix4& operator*=(const T& v); + + Matrix4 operator/(const T& v) const; + const Matrix4& operator/=(const T& v); + + bool operator==(const Matrix4& m) const; + bool operator!=(const Matrix4& m) const; + + void SetZero(); + void SetIdentity(); + void SetPerspectiveProjection(const T& fov, const T& aspect, const T& nearPlane, const T& farPlane); + void SetOrthographicProjection(const T& left, const T& right, const T& bottom, const T& top, const T& nearPlane, const T& farPlane); + + void SetLookAt(const Vector3& eyePosition, const Vector3& lookAtPosition, Vector3 upVector = Vector3(T(0), T(1), T(0))); + + bool IsZero() const; + bool IsIdentity() const; + + void ApplyTranslation(const T& x, const T& y, const T& z); + void ApplyRotation(const T& angle, const T& x, const T& y, const T& z); + void ApplyScale(const T& x, const T& y, const T& z); + + Vector3 GetTranslation() const; + + const T* GetInternalValues() const; + T* GetInternalValues(); + std::string ToString(const std::string& lineBegin = "|", const std::string& lineEnd = "|\n") const; + + private: + union { + // column-major matrix + struct + { + T m_11, m_21, m_31, m_41, m_12, m_22, m_32, m_42, m_13, m_23, m_33, m_43, m_14, m_24, m_34, m_44; + }; + T m_values[16]; + }; +}; + +typedef Matrix4 Matrix4i; +typedef Matrix4 Matrix4f; +typedef Matrix4 Matrix4d; + +template +const Matrix4 Matrix4::ZERO = Matrix4(0); + +template +const Matrix4 Matrix4::IDENTITY = Matrix4( + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + + template +std::ostream& operator<<(std::ostream& out, const Matrix4& m) +{ + out << m.ToString(); + return out; +} + + template +Matrix4::Matrix4() +{ + // Leave matrix uninitialized +} + + template +Matrix4::Matrix4(const T& v) +{ + for(int i = 0; i < 16; ++i) + m_values[i] = v; +} + + template +Matrix4::Matrix4(const Matrix4& m) +{ + for(int i = 0; i < 16; ++i) + m_values[i] = m.m_values[i]; +} + + template +Matrix4::Matrix4(const T& m_11, const T& m_12, const T& m_13, const T& m_14, + const T& m_21, const T& m_22, const T& m_23, const T& m_24, + const T& m_31, const T& m_32, const T& m_33, const T& m_34, + const T& m_41, const T& m_42, const T& m_43, const T& m_44) +{ + this->m_11 = m_11; + this->m_12 = m_12; + this->m_13 = m_13; + this->m_14 = m_14; + this->m_21 = m_21; + this->m_22 = m_22; + this->m_23 = m_23; + this->m_24 = m_24; + this->m_31 = m_31; + this->m_32 = m_32; + this->m_33 = m_33; + this->m_34 = m_34; + this->m_41 = m_41; + this->m_42 = m_42; + this->m_43 = m_43; + this->m_44 = m_44; +} + +template +const T& Matrix4::Get11() const +{ + return m_11; +} + +template +const T& Matrix4::Get12() const +{ + return m_12; +} + +template +const T& Matrix4::Get13() const +{ + return m_13; +} + +template +const T& Matrix4::Get14() const +{ + return m_14; +} + +template +const T& Matrix4::Get21() const +{ + return m_21; +} + +template +const T& Matrix4::Get22() const +{ + return m_22; +} + +template +const T& Matrix4::Get23() const +{ + return m_23; +} + +template +const T& Matrix4::Get24() const +{ + return m_24; +} + +template +const T& Matrix4::Get31() const +{ + return m_31; +} + +template +const T& Matrix4::Get32() const +{ + return m_32; +} + +template +const T& Matrix4::Get33() const +{ + return m_33; +} + +template +const T& Matrix4::Get34() const +{ + return m_34; +} + +template +const T& Matrix4::Get41() const +{ + return m_41; +} + +template +const T& Matrix4::Get42() const +{ + return m_42; +} + +template +const T& Matrix4::Get43() const +{ + return m_43; +} + +template +const T& Matrix4::Get44() const +{ + return m_44; +} + + template +Matrix4& Matrix4::operator=(const Matrix4& m) +{ + if(this != &m) + { + for(int i = 0; i < 16; ++i) + m_values[i] = m.m_values[i]; + } + + return *this; +} + +template +Matrix4 Matrix4::operator+(const Matrix4& m) const +{ + return Matrix4( + m_11 + m.m_11, m_12 + m.m_12, m_13 + m.m_13, m_14 + m.m_14, + m_21 + m.m_21, m_22 + m.m_22, m_23 + m.m_23, m_24 + m.m_24, + m_31 + m.m_31, m_32 + m.m_32, m_33 + m.m_33, m_34 + m.m_34, + m_41 + m.m_41, m_42 + m.m_42, m_43 + m.m_43, m_44 + m.m_44); +} + + template +const Matrix4& Matrix4::operator+=(const Matrix4& m) +{ + *this = *this + m; + return *this; +} + +template +Matrix4 Matrix4::operator-(const Matrix4& m) const +{ + return Matrix4( + m_11 - m.m_11, m_12 - m.m_12, m_13 - m.m_13, m_14 - m.m_14, + m_21 - m.m_21, m_22 - m.m_22, m_23 - m.m_23, m_24 - m.m_24, + m_31 - m.m_31, m_32 - m.m_32, m_33 - m.m_33, m_34 - m.m_34, + m_41 - m.m_41, m_42 - m.m_42, m_43 - m.m_43, m_44 - m.m_44); +} + +template +Matrix4 Matrix4::operator-() const +{ + return Matrix4( + -m_11, -m_12, -m_13, -m_14, + -m_21, -m_22, -m_23, -m_24, + -m_31, -m_32, -m_33, -m_34, + -m_41, -m_42, -m_43, -m_44); +} + + template +const Matrix4& Matrix4::operator-=(const Matrix4& m) +{ + *this = *this - m; + return *this; +} + +template +Matrix4 Matrix4::operator*(const Matrix4& m) const +{ + return Matrix4( + m_11 * m.m_11 + m_12 * m.m_21 + m_13 * m.m_31 + m_14 * m.m_41, + m_11 * m.m_12 + m_12 * m.m_22 + m_13 * m.m_32 + m_14 * m.m_42, + m_11 * m.m_13 + m_12 * m.m_23 + m_13 * m.m_33 + m_14 * m.m_43, + m_11 * m.m_14 + m_12 * m.m_24 + m_13 * m.m_34 + m_14 * m.m_44, + + m_21 * m.m_11 + m_22 * m.m_21 + m_23 * m.m_31 + m_24 * m.m_41, + m_21 * m.m_12 + m_22 * m.m_22 + m_23 * m.m_32 + m_24 * m.m_42, + m_21 * m.m_13 + m_22 * m.m_23 + m_23 * m.m_33 + m_24 * m.m_43, + m_21 * m.m_14 + m_22 * m.m_24 + m_23 * m.m_34 + m_24 * m.m_44, + + m_31 * m.m_11 + m_32 * m.m_21 + m_33 * m.m_31 + m_34 * m.m_41, + m_31 * m.m_12 + m_32 * m.m_22 + m_33 * m.m_32 + m_34 * m.m_42, + m_31 * m.m_13 + m_32 * m.m_23 + m_33 * m.m_33 + m_34 * m.m_43, + m_31 * m.m_14 + m_32 * m.m_24 + m_33 * m.m_34 + m_34 * m.m_44, + + m_41 * m.m_11 + m_42 * m.m_21 + m_43 * m.m_31 + m_44 * m.m_41, + m_41 * m.m_12 + m_42 * m.m_22 + m_43 * m.m_32 + m_44 * m.m_42, + m_41 * m.m_13 + m_42 * m.m_23 + m_43 * m.m_33 + m_44 * m.m_43, + m_41 * m.m_14 + m_42 * m.m_24 + m_43 * m.m_34 + m_44 * m.m_44); +} + +template +Matrix4 Matrix4::operator*(const T& v) const +{ + return Matrix4( + m_11 * v, m_12 * v, m_13 * v, m_14 * v, + m_21 * v, m_22 * v, m_23 * v, m_24 * v, + m_31 * v, m_32 * v, m_33 * v, m_34 * v, + m_41 * v, m_42 * v, m_43 * v, m_44 * v); +} + + template +const Matrix4& Matrix4::operator*=(const Matrix4& m) +{ + *this = *this * m; + return *this; +} + + template +const Matrix4& Matrix4::operator*=(const T& v) +{ + *this = *this * v; + return *this; +} + +template +Matrix4 Matrix4::operator/(const T& v) const +{ + return Matrix4( + m_11 / v, m_12 / v, m_13 / v, m_14 / v, + m_21 / v, m_22 / v, m_23 / v, m_24 / v, + m_31 / v, m_32 / v, m_33 / v, m_34 / v, + m_41 / v, m_42 / v, m_43 / v, m_44 / v); +} + + template +const Matrix4& Matrix4::operator/=(const T& v) +{ + *this = *this / v; + return *this; +} + +template +bool Matrix4::operator==(const Matrix4& m) const +{ + for(int i = 0; i < 16; ++i) + if(m_values[i] != m.m_values[i]) + return false; + + return true; +} + +template +bool Matrix4::operator!=(const Matrix4& m) const +{ + return !(*this == m); +} + + template +void Matrix4::SetZero() +{ + *this = ZERO; +} + + template +void Matrix4::SetIdentity() +{ + *this = IDENTITY; +} + + template +void Matrix4::SetPerspectiveProjection(const T& fov, const T& aspect, const T& nearPlane, const T& farPlane) +{ + const float h = T(1) / tan(fov * T(M_PI / 360.f)); + T negDepth = nearPlane - farPlane; + + SetZero(); + + m_11 = h / aspect; + m_22 = h; + m_33 = (farPlane + nearPlane) / negDepth; + m_34 = T(2) * (nearPlane * farPlane) / negDepth; + m_43 = -T(1); +} + + template +void Matrix4::SetOrthographicProjection(const T& left, const T& right, const T& bottom, const T& top, const T& nearPlane, const T& farPlane) +{ + m_11 = T(2) / (right - left); + m_12 = T(0); + m_13 = T(0); + m_14 = -(right + left) / (right - left); + + m_21 = T(0); + m_22 = T(2) / (top - bottom); + m_23 = T(0); + m_24 = -(top + bottom) / (top - bottom); + + m_31 = T(0); + m_32 = T(0); + m_33 = -T(2) / (farPlane - nearPlane); + m_34 = -(farPlane + nearPlane) / (farPlane - nearPlane); + + m_41 = T(0); + m_42 = T(0); + m_43 = T(0); + m_44 = T(1); +} + + template +void Matrix4::SetLookAt(const Vector3& eyePosition, const Vector3& lookAtPosition, Vector3 upVector) +{ + Vector3f L = lookAtPosition - eyePosition; + L.Normalize(); + + upVector.Normalize(); + Vector3f S = L.Cross(upVector); + S.Normalize(); + + Vector3f U = S.Cross(L); + + Matrix4 M; + M.m_11 = S.x; + M.m_12 = S.y; + M.m_13 = S.z; + M.m_14 = 0; + + M.m_21 = U.x; + M.m_22 = U.y; + M.m_23 = U.z; + M.m_24 = 0; + + M.m_31 = -L.x; + M.m_32 = -L.y; + M.m_33 = -L.z; + M.m_34 = 0; + + M.m_41 = 0; + M.m_42 = 0; + M.m_43 = 0; + M.m_44 = 1.f; + + SetIdentity(); + *this *= M; + ApplyTranslation(-eyePosition.x, -eyePosition.y, -eyePosition.z); +} + + template +void Matrix4::ApplyTranslation(const T& x, const T& y, const T& z) +{ + Matrix4 tmp( + 1, 0, 0, x, + 0, 1, 0, y, + 0, 0, 1, z, + 0, 0, 0, 1); + + *this *= tmp; +} + + template +void Matrix4::ApplyRotation(const T& angle, const T& x, const T& y, const T& z) +{ + // TODO axis (x, y, z) must be normalized... + + T s = sin(DEGTORAD(angle)); + T c = cos(DEGTORAD(angle)); + T ic = T(1) - c; + + Matrix4 tmp( + x * x * ic + c, y * x * ic + (z * s), z * x * ic - (y * s), 0, + x * y * ic - (z * s), y * y * ic + c, z * y * ic + (x * s), 0, + x * z * ic + (y * s), y * z * ic - (x * s), z * z * ic + c, 0, + 0, 0, 0, 1); + + *this *= tmp; +} + + template +void Matrix4::ApplyScale(const T& x, const T& y, const T& z) +{ + Matrix4 tmp( + x, 0, 0, 0, + 0, y, 0, 0, + 0, 0, z, 0, + 0, 0, 0, 1); + + *this *= tmp; +} + +template +Vector3 Matrix4::GetTranslation() const +{ + // NOTE: Works only if the matrix doesn't contains scale information (only rotation and translation) + // Reference: http://www.gamedev.net/topic/397751-how-to-get-camera-position/ + T x = -(m_11 * m_14 + m_21 * m_24 + m_31 * m_34); + T y = -(m_12 * m_14 + m_22 * m_24 + m_32 * m_34); + T z = -(m_13 * m_14 + m_23 * m_24 + m_33 * m_34); + + return Vector3(x, y, z); +} + + template +T* Matrix4::GetInternalValues() +{ + return m_values; +} + +template +const T* Matrix4::GetInternalValues() const +{ + return m_values; +} + +template +std::string Matrix4::ToString(const std::string& lineBegin, const std::string& lineEnd) const +{ + std::ostringstream ss; + ss << lineBegin << m_11 << " " << m_12 << " " << m_13 << " " << m_14 << lineEnd; + ss << lineBegin << m_21 << " " << m_22 << " " << m_23 << " " << m_24 << lineEnd; + ss << lineBegin << m_31 << " " << m_32 << " " << m_33 << " " << m_34 << lineEnd; + ss << lineBegin << m_41 << " " << m_42 << " " << m_43 << " " << m_44 << lineEnd; + + return ss.str(); +} + +#endif // MATRIX4_H__ diff --git a/SQCSim-common/opensimplex.cpp b/SQCSim-common/opensimplex.cpp new file mode 100644 index 0000000..a64d31f --- /dev/null +++ b/SQCSim-common/opensimplex.cpp @@ -0,0 +1,2542 @@ +#include "opensimplex.h" + +#include +namespace OpenSimplexNoise +{ + using namespace std; + + Noise::Noise() + : m_stretch2d(-0.211324865405187) //(1/Math.sqrt(2+1)-1)/2; + , m_squish2d(0.366025403784439) //(Math.sqrt(2+1)-1)/2; + , m_stretch3d(-1.0 / 6) //(1/Math.sqrt(3+1)-1)/3; + , m_squish3d(1.0 / 3) //(Math.sqrt(3+1)-1)/3; + , m_stretch4d(-0.138196601125011) //(1/Math.sqrt(4+1)-1)/4; + , m_squish4d(0.309016994374947) //(Math.sqrt(4+1)-1)/4; + , m_norm2d(47) + , m_norm3d(103) + , m_norm4d(30) + , m_defaultSeed(0) + , m_perm{0} + , m_permGradIndex3d{0} + , m_gradients2d{ 5, 2, 2, 5, + -5, 2, -2, 5, + 5, -2, 2, -5, + -5, -2, -2, -5, } + , m_gradients3d{-11, 4, 4, -4, 11, 4, -4, 4, 11, + 11, 4, 4, 4, 11, 4, 4, 4, 11, + -11, -4, 4, -4, -11, 4, -4, -4, 11, + 11, -4, 4, 4, -11, 4, 4, -4, 11, + -11, 4, -4, -4, 11, -4, -4, 4, -11, + 11, 4, -4, 4, 11, -4, 4, 4, -11, + -11, -4, -4, -4, -11, -4, -4, -4, -11, + 11, -4, -4, 4, -11, -4, 4, -4, -11, } + , m_gradients4d{ 3, 1, 1, 1, 1, 3, 1, 1, 1, 1, 3, 1, 1, 1, 1, 3, + -3, 1, 1, 1, -1, 3, 1, 1, -1, 1, 3, 1, -1, 1, 1, 3, + 3, -1, 1, 1, 1, -3, 1, 1, 1, -1, 3, 1, 1, -1, 1, 3, + -3, -1, 1, 1, -1, -3, 1, 1, -1, -1, 3, 1, -1, -1, 1, 3, + 3, 1, -1, 1, 1, 3, -1, 1, 1, 1, -3, 1, 1, 1, -1, 3, + -3, 1, -1, 1, -1, 3, -1, 1, -1, 1, -3, 1, -1, 1, -1, 3, + 3, -1, -1, 1, 1, -3, -1, 1, 1, -1, -3, 1, 1, -1, -1, 3, + -3, -1, -1, 1, -1, -3, -1, 1, -1, -1, -3, 1, -1, -1, -1, 3, + 3, 1, 1, -1, 1, 3, 1, -1, 1, 1, 3, -1, 1, 1, 1, -3, + -3, 1, 1, -1, -1, 3, 1, -1, -1, 1, 3, -1, -1, 1, 1, -3, + 3, -1, 1, -1, 1, -3, 1, -1, 1, -1, 3, -1, 1, -1, 1, -3, + -3, -1, 1, -1, -1, -3, 1, -1, -1, -1, 3, -1, -1, -1, 1, -3, + 3, 1, -1, -1, 1, 3, -1, -1, 1, 1, -3, -1, 1, 1, -1, -3, + -3, 1, -1, -1, -1, 3, -1, -1, -1, 1, -3, -1, -1, 1, -1, -3, + 3, -1, -1, -1, 1, -3, -1, -1, 1, -1, -3, -1, 1, -1, -1, -3, + -3, -1, -1, -1, -1, -3, -1, -1, -1, -1, -3, -1, -1, -1, -1, -3, } + { + } + + Noise::Noise(int64_t seed) + : Noise() + { + short source[256]; + for (short i = 0; i < 256; i++) + { + source[i] = i; + } + seed = seed * 6364136223846793005l + 1442695040888963407l; + seed = seed * 6364136223846793005l + 1442695040888963407l; + seed = seed * 6364136223846793005l + 1442695040888963407l; + for (int i = 255; i >= 0; i--) + { + seed = seed * 6364136223846793005l + 1442695040888963407l; + int r = static_cast((seed + 31) % (i + 1)); + if (r < 0) + { + r += (i + 1); + } + m_perm[i] = source[r]; + m_permGradIndex3d[i] = static_cast((m_perm[i] % (m_gradients3d.size() / 3)) * 3); + source[r] = source[i]; + } + } + + double Noise::eval(double x, double y) const + { + //Place input coordinates onto grid. + double stretchOffset = (x + y) * m_stretch2d; + double xs = x + stretchOffset; + double ys = y + stretchOffset; + + //Floor to get grid coordinates of rhombus (stretched square) super-cell origin. + int xsb = static_cast(floor(xs)); + int ysb = static_cast(floor(ys)); + + //Skew out to get actual coordinates of rhombus origin. We'll need these later. + double squishOffset = (xsb + ysb) * m_squish2d; + double xb = xsb + squishOffset; + double yb = ysb + squishOffset; + + //Compute grid coordinates relative to rhombus origin. + double xins = xs - xsb; + double yins = ys - ysb; + + //Sum those together to get a value that determines which region we're in. + double inSum = xins + yins; + + //Positions relative to origin point. + double dx0 = x - xb; + double dy0 = y - yb; + + //We'll be defining these inside the next block and using them afterwards. + double dx_ext, dy_ext; + int xsv_ext, ysv_ext; + + double value = 0; + + //Contribution (1,0) + double dx1 = dx0 - 1 - m_squish2d; + double dy1 = dy0 - 0 - m_squish2d; + double attn1 = 2 - dx1 * dx1 - dy1 * dy1; + if (attn1 > 0) + { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, dx1, dy1); + } + + //Contribution (0,1) + double dx2 = dx0 - 0 - m_squish2d; + double dy2 = dy0 - 1 - m_squish2d; + double attn2 = 2 - dx2 * dx2 - dy2 * dy2; + if (attn2 > 0) + { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, dx2, dy2); + } + + if (inSum <= 1) + { //We're inside the triangle (2-Simplex) at (0,0) + double zins = 1 - inSum; + if (zins > xins || zins > yins) + { //(0,0) is one of the closest two triangular vertices + if (xins > yins) + { + xsv_ext = xsb + 1; + ysv_ext = ysb - 1; + dx_ext = dx0 - 1; + dy_ext = dy0 + 1; + } + else + { + xsv_ext = xsb - 1; + ysv_ext = ysb + 1; + dx_ext = dx0 + 1; + dy_ext = dy0 - 1; + } + } + else + { //(1,0) and (0,1) are the closest two vertices. + xsv_ext = xsb + 1; + ysv_ext = ysb + 1; + dx_ext = dx0 - 1 - 2 * m_squish2d; + dy_ext = dy0 - 1 - 2 * m_squish2d; + } + } + else + { //We're inside the triangle (2-Simplex) at (1,1) + double zins = 2 - inSum; + if (zins < xins || zins < yins) + { //(0,0) is one of the closest two triangular vertices + if (xins > yins) + { + xsv_ext = xsb + 2; + ysv_ext = ysb + 0; + dx_ext = dx0 - 2 - 2 * m_squish2d; + dy_ext = dy0 + 0 - 2 * m_squish2d; + } + else + { + xsv_ext = xsb + 0; + ysv_ext = ysb + 2; + dx_ext = dx0 + 0 - 2 * m_squish2d; + dy_ext = dy0 - 2 - 2 * m_squish2d; + } + } + else + { //(1,0) and (0,1) are the closest two vertices. + dx_ext = dx0; + dy_ext = dy0; + xsv_ext = xsb; + ysv_ext = ysb; + } + xsb += 1; + ysb += 1; + dx0 = dx0 - 1 - 2 * m_squish2d; + dy0 = dy0 - 1 - 2 * m_squish2d; + } + + //Contribution (0,0) or (1,1) + double attn0 = 2 - dx0 * dx0 - dy0 * dy0; + if (attn0 > 0) + { + attn0 *= attn0; + value += attn0 * attn0 * extrapolate(xsb, ysb, dx0, dy0); + } + + //Extra Vertex + double attn_ext = 2 - dx_ext * dx_ext - dy_ext * dy_ext; + if (attn_ext > 0) + { + attn_ext *= attn_ext; + value += attn_ext * attn_ext * extrapolate(xsv_ext, ysv_ext, dx_ext, dy_ext); + } + + return value / m_norm2d; + } + + double Noise::eval(double x, double y, double z) const + { + //Place input coordinates on simplectic honeycomb. + double stretchOffset = (x + y + z) * m_stretch3d; + double xs = x + stretchOffset; + double ys = y + stretchOffset; + double zs = z + stretchOffset; + + //static_cast(floor to get simplectic honeycomb coordinates of rhombohedron (stretched cube) super-cell origin. + int xsb = static_cast(floor(xs)); + int ysb = static_cast(floor(ys)); + int zsb = static_cast(floor(zs)); + + //Skew out to get actual coordinates of rhombohedron origin. We'll need these later. + double squishOffset = (xsb + ysb + zsb) * m_squish3d; + double xb = xsb + squishOffset; + double yb = ysb + squishOffset; + double zb = zsb + squishOffset; + + //Compute simplectic honeycomb coordinates relative to rhombohedral origin. + double xins = xs - xsb; + double yins = ys - ysb; + double zins = zs - zsb; + + //Sum those together to get a value that determines which region we're in. + double inSum = xins + yins + zins; + + //Positions relative to origin point. + double dx0 = x - xb; + double dy0 = y - yb; + double dz0 = z - zb; + + //We'll be defining these inside the next block and using them afterwards. + double dx_ext0, dy_ext0, dz_ext0; + double dx_ext1, dy_ext1, dz_ext1; + int xsv_ext0, ysv_ext0, zsv_ext0; + int xsv_ext1, ysv_ext1, zsv_ext1; + + double value = 0; + if (inSum <= 1) + { //We're inside the tetrahedron (3-Simplex) at (0,0,0) + +//Determine which two of (0,0,1), (0,1,0), (1,0,0) are closest. + char aPoint = 0x01; + double aScore = xins; + char bPoint = 0x02; + double bScore = yins; + if (aScore >= bScore && zins > bScore) + { + bScore = zins; + bPoint = 0x04; + } + else if (aScore < bScore && zins > aScore) + { + aScore = zins; + aPoint = 0x04; + } + + //Now we determine the two lattice points not part of the tetrahedron that may contribute. + //This depends on the closest two tetrahedral vertices, including (0,0,0) + double wins = 1 - inSum; + if (wins > aScore || wins > bScore) + { //(0,0,0) is one of the closest two tetrahedral vertices. + char c = (bScore > aScore ? bPoint : aPoint); //Our other closest vertex is the closest out of a and b. + + if ((c & 0x01) == 0) + { + xsv_ext0 = xsb - 1; + xsv_ext1 = xsb; + dx_ext0 = dx0 + 1; + dx_ext1 = dx0; + } + else + { + xsv_ext0 = xsv_ext1 = xsb + 1; + dx_ext0 = dx_ext1 = dx0 - 1; + } + + if ((c & 0x02) == 0) + { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy_ext1 = dy0; + if ((c & 0x01) == 0) + { + ysv_ext1 -= 1; + dy_ext1 += 1; + } + else + { + ysv_ext0 -= 1; + dy_ext0 += 1; + } + } + else + { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy_ext1 = dy0 - 1; + } + + if ((c & 0x04) == 0) + { + zsv_ext0 = zsb; + zsv_ext1 = zsb - 1; + dz_ext0 = dz0; + dz_ext1 = dz0 + 1; + } + else + { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz_ext1 = dz0 - 1; + } + } + else + { //(0,0,0) is not one of the closest two tetrahedral vertices. + char c = static_cast(aPoint | bPoint); //Our two extra vertices are determined by the closest two. + + if ((c & 0x01) == 0) + { + xsv_ext0 = xsb; + xsv_ext1 = xsb - 1; + dx_ext0 = dx0 - 2 * m_squish3d; + dx_ext1 = dx0 + 1 - m_squish3d; + } + else + { + xsv_ext0 = xsv_ext1 = xsb + 1; + dx_ext0 = dx0 - 1 - 2 * m_squish3d; + dx_ext1 = dx0 - 1 - m_squish3d; + } + + if ((c & 0x02) == 0) + { + ysv_ext0 = ysb; + ysv_ext1 = ysb - 1; + dy_ext0 = dy0 - 2 * m_squish3d; + dy_ext1 = dy0 + 1 - m_squish3d; + } + else + { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy0 - 1 - 2 * m_squish3d; + dy_ext1 = dy0 - 1 - m_squish3d; + } + + if ((c & 0x04) == 0) + { + zsv_ext0 = zsb; + zsv_ext1 = zsb - 1; + dz_ext0 = dz0 - 2 * m_squish3d; + dz_ext1 = dz0 + 1 - m_squish3d; + } + else + { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz0 - 1 - 2 * m_squish3d; + dz_ext1 = dz0 - 1 - m_squish3d; + } + } + + //Contribution (0,0,0) + double attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0; + if (attn0 > 0) + { + attn0 *= attn0; + value += attn0 * attn0 * extrapolate(xsb + 0, ysb + 0, zsb + 0, dx0, dy0, dz0); + } + + //Contribution (1,0,0) + double dx1 = dx0 - 1 - m_squish3d; + double dy1 = dy0 - 0 - m_squish3d; + double dz1 = dz0 - 0 - m_squish3d; + double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1; + if (attn1 > 0) + { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, zsb + 0, dx1, dy1, dz1); + } + + //Contribution (0,1,0) + double dx2 = dx0 - 0 - m_squish3d; + double dy2 = dy0 - 1 - m_squish3d; + double dz2 = dz1; + double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2; + if (attn2 > 0) + { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, zsb + 0, dx2, dy2, dz2); + } + + //Contribution (0,0,1) + double dx3 = dx2; + double dy3 = dy1; + double dz3 = dz0 - 1 - m_squish3d; + double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3; + if (attn3 > 0) + { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate(xsb + 0, ysb + 0, zsb + 1, dx3, dy3, dz3); + } + } + else if (inSum >= 2) + { //We're inside the tetrahedron (3-Simplex) at (1,1,1) + +//Determine which two tetrahedral vertices are the closest, out of (1,1,0), (1,0,1), (0,1,1) but not (1,1,1). + char aPoint = 0x06; + double aScore = xins; + char bPoint = 0x05; + double bScore = yins; + if (aScore <= bScore && zins < bScore) + { + bScore = zins; + bPoint = 0x03; + } + else if (aScore > bScore && zins < aScore) + { + aScore = zins; + aPoint = 0x03; + } + + //Now we determine the two lattice points not part of the tetrahedron that may contribute. + //This depends on the closest two tetrahedral vertices, including (1,1,1) + double wins = 3 - inSum; + if (wins < aScore || wins < bScore) + { //(1,1,1) is one of the closest two tetrahedral vertices. + char c = (bScore < aScore ? bPoint : aPoint); //Our other closest vertex is the closest out of a and b. + + if ((c & 0x01) != 0) + { + xsv_ext0 = xsb + 2; + xsv_ext1 = xsb + 1; + dx_ext0 = dx0 - 2 - 3 * m_squish3d; + dx_ext1 = dx0 - 1 - 3 * m_squish3d; + } + else + { + xsv_ext0 = xsv_ext1 = xsb; + dx_ext0 = dx_ext1 = dx0 - 3 * m_squish3d; + } + + if ((c & 0x02) != 0) + { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy_ext1 = dy0 - 1 - 3 * m_squish3d; + if ((c & 0x01) != 0) + { + ysv_ext1 += 1; + dy_ext1 -= 1; + } + else + { + ysv_ext0 += 1; + dy_ext0 -= 1; + } + } + else + { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy_ext1 = dy0 - 3 * m_squish3d; + } + + if ((c & 0x04) != 0) + { + zsv_ext0 = zsb + 1; + zsv_ext1 = zsb + 2; + dz_ext0 = dz0 - 1 - 3 * m_squish3d; + dz_ext1 = dz0 - 2 - 3 * m_squish3d; + } + else + { + zsv_ext0 = zsv_ext1 = zsb; + dz_ext0 = dz_ext1 = dz0 - 3 * m_squish3d; + } + } + else + { //(1,1,1) is not one of the closest two tetrahedral vertices. + char c = static_cast(aPoint & bPoint); //Our two extra vertices are determined by the closest two. + + if ((c & 0x01) != 0) + { + xsv_ext0 = xsb + 1; + xsv_ext1 = xsb + 2; + dx_ext0 = dx0 - 1 - m_squish3d; + dx_ext1 = dx0 - 2 - 2 * m_squish3d; + } + else + { + xsv_ext0 = xsv_ext1 = xsb; + dx_ext0 = dx0 - m_squish3d; + dx_ext1 = dx0 - 2 * m_squish3d; + } + + if ((c & 0x02) != 0) + { + ysv_ext0 = ysb + 1; + ysv_ext1 = ysb + 2; + dy_ext0 = dy0 - 1 - m_squish3d; + dy_ext1 = dy0 - 2 - 2 * m_squish3d; + } + else + { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy0 - m_squish3d; + dy_ext1 = dy0 - 2 * m_squish3d; + } + + if ((c & 0x04) != 0) + { + zsv_ext0 = zsb + 1; + zsv_ext1 = zsb + 2; + dz_ext0 = dz0 - 1 - m_squish3d; + dz_ext1 = dz0 - 2 - 2 * m_squish3d; + } + else + { + zsv_ext0 = zsv_ext1 = zsb; + dz_ext0 = dz0 - m_squish3d; + dz_ext1 = dz0 - 2 * m_squish3d; + } + } + + //Contribution (1,1,0) + double dx3 = dx0 - 1 - 2 * m_squish3d; + double dy3 = dy0 - 1 - 2 * m_squish3d; + double dz3 = dz0 - 0 - 2 * m_squish3d; + double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3; + if (attn3 > 0) + { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate(xsb + 1, ysb + 1, zsb + 0, dx3, dy3, dz3); + } + + //Contribution (1,0,1) + double dx2 = dx3; + double dy2 = dy0 - 0 - 2 * m_squish3d; + double dz2 = dz0 - 1 - 2 * m_squish3d; + double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2; + if (attn2 > 0) + { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate(xsb + 1, ysb + 0, zsb + 1, dx2, dy2, dz2); + } + + //Contribution (0,1,1) + double dx1 = dx0 - 0 - 2 * m_squish3d; + double dy1 = dy3; + double dz1 = dz2; + double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1; + if (attn1 > 0) + { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate(xsb + 0, ysb + 1, zsb + 1, dx1, dy1, dz1); + } + + //Contribution (1,1,1) + dx0 = dx0 - 1 - 3 * m_squish3d; + dy0 = dy0 - 1 - 3 * m_squish3d; + dz0 = dz0 - 1 - 3 * m_squish3d; + double attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0; + if (attn0 > 0) + { + attn0 *= attn0; + value += attn0 * attn0 * extrapolate(xsb + 1, ysb + 1, zsb + 1, dx0, dy0, dz0); + } + } + else + { //We're inside the octahedron (Rectified 3-Simplex) in between. + double aScore; + char aPoint; + bool aIsFurtherSide; + double bScore; + char bPoint; + bool bIsFurtherSide; + + //Decide between point (0,0,1) and (1,1,0) as closest + double p1 = xins + yins; + if (p1 > 1) + { + aScore = p1 - 1; + aPoint = 0x03; + aIsFurtherSide = true; + } + else + { + aScore = 1 - p1; + aPoint = 0x04; + aIsFurtherSide = false; + } + + //Decide between point (0,1,0) and (1,0,1) as closest + double p2 = xins + zins; + if (p2 > 1) + { + bScore = p2 - 1; + bPoint = 0x05; + bIsFurtherSide = true; + } + else + { + bScore = 1 - p2; + bPoint = 0x02; + bIsFurtherSide = false; + } + + //The closest out of the two (1,0,0) and (0,1,1) will replace the furthest out of the two decided above, if closer. + double p3 = yins + zins; + if (p3 > 1) + { + double score = p3 - 1; + if (aScore <= bScore && aScore < score) + { + aScore = score; + aPoint = 0x06; + aIsFurtherSide = true; + } + else if (aScore > bScore && bScore < score) + { + bScore = score; + bPoint = 0x06; + bIsFurtherSide = true; + } + } + else + { + double score = 1 - p3; + if (aScore <= bScore && aScore < score) + { + aScore = score; + aPoint = 0x01; + aIsFurtherSide = false; + } + else if (aScore > bScore && bScore < score) + { + bScore = score; + bPoint = 0x01; + bIsFurtherSide = false; + } + } + + //Where each of the two closest points are determines how the extra two vertices are calculated. + if (aIsFurtherSide == bIsFurtherSide) + { + if (aIsFurtherSide) + { //Both closest points on (1,1,1) side + +//One of the two extra points is (1,1,1) + dx_ext0 = dx0 - 1 - 3 * m_squish3d; + dy_ext0 = dy0 - 1 - 3 * m_squish3d; + dz_ext0 = dz0 - 1 - 3 * m_squish3d; + xsv_ext0 = xsb + 1; + ysv_ext0 = ysb + 1; + zsv_ext0 = zsb + 1; + + //Other extra point is based on the shared axis. + char c = static_cast(aPoint & bPoint); + if ((c & 0x01) != 0) + { + dx_ext1 = dx0 - 2 - 2 * m_squish3d; + dy_ext1 = dy0 - 2 * m_squish3d; + dz_ext1 = dz0 - 2 * m_squish3d; + xsv_ext1 = xsb + 2; + ysv_ext1 = ysb; + zsv_ext1 = zsb; + } + else if ((c & 0x02) != 0) + { + dx_ext1 = dx0 - 2 * m_squish3d; + dy_ext1 = dy0 - 2 - 2 * m_squish3d; + dz_ext1 = dz0 - 2 * m_squish3d; + xsv_ext1 = xsb; + ysv_ext1 = ysb + 2; + zsv_ext1 = zsb; + } + else + { + dx_ext1 = dx0 - 2 * m_squish3d; + dy_ext1 = dy0 - 2 * m_squish3d; + dz_ext1 = dz0 - 2 - 2 * m_squish3d; + xsv_ext1 = xsb; + ysv_ext1 = ysb; + zsv_ext1 = zsb + 2; + } + } + else + {//Both closest points on (0,0,0) side + + //One of the two extra points is (0,0,0) + dx_ext0 = dx0; + dy_ext0 = dy0; + dz_ext0 = dz0; + xsv_ext0 = xsb; + ysv_ext0 = ysb; + zsv_ext0 = zsb; + + //Other extra point is based on the omitted axis. + char c = static_cast(aPoint | bPoint); + if ((c & 0x01) == 0) + { + dx_ext1 = dx0 + 1 - m_squish3d; + dy_ext1 = dy0 - 1 - m_squish3d; + dz_ext1 = dz0 - 1 - m_squish3d; + xsv_ext1 = xsb - 1; + ysv_ext1 = ysb + 1; + zsv_ext1 = zsb + 1; + } + else if ((c & 0x02) == 0) + { + dx_ext1 = dx0 - 1 - m_squish3d; + dy_ext1 = dy0 + 1 - m_squish3d; + dz_ext1 = dz0 - 1 - m_squish3d; + xsv_ext1 = xsb + 1; + ysv_ext1 = ysb - 1; + zsv_ext1 = zsb + 1; + } + else + { + dx_ext1 = dx0 - 1 - m_squish3d; + dy_ext1 = dy0 - 1 - m_squish3d; + dz_ext1 = dz0 + 1 - m_squish3d; + xsv_ext1 = xsb + 1; + ysv_ext1 = ysb + 1; + zsv_ext1 = zsb - 1; + } + } + } + else + { //One point on (0,0,0) side, one point on (1,1,1) side + char c1, c2; + if (aIsFurtherSide) + { + c1 = aPoint; + c2 = bPoint; + } + else + { + c1 = bPoint; + c2 = aPoint; + } + + //One contribution is a permutation of (1,1,-1) + if ((c1 & 0x01) == 0) + { + dx_ext0 = dx0 + 1 - m_squish3d; + dy_ext0 = dy0 - 1 - m_squish3d; + dz_ext0 = dz0 - 1 - m_squish3d; + xsv_ext0 = xsb - 1; + ysv_ext0 = ysb + 1; + zsv_ext0 = zsb + 1; + } + else if ((c1 & 0x02) == 0) + { + dx_ext0 = dx0 - 1 - m_squish3d; + dy_ext0 = dy0 + 1 - m_squish3d; + dz_ext0 = dz0 - 1 - m_squish3d; + xsv_ext0 = xsb + 1; + ysv_ext0 = ysb - 1; + zsv_ext0 = zsb + 1; + } + else + { + dx_ext0 = dx0 - 1 - m_squish3d; + dy_ext0 = dy0 - 1 - m_squish3d; + dz_ext0 = dz0 + 1 - m_squish3d; + xsv_ext0 = xsb + 1; + ysv_ext0 = ysb + 1; + zsv_ext0 = zsb - 1; + } + + //One contribution is a permutation of (0,0,2) + dx_ext1 = dx0 - 2 * m_squish3d; + dy_ext1 = dy0 - 2 * m_squish3d; + dz_ext1 = dz0 - 2 * m_squish3d; + xsv_ext1 = xsb; + ysv_ext1 = ysb; + zsv_ext1 = zsb; + if ((c2 & 0x01) != 0) + { + dx_ext1 -= 2; + xsv_ext1 += 2; + } + else if ((c2 & 0x02) != 0) + { + dy_ext1 -= 2; + ysv_ext1 += 2; + } + else + { + dz_ext1 -= 2; + zsv_ext1 += 2; + } + } + + //Contribution (1,0,0) + double dx1 = dx0 - 1 - m_squish3d; + double dy1 = dy0 - 0 - m_squish3d; + double dz1 = dz0 - 0 - m_squish3d; + double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1; + if (attn1 > 0) + { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, zsb + 0, dx1, dy1, dz1); + } + + //Contribution (0,1,0) + double dx2 = dx0 - 0 - m_squish3d; + double dy2 = dy0 - 1 - m_squish3d; + double dz2 = dz1; + double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2; + if (attn2 > 0) + { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, zsb + 0, dx2, dy2, dz2); + } + + //Contribution (0,0,1) + double dx3 = dx2; + double dy3 = dy1; + double dz3 = dz0 - 1 - m_squish3d; + double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3; + if (attn3 > 0) + { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate(xsb + 0, ysb + 0, zsb + 1, dx3, dy3, dz3); + } + + //Contribution (1,1,0) + double dx4 = dx0 - 1 - 2 * m_squish3d; + double dy4 = dy0 - 1 - 2 * m_squish3d; + double dz4 = dz0 - 0 - 2 * m_squish3d; + double attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4; + if (attn4 > 0) + { + attn4 *= attn4; + value += attn4 * attn4 * extrapolate(xsb + 1, ysb + 1, zsb + 0, dx4, dy4, dz4); + } + + //Contribution (1,0,1) + double dx5 = dx4; + double dy5 = dy0 - 0 - 2 * m_squish3d; + double dz5 = dz0 - 1 - 2 * m_squish3d; + double attn5 = 2 - dx5 * dx5 - dy5 * dy5 - dz5 * dz5; + if (attn5 > 0) + { + attn5 *= attn5; + value += attn5 * attn5 * extrapolate(xsb + 1, ysb + 0, zsb + 1, dx5, dy5, dz5); + } + + //Contribution (0,1,1) + double dx6 = dx0 - 0 - 2 * m_squish3d; + double dy6 = dy4; + double dz6 = dz5; + double attn6 = 2 - dx6 * dx6 - dy6 * dy6 - dz6 * dz6; + if (attn6 > 0) + { + attn6 *= attn6; + value += attn6 * attn6 * extrapolate(xsb + 0, ysb + 1, zsb + 1, dx6, dy6, dz6); + } + } + + //First extra vertex + double attn_ext0 = 2 - dx_ext0 * dx_ext0 - dy_ext0 * dy_ext0 - dz_ext0 * dz_ext0; + if (attn_ext0 > 0) + { + attn_ext0 *= attn_ext0; + value += attn_ext0 * attn_ext0 * extrapolate(xsv_ext0, ysv_ext0, zsv_ext0, dx_ext0, dy_ext0, dz_ext0); + } + + //Second extra vertex + double attn_ext1 = 2 - dx_ext1 * dx_ext1 - dy_ext1 * dy_ext1 - dz_ext1 * dz_ext1; + if (attn_ext1 > 0) + { + attn_ext1 *= attn_ext1; + value += attn_ext1 * attn_ext1 * extrapolate(xsv_ext1, ysv_ext1, zsv_ext1, dx_ext1, dy_ext1, dz_ext1); + } + + return value / m_norm3d; + } + + double Noise::eval(double x, double y, double z, double w) const + { + //Place input coordinates on simplectic honeycomb. + double stretchOffset = (x + y + z + w) * m_stretch4d; + double xs = x + stretchOffset; + double ys = y + stretchOffset; + double zs = z + stretchOffset; + double ws = w + stretchOffset; + + //static_cast(floor to get simplectic honeycomb coordinates of rhombo-hypercube super-cell origin. + int xsb = static_cast(floor(xs)); + int ysb = static_cast(floor(ys)); + int zsb = static_cast(floor(zs)); + int wsb = static_cast(floor(ws)); + + //Skew out to get actual coordinates of stretched rhombo-hypercube origin. We'll need these later. + double squishOffset = (xsb + ysb + zsb + wsb) * m_squish4d; + double xb = xsb + squishOffset; + double yb = ysb + squishOffset; + double zb = zsb + squishOffset; + double wb = wsb + squishOffset; + + //Compute simplectic honeycomb coordinates relative to rhombo-hypercube origin. + double xins = xs - xsb; + double yins = ys - ysb; + double zins = zs - zsb; + double wins = ws - wsb; + + //Sum those together to get a value that determines which region we're in. + double inSum = xins + yins + zins + wins; + + //Positions relative to origin point. + double dx0 = x - xb; + double dy0 = y - yb; + double dz0 = z - zb; + double dw0 = w - wb; + + //We'll be defining these inside the next block and using them afterwards. + double dx_ext0, dy_ext0, dz_ext0, dw_ext0; + double dx_ext1, dy_ext1, dz_ext1, dw_ext1; + double dx_ext2, dy_ext2, dz_ext2, dw_ext2; + int xsv_ext0, ysv_ext0, zsv_ext0, wsv_ext0; + int xsv_ext1, ysv_ext1, zsv_ext1, wsv_ext1; + int xsv_ext2, ysv_ext2, zsv_ext2, wsv_ext2; + + double value = 0; + if (inSum <= 1) + { //We're inside the pentachoron (4-Simplex) at (0,0,0,0) + +//Determine which two of (0,0,0,1), (0,0,1,0), (0,1,0,0), (1,0,0,0) are closest. + char aPoint = 0x01; + double aScore = xins; + char bPoint = 0x02; + double bScore = yins; + if (aScore >= bScore && zins > bScore) + { + bScore = zins; + bPoint = 0x04; + } + else if (aScore < bScore && zins > aScore) + { + aScore = zins; + aPoint = 0x04; + } + if (aScore >= bScore && wins > bScore) + { + bScore = wins; + bPoint = 0x08; + } + else if (aScore < bScore && wins > aScore) + { + aScore = wins; + aPoint = 0x08; + } + + //Now we determine the three lattice points not part of the pentachoron that may contribute. + //This depends on the closest two pentachoron vertices, including (0,0,0,0) + double uins = 1 - inSum; + if (uins > aScore || uins > bScore) + { //(0,0,0,0) is one of the closest two pentachoron vertices. + char c = (bScore > aScore ? bPoint : aPoint); //Our other closest vertex is the closest out of a and b. + if ((c & 0x01) == 0) + { + xsv_ext0 = xsb - 1; + xsv_ext1 = xsv_ext2 = xsb; + dx_ext0 = dx0 + 1; + dx_ext1 = dx_ext2 = dx0; + } + else + { + xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb + 1; + dx_ext0 = dx_ext1 = dx_ext2 = dx0 - 1; + } + + if ((c & 0x02) == 0) + { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb; + dy_ext0 = dy_ext1 = dy_ext2 = dy0; + if ((c & 0x01) == 0x01) + { + ysv_ext0 -= 1; + dy_ext0 += 1; + } + else + { + ysv_ext1 -= 1; + dy_ext1 += 1; + } + } + else + { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1; + dy_ext0 = dy_ext1 = dy_ext2 = dy0 - 1; + } + + if ((c & 0x04) == 0) + { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb; + dz_ext0 = dz_ext1 = dz_ext2 = dz0; + if ((c & 0x03) != 0) + { + if ((c & 0x03) == 0x03) + { + zsv_ext0 -= 1; + dz_ext0 += 1; + } + else + { + zsv_ext1 -= 1; + dz_ext1 += 1; + } + } + else + { + zsv_ext2 -= 1; + dz_ext2 += 1; + } + } + else + { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1; + dz_ext0 = dz_ext1 = dz_ext2 = dz0 - 1; + } + + if ((c & 0x08) == 0) + { + wsv_ext0 = wsv_ext1 = wsb; + wsv_ext2 = wsb - 1; + dw_ext0 = dw_ext1 = dw0; + dw_ext2 = dw0 + 1; + } + else + { + wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb + 1; + dw_ext0 = dw_ext1 = dw_ext2 = dw0 - 1; + } + } + else + { //(0,0,0,0) is not one of the closest two pentachoron vertices. + char c = static_cast(aPoint | bPoint); //Our three extra vertices are determined by the closest two. + + if ((c & 0x01) == 0) + { + xsv_ext0 = xsv_ext2 = xsb; + xsv_ext1 = xsb - 1; + dx_ext0 = dx0 - 2 * m_squish4d; + dx_ext1 = dx0 + 1 - m_squish4d; + dx_ext2 = dx0 - m_squish4d; + } + else + { + xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb + 1; + dx_ext0 = dx0 - 1 - 2 * m_squish4d; + dx_ext1 = dx_ext2 = dx0 - 1 - m_squish4d; + } + + if ((c & 0x02) == 0) + { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb; + dy_ext0 = dy0 - 2 * m_squish4d; + dy_ext1 = dy_ext2 = dy0 - m_squish4d; + if ((c & 0x01) == 0x01) + { + ysv_ext1 -= 1; + dy_ext1 += 1; + } + else + { + ysv_ext2 -= 1; + dy_ext2 += 1; + } + } + else + { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1; + dy_ext0 = dy0 - 1 - 2 * m_squish4d; + dy_ext1 = dy_ext2 = dy0 - 1 - m_squish4d; + } + + if ((c & 0x04) == 0) + { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb; + dz_ext0 = dz0 - 2 * m_squish4d; + dz_ext1 = dz_ext2 = dz0 - m_squish4d; + if ((c & 0x03) == 0x03) + { + zsv_ext1 -= 1; + dz_ext1 += 1; + } + else + { + zsv_ext2 -= 1; + dz_ext2 += 1; + } + } + else + { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1; + dz_ext0 = dz0 - 1 - 2 * m_squish4d; + dz_ext1 = dz_ext2 = dz0 - 1 - m_squish4d; + } + + if ((c & 0x08) == 0) + { + wsv_ext0 = wsv_ext1 = wsb; + wsv_ext2 = wsb - 1; + dw_ext0 = dw0 - 2 * m_squish4d; + dw_ext1 = dw0 - m_squish4d; + dw_ext2 = dw0 + 1 - m_squish4d; + } + else + { + wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb + 1; + dw_ext0 = dw0 - 1 - 2 * m_squish4d; + dw_ext1 = dw_ext2 = dw0 - 1 - m_squish4d; + } + } + + //Contribution (0,0,0,0) + double attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0 - dw0 * dw0; + if (attn0 > 0) + { + attn0 *= attn0; + value += attn0 * attn0 * extrapolate(xsb + 0, ysb + 0, zsb + 0, wsb + 0, dx0, dy0, dz0, dw0); + } + + //Contribution (1,0,0,0) + double dx1 = dx0 - 1 - m_squish4d; + double dy1 = dy0 - 0 - m_squish4d; + double dz1 = dz0 - 0 - m_squish4d; + double dw1 = dw0 - 0 - m_squish4d; + double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1; + if (attn1 > 0) + { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, zsb + 0, wsb + 0, dx1, dy1, dz1, dw1); + } + + //Contribution (0,1,0,0) + double dx2 = dx0 - 0 - m_squish4d; + double dy2 = dy0 - 1 - m_squish4d; + double dz2 = dz1; + double dw2 = dw1; + double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2; + if (attn2 > 0) + { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, zsb + 0, wsb + 0, dx2, dy2, dz2, dw2); + } + + //Contribution (0,0,1,0) + double dx3 = dx2; + double dy3 = dy1; + double dz3 = dz0 - 1 - m_squish4d; + double dw3 = dw1; + double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3; + if (attn3 > 0) + { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate(xsb + 0, ysb + 0, zsb + 1, wsb + 0, dx3, dy3, dz3, dw3); + } + + //Contribution (0,0,0,1) + double dx4 = dx2; + double dy4 = dy1; + double dz4 = dz1; + double dw4 = dw0 - 1 - m_squish4d; + double attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4; + if (attn4 > 0) + { + attn4 *= attn4; + value += attn4 * attn4 * extrapolate(xsb + 0, ysb + 0, zsb + 0, wsb + 1, dx4, dy4, dz4, dw4); + } + } + else if (inSum >= 3) + { //We're inside the pentachoron (4-Simplex) at (1,1,1,1) +//Determine which two of (1,1,1,0), (1,1,0,1), (1,0,1,1), (0,1,1,1) are closest. + char aPoint = 0x0E; + double aScore = xins; + char bPoint = 0x0D; + double bScore = yins; + if (aScore <= bScore && zins < bScore) + { + bScore = zins; + bPoint = 0x0B; + } + else if (aScore > bScore && zins < aScore) + { + aScore = zins; + aPoint = 0x0B; + } + if (aScore <= bScore && wins < bScore) + { + bScore = wins; + bPoint = 0x07; + } + else if (aScore > bScore && wins < aScore) + { + aScore = wins; + aPoint = 0x07; + } + + //Now we determine the three lattice points not part of the pentachoron that may contribute. + //This depends on the closest two pentachoron vertices, including (0,0,0,0) + double uins = 4 - inSum; + if (uins < aScore || uins < bScore) + { //(1,1,1,1) is one of the closest two pentachoron vertices. + char c = (bScore < aScore ? bPoint : aPoint); //Our other closest vertex is the closest out of a and b. + + if ((c & 0x01) != 0) + { + xsv_ext0 = xsb + 2; + xsv_ext1 = xsv_ext2 = xsb + 1; + dx_ext0 = dx0 - 2 - 4 * m_squish4d; + dx_ext1 = dx_ext2 = dx0 - 1 - 4 * m_squish4d; + } + else + { + xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb; + dx_ext0 = dx_ext1 = dx_ext2 = dx0 - 4 * m_squish4d; + } + + if ((c & 0x02) != 0) + { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1; + dy_ext0 = dy_ext1 = dy_ext2 = dy0 - 1 - 4 * m_squish4d; + if ((c & 0x01) != 0) + { + ysv_ext1 += 1; + dy_ext1 -= 1; + } + else + { + ysv_ext0 += 1; + dy_ext0 -= 1; + } + } + else + { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb; + dy_ext0 = dy_ext1 = dy_ext2 = dy0 - 4 * m_squish4d; + } + + if ((c & 0x04) != 0) + { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1; + dz_ext0 = dz_ext1 = dz_ext2 = dz0 - 1 - 4 * m_squish4d; + if ((c & 0x03) != 0x03) + { + if ((c & 0x03) == 0) + { + zsv_ext0 += 1; + dz_ext0 -= 1; + } + else + { + zsv_ext1 += 1; + dz_ext1 -= 1; + } + } + else + { + zsv_ext2 += 1; + dz_ext2 -= 1; + } + } + else + { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb; + dz_ext0 = dz_ext1 = dz_ext2 = dz0 - 4 * m_squish4d; + } + + if ((c & 0x08) != 0) + { + wsv_ext0 = wsv_ext1 = wsb + 1; + wsv_ext2 = wsb + 2; + dw_ext0 = dw_ext1 = dw0 - 1 - 4 * m_squish4d; + dw_ext2 = dw0 - 2 - 4 * m_squish4d; + } + else + { + wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb; + dw_ext0 = dw_ext1 = dw_ext2 = dw0 - 4 * m_squish4d; + } + } + else + { //(1,1,1,1) is not one of the closest two pentachoron vertices. + char c = static_cast(aPoint & bPoint); //Our three extra vertices are determined by the closest two. + + if ((c & 0x01) != 0) + { + xsv_ext0 = xsv_ext2 = xsb + 1; + xsv_ext1 = xsb + 2; + dx_ext0 = dx0 - 1 - 2 * m_squish4d; + dx_ext1 = dx0 - 2 - 3 * m_squish4d; + dx_ext2 = dx0 - 1 - 3 * m_squish4d; + } + else + { + xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb; + dx_ext0 = dx0 - 2 * m_squish4d; + dx_ext1 = dx_ext2 = dx0 - 3 * m_squish4d; + } + + if ((c & 0x02) != 0) + { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1; + dy_ext0 = dy0 - 1 - 2 * m_squish4d; + dy_ext1 = dy_ext2 = dy0 - 1 - 3 * m_squish4d; + if ((c & 0x01) != 0) + { + ysv_ext2 += 1; + dy_ext2 -= 1; + } + else + { + ysv_ext1 += 1; + dy_ext1 -= 1; + } + } + else + { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb; + dy_ext0 = dy0 - 2 * m_squish4d; + dy_ext1 = dy_ext2 = dy0 - 3 * m_squish4d; + } + + if ((c & 0x04) != 0) + { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1; + dz_ext0 = dz0 - 1 - 2 * m_squish4d; + dz_ext1 = dz_ext2 = dz0 - 1 - 3 * m_squish4d; + if ((c & 0x03) != 0) + { + zsv_ext2 += 1; + dz_ext2 -= 1; + } + else + { + zsv_ext1 += 1; + dz_ext1 -= 1; + } + } + else + { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb; + dz_ext0 = dz0 - 2 * m_squish4d; + dz_ext1 = dz_ext2 = dz0 - 3 * m_squish4d; + } + + if ((c & 0x08) != 0) + { + wsv_ext0 = wsv_ext1 = wsb + 1; + wsv_ext2 = wsb + 2; + dw_ext0 = dw0 - 1 - 2 * m_squish4d; + dw_ext1 = dw0 - 1 - 3 * m_squish4d; + dw_ext2 = dw0 - 2 - 3 * m_squish4d; + } + else + { + wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb; + dw_ext0 = dw0 - 2 * m_squish4d; + dw_ext1 = dw_ext2 = dw0 - 3 * m_squish4d; + } + } + + //Contribution (1,1,1,0) + double dx4 = dx0 - 1 - 3 * m_squish4d; + double dy4 = dy0 - 1 - 3 * m_squish4d; + double dz4 = dz0 - 1 - 3 * m_squish4d; + double dw4 = dw0 - 3 * m_squish4d; + double attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4; + if (attn4 > 0) + { + attn4 *= attn4; + value += attn4 * attn4 * extrapolate(xsb + 1, ysb + 1, zsb + 1, wsb + 0, dx4, dy4, dz4, dw4); + } + + //Contribution (1,1,0,1) + double dx3 = dx4; + double dy3 = dy4; + double dz3 = dz0 - 3 * m_squish4d; + double dw3 = dw0 - 1 - 3 * m_squish4d; + double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3; + if (attn3 > 0) + { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate(xsb + 1, ysb + 1, zsb + 0, wsb + 1, dx3, dy3, dz3, dw3); + } + + //Contribution (1,0,1,1) + double dx2 = dx4; + double dy2 = dy0 - 3 * m_squish4d; + double dz2 = dz4; + double dw2 = dw3; + double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2; + if (attn2 > 0) + { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate(xsb + 1, ysb + 0, zsb + 1, wsb + 1, dx2, dy2, dz2, dw2); + } + + //Contribution (0,1,1,1) + double dx1 = dx0 - 3 * m_squish4d; + double dz1 = dz4; + double dy1 = dy4; + double dw1 = dw3; + double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1; + if (attn1 > 0) + { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate(xsb + 0, ysb + 1, zsb + 1, wsb + 1, dx1, dy1, dz1, dw1); + } + + //Contribution (1,1,1,1) + dx0 = dx0 - 1 - 4 * m_squish4d; + dy0 = dy0 - 1 - 4 * m_squish4d; + dz0 = dz0 - 1 - 4 * m_squish4d; + dw0 = dw0 - 1 - 4 * m_squish4d; + double attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0 - dw0 * dw0; + if (attn0 > 0) + { + attn0 *= attn0; + value += attn0 * attn0 * extrapolate(xsb + 1, ysb + 1, zsb + 1, wsb + 1, dx0, dy0, dz0, dw0); + } + } + else if (inSum <= 2) + { //We're inside the first dispentachoron (Rectified 4-Simplex) + double aScore; + char aPoint; + bool aIsBiggerSide = true; + double bScore; + char bPoint; + bool bIsBiggerSide = true; + + //Decide between (1,1,0,0) and (0,0,1,1) + if (xins + yins > zins + wins) + { + aScore = xins + yins; + aPoint = 0x03; + } + else + { + aScore = zins + wins; + aPoint = 0x0C; + } + + //Decide between (1,0,1,0) and (0,1,0,1) + if (xins + zins > yins + wins) + { + bScore = xins + zins; + bPoint = 0x05; + } + else + { + bScore = yins + wins; + bPoint = 0x0A; + } + + //Closer between (1,0,0,1) and (0,1,1,0) will replace the further of a and b, if closer. + if (xins + wins > yins + zins) + { + double score = xins + wins; + if (aScore >= bScore && score > bScore) + { + bScore = score; + bPoint = 0x09; + } + else if (aScore < bScore && score > aScore) + { + aScore = score; + aPoint = 0x09; + } + } + else + { + double score = yins + zins; + if (aScore >= bScore && score > bScore) + { + bScore = score; + bPoint = 0x06; + } + else if (aScore < bScore && score > aScore) + { + aScore = score; + aPoint = 0x06; + } + } + + //Decide if (1,0,0,0) is closer. + double p1 = 2 - inSum + xins; + if (aScore >= bScore && p1 > bScore) + { + bScore = p1; + bPoint = 0x01; + bIsBiggerSide = false; + } + else if (aScore < bScore && p1 > aScore) + { + aScore = p1; + aPoint = 0x01; + aIsBiggerSide = false; + } + + //Decide if (0,1,0,0) is closer. + double p2 = 2 - inSum + yins; + if (aScore >= bScore && p2 > bScore) + { + bScore = p2; + bPoint = 0x02; + bIsBiggerSide = false; + } + else if (aScore < bScore && p2 > aScore) + { + aScore = p2; + aPoint = 0x02; + aIsBiggerSide = false; + } + + //Decide if (0,0,1,0) is closer. + double p3 = 2 - inSum + zins; + if (aScore >= bScore && p3 > bScore) + { + bScore = p3; + bPoint = 0x04; + bIsBiggerSide = false; + } + else if (aScore < bScore && p3 > aScore) + { + aScore = p3; + aPoint = 0x04; + aIsBiggerSide = false; + } + + //Decide if (0,0,0,1) is closer. + double p4 = 2 - inSum + wins; + if (aScore >= bScore && p4 > bScore) + { + bScore = p4; + bPoint = 0x08; + bIsBiggerSide = false; + } + else if (aScore < bScore && p4 > aScore) + { + aScore = p4; + aPoint = 0x08; + aIsBiggerSide = false; + } + + //Where each of the two closest points are determines how the extra three vertices are calculated. + if (aIsBiggerSide == bIsBiggerSide) + { + if (aIsBiggerSide) + { //Both closest points on the bigger side + char c1 = static_cast(aPoint | bPoint); + char c2 = static_cast(aPoint & bPoint); + if ((c1 & 0x01) == 0) + { + xsv_ext0 = xsb; + xsv_ext1 = xsb - 1; + dx_ext0 = dx0 - 3 * m_squish4d; + dx_ext1 = dx0 + 1 - 2 * m_squish4d; + } + else + { + xsv_ext0 = xsv_ext1 = xsb + 1; + dx_ext0 = dx0 - 1 - 3 * m_squish4d; + dx_ext1 = dx0 - 1 - 2 * m_squish4d; + } + + if ((c1 & 0x02) == 0) + { + ysv_ext0 = ysb; + ysv_ext1 = ysb - 1; + dy_ext0 = dy0 - 3 * m_squish4d; + dy_ext1 = dy0 + 1 - 2 * m_squish4d; + } + else + { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy0 - 1 - 3 * m_squish4d; + dy_ext1 = dy0 - 1 - 2 * m_squish4d; + } + + if ((c1 & 0x04) == 0) + { + zsv_ext0 = zsb; + zsv_ext1 = zsb - 1; + dz_ext0 = dz0 - 3 * m_squish4d; + dz_ext1 = dz0 + 1 - 2 * m_squish4d; + } + else + { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz0 - 1 - 3 * m_squish4d; + dz_ext1 = dz0 - 1 - 2 * m_squish4d; + } + + if ((c1 & 0x08) == 0) + { + wsv_ext0 = wsb; + wsv_ext1 = wsb - 1; + dw_ext0 = dw0 - 3 * m_squish4d; + dw_ext1 = dw0 + 1 - 2 * m_squish4d; + } + else + { + wsv_ext0 = wsv_ext1 = wsb + 1; + dw_ext0 = dw0 - 1 - 3 * m_squish4d; + dw_ext1 = dw0 - 1 - 2 * m_squish4d; + } + + //One combination is a permutation of (0,0,0,2) based on c2 + xsv_ext2 = xsb; + ysv_ext2 = ysb; + zsv_ext2 = zsb; + wsv_ext2 = wsb; + dx_ext2 = dx0 - 2 * m_squish4d; + dy_ext2 = dy0 - 2 * m_squish4d; + dz_ext2 = dz0 - 2 * m_squish4d; + dw_ext2 = dw0 - 2 * m_squish4d; + if ((c2 & 0x01) != 0) + { + xsv_ext2 += 2; + dx_ext2 -= 2; + } + else if ((c2 & 0x02) != 0) + { + ysv_ext2 += 2; + dy_ext2 -= 2; + } + else if ((c2 & 0x04) != 0) + { + zsv_ext2 += 2; + dz_ext2 -= 2; + } + else + { + wsv_ext2 += 2; + dw_ext2 -= 2; + } + + } + else + { //Both closest points on the smaller side + //One of the two extra points is (0,0,0,0) + xsv_ext2 = xsb; + ysv_ext2 = ysb; + zsv_ext2 = zsb; + wsv_ext2 = wsb; + dx_ext2 = dx0; + dy_ext2 = dy0; + dz_ext2 = dz0; + dw_ext2 = dw0; + + //Other two points are based on the omitted axes. + char c = static_cast(aPoint | bPoint); + + if ((c & 0x01) == 0) + { + xsv_ext0 = xsb - 1; + xsv_ext1 = xsb; + dx_ext0 = dx0 + 1 - m_squish4d; + dx_ext1 = dx0 - m_squish4d; + } + else + { + xsv_ext0 = xsv_ext1 = xsb + 1; + dx_ext0 = dx_ext1 = dx0 - 1 - m_squish4d; + } + + if ((c & 0x02) == 0) + { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy_ext1 = dy0 - m_squish4d; + if ((c & 0x01) == 0x01) + { + ysv_ext0 -= 1; + dy_ext0 += 1; + } + else + { + ysv_ext1 -= 1; + dy_ext1 += 1; + } + } + else + { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy_ext1 = dy0 - 1 - m_squish4d; + } + + if ((c & 0x04) == 0) + { + zsv_ext0 = zsv_ext1 = zsb; + dz_ext0 = dz_ext1 = dz0 - m_squish4d; + if ((c & 0x03) == 0x03) + { + zsv_ext0 -= 1; + dz_ext0 += 1; + } + else + { + zsv_ext1 -= 1; + dz_ext1 += 1; + } + } + else + { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz_ext1 = dz0 - 1 - m_squish4d; + } + + if ((c & 0x08) == 0) + { + wsv_ext0 = wsb; + wsv_ext1 = wsb - 1; + dw_ext0 = dw0 - m_squish4d; + dw_ext1 = dw0 + 1 - m_squish4d; + } + else + { + wsv_ext0 = wsv_ext1 = wsb + 1; + dw_ext0 = dw_ext1 = dw0 - 1 - m_squish4d; + } + + } + } + else + { //One point on each "side" + char c1, c2; + if (aIsBiggerSide) + { + c1 = aPoint; + c2 = bPoint; + } + else + { + c1 = bPoint; + c2 = aPoint; + } + + //Two contributions are the bigger-sided point with each 0 replaced with -1. + if ((c1 & 0x01) == 0) + { + xsv_ext0 = xsb - 1; + xsv_ext1 = xsb; + dx_ext0 = dx0 + 1 - m_squish4d; + dx_ext1 = dx0 - m_squish4d; + } + else + { + xsv_ext0 = xsv_ext1 = xsb + 1; + dx_ext0 = dx_ext1 = dx0 - 1 - m_squish4d; + } + + if ((c1 & 0x02) == 0) + { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy_ext1 = dy0 - m_squish4d; + if ((c1 & 0x01) == 0x01) + { + ysv_ext0 -= 1; + dy_ext0 += 1; + } + else + { + ysv_ext1 -= 1; + dy_ext1 += 1; + } + } + else + { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy_ext1 = dy0 - 1 - m_squish4d; + } + + if ((c1 & 0x04) == 0) + { + zsv_ext0 = zsv_ext1 = zsb; + dz_ext0 = dz_ext1 = dz0 - m_squish4d; + if ((c1 & 0x03) == 0x03) + { + zsv_ext0 -= 1; + dz_ext0 += 1; + } + else + { + zsv_ext1 -= 1; + dz_ext1 += 1; + } + } + else + { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz_ext1 = dz0 - 1 - m_squish4d; + } + + if ((c1 & 0x08) == 0) + { + wsv_ext0 = wsb; + wsv_ext1 = wsb - 1; + dw_ext0 = dw0 - m_squish4d; + dw_ext1 = dw0 + 1 - m_squish4d; + } + else + { + wsv_ext0 = wsv_ext1 = wsb + 1; + dw_ext0 = dw_ext1 = dw0 - 1 - m_squish4d; + } + + //One contribution is a permutation of (0,0,0,2) based on the smaller-sided point + xsv_ext2 = xsb; + ysv_ext2 = ysb; + zsv_ext2 = zsb; + wsv_ext2 = wsb; + dx_ext2 = dx0 - 2 * m_squish4d; + dy_ext2 = dy0 - 2 * m_squish4d; + dz_ext2 = dz0 - 2 * m_squish4d; + dw_ext2 = dw0 - 2 * m_squish4d; + if ((c2 & 0x01) != 0) + { + xsv_ext2 += 2; + dx_ext2 -= 2; + } + else if ((c2 & 0x02) != 0) + { + ysv_ext2 += 2; + dy_ext2 -= 2; + } + else if ((c2 & 0x04) != 0) + { + zsv_ext2 += 2; + dz_ext2 -= 2; + } + else + { + wsv_ext2 += 2; + dw_ext2 -= 2; + } + } + + //Contribution (1,0,0,0) + double dx1 = dx0 - 1 - m_squish4d; + double dy1 = dy0 - 0 - m_squish4d; + double dz1 = dz0 - 0 - m_squish4d; + double dw1 = dw0 - 0 - m_squish4d; + double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1; + if (attn1 > 0) + { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, zsb + 0, wsb + 0, dx1, dy1, dz1, dw1); + } + + //Contribution (0,1,0,0) + double dx2 = dx0 - 0 - m_squish4d; + double dy2 = dy0 - 1 - m_squish4d; + double dz2 = dz1; + double dw2 = dw1; + double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2; + if (attn2 > 0) + { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, zsb + 0, wsb + 0, dx2, dy2, dz2, dw2); + } + + //Contribution (0,0,1,0) + double dx3 = dx2; + double dy3 = dy1; + double dz3 = dz0 - 1 - m_squish4d; + double dw3 = dw1; + double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3; + if (attn3 > 0) + { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate(xsb + 0, ysb + 0, zsb + 1, wsb + 0, dx3, dy3, dz3, dw3); + } + + //Contribution (0,0,0,1) + double dx4 = dx2; + double dy4 = dy1; + double dz4 = dz1; + double dw4 = dw0 - 1 - m_squish4d; + double attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4; + if (attn4 > 0) + { + attn4 *= attn4; + value += attn4 * attn4 * extrapolate(xsb + 0, ysb + 0, zsb + 0, wsb + 1, dx4, dy4, dz4, dw4); + } + + //Contribution (1,1,0,0) + double dx5 = dx0 - 1 - 2 * m_squish4d; + double dy5 = dy0 - 1 - 2 * m_squish4d; + double dz5 = dz0 - 0 - 2 * m_squish4d; + double dw5 = dw0 - 0 - 2 * m_squish4d; + double attn5 = 2 - dx5 * dx5 - dy5 * dy5 - dz5 * dz5 - dw5 * dw5; + if (attn5 > 0) + { + attn5 *= attn5; + value += attn5 * attn5 * extrapolate(xsb + 1, ysb + 1, zsb + 0, wsb + 0, dx5, dy5, dz5, dw5); + } + + //Contribution (1,0,1,0) + double dx6 = dx0 - 1 - 2 * m_squish4d; + double dy6 = dy0 - 0 - 2 * m_squish4d; + double dz6 = dz0 - 1 - 2 * m_squish4d; + double dw6 = dw0 - 0 - 2 * m_squish4d; + double attn6 = 2 - dx6 * dx6 - dy6 * dy6 - dz6 * dz6 - dw6 * dw6; + if (attn6 > 0) + { + attn6 *= attn6; + value += attn6 * attn6 * extrapolate(xsb + 1, ysb + 0, zsb + 1, wsb + 0, dx6, dy6, dz6, dw6); + } + + //Contribution (1,0,0,1) + double dx7 = dx0 - 1 - 2 * m_squish4d; + double dy7 = dy0 - 0 - 2 * m_squish4d; + double dz7 = dz0 - 0 - 2 * m_squish4d; + double dw7 = dw0 - 1 - 2 * m_squish4d; + double attn7 = 2 - dx7 * dx7 - dy7 * dy7 - dz7 * dz7 - dw7 * dw7; + if (attn7 > 0) + { + attn7 *= attn7; + value += attn7 * attn7 * extrapolate(xsb + 1, ysb + 0, zsb + 0, wsb + 1, dx7, dy7, dz7, dw7); + } + + //Contribution (0,1,1,0) + double dx8 = dx0 - 0 - 2 * m_squish4d; + double dy8 = dy0 - 1 - 2 * m_squish4d; + double dz8 = dz0 - 1 - 2 * m_squish4d; + double dw8 = dw0 - 0 - 2 * m_squish4d; + double attn8 = 2 - dx8 * dx8 - dy8 * dy8 - dz8 * dz8 - dw8 * dw8; + if (attn8 > 0) + { + attn8 *= attn8; + value += attn8 * attn8 * extrapolate(xsb + 0, ysb + 1, zsb + 1, wsb + 0, dx8, dy8, dz8, dw8); + } + + //Contribution (0,1,0,1) + double dx9 = dx0 - 0 - 2 * m_squish4d; + double dy9 = dy0 - 1 - 2 * m_squish4d; + double dz9 = dz0 - 0 - 2 * m_squish4d; + double dw9 = dw0 - 1 - 2 * m_squish4d; + double attn9 = 2 - dx9 * dx9 - dy9 * dy9 - dz9 * dz9 - dw9 * dw9; + if (attn9 > 0) + { + attn9 *= attn9; + value += attn9 * attn9 * extrapolate(xsb + 0, ysb + 1, zsb + 0, wsb + 1, dx9, dy9, dz9, dw9); + } + + //Contribution (0,0,1,1) + double dx10 = dx0 - 0 - 2 * m_squish4d; + double dy10 = dy0 - 0 - 2 * m_squish4d; + double dz10 = dz0 - 1 - 2 * m_squish4d; + double dw10 = dw0 - 1 - 2 * m_squish4d; + double attn10 = 2 - dx10 * dx10 - dy10 * dy10 - dz10 * dz10 - dw10 * dw10; + if (attn10 > 0) + { + attn10 *= attn10; + value += attn10 * attn10 * extrapolate(xsb + 0, ysb + 0, zsb + 1, wsb + 1, dx10, dy10, dz10, dw10); + } + } + else + { //We're inside the second dispentachoron (Rectified 4-Simplex) + double aScore; + char aPoint; + bool aIsBiggerSide = true; + double bScore; + char bPoint; + bool bIsBiggerSide = true; + + //Decide between (0,0,1,1) and (1,1,0,0) + if (xins + yins < zins + wins) + { + aScore = xins + yins; + aPoint = 0x0C; + } + else + { + aScore = zins + wins; + aPoint = 0x03; + } + + //Decide between (0,1,0,1) and (1,0,1,0) + if (xins + zins < yins + wins) + { + bScore = xins + zins; + bPoint = 0x0A; + } + else + { + bScore = yins + wins; + bPoint = 0x05; + } + + //Closer between (0,1,1,0) and (1,0,0,1) will replace the further of a and b, if closer. + if (xins + wins < yins + zins) + { + double score = xins + wins; + if (aScore <= bScore && score < bScore) + { + bScore = score; + bPoint = 0x06; + } + else if (aScore > bScore && score < aScore) + { + aScore = score; + aPoint = 0x06; + } + } + else + { + double score = yins + zins; + if (aScore <= bScore && score < bScore) + { + bScore = score; + bPoint = 0x09; + } + else if (aScore > bScore && score < aScore) + { + aScore = score; + aPoint = 0x09; + } + } + + //Decide if (0,1,1,1) is closer. + double p1 = 3 - inSum + xins; + if (aScore <= bScore && p1 < bScore) + { + bScore = p1; + bPoint = 0x0E; + bIsBiggerSide = false; + } + else if (aScore > bScore && p1 < aScore) + { + aScore = p1; + aPoint = 0x0E; + aIsBiggerSide = false; + } + + //Decide if (1,0,1,1) is closer. + double p2 = 3 - inSum + yins; + if (aScore <= bScore && p2 < bScore) + { + bScore = p2; + bPoint = 0x0D; + bIsBiggerSide = false; + } + else if (aScore > bScore && p2 < aScore) + { + aScore = p2; + aPoint = 0x0D; + aIsBiggerSide = false; + } + + //Decide if (1,1,0,1) is closer. + double p3 = 3 - inSum + zins; + if (aScore <= bScore && p3 < bScore) + { + bScore = p3; + bPoint = 0x0B; + bIsBiggerSide = false; + } + else if (aScore > bScore && p3 < aScore) + { + aScore = p3; + aPoint = 0x0B; + aIsBiggerSide = false; + } + + //Decide if (1,1,1,0) is closer. + double p4 = 3 - inSum + wins; + if (aScore <= bScore && p4 < bScore) + { + bScore = p4; + bPoint = 0x07; + bIsBiggerSide = false; + } + else if (aScore > bScore && p4 < aScore) + { + aScore = p4; + aPoint = 0x07; + aIsBiggerSide = false; + } + + //Where each of the two closest points are determines how the extra three vertices are calculated. + if (aIsBiggerSide == bIsBiggerSide) + { + if (aIsBiggerSide) + { //Both closest points on the bigger side + char c1 = static_cast(aPoint & bPoint); + char c2 = static_cast(aPoint | bPoint); + + //Two contributions are permutations of (0,0,0,1) and (0,0,0,2) based on c1 + xsv_ext0 = xsv_ext1 = xsb; + ysv_ext0 = ysv_ext1 = ysb; + zsv_ext0 = zsv_ext1 = zsb; + wsv_ext0 = wsv_ext1 = wsb; + dx_ext0 = dx0 - m_squish4d; + dy_ext0 = dy0 - m_squish4d; + dz_ext0 = dz0 - m_squish4d; + dw_ext0 = dw0 - m_squish4d; + dx_ext1 = dx0 - 2 * m_squish4d; + dy_ext1 = dy0 - 2 * m_squish4d; + dz_ext1 = dz0 - 2 * m_squish4d; + dw_ext1 = dw0 - 2 * m_squish4d; + if ((c1 & 0x01) != 0) + { + xsv_ext0 += 1; + dx_ext0 -= 1; + xsv_ext1 += 2; + dx_ext1 -= 2; + } + else if ((c1 & 0x02) != 0) + { + ysv_ext0 += 1; + dy_ext0 -= 1; + ysv_ext1 += 2; + dy_ext1 -= 2; + } + else if ((c1 & 0x04) != 0) + { + zsv_ext0 += 1; + dz_ext0 -= 1; + zsv_ext1 += 2; + dz_ext1 -= 2; + } + else + { + wsv_ext0 += 1; + dw_ext0 -= 1; + wsv_ext1 += 2; + dw_ext1 -= 2; + } + + //One contribution is a permutation of (1,1,1,-1) based on c2 + xsv_ext2 = xsb + 1; + ysv_ext2 = ysb + 1; + zsv_ext2 = zsb + 1; + wsv_ext2 = wsb + 1; + dx_ext2 = dx0 - 1 - 2 * m_squish4d; + dy_ext2 = dy0 - 1 - 2 * m_squish4d; + dz_ext2 = dz0 - 1 - 2 * m_squish4d; + dw_ext2 = dw0 - 1 - 2 * m_squish4d; + if ((c2 & 0x01) == 0) + { + xsv_ext2 -= 2; + dx_ext2 += 2; + } + else if ((c2 & 0x02) == 0) + { + ysv_ext2 -= 2; + dy_ext2 += 2; + } + else if ((c2 & 0x04) == 0) + { + zsv_ext2 -= 2; + dz_ext2 += 2; + } + else + { + wsv_ext2 -= 2; + dw_ext2 += 2; + } + } + else + { //Both closest points on the smaller side + //One of the two extra points is (1,1,1,1) + xsv_ext2 = xsb + 1; + ysv_ext2 = ysb + 1; + zsv_ext2 = zsb + 1; + wsv_ext2 = wsb + 1; + dx_ext2 = dx0 - 1 - 4 * m_squish4d; + dy_ext2 = dy0 - 1 - 4 * m_squish4d; + dz_ext2 = dz0 - 1 - 4 * m_squish4d; + dw_ext2 = dw0 - 1 - 4 * m_squish4d; + + //Other two points are based on the shared axes. + char c = static_cast(aPoint & bPoint); + + if ((c & 0x01) != 0) + { + xsv_ext0 = xsb + 2; + xsv_ext1 = xsb + 1; + dx_ext0 = dx0 - 2 - 3 * m_squish4d; + dx_ext1 = dx0 - 1 - 3 * m_squish4d; + } + else + { + xsv_ext0 = xsv_ext1 = xsb; + dx_ext0 = dx_ext1 = dx0 - 3 * m_squish4d; + } + + if ((c & 0x02) != 0) + { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy_ext1 = dy0 - 1 - 3 * m_squish4d; + if ((c & 0x01) == 0) + { + ysv_ext0 += 1; + dy_ext0 -= 1; + } + else + { + ysv_ext1 += 1; + dy_ext1 -= 1; + } + } + else + { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy_ext1 = dy0 - 3 * m_squish4d; + } + + if ((c & 0x04) != 0) + { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz_ext1 = dz0 - 1 - 3 * m_squish4d; + if ((c & 0x03) == 0) + { + zsv_ext0 += 1; + dz_ext0 -= 1; + } + else + { + zsv_ext1 += 1; + dz_ext1 -= 1; + } + } + else + { + zsv_ext0 = zsv_ext1 = zsb; + dz_ext0 = dz_ext1 = dz0 - 3 * m_squish4d; + } + + if ((c & 0x08) != 0) + { + wsv_ext0 = wsb + 1; + wsv_ext1 = wsb + 2; + dw_ext0 = dw0 - 1 - 3 * m_squish4d; + dw_ext1 = dw0 - 2 - 3 * m_squish4d; + } + else + { + wsv_ext0 = wsv_ext1 = wsb; + dw_ext0 = dw_ext1 = dw0 - 3 * m_squish4d; + } + } + } + else + { //One point on each "side" + char c1, c2; + if (aIsBiggerSide) + { + c1 = aPoint; + c2 = bPoint; + } + else + { + c1 = bPoint; + c2 = aPoint; + } + + //Two contributions are the bigger-sided point with each 1 replaced with 2. + if ((c1 & 0x01) != 0) + { + xsv_ext0 = xsb + 2; + xsv_ext1 = xsb + 1; + dx_ext0 = dx0 - 2 - 3 * m_squish4d; + dx_ext1 = dx0 - 1 - 3 * m_squish4d; + } + else + { + xsv_ext0 = xsv_ext1 = xsb; + dx_ext0 = dx_ext1 = dx0 - 3 * m_squish4d; + } + + if ((c1 & 0x02) != 0) + { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy_ext1 = dy0 - 1 - 3 * m_squish4d; + if ((c1 & 0x01) == 0) + { + ysv_ext0 += 1; + dy_ext0 -= 1; + } + else + { + ysv_ext1 += 1; + dy_ext1 -= 1; + } + } + else + { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy_ext1 = dy0 - 3 * m_squish4d; + } + + if ((c1 & 0x04) != 0) + { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz_ext1 = dz0 - 1 - 3 * m_squish4d; + if ((c1 & 0x03) == 0) + { + zsv_ext0 += 1; + dz_ext0 -= 1; + } + else + { + zsv_ext1 += 1; + dz_ext1 -= 1; + } + } + else + { + zsv_ext0 = zsv_ext1 = zsb; + dz_ext0 = dz_ext1 = dz0 - 3 * m_squish4d; + } + + if ((c1 & 0x08) != 0) + { + wsv_ext0 = wsb + 1; + wsv_ext1 = wsb + 2; + dw_ext0 = dw0 - 1 - 3 * m_squish4d; + dw_ext1 = dw0 - 2 - 3 * m_squish4d; + } + else + { + wsv_ext0 = wsv_ext1 = wsb; + dw_ext0 = dw_ext1 = dw0 - 3 * m_squish4d; + } + + //One contribution is a permutation of (1,1,1,-1) based on the smaller-sided point + xsv_ext2 = xsb + 1; + ysv_ext2 = ysb + 1; + zsv_ext2 = zsb + 1; + wsv_ext2 = wsb + 1; + dx_ext2 = dx0 - 1 - 2 * m_squish4d; + dy_ext2 = dy0 - 1 - 2 * m_squish4d; + dz_ext2 = dz0 - 1 - 2 * m_squish4d; + dw_ext2 = dw0 - 1 - 2 * m_squish4d; + if ((c2 & 0x01) == 0) + { + xsv_ext2 -= 2; + dx_ext2 += 2; + } + else if ((c2 & 0x02) == 0) + { + ysv_ext2 -= 2; + dy_ext2 += 2; + } + else if ((c2 & 0x04) == 0) + { + zsv_ext2 -= 2; + dz_ext2 += 2; + } + else + { + wsv_ext2 -= 2; + dw_ext2 += 2; + } + } + + //Contribution (1,1,1,0) + double dx4 = dx0 - 1 - 3 * m_squish4d; + double dy4 = dy0 - 1 - 3 * m_squish4d; + double dz4 = dz0 - 1 - 3 * m_squish4d; + double dw4 = dw0 - 3 * m_squish4d; + double attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4; + if (attn4 > 0) + { + attn4 *= attn4; + value += attn4 * attn4 * extrapolate(xsb + 1, ysb + 1, zsb + 1, wsb + 0, dx4, dy4, dz4, dw4); + } + + //Contribution (1,1,0,1) + double dx3 = dx4; + double dy3 = dy4; + double dz3 = dz0 - 3 * m_squish4d; + double dw3 = dw0 - 1 - 3 * m_squish4d; + double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3; + if (attn3 > 0) + { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate(xsb + 1, ysb + 1, zsb + 0, wsb + 1, dx3, dy3, dz3, dw3); + } + + //Contribution (1,0,1,1) + double dx2 = dx4; + double dy2 = dy0 - 3 * m_squish4d; + double dz2 = dz4; + double dw2 = dw3; + double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2; + if (attn2 > 0) + { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate(xsb + 1, ysb + 0, zsb + 1, wsb + 1, dx2, dy2, dz2, dw2); + } + + //Contribution (0,1,1,1) + double dx1 = dx0 - 3 * m_squish4d; + double dz1 = dz4; + double dy1 = dy4; + double dw1 = dw3; + double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1; + if (attn1 > 0) + { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate(xsb + 0, ysb + 1, zsb + 1, wsb + 1, dx1, dy1, dz1, dw1); + } + + //Contribution (1,1,0,0) + double dx5 = dx0 - 1 - 2 * m_squish4d; + double dy5 = dy0 - 1 - 2 * m_squish4d; + double dz5 = dz0 - 0 - 2 * m_squish4d; + double dw5 = dw0 - 0 - 2 * m_squish4d; + double attn5 = 2 - dx5 * dx5 - dy5 * dy5 - dz5 * dz5 - dw5 * dw5; + if (attn5 > 0) + { + attn5 *= attn5; + value += attn5 * attn5 * extrapolate(xsb + 1, ysb + 1, zsb + 0, wsb + 0, dx5, dy5, dz5, dw5); + } + + //Contribution (1,0,1,0) + double dx6 = dx0 - 1 - 2 * m_squish4d; + double dy6 = dy0 - 0 - 2 * m_squish4d; + double dz6 = dz0 - 1 - 2 * m_squish4d; + double dw6 = dw0 - 0 - 2 * m_squish4d; + double attn6 = 2 - dx6 * dx6 - dy6 * dy6 - dz6 * dz6 - dw6 * dw6; + if (attn6 > 0) + { + attn6 *= attn6; + value += attn6 * attn6 * extrapolate(xsb + 1, ysb + 0, zsb + 1, wsb + 0, dx6, dy6, dz6, dw6); + } + + //Contribution (1,0,0,1) + double dx7 = dx0 - 1 - 2 * m_squish4d; + double dy7 = dy0 - 0 - 2 * m_squish4d; + double dz7 = dz0 - 0 - 2 * m_squish4d; + double dw7 = dw0 - 1 - 2 * m_squish4d; + double attn7 = 2 - dx7 * dx7 - dy7 * dy7 - dz7 * dz7 - dw7 * dw7; + if (attn7 > 0) + { + attn7 *= attn7; + value += attn7 * attn7 * extrapolate(xsb + 1, ysb + 0, zsb + 0, wsb + 1, dx7, dy7, dz7, dw7); + } + + //Contribution (0,1,1,0) + double dx8 = dx0 - 0 - 2 * m_squish4d; + double dy8 = dy0 - 1 - 2 * m_squish4d; + double dz8 = dz0 - 1 - 2 * m_squish4d; + double dw8 = dw0 - 0 - 2 * m_squish4d; + double attn8 = 2 - dx8 * dx8 - dy8 * dy8 - dz8 * dz8 - dw8 * dw8; + if (attn8 > 0) + { + attn8 *= attn8; + value += attn8 * attn8 * extrapolate(xsb + 0, ysb + 1, zsb + 1, wsb + 0, dx8, dy8, dz8, dw8); + } + + //Contribution (0,1,0,1) + double dx9 = dx0 - 0 - 2 * m_squish4d; + double dy9 = dy0 - 1 - 2 * m_squish4d; + double dz9 = dz0 - 0 - 2 * m_squish4d; + double dw9 = dw0 - 1 - 2 * m_squish4d; + double attn9 = 2 - dx9 * dx9 - dy9 * dy9 - dz9 * dz9 - dw9 * dw9; + if (attn9 > 0) + { + attn9 *= attn9; + value += attn9 * attn9 * extrapolate(xsb + 0, ysb + 1, zsb + 0, wsb + 1, dx9, dy9, dz9, dw9); + } + + //Contribution (0,0,1,1) + double dx10 = dx0 - 0 - 2 * m_squish4d; + double dy10 = dy0 - 0 - 2 * m_squish4d; + double dz10 = dz0 - 1 - 2 * m_squish4d; + double dw10 = dw0 - 1 - 2 * m_squish4d; + double attn10 = 2 - dx10 * dx10 - dy10 * dy10 - dz10 * dz10 - dw10 * dw10; + if (attn10 > 0) + { + attn10 *= attn10; + value += attn10 * attn10 * extrapolate(xsb + 0, ysb + 0, zsb + 1, wsb + 1, dx10, dy10, dz10, dw10); + } + } + + //First extra vertex + double attn_ext0 = 2 - dx_ext0 * dx_ext0 - dy_ext0 * dy_ext0 - dz_ext0 * dz_ext0 - dw_ext0 * dw_ext0; + if (attn_ext0 > 0) + { + attn_ext0 *= attn_ext0; + value += attn_ext0 * attn_ext0 * extrapolate(xsv_ext0, ysv_ext0, zsv_ext0, wsv_ext0, dx_ext0, dy_ext0, dz_ext0, dw_ext0); + } + + //Second extra vertex + double attn_ext1 = 2 - dx_ext1 * dx_ext1 - dy_ext1 * dy_ext1 - dz_ext1 * dz_ext1 - dw_ext1 * dw_ext1; + if (attn_ext1 > 0) + { + attn_ext1 *= attn_ext1; + value += attn_ext1 * attn_ext1 * extrapolate(xsv_ext1, ysv_ext1, zsv_ext1, wsv_ext1, dx_ext1, dy_ext1, dz_ext1, dw_ext1); + } + + //Third extra vertex + double attn_ext2 = 2 - dx_ext2 * dx_ext2 - dy_ext2 * dy_ext2 - dz_ext2 * dz_ext2 - dw_ext2 * dw_ext2; + if (attn_ext2 > 0) + { + attn_ext2 *= attn_ext2; + value += attn_ext2 * attn_ext2 * extrapolate(xsv_ext2, ysv_ext2, zsv_ext2, wsv_ext2, dx_ext2, dy_ext2, dz_ext2, dw_ext2); + } + + return value / m_norm4d; + } + + double Noise::extrapolate(int xsb, int ysb, double dx, double dy) const + { + int index = m_perm[(m_perm[xsb & 0xFF] + ysb) & 0xFF] & 0x0E; + return m_gradients2d[index] * dx + + m_gradients2d[index + 1] * dy; + } + + double Noise::extrapolate(int xsb, int ysb, int zsb, double dx, double dy, double dz) const + { + int index = m_permGradIndex3d[(m_perm[(m_perm[xsb & 0xFF] + ysb) & 0xFF] + zsb) & 0xFF]; + return m_gradients3d[index] * dx + + m_gradients3d[index + 1] * dy + + m_gradients3d[index + 2] * dz; + } + + double Noise::extrapolate(int xsb, int ysb, int zsb, int wsb, double dx, double dy, double dz, double dw) const + { + int index = m_perm[(m_perm[(m_perm[(m_perm[xsb & 0xFF] + ysb) & 0xFF] + zsb) & 0xFF] + wsb) & 0xFF] & 0xFC; + return m_gradients4d[index] * dx + + m_gradients4d[index + 1] * dy + + m_gradients4d[index + 2] * dz + + m_gradients4d[index + 3] * dw; + } + +} \ No newline at end of file diff --git a/SQCSim-common/opensimplex.h b/SQCSim-common/opensimplex.h new file mode 100644 index 0000000..4b1b1f4 --- /dev/null +++ b/SQCSim-common/opensimplex.h @@ -0,0 +1,51 @@ +/** + Open Simple Noise for C++ + + Port to C++ from https://gist.github.com/KdotJPG/b1270127455a94ac5d19 + by Rickard Lundberg, 2019. +*/ +#ifndef _OPENSIMPLEX_H__ +#define _OPENSIMPLEX_H__ + +#include +#include + +namespace OpenSimplexNoise +{ + class Noise + { + public: + Noise(); + Noise(int64_t seed); + //2D Open Simplex Noise. + double eval(const double x, const double y) const; + //3D Open Simplex Noise. + double eval(double x, double y, double z) const; + //4D Open Simplex Noise. + double eval(double x, double y, double z, double w) const; + private: + const double m_stretch2d; + const double m_squish2d; + const double m_stretch3d; + const double m_squish3d; + const double m_stretch4d; + const double m_squish4d; + + const double m_norm2d; + const double m_norm3d; + const double m_norm4d; + + const long m_defaultSeed; + + std::array m_perm; + std::array m_permGradIndex3d; + std::array m_gradients2d; + std::array m_gradients3d; + std::array m_gradients4d; + double extrapolate(int xsb, int ysb, double dx, double dy) const; + double extrapolate(int xsb, int ysb, int zsb, double dx, double dy, double dz) const; + double extrapolate(int xsb, int ysb, int zsb, int wsb, double dx, double dy, double dz, double dw) const; + }; +} + +#endif // _OPENSIMPLEX_H__ \ No newline at end of file diff --git a/SQCSim-common/player.cpp b/SQCSim-common/player.cpp new file mode 100644 index 0000000..080fc2d --- /dev/null +++ b/SQCSim-common/player.cpp @@ -0,0 +1,160 @@ +#include "player.h" +#include "world.h" + +Player::Player(const Vector3f& position, float rotX, float rotY) : m_position(position), m_rotX(rotX), m_rotY(rotY) { + m_velocity = Vector3f(0, 0, 0); + m_airborne = true; +} + +void Player::TurnLeftRight(float value) { + m_rotY += value; + if (m_rotY > 360) m_rotY = 0; + else if (m_rotY < -360) m_rotY = 0; +} + +void Player::TurnTopBottom(float value) { + m_rotX += value; + if (m_rotX > 80) m_rotX = 80; + else if (m_rotX < -80) m_rotX = -80; +} + +Vector3f Player::GetInput(bool front, bool back, bool left, bool right, bool jump, bool shoot, float elapsedTime) { + + Vector3f delta = Vector3f(0, 0, 0); + + float yrotrad = (m_rotY / 57.2957795056f); // 180/Pi = 57.295... + float xrotrad = (m_rotX / 57.2957795056f); + + m_direction = Vector3f(cos(xrotrad) * sin(yrotrad), + -sin(xrotrad), + cos(xrotrad) * -cos(yrotrad)); + + m_direction.Normalize(); + + if (front) { + delta.x += float(sin(yrotrad)) * elapsedTime * 10.f; + delta.z += float(-cos(yrotrad)) * elapsedTime * 10.f; + } + else if (back) { + delta.x += float(-sin(yrotrad)) * elapsedTime * 10.f; + delta.z += float(cos(yrotrad)) * elapsedTime * 10.f; + } + + if (left) { + delta.x += float(-cos(yrotrad)) * elapsedTime * 10.f; + delta.z += float(-sin(yrotrad)) * elapsedTime * 10.f; + } + else if (right) { + delta.x += float(cos(yrotrad)) * elapsedTime * 10.f; + delta.z += float(sin(yrotrad)) * elapsedTime * 10.f; + } + + delta.Normalize(); + delta.x *= .6f; + delta.z *= .6f; + + if ((jump || shoot ) && !m_airborne) { + delta.y += jump? .32f: shoot? .1f : 0.f; + m_airborne = true; + } + + if (shoot) // Recoil! + TurnTopBottom(-1); + + return delta; +} + +void Player::ApplyPhysics(Vector3f input, World* world, float elapsedTime) { + static float timing = 0.f; + /* Gestion de collisions */ + BlockType bt1, bt2, bt3; + + bt1 = world->BlockAt(GetPosition().x, GetPosition().y + input.y, GetPosition().z); + bt2 = world->BlockAt(GetPosition().x, GetPosition().y + input.y - 0.9f, GetPosition().z); + bt3 = world->BlockAt(GetPosition().x, GetPosition().y + input.y - 1.7f, GetPosition().z); + if ((bt1 != BTYPE_AIR || bt2 != BTYPE_AIR || bt3 != BTYPE_AIR) && m_position.y < 129.7f) { + bt1 = world->BlockAt(GetPosition().x, GetPosition().y + .3f, GetPosition().z); + if (bt1 == BTYPE_AIR) m_position.y = (int)m_position.y + .7f; + m_velocity.y = input.y = 0; + m_airborne = false; + } + else { + if (abs(m_velocity.y) < 1.1f) m_velocity.y += input.y - 1.1f * elapsedTime; + bt3 = world->BlockAt(GetPosition().x, GetPosition().y + m_velocity.y - 1.7f, GetPosition().z); + bt1 = world->BlockAt(GetPosition().x, GetPosition().y + .3f, GetPosition().z); + if (bt3 != BTYPE_AIR) { + m_velocity.y = 0; + if (timing == 0.f) { + timing = .3f; + } + m_airborne = false; + } + else if (bt1 != BTYPE_AIR) { + m_velocity.y = -.1f; + } + else m_airborne = true; + } + + if (timing > 0.f) timing -= elapsedTime; + if (timing < 0.f) timing = 0.f; + + bt1 = world->BlockAt(GetPosition().x + input.x, GetPosition().y, GetPosition().z); + bt2 = world->BlockAt(GetPosition().x + input.x, GetPosition().y - 0.9f, GetPosition().z); + bt3 = world->BlockAt(GetPosition().x + input.x, GetPosition().y - 1.7f, GetPosition().z); + if (bt1 != BTYPE_AIR || bt2 != BTYPE_AIR || bt3 != BTYPE_AIR) { + input.x = m_velocity.x = 0; + m_velocity.z *= .5f; + } + + bt1 = world->BlockAt(GetPosition().x, GetPosition().y, GetPosition().z + input.z); + bt2 = world->BlockAt(GetPosition().x, GetPosition().y - 0.9f, GetPosition().z + input.z); + bt3 = world->BlockAt(GetPosition().x, GetPosition().y - 1.7f, GetPosition().z + input.z); + if (bt1 != BTYPE_AIR || bt2 != BTYPE_AIR || bt3 != BTYPE_AIR) { + input.z = m_velocity.z = 0; + m_velocity.x *= .5f; + } + + /* Fin gestion de collisions */ + /* Gestion de la friction */ + + if (!m_airborne) { + m_velocity.x += input.x * 2.f * elapsedTime; + m_velocity.z += input.z * 2.f * elapsedTime; + + if (input.x == 0.f) + m_velocity.x *= .8f; + + if (input.z == 0.f) + m_velocity.z *= .8f; + } + else { + m_velocity.x += input.x * .4f * elapsedTime; // Techniquement contre les lois de la physique, mais c'est beaucoup moins chiant pour grimper sur les blocs. + m_velocity.z += input.z * .4f * elapsedTime; + m_velocity.x *= .99f; + m_velocity.z *= .99f; + } + + /* Fin gestion de la friction */ + + float vy = m_velocity.y; + m_velocity.y = 1.f; // Padding pour limiter le x et z lors du Normalize(). + if (m_velocity.Length() >= 1.f) m_velocity.Normalize(); // Limiteur de vitesse en x/z. + m_velocity.y = 0; + if (m_velocity.Length() < .005f) m_velocity.Zero(); // Threshold en x/z. + m_velocity.y = vy; + + m_position += m_velocity; +} + +Vector3f Player::GetPosition() const { return Vector3f(m_position.x + CHUNK_SIZE_X * WORLD_SIZE_X / 2, m_position.y, m_position.z + CHUNK_SIZE_Z * WORLD_SIZE_Y / 2); } + +Vector3f Player::GetVelocity() const { return m_velocity; } + +Vector3f Player::GetPOV() const { return Vector3f(GetPosition().x, m_POV, GetPosition().z); } + +Vector3f Player::GetDirection() const { return m_direction; } + +void Player::Teleport(int& x, int& z) { + m_position.x -= x * CHUNK_SIZE_X; + m_position.z -= z * CHUNK_SIZE_Z; +} diff --git a/SQCSim-common/player.h b/SQCSim-common/player.h new file mode 100644 index 0000000..f889b68 --- /dev/null +++ b/SQCSim-common/player.h @@ -0,0 +1,34 @@ +#ifndef _PLAYER_H__ +#define _PLAYER_H__ +#include "vector3.h" +#include + +class World; + +class Player { +public: + Player(const Vector3f& position, float rotX = 0, float rotY = 0); + void TurnLeftRight(float value); + void TurnTopBottom(float value); + Vector3f GetInput(bool front, bool back, bool left, bool right, bool jump, bool dash, float elapsedTime); + void ApplyPhysics(Vector3f input, World* world, float elapsedTime); + + Vector3f GetPosition() const; + Vector3f GetDirection() const; + Vector3f GetVelocity() const; + Vector3f GetPOV() const; + void Teleport(int& x, int& z); + +protected: + Vector3f m_position; + Vector3f m_velocity; + Vector3f m_direction; + + float m_rotX = 0; + float m_rotY = 0; + float m_POV; + + bool m_airborne; +}; +#endif //_PLAYER_H__ + diff --git a/SQCSim-common/vector3.h b/SQCSim-common/vector3.h new file mode 100644 index 0000000..b191681 --- /dev/null +++ b/SQCSim-common/vector3.h @@ -0,0 +1,219 @@ +#ifndef VECTOR3_H__ +#define VECTOR3_H__ + +#include +#include + +template +class Vector3 +{ +public: + Vector3(); + Vector3(const T& x, const T& y, const T& z); + ~Vector3(); + + T Length() const; + void Normalize(); + void Zero(); + + T Dot(const Vector3& v) const; + Vector3 Cross(const Vector3& v) const; + + Vector3 operator+(const Vector3& v) const; + Vector3 operator-(const Vector3& v) const; + Vector3 operator-() const; + Vector3 operator+(const T& v) const; + Vector3 operator-(const T& v) const; + Vector3 operator/(const T& v) const; + Vector3 operator*(const T& v) const; + + Vector3& operator=(const Vector3& v); + + Vector3& operator+=(const Vector3& v); + Vector3& operator-=(const Vector3& v); + Vector3& operator+=(const T& v); + Vector3& operator-=(const T& v); + Vector3& operator/=(const T& v); + Vector3& operator*=(const T& v); + + bool operator==(const Vector3& v) const; + bool operator!=(const Vector3& v) const; + + void Afficher() const; + +public: + T x, y, z; +}; + +typedef Vector3 Vector3i; +typedef Vector3 Vector3f; + +template +inline std::ostream& operator<<(std::ostream& out, const Vector3& v) +{ + out << "[" << v.x << ", " << v.y << ", " << v.z << "]"; + return out; +} + + +template +Vector3::Vector3() +{ +} + +template +Vector3::Vector3(const T& x, const T& y, const T& z) : x(x), y(y), z(z) +{ +} + +template +Vector3::~Vector3() +{ +} + +template +T Vector3::Length() const +{ + return sqrt(x*x + y*y + z*z); +} + +template +void Vector3::Normalize() +{ + T len = Length(); + if (len != 0) + { + x /= len; + y /= len; + z /= len; + } +} + +template +void Vector3::Zero() +{ + x = y = z = 0; +} + +template +T Vector3::Dot(const Vector3& v) const +{ + return (x * v.x) + (y * v.y) + (z * v.z); +} + +template +Vector3 Vector3::Cross(const Vector3& v) const +{ + return Vector3( + y * v.z - v.y * z, + z * v.x - v.z * x, + x * v.y - v.x * y); +} + +template +Vector3 Vector3::operator+(const Vector3& v) const +{ + return Vector3(x + v.x, y + v.y, z + v.z); +} + +template +Vector3 Vector3::operator-(const Vector3& v) const +{ + return Vector3(x - v.x, y - v.y, z - v.z); +} + +template +Vector3 Vector3::operator-() const +{ + return Vector3(-x, -y, -z); +} + +template +Vector3 Vector3::operator+(const T& v) const +{ + return Vector3(x + v, y + v, z + v); +} + +template +Vector3 Vector3::operator-(const T& v) const +{ + return Vector3(x - v, y - v, z - v); +} + +template +Vector3 Vector3::operator/(const T& v) const +{ + return Vector3(x / v, y / v, z / v); +} + +template +Vector3 Vector3::operator*(const T& v) const +{ + return Vector3(x * v, y * v, z * v); +} + +template +Vector3& Vector3::operator=(const Vector3& v) +{ + x = v.x; + y = v.y; + z = v.z; + return *this; +} + +template +Vector3& Vector3::operator+=(const Vector3& v) +{ + return (*this = *this + v); +} + +template +Vector3& Vector3::operator-=(const Vector3& v) +{ + return (*this = *this - v); +} + +template +Vector3& Vector3::operator+=(const T& v) +{ + return (*this = *this + v); +} + +template +Vector3& Vector3::operator-=(const T& v) +{ + return (*this = *this - v); +} + +template +Vector3& Vector3::operator/=(const T& v) +{ + return (*this = *this / v); +} + +template +Vector3& Vector3::operator*=(const T& v) +{ + return (*this = *this * v); +} + +template +bool Vector3::operator==(const Vector3& v) const +{ + return (x == v.x && y == v.y && z == v.z); +} + +template +bool Vector3::operator!=(const Vector3& v) const +{ + return !(*this == v); +} + +template +void Vector3::Afficher() const +{ + std::cout << "[" << x << ", " << y << ", " << z << "]" << std::endl; +} + + +#endif // VECTOR3_H__ diff --git a/SQCSim-common/world.cpp b/SQCSim-common/world.cpp new file mode 100644 index 0000000..6b4ccf6 --- /dev/null +++ b/SQCSim-common/world.cpp @@ -0,0 +1,109 @@ +#include "world.h" + +World::World() {} + +World::~World() {} + +Array2d& World::GetChunks() { return m_chunks; } + +Chunk* World::ChunkAt(float x, float y, float z) const { + int cx = (int)x / CHUNK_SIZE_X; + int cz = (int)z / CHUNK_SIZE_Z; + + if (x < 0 || y < 0 || z < 0 || + x >= WORLD_SIZE_X * CHUNK_SIZE_X || + z >= CHUNK_SIZE_Z * WORLD_SIZE_Y || + y > CHUNK_SIZE_Y) + return 0; + + return m_chunks.Get(cx, cz); +} + +Chunk* World::ChunkAt(const Vector3f& pos) const { return ChunkAt(pos.x, pos.y, pos.z); } + +BlockType World::BlockAt(float x, float y, float z, BlockType defaultBlockType) const { + Chunk* c = ChunkAt(x, y, z); + + if (!c) + return defaultBlockType; + + int bx = (int)x % CHUNK_SIZE_X; + int by = (int)y % CHUNK_SIZE_Y; + int bz = (int)z % CHUNK_SIZE_Z; + + return c->GetBlock(bx, by, bz); +} + +BlockType World::BlockAt(const Vector3f& pos, BlockType defaultBlockType) const { + return BlockAt(pos.x, pos.y, pos.z, defaultBlockType); +} + +void World::GetScope(unsigned int& x, unsigned int& y) { + x = m_center[0]; + y = m_center[1]; +} + +void World::ChangeBlockAtPosition(BlockType blockType, Vector3f pos) { + int bx = (int)pos.x % CHUNK_SIZE_X; + int by = (int)pos.y % CHUNK_SIZE_Y; + int bz = (int)pos.z % CHUNK_SIZE_Z; + + ChunkAt(pos)->SetBlock(bx, by, bz, blockType, this); +} + +void World::ChangeBlockAtCursor(BlockType blockType, Player& player, bool& block) { + Vector3f currentPos = player.GetPosition(); + Vector3f currentBlock = currentPos; + Vector3f ray = player.GetDirection(); + bool found = false; + + if (block) return; + + while ((currentPos - currentBlock).Length() <= MAX_SELECTION_DISTANCE && !found) { + currentBlock += ray / 10.f; + + BlockType bt = BlockAt(currentBlock); + + if (bt != BTYPE_AIR) + found = true; + } + + if (found) + if (blockType != BTYPE_AIR) { + found = false; + while ((currentPos - currentBlock).Length() >= 1.7f && !found) { + currentBlock -= ray / 10.f; + + BlockType bt = BlockAt(currentBlock); + + if (bt == BTYPE_AIR) { // V?rification pour ?tre s?r que le bloc ? changer n'est pas dans le joueur. + int Bx = (int)currentBlock.x; + int By = (int)currentBlock.y; + int Bz = (int)currentBlock.z; + + int Px = (int)currentPos.x; + int PyA = (int)currentPos.y; + int PyB = (int)(currentPos.y - .9f); + int PyC = (int)(currentPos.y - 1.7f); + int Pz = (int)currentPos.z; + + if (!(Bx == Px && + (By == PyA || + By == PyB || + By == PyC) && + Bz == Pz)) + found = true; + } + } + } + + if (found && (int)currentBlock.y < CHUNK_SIZE_Y) { + int bx = (int)currentBlock.x % CHUNK_SIZE_X; + int by = (int)currentBlock.y % CHUNK_SIZE_Y; + int bz = (int)currentBlock.z % CHUNK_SIZE_Z; + + ChunkAt(currentBlock)->SetBlock(bx, by, bz, blockType, this); + ChunkAt(currentBlock)->MakeModified(); + block = true; + } +} diff --git a/SQCSim-common/world.h b/SQCSim-common/world.h new file mode 100644 index 0000000..dd9967a --- /dev/null +++ b/SQCSim-common/world.h @@ -0,0 +1,45 @@ +#ifndef WORLD_H__ +#define WORLD_H__ +#include +#include +#include +#include +#include +#include "define.h" +#include "chunk.h" +#include "array2d.h" +#include "vector3.h" +#include "player.h" +#include "bullet.h" + +class Chunk; +class Player; +class Bullet; + +class World { +public: + World(); + ~World(); + + Array2d& GetChunks(); + + Chunk* ChunkAt(float x, float y, float z) const; + Chunk* ChunkAt(const Vector3f& pos) const; + + BlockType BlockAt(float x, float y, float z, BlockType defaultBlockType = BTYPE_AIR) const; + BlockType BlockAt(const Vector3f& pos, BlockType defaultBlockType = BTYPE_AIR) const; + + void GetScope(unsigned int& x, unsigned int& y); + + void ChangeBlockAtCursor(BlockType blockType, Player& player, bool& block); + void ChangeBlockAtPosition(BlockType blockType, Vector3f pos); + +private: + Array2d m_chunks = Array2d(WORLD_SIZE_X, WORLD_SIZE_Y); + std::vector m_tbDeleted; + + unsigned int m_center[2] = { UINT16_MAX / 2 - WORLD_SIZE_X, UINT16_MAX / 2 - WORLD_SIZE_Y }; + +}; +#endif // WORLD_H__ + diff --git a/SQCSim-srv/SQCSim-srv.vcxproj b/SQCSim-srv/SQCSim-srv.vcxproj new file mode 100644 index 0000000..ae7b24f --- /dev/null +++ b/SQCSim-srv/SQCSim-srv.vcxproj @@ -0,0 +1,138 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + 17.0 + Win32Proj + {d3bb3300-25f0-42bf-a934-fac33b5c7d25} + SQCSimsrv + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + diff --git a/SQCSim-srv/SQCSim-srv.vcxproj.filters b/SQCSim-srv/SQCSim-srv.vcxproj.filters new file mode 100644 index 0000000..1e51e38 --- /dev/null +++ b/SQCSim-srv/SQCSim-srv.vcxproj.filters @@ -0,0 +1,17 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + \ No newline at end of file diff --git a/SQCSim2021/SQCSim2021.vcxproj b/SQCSim2021/SQCSim2021.vcxproj index 5cda6d0..dfed398 100644 --- a/SQCSim2021/SQCSim2021.vcxproj +++ b/SQCSim2021/SQCSim2021.vcxproj @@ -112,8 +112,8 @@ true - external\irrKlang-1.6.0\include;external\glew170\include;external\devil178\include;external\sfml23\include;$(IncludePath) - external\glew170\lib;external\devil178\lib;external\sfml23\lib;$(LibraryPath);external\irrKlang-1.6.0\lib\Win32-visualStudio + external\irrKlang-1.6.0\include;external\glew210\include;external\devil178\include;external\sfml251\include;$(IncludePath) + external\glew210\lib;external\sfml251\lib;external\devil178\lib;external\irrKlang-1.6.0\lib\Win32-visualStudio;$(LibraryPath) true @@ -122,8 +122,8 @@ false - external\irrKlang-1.6.0\include;external\sfml23\include;external\devil178\include;external\glew170\include;$(IncludePath) - external\sfml23\lib;external\devil178\lib;external\glew170\lib;$(LibraryPath);external\irrKlang-1.6.0\lib\Win32-visualStudio + external\devil180\include;external\irrKlang-1.6.0\include;external\sfml251\include;external\glew210\include;$(IncludePath) + external\sfml251\lib;external\devil180\lib\x86\Release;external\glew210\lib\Release\Win32;$(LibraryPath);external\irrKlang-1.6.0\lib\Win32-visualStudio false diff --git a/SQCSim2021/define.h b/SQCSim2021/define.h index 6104552..6fc950f 100644 --- a/SQCSim2021/define.h +++ b/SQCSim2021/define.h @@ -12,9 +12,9 @@ #include #endif -#define CHUNK_SIZE_X 16 -#define CHUNK_SIZE_Y 128 -#define CHUNK_SIZE_Z 16 +#define CHUNK_SIZE_X 4 +#define CHUNK_SIZE_Y 64 +#define CHUNK_SIZE_Z 4 #define MAX_SELECTION_DISTANCE 5 #define SEED 12345 @@ -36,18 +36,18 @@ #endif #ifdef NDEBUG -#define WORLD_SIZE_X 16 -#define WORLD_SIZE_Y 16 +#define WORLD_SIZE_X 64 +#define WORLD_SIZE_Y 64 #define FRAMES_RENDER_CHUNKS 1 #define FRAMES_UPDATE_CHUNKS 1 #define FRAMES_DELETE_CHUNKS 1 -#define THREADS_GENERATE_CHUNKS 12 -#define THREADS_UPDATE_CHUNKS 5 +#define THREADS_GENERATE_CHUNKS 6 +#define THREADS_UPDATE_CHUNKS 3 #define THREADS_DELETE_CHUNKS 2 -#define VIEW_DISTANCE 1024 +#define VIEW_DISTANCE 512 #define TEXTURE_SIZE 512 #define MAX_BULLETS 512 #endif @@ -56,11 +56,6 @@ typedef uint8_t BlockType; enum BLOCK_TYPE { BTYPE_AIR, BTYPE_DIRT, BTYPE_GRASS, BTYPE_METAL, BTYPE_ICE, BTYPE_LAST }; -//#define TEXTURE_PATH "../SQCSim2021/media/textures/" -//#define SHADER_PATH "../SQCSim2021/media/shaders/" -//#define AUDIO_PATH "../SQCSim2021/media/audio/" -//#define CHUNK_PATH "../SQCSim2021/media/chunks/" - #define TEXTURE_PATH "./media/textures/" #define SHADER_PATH "./media/shaders/" #define AUDIO_PATH "./media/audio/" diff --git a/SQCSim2021/world.cpp b/SQCSim2021/world.cpp index 57b2bbe..bbd86c0 100644 --- a/SQCSim2021/world.cpp +++ b/SQCSim2021/world.cpp @@ -223,7 +223,7 @@ void World::RenderWorld(int& rendercount, Player& player, Transformation& world, direct.Normalize(); pos.y = 1; - static Vector3 renderManifest[VIEW_DISTANCE * 4]; // Nombre de Chunks maximal à être rendus. + static Vector3 renderManifest[VIEW_DISTANCE * 8]; // Nombre de Chunks maximal à être rendus. //for (int dist = VIEW_DISTANCE; dist >= 0; dist -= CHUNK_SIZE_X) { for (int dist = 0; dist <= VIEW_DISTANCE; dist += CHUNK_SIZE_X) { @@ -231,26 +231,26 @@ void World::RenderWorld(int& rendercount, Player& player, Transformation& world, float sinus, cosinus; int echantillons; - if (dist > VIEW_DISTANCE * .5f) { + if (dist > VIEW_DISTANCE * .1f) { sinus = .00872653549f; // sin(1/2 degré) cosinus = .99996192306; // cos(1/2 degré) echantillons = 180; } - else if (dist > VIEW_DISTANCE * .4f) { - sinus = .01151891831f; // sin(2/3 degré) - cosinus = .99993365506; // cos(2/3 degré) - echantillons = 120; - } - else if (dist > VIEW_DISTANCE * .3f) { - sinus = .01745240643; // sin(1 degré) - cosinus = .99984769515; // cos(1 degré) - echantillons = 90; - } - else if (dist > VIEW_DISTANCE * .2f) { - sinus = .0261769483; - cosinus = .99965732497; - echantillons = 60; - } + //else if (dist > VIEW_DISTANCE * .3f) { + // sinus = .01151891831f; // sin(2/3 degré) + // cosinus = .99993365506; // cos(2/3 degré) + // echantillons = 120; + //} + //else if (dist > VIEW_DISTANCE * .2f) { + // sinus = .01745240643; // sin(1 degré) + // cosinus = .99984769515; // cos(1 degré) + // echantillons = 90; + //} + //else if (dist > VIEW_DISTANCE * .1f) { + // sinus = .0261769483; + // cosinus = .99965732497; + // echantillons = 60; + //} else { sinus = .0348994967; cosinus = .99939082701; @@ -293,7 +293,7 @@ void World::RenderWorld(int& rendercount, Player& player, Transformation& world, world.ApplyTranslation(chx, 0, chy); world.Use(); - float blcolor = renderManifest[index].y / (VIEW_DISTANCE * 1000.f); + float blcolor = renderManifest[index].y / (VIEW_DISTANCE / 50.f); glBlendColor(blcolor, blcolor, blcolor, 1.f); ChunkAt(chx, 1, chy)->Render(); world.ApplyTranslation(-chx, 0, -chy); diff --git a/SQCSim2023.sln b/SQCSim2023.sln index 7f8c39d..640b854 100644 --- a/SQCSim2023.sln +++ b/SQCSim2023.sln @@ -1,10 +1,14 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31702.278 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.34031.279 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SQCSim2021", "SQCSim2021\SQCSim2021.vcxproj", "{A21FD938-1FEA-4687-AB86-0EABAC30877B}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SQCSim-common", "SQCSim-common\SQCSim-common.vcxproj", "{EE91AB12-4225-4A4D-931D-69D72F6D91FB}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SQCSim-srv", "SQCSim-srv\SQCSim-srv.vcxproj", "{D3BB3300-25F0-42BF-A934-FAC33B5C7D25}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -21,6 +25,22 @@ Global {A21FD938-1FEA-4687-AB86-0EABAC30877B}.Release|x64.Build.0 = Release|x64 {A21FD938-1FEA-4687-AB86-0EABAC30877B}.Release|x86.ActiveCfg = Release|Win32 {A21FD938-1FEA-4687-AB86-0EABAC30877B}.Release|x86.Build.0 = Release|Win32 + {EE91AB12-4225-4A4D-931D-69D72F6D91FB}.Debug|x64.ActiveCfg = Debug|x64 + {EE91AB12-4225-4A4D-931D-69D72F6D91FB}.Debug|x64.Build.0 = Debug|x64 + {EE91AB12-4225-4A4D-931D-69D72F6D91FB}.Debug|x86.ActiveCfg = Debug|Win32 + {EE91AB12-4225-4A4D-931D-69D72F6D91FB}.Debug|x86.Build.0 = Debug|Win32 + {EE91AB12-4225-4A4D-931D-69D72F6D91FB}.Release|x64.ActiveCfg = Release|x64 + {EE91AB12-4225-4A4D-931D-69D72F6D91FB}.Release|x64.Build.0 = Release|x64 + {EE91AB12-4225-4A4D-931D-69D72F6D91FB}.Release|x86.ActiveCfg = Release|Win32 + {EE91AB12-4225-4A4D-931D-69D72F6D91FB}.Release|x86.Build.0 = Release|Win32 + {D3BB3300-25F0-42BF-A934-FAC33B5C7D25}.Debug|x64.ActiveCfg = Debug|x64 + {D3BB3300-25F0-42BF-A934-FAC33B5C7D25}.Debug|x64.Build.0 = Debug|x64 + {D3BB3300-25F0-42BF-A934-FAC33B5C7D25}.Debug|x86.ActiveCfg = Debug|Win32 + {D3BB3300-25F0-42BF-A934-FAC33B5C7D25}.Debug|x86.Build.0 = Debug|Win32 + {D3BB3300-25F0-42BF-A934-FAC33B5C7D25}.Release|x64.ActiveCfg = Release|x64 + {D3BB3300-25F0-42BF-A934-FAC33B5C7D25}.Release|x64.Build.0 = Release|x64 + {D3BB3300-25F0-42BF-A934-FAC33B5C7D25}.Release|x86.ActiveCfg = Release|Win32 + {D3BB3300-25F0-42BF-A934-FAC33B5C7D25}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 51b0795c62427dddc6b8c8ba50c35b893624d672 Mon Sep 17 00:00:00 2001 From: MarcEricMartel Date: Sun, 24 Sep 2023 08:45:40 -0400 Subject: [PATCH 02/19] =?UTF-8?q?D=C3=A9but=20sockets.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SQCSim-common/define.h | 3 + SQCSim-common/player.cpp | 20 ++- SQCSim-srv/SQCSim-srv.vcxproj | 50 +++--- SQCSim-srv/SQCSim-srv.vcxproj.filters | 22 +++ SQCSim-srv/connection.cpp | 31 ++++ SQCSim-srv/connection.h | 42 +++++ SQCSim-srv/define.h | 38 +++++ SQCSim-srv/main.cpp | 10 ++ SQCSim-srv/server.cpp | 224 ++++++++++++++++++++++++++ SQCSim-srv/server.h | 34 ++++ SQCSim2021/define.h | 26 +-- 11 files changed, 455 insertions(+), 45 deletions(-) create mode 100644 SQCSim-srv/connection.cpp create mode 100644 SQCSim-srv/connection.h create mode 100644 SQCSim-srv/define.h create mode 100644 SQCSim-srv/main.cpp create mode 100644 SQCSim-srv/server.cpp create mode 100644 SQCSim-srv/server.h diff --git a/SQCSim-common/define.h b/SQCSim-common/define.h index aea5ea2..3501dd6 100644 --- a/SQCSim-common/define.h +++ b/SQCSim-common/define.h @@ -9,6 +9,9 @@ #define MAX_SELECTION_DISTANCE 5 #define SEED 12345 +#define SRV_PORT 1025 +#define CLI_PORT 1026 + #ifdef _DEBUG #define WORLD_SIZE_X 64 #define WORLD_SIZE_Y 64 diff --git a/SQCSim-common/player.cpp b/SQCSim-common/player.cpp index 080fc2d..b42968a 100644 --- a/SQCSim-common/player.cpp +++ b/SQCSim-common/player.cpp @@ -101,7 +101,15 @@ void Player::ApplyPhysics(Vector3f input, World* world, float elapsedTime) { bt1 = world->BlockAt(GetPosition().x + input.x, GetPosition().y, GetPosition().z); bt2 = world->BlockAt(GetPosition().x + input.x, GetPosition().y - 0.9f, GetPosition().z); bt3 = world->BlockAt(GetPosition().x + input.x, GetPosition().y - 1.7f, GetPosition().z); - if (bt1 != BTYPE_AIR || bt2 != BTYPE_AIR || bt3 != BTYPE_AIR) { + if (bt1 == BTYPE_AIR && bt2 != BTYPE_AIR && bt3 != BTYPE_AIR) { + if (input.x > 0) + input.x = m_velocity.x = 0.5f; + else + input.x = m_velocity.x = -0.5f; + m_velocity.y = 0.3; + m_velocity.z *= .5f; + } + else if (bt1 != BTYPE_AIR || bt2 != BTYPE_AIR || bt3 != BTYPE_AIR) { input.x = m_velocity.x = 0; m_velocity.z *= .5f; } @@ -109,7 +117,15 @@ void Player::ApplyPhysics(Vector3f input, World* world, float elapsedTime) { bt1 = world->BlockAt(GetPosition().x, GetPosition().y, GetPosition().z + input.z); bt2 = world->BlockAt(GetPosition().x, GetPosition().y - 0.9f, GetPosition().z + input.z); bt3 = world->BlockAt(GetPosition().x, GetPosition().y - 1.7f, GetPosition().z + input.z); - if (bt1 != BTYPE_AIR || bt2 != BTYPE_AIR || bt3 != BTYPE_AIR) { + if (bt1 == BTYPE_AIR && bt2 != BTYPE_AIR && bt3 != BTYPE_AIR) { + if (input.z > 0) + input.z = m_velocity.z = 0.5f; + else + input.z = m_velocity.z = -0.5f; + m_velocity.y = 0.3; + m_velocity.x *= .5f; + } + else if (bt1 != BTYPE_AIR || bt2 != BTYPE_AIR || bt3 != BTYPE_AIR) { input.z = m_velocity.z = 0; m_velocity.x *= .5f; } diff --git a/SQCSim-srv/SQCSim-srv.vcxproj b/SQCSim-srv/SQCSim-srv.vcxproj index ae7b24f..525fc03 100644 --- a/SQCSim-srv/SQCSim-srv.vcxproj +++ b/SQCSim-srv/SQCSim-srv.vcxproj @@ -17,7 +17,6 @@ Release x64 - 17.0 @@ -53,27 +52,24 @@ true Unicode - - + + + + + + + + + + + + + - - - - - - - - - - - - - - Level3 @@ -94,6 +90,7 @@ true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true + Default Console @@ -130,9 +127,22 @@ true - - + + + + + + + + + + + + + {ee91ab12-4225-4a4d-931d-69d72f6d91fb} + + - + \ No newline at end of file diff --git a/SQCSim-srv/SQCSim-srv.vcxproj.filters b/SQCSim-srv/SQCSim-srv.vcxproj.filters index 1e51e38..67c9660 100644 --- a/SQCSim-srv/SQCSim-srv.vcxproj.filters +++ b/SQCSim-srv/SQCSim-srv.vcxproj.filters @@ -14,4 +14,26 @@ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + + + Fichiers d%27en-tête + + + Fichiers d%27en-tête + + + Fichiers d%27en-tête + + \ No newline at end of file diff --git a/SQCSim-srv/connection.cpp b/SQCSim-srv/connection.cpp new file mode 100644 index 0000000..964cc07 --- /dev/null +++ b/SQCSim-srv/connection.cpp @@ -0,0 +1,31 @@ +#include "connection.h" + +Connection::Connection(in_addr addr, + std::string name, + UINT64 hash, + UINT64 self_hash, + UINT64 team_hash): + m_addr(addr), + m_hash(hash), + m_shash(self_hash), + m_thash(team_hash), + m_name(name) { + +} + +Connection::~Connection() { + +} + +in_addr Connection::GetAddr() const { return m_addr; } + +UINT64 Connection::GetHash(bool self) const { return self? m_shash: m_hash; } + +UINT64 Connection::GetTeamHash() const { return m_thash; } + +std::string Connection::GetName() const { return m_name; } + +void Connection::Clean(std::chrono::system_clock::time_point time) { + while (m_input_manifest.front().timestamp < time || !m_input_manifest.empty()) + m_input_manifest.pop_front(); +} diff --git a/SQCSim-srv/connection.h b/SQCSim-srv/connection.h new file mode 100644 index 0000000..198a39f --- /dev/null +++ b/SQCSim-srv/connection.h @@ -0,0 +1,42 @@ +#ifndef _CONNECTION_H__ +#define _CONNECTION_H__ +#include +#include +#include "../SQCSim-common/player.h" +#include "../SQCSim-common/vector3.h" +#include "define.h" + +struct Input { + std::chrono::system_clock::time_point timestamp; + UINT8 keys; // 0bFBLRJS__ + Vector3f direction; +}; + +class Connection { +public: + Connection( + in_addr addr, + std::string name, + UINT64 hash, + UINT64 self_hash, + UINT64 team_hash); + ~Connection(); + + Player* player = nullptr; + + in_addr GetAddr() const; + UINT64 GetHash(bool self = true) const; + UINT64 GetTeamHash() const; + std::string GetName() const; + + void Clean(std::chrono::system_clock::time_point time); +private: + std::deque m_input_manifest; + in_addr m_addr; + UINT64 m_hash, + m_shash, + m_thash; + std::string m_name; + +}; +#endif \ No newline at end of file diff --git a/SQCSim-srv/define.h b/SQCSim-srv/define.h new file mode 100644 index 0000000..3a37bd4 --- /dev/null +++ b/SQCSim-srv/define.h @@ -0,0 +1,38 @@ +#ifndef _SRV_DEFINE_H__ +#define _SRV_DEFINE_H__ + +#include "../SQCSim-common/define.h" +#include +#include +#include +#include + +enum LogDest { CONSOLE, LOGFILE, LOG_LAST }; + +#ifdef _WIN32 + +#pragma comment(lib,"wsock32.lib") // Pour pouvoir faire fonctionner le linker sans le vcxproject + +#include +#include +#include + +#define popen _popen +#define pclose _pclose + +#else // Pas _WIN32 + +#include +#include +#include +#include +#include +#include + +#define SOCKET int +#define INVALID_SOCKET -1 +#define closesocket close + +#endif // _WIN32 + +#endif \ No newline at end of file diff --git a/SQCSim-srv/main.cpp b/SQCSim-srv/main.cpp new file mode 100644 index 0000000..d67d836 --- /dev/null +++ b/SQCSim-srv/main.cpp @@ -0,0 +1,10 @@ +#include "define.h" +#include "server.h" + +int main() { + Server* server = new Server(); + if (server->Init() == 0) + if (server->Ready() == 0) + server->Run(); + delete server; +} \ No newline at end of file diff --git a/SQCSim-srv/server.cpp b/SQCSim-srv/server.cpp new file mode 100644 index 0000000..afe262d --- /dev/null +++ b/SQCSim-srv/server.cpp @@ -0,0 +1,224 @@ +#include "server.h" + +Server::Server(LogDest log) : m_log(log) { + if (log == LogDest::LOGFILE) { + m_logfile = std::ofstream("server.log", std::ofstream::out); + if (!m_logfile.is_open()) { + m_log = LogDest::CONSOLE; // Fallback console. + Log("Ouverture fichier log: repli vers console.", true, false); + } + } +} + +Server::~Server() { + if (m_logfile.is_open()) + m_logfile.close(); + if (m_sock_udp) + closesocket(m_sock_udp); + if (m_sock_tcp) + closesocket(m_sock_tcp); +#ifdef _WIN32 + WSACleanup(); +#endif +} + +int Server::Init() { + Log("Initialisation du serveur...", false, false); + +#ifdef _WIN32 + if (WSAStartup(MAKEWORD(2, 2), &m_wsaData) != 0) { /* Initialisation de l'environnement reseau (Windows only) */ + Log("Initialisation WinSock.", true, true); + return 1; + } +#endif + + m_sock_udp = socket(AF_INET, SOCK_DGRAM, 0); + if (m_sock_udp == INVALID_SOCKET) { /* Creation du socket UDP */ + Log("Creation Socket UDP.", true, true); + return 2; + } + + m_sock_tcp = socket(AF_INET, SOCK_STREAM, 0); + if (m_sock_tcp == INVALID_SOCKET) { /* Creation du socket TCP */ + Log("Creation Socket TCP.", true, true); + return 3; + } + + /* Creation structure donnes descripteur du socket serveur */ + sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(SRV_PORT); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + if (bind(m_sock_udp, (sockaddr*)&addr, sizeof(addr)) != 0) { /* Associer le socket UDP au port */ + Log("Association Socket UDP.", true, true); + return 4; + } + + if (bind(m_sock_tcp, (sockaddr*)&addr, sizeof(addr)) != 0) { /* Associer le socket TCP au port */ + Log("Association Socket TCP.", true, true); + return 5; + } + + return 0; +} + +int Server::Ready() { + Log("Prêt à démarrer...", false, false); + return 0; +} + +void Server::Run() { + Log("Partie en cours...", false, false); +} + +inline std::string Server::Timestamp() { + time_t rawtime; + struct tm timeinfo; + char buffer[80]; + + time(&rawtime); + +#ifdef _WIN32 + localtime_s(&timeinfo, &rawtime); +#else + localtime_r(&rawtime, &timeinfo); +#endif + + strftime(buffer, sizeof(buffer), "%d-%m-%Y %H:%M:%S", &timeinfo); + std::string str(buffer); + + return "[" + str + "] "; +} + +void Server::Log(std::string str, bool is_error = false, bool is_fatal = false) { + switch (m_log) { + case LogDest::LOGFILE: + m_logfile << Timestamp() << (is_fatal ? "FATAL " : "") << (is_error ? "ERROR " : "") << str << std::endl; + break; + case LogDest::CONSOLE: + default: + std::cout << Timestamp() << (is_fatal? "FATAL ": "") << (is_error ? "ERROR ": "") << str << std::endl; + break; + } + + if (is_fatal) { + if (m_sock_udp) + closesocket(m_sock_udp); + if (m_sock_tcp) + closesocket(m_sock_tcp); +#ifdef _WIN32 + WSACleanup(); +#endif + } +} + +///* Recevoir paquet */ +//while (true) { +// char buffer[2048]; +// sockaddr_in client; +// +//#ifdef _WIN32 // Mais pourquoi? +// int clen = sizeof(client); +//#else +// unsigned int clen = sizeof(client); +//#endif +// +// int count = recvfrom(socket_udp, buffer, sizeof(buffer) - 1, 0, (sockaddr*)&client, &clen); +// +// if (count < 0) { +// Log("Erreur de reception de paquet.", socket_udp); +// return 4; +// } +// +// buffer[count] = '\0'; +// +// /* Gérer le paquet reçu */ +// std::string commande(buffer); +// +// if (commande.find("echo ") == 0) { /* ECHO */ +// std::string::size_type pos = commande.find(' '); +// std::string parametres = commande.substr(pos + 1); +// +// sendto(socket_udp, parametres.c_str(), parametres.length(), 0, (const sockaddr*)&client, sizeof(client)); +// } +// else if (commande.find("date ") == 0) { /* DATE */ +// time_t rawtime; +// struct tm* timeinfo = new tm(); +// char tbuffer[80]; +// +// time(&rawtime); +// +//#ifdef _WIN32 +// localtime_s(timeinfo, &rawtime); +//#else +// localtime_r(&rawtime, timeinfo); +//#endif +// +// strftime(tbuffer, 80, "%a %b %e %T %G", timeinfo); +// +// sendto(socket_udp, tbuffer, sizeof(tbuffer), 0, (const sockaddr*)&client, sizeof(client)); +// delete timeinfo; +// } +// else if (commande.find("ping ") == 0) { /* PING */ +// sendto(socket_udp, "pong", sizeof("pong"), 0, (const sockaddr*)&client, sizeof(client)); +// } +// else if (commande.find("usager ") == 0) { /* USAGER */ +// std::string user; +// +//#ifdef _WIN32 +// wchar_t userbuf[30]; +// DWORD usersize = 30; +// +// GetUserNameW(userbuf, &usersize); +// +// std::wstring wuser = userbuf; +// user = std::string(wuser.begin(), wuser.end()); +//#else +// char ptr[30]; +// getlogin_r(ptr, sizeof(ptr) - 1); +// user = std::string(ptr); +//#endif +// +// sendto(socket_udp, user.c_str(), user.length(), 0, (const sockaddr*)&client, sizeof(client)); +// } +// else if (commande.find("exec ") == 0) { /* EXEC */ +// std::string::size_type pos = commande.find(' '); +// std::string parametres = commande.substr(pos + 1); +// +// FILE* pipe = nullptr; +// char buffer[301]; // 300 caractères + '\0' +// std::string reponse; +// +// pipe = popen(parametres.c_str(), "r"); +// +// if (!pipe) +// reponse = "Erreur de commande!"; +// else while (!feof(pipe)) { +// if (fgets(buffer, sizeof(buffer) - 1, pipe)) +// reponse += buffer; +// } +// +// if (pipe) +// pclose(pipe); +// +// if (reponse.length() > 300) +// reponse = reponse.substr(0, 300); +// else if (reponse.length() < 1) +// reponse = "OK!"; +// +// sendto(socket_udp, reponse.c_str(), reponse.length(), 0, (const sockaddr*)&client, sizeof(client)); +// } +// else if (commande.find("bye ") == 0) { /* BYE */ +// Log("", 0); // Message d'erreur pas de message d'erreur! +// return 0; +// } +// else sendto(socket_udp, "huh?", sizeof("huh?"), 0, (const sockaddr*)&client, sizeof(client)); /* DEFAULT */ +//} +// +///* Ce bout de code ne devrait theoriquement jamais etre atteint, mais au cas. */ +//Log("", 0); +//return 0; +//return false; +//} +// diff --git a/SQCSim-srv/server.h b/SQCSim-srv/server.h new file mode 100644 index 0000000..246ad16 --- /dev/null +++ b/SQCSim-srv/server.h @@ -0,0 +1,34 @@ +#ifndef _SERVER_H__ +#define _SERVER_H__ + +#include +#include +#include +#include "../SQCSim-common/world.h" +#include "define.h" + +class Server { +public: + Server(LogDest log = LogDest::CONSOLE); + ~Server(); + + int Init(); + int Ready(); + void Run(); + +private: +#ifdef _WIN32 + WSADATA m_wsaData; +#endif + SOCKET m_sock_udp = 0, + m_sock_tcp = 0; + LogDest m_log; + + std::ofstream m_logfile; + World* m_world = nullptr; + std::vector m_players; + + std::string Timestamp(); + void Log(std::string str, bool is_error, bool is_fatal); +}; +#endif diff --git a/SQCSim2021/define.h b/SQCSim2021/define.h index 68e94b1..660b62e 100644 --- a/SQCSim2021/define.h +++ b/SQCSim2021/define.h @@ -16,29 +16,10 @@ #define CHUNK_SIZE_Y 64 #define CHUNK_SIZE_Z 4 #define MAX_SELECTION_DISTANCE 5 -#define SEED 12345 - -#ifdef _DEBUG -#define WORLD_SIZE_X 64 -#define WORLD_SIZE_Y 64 - -#define FRAMES_RENDER_CHUNKS 4 -#define FRAMES_UPDATE_CHUNKS 4 -#define FRAMES_DELETE_CHUNKS 4 - -#define THREADS_GENERATE_CHUNKS 1 -#define THREADS_UPDATE_CHUNKS 1 -#define THREADS_DELETE_CHUNKS 1 - -#define VIEW_DISTANCE 256 -#define TEXTURE_SIZE 128 -#define MAX_BULLETS 64 - #define BASE_WIDTH 640 #define BASE_HEIGHT 480 -#endif +#define SEED 12345 -#ifdef NDEBUG #define WORLD_SIZE_X 64 #define WORLD_SIZE_Y 64 @@ -46,14 +27,13 @@ #define FRAMES_UPDATE_CHUNKS 1 #define FRAMES_DELETE_CHUNKS 1 -#define THREADS_GENERATE_CHUNKS 6 +#define THREADS_GENERATE_CHUNKS 8 #define THREADS_UPDATE_CHUNKS 3 -#define THREADS_DELETE_CHUNKS 2 +#define THREADS_DELETE_CHUNKS 3 #define VIEW_DISTANCE 512 #define TEXTURE_SIZE 512 #define MAX_BULLETS 512 -#endif typedef uint8_t BlockType; From 2bb55e5bf62334d84aa1707d5de01190c38394cc Mon Sep 17 00:00:00 2001 From: MarcEricMartel Date: Sun, 24 Sep 2023 11:07:03 -0400 Subject: [PATCH 03/19] Zigonnage dans le srv. --- SQCSim-srv/connection.cpp | 47 ++++++++++++++++++++++++++++++--------- SQCSim-srv/connection.h | 36 ++++++++++++++++++++++-------- SQCSim-srv/define.h | 11 ++++++--- SQCSim-srv/server.cpp | 30 ++++++++++++++++++++----- SQCSim-srv/server.h | 11 ++++----- 5 files changed, 101 insertions(+), 34 deletions(-) diff --git a/SQCSim-srv/connection.cpp b/SQCSim-srv/connection.cpp index 964cc07..2b93114 100644 --- a/SQCSim-srv/connection.cpp +++ b/SQCSim-srv/connection.cpp @@ -2,13 +2,13 @@ Connection::Connection(in_addr addr, std::string name, - UINT64 hash, - UINT64 self_hash, - UINT64 team_hash): + UINT64 id, + UINT64 self_id, + UINT64 team_id): m_addr(addr), - m_hash(hash), - m_shash(self_hash), - m_thash(team_hash), + m_id(id), + m_sid(self_id), + m_tid(team_id), m_name(name) { } @@ -19,13 +19,38 @@ Connection::~Connection() { in_addr Connection::GetAddr() const { return m_addr; } -UINT64 Connection::GetHash(bool self) const { return self? m_shash: m_hash; } +UINT64 Connection::GetHash(bool self) const { return self? m_sid: m_id; } -UINT64 Connection::GetTeamHash() const { return m_thash; } +UINT64 Connection::GetTeamHash() const { return m_tid; } std::string Connection::GetName() const { return m_name; } -void Connection::Clean(std::chrono::system_clock::time_point time) { - while (m_input_manifest.front().timestamp < time || !m_input_manifest.empty()) - m_input_manifest.pop_front(); +void Connection::AddInput(Input in) { + m_input_manifest.insert({in.timestamp, in}); +} + +Output* Connection::getOutput(Timestamp time) { + auto out = m_output_manifest.find(time); + if (out != m_output_manifest.end()) + return &out->second; + return nullptr; +} + +Sync Connection::getSync(Timestamp time) { + Sync sync; + auto out = m_output_manifest.find(time); + if (out != m_output_manifest.end()) { + sync.timestamp = out->second.timestamp; + sync.position = out->second.position; + sync.sid = m_sid; + } + return sync; +} + +void Connection::CleanInputManifest(Timestamp time) { + auto wat = m_input_manifest.find(time); + + while (wat != m_input_manifest.begin()) + m_input_manifest.erase(wat--); + } diff --git a/SQCSim-srv/connection.h b/SQCSim-srv/connection.h index 198a39f..5b0134d 100644 --- a/SQCSim-srv/connection.h +++ b/SQCSim-srv/connection.h @@ -1,17 +1,30 @@ -#ifndef _CONNECTION_H__ -#define _CONNECTION_H__ +#ifndef CONNECTION_H__ +#define CONNECTION_H__ #include -#include +#include #include "../SQCSim-common/player.h" #include "../SQCSim-common/vector3.h" #include "define.h" struct Input { - std::chrono::system_clock::time_point timestamp; + Timestamp timestamp; UINT8 keys; // 0bFBLRJS__ Vector3f direction; }; +struct Output { + Timestamp timestamp; + UINT64 id = 0; + Vector3f position, direction; + bool is_shooting, is_jumping; +}; + +struct Sync { + Timestamp timestamp; + UINT64 sid = 0; + Vector3f position; +}; + class Connection { public: Connection( @@ -29,13 +42,18 @@ public: UINT64 GetTeamHash() const; std::string GetName() const; - void Clean(std::chrono::system_clock::time_point time); + void AddInput(Input in); + Output* getOutput(Timestamp time); + Sync getSync(Timestamp time); + + void CleanInputManifest(Timestamp time); private: - std::deque m_input_manifest; + std::map m_input_manifest; + std::map m_output_manifest; in_addr m_addr; - UINT64 m_hash, - m_shash, - m_thash; + UINT64 m_id, + m_sid, + m_tid; std::string m_name; }; diff --git a/SQCSim-srv/define.h b/SQCSim-srv/define.h index 3a37bd4..74c1c26 100644 --- a/SQCSim-srv/define.h +++ b/SQCSim-srv/define.h @@ -1,13 +1,18 @@ -#ifndef _SRV_DEFINE_H__ -#define _SRV_DEFINE_H__ +#ifndef SRV_DEFINE_H__ +#define SRV_DEFINE_H__ #include "../SQCSim-common/define.h" #include #include #include #include +#include + +#define MAX_CONNECTIONS 16 +typedef unsigned char LogDest; +enum LOG_DEST { CONSOLE, LOGFILE, LOG_LAST }; +typedef std::chrono::system_clock::time_point Timestamp; -enum LogDest { CONSOLE, LOGFILE, LOG_LAST }; #ifdef _WIN32 diff --git a/SQCSim-srv/server.cpp b/SQCSim-srv/server.cpp index afe262d..e2740ba 100644 --- a/SQCSim-srv/server.cpp +++ b/SQCSim-srv/server.cpp @@ -1,10 +1,11 @@ #include "server.h" -Server::Server(LogDest log) : m_log(log) { - if (log == LogDest::LOGFILE) { +Server::Server(LogDest log) { + m_log = log; + if (log == LOG_DEST::LOGFILE) { m_logfile = std::ofstream("server.log", std::ofstream::out); if (!m_logfile.is_open()) { - m_log = LogDest::CONSOLE; // Fallback console. + m_log = LOG_DEST::CONSOLE; // Fallback console. Log("Ouverture fichier log: repli vers console.", true, false); } } @@ -60,11 +61,28 @@ int Server::Init() { return 5; } + for (auto& conn : m_conn) + conn = nullptr; + return 0; } int Server::Ready() { - Log("Prêt à démarrer...", false, false); + if (listen(m_sock_tcp, MAX_CONNECTIONS) < 0) { + Log("Écoute sur le port TCP.", true, true); + return 1; + } + + char buffer[2048]; + bool readystart = false; + + Log("À l'écoute sur le port: " + std::to_string(SRV_PORT), false, false); + + while (!readystart) { + + Log("trololo", false, false); + readystart = true; + } return 0; } @@ -93,10 +111,10 @@ inline std::string Server::Timestamp() { void Server::Log(std::string str, bool is_error = false, bool is_fatal = false) { switch (m_log) { - case LogDest::LOGFILE: + case LOG_DEST::LOGFILE: m_logfile << Timestamp() << (is_fatal ? "FATAL " : "") << (is_error ? "ERROR " : "") << str << std::endl; break; - case LogDest::CONSOLE: + case LOG_DEST::CONSOLE: default: std::cout << Timestamp() << (is_fatal? "FATAL ": "") << (is_error ? "ERROR ": "") << str << std::endl; break; diff --git a/SQCSim-srv/server.h b/SQCSim-srv/server.h index 246ad16..2cb0618 100644 --- a/SQCSim-srv/server.h +++ b/SQCSim-srv/server.h @@ -1,15 +1,16 @@ -#ifndef _SERVER_H__ -#define _SERVER_H__ +#ifndef SERVER_H__ +#define SERVER_H__ #include #include #include #include "../SQCSim-common/world.h" #include "define.h" +#include "connection.h" class Server { public: - Server(LogDest log = LogDest::CONSOLE); + Server(LogDest log = LOG_DEST::CONSOLE); ~Server(); int Init(); @@ -23,10 +24,10 @@ private: SOCKET m_sock_udp = 0, m_sock_tcp = 0; LogDest m_log; - std::ofstream m_logfile; + + Connection* m_conn[MAX_CONNECTIONS]; World* m_world = nullptr; - std::vector m_players; std::string Timestamp(); void Log(std::string str, bool is_error, bool is_fatal); From 20d15a15590a1eb64b270a6abcdc022cd0a8db73 Mon Sep 17 00:00:00 2001 From: MarcEricMartel Date: Mon, 25 Sep 2023 08:23:52 -0400 Subject: [PATCH 04/19] =?UTF-8?q?D=C3=A9but=20protocole?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SQCSim-common/SQCSim-common.vcxproj | 1 + SQCSim-common/SQCSim-common.vcxproj.filters | 3 + SQCSim-common/define.h | 70 ++++++++-------- SQCSim-common/netprotocol.h | 89 +++++++++++++++++++++ SQCSim-srv/connection.cpp | 10 +-- SQCSim-srv/connection.h | 32 ++------ SQCSim-srv/define.h | 29 ------- SQCSim2021/SQCSim2021.vcxproj | 2 + SQCSim2021/SQCSim2021.vcxproj.filters | 6 ++ SQCSim2021/chunk.cpp | 4 +- SQCSim2021/chunk.h | 2 +- SQCSim2021/connector.cpp | 21 +++++ SQCSim2021/connector.h | 46 +++++++++++ SQCSim2021/define.h | 32 ++++++++ SQCSim2021/engine.cpp | 26 +++++- SQCSim2021/engine.h | 4 + SQCSim2021/world.cpp | 32 +++++++- SQCSim2021/world.h | 3 + 18 files changed, 308 insertions(+), 104 deletions(-) create mode 100644 SQCSim-common/netprotocol.h create mode 100644 SQCSim2021/connector.cpp create mode 100644 SQCSim2021/connector.h diff --git a/SQCSim-common/SQCSim-common.vcxproj b/SQCSim-common/SQCSim-common.vcxproj index 5db21d0..5f61a2a 100644 --- a/SQCSim-common/SQCSim-common.vcxproj +++ b/SQCSim-common/SQCSim-common.vcxproj @@ -137,6 +137,7 @@ + diff --git a/SQCSim-common/SQCSim-common.vcxproj.filters b/SQCSim-common/SQCSim-common.vcxproj.filters index af8b625..a6555fb 100644 --- a/SQCSim-common/SQCSim-common.vcxproj.filters +++ b/SQCSim-common/SQCSim-common.vcxproj.filters @@ -48,6 +48,9 @@ Fichiers d%27en-tête + + Fichiers d%27en-tête + diff --git a/SQCSim-common/define.h b/SQCSim-common/define.h index 3501dd6..78284f5 100644 --- a/SQCSim-common/define.h +++ b/SQCSim-common/define.h @@ -2,51 +2,53 @@ #define DEFINE_H__ #include - -#define CHUNK_SIZE_X 16 -#define CHUNK_SIZE_Y 128 -#define CHUNK_SIZE_Z 16 -#define MAX_SELECTION_DISTANCE 5 -#define SEED 12345 +#include #define SRV_PORT 1025 #define CLI_PORT 1026 -#ifdef _DEBUG +#define CHUNK_SIZE_X 4 +#define CHUNK_SIZE_Y 64 +#define CHUNK_SIZE_Z 4 +#define MAX_SELECTION_DISTANCE 5 +#define SEED 12345 + #define WORLD_SIZE_X 64 #define WORLD_SIZE_Y 64 -#define FRAMES_RENDER_CHUNKS 4 -#define FRAMES_UPDATE_CHUNKS 4 -#define FRAMES_DELETE_CHUNKS 4 - -#define THREADS_GENERATE_CHUNKS 1 -#define THREADS_UPDATE_CHUNKS 1 -#define THREADS_DELETE_CHUNKS 1 - -#define VIEW_DISTANCE 256 -#define TEXTURE_SIZE 128 -#define MAX_BULLETS 64 -#endif - -#ifdef NDEBUG -#define WORLD_SIZE_X 16 -#define WORLD_SIZE_Y 16 - -#define FRAMES_RENDER_CHUNKS 1 -#define FRAMES_UPDATE_CHUNKS 1 -#define FRAMES_DELETE_CHUNKS 1 - -#define THREADS_GENERATE_CHUNKS 12 -#define THREADS_UPDATE_CHUNKS 5 -#define THREADS_DELETE_CHUNKS 2 - -#define VIEW_DISTANCE 1024 +#define VIEW_DISTANCE 512 #define TEXTURE_SIZE 512 #define MAX_BULLETS 512 -#endif typedef uint8_t BlockType; enum BLOCK_TYPE { BTYPE_AIR, BTYPE_DIRT, BTYPE_GRASS, BTYPE_METAL, BTYPE_ICE, BTYPE_LAST }; +typedef std::chrono::system_clock::time_point Timestamp; + +#ifdef _WIN32 + +#pragma comment(lib,"wsock32.lib") // Pour pouvoir faire fonctionner le linker sans le vcxproject + +#include +#include +#include + +#define popen _popen +#define pclose _pclose + +#else // Pas _WIN32 + +#include +#include +#include +#include +#include +#include + +#define SOCKET int +#define INVALID_SOCKET -1 +#define closesocket close + +#endif // _WIN32 + #endif // DEFINE_H__ diff --git a/SQCSim-common/netprotocol.h b/SQCSim-common/netprotocol.h new file mode 100644 index 0000000..3609de9 --- /dev/null +++ b/SQCSim-common/netprotocol.h @@ -0,0 +1,89 @@ +#ifndef NETPROTOCOL_H__ +#define NETPROTOCOL_H__ +#include "define.h" +#include "vector3.h" + +/* Protocole Particulier de Partie à Plusieurs Personnes (PPPPP) */ + +namespace netprot { + typedef uint8_t PacketType; + enum PACKET_TYPE { + ERR, INPUT, OUTPUT, SYNC, + TEAMINF, SELFINF, PLAYINF, + CHUNKMOD, PLAYERMOD, PICKUPMOD, + GAMEINFO, ENDINFO , CHAT + }; + + typedef struct { // cli -> srv UDP ~frame + Timestamp timestamp; + uint8_t keys; // 0bFBLRJS__ + Vector3f direction; + } Input; + + typedef struct { // srv -> cli UDP ~frame + Timestamp timestamp; + uint64_t id = 0; + Vector3f position, + direction; + bool is_shooting, + is_jumping; + } Output; + + typedef struct { // srv -> cli TCP ~second + Timestamp timestamp; + uint64_t sid = 0, + timer = 0; + uint8_t hp = 0; + uint16_t ammo = 0; + Vector3f position; + } Sync; + + typedef struct { // cli <-> srv TCP once + char name[32]; + uint64_t id = 0; + } TeamInfo; + + typedef struct { // cli <-> srv TCP once + char name[32]; + uint64_t sid = 0, + tid = 0; + } SelfInfo; + + typedef struct { // cli <-> srv TCP once + char name[32]; + uint64_t id = 0, + tid = 0; + } PlayerInfo; + + typedef struct { + uint64_t seed; + // uint8_t gameType; + // uint8_t time; + } GameInfo; + + typedef struct { // cli <-> srv TCP event + uint64_t src_id = 0, + dest_id = 0, + dest_team_id = 0; + char mess[140]; // Good 'nough for twitr, good 'nough for me. + } Chat; + + inline void Serialize(Input* in, char* buf, uint32_t* buflen); // cli + inline void Serialize(Output* out, char* buf, uint32_t* buflen); // srv + inline void Serialize(Sync* sync, char* buf, uint32_t* buflen); // srv + inline void Serialize(TeamInfo* tinfo, char* buf, uint32_t* buflen); // cli/srv + inline void Serialize(SelfInfo* sinfo, char* buf, uint32_t* buflen); // cli/srv + inline void Serialize(PlayerInfo* pinfo, char* buf, uint32_t* buflen); // srv + inline void Serialize(Chat* chat, char* buf, uint32_t* buflen); // cli/srv + + inline void Deserialize(Input* in, char* buf, uint32_t* buflen); // srv + inline void Deserialize(Output* out, char* buf, uint32_t* buflen); // cli + inline void Deserialize(Sync* sync, char* buf, uint32_t* buflen); // cli + inline void Deserialize(TeamInfo* tinfo, char* buf, uint32_t* buflen); // cli/srv + inline void Deserialize(SelfInfo* sinfo, char* buf, uint32_t* buflen); // cli/srv + inline void Deserialize(PlayerInfo* spinfoync, char* buf, uint32_t* buflen); // cli + inline void Deserialize(Chat* chat, char* buf, uint32_t* buflen); // srv/cli + + inline PacketType getType(char* buf, uint32_t* buflen); // srv/cli +} +#endif diff --git a/SQCSim-srv/connection.cpp b/SQCSim-srv/connection.cpp index 2b93114..0e8d252 100644 --- a/SQCSim-srv/connection.cpp +++ b/SQCSim-srv/connection.cpp @@ -2,9 +2,9 @@ Connection::Connection(in_addr addr, std::string name, - UINT64 id, - UINT64 self_id, - UINT64 team_id): + uint64_t id, + uint64_t self_id, + uint64_t team_id): m_addr(addr), m_id(id), m_sid(self_id), @@ -19,9 +19,9 @@ Connection::~Connection() { in_addr Connection::GetAddr() const { return m_addr; } -UINT64 Connection::GetHash(bool self) const { return self? m_sid: m_id; } +uint64_t Connection::GetHash(bool self) const { return self? m_sid: m_id; } -UINT64 Connection::GetTeamHash() const { return m_tid; } +uint64_t Connection::GetTeamHash() const { return m_tid; } std::string Connection::GetName() const { return m_name; } diff --git a/SQCSim-srv/connection.h b/SQCSim-srv/connection.h index 5b0134d..6db8b06 100644 --- a/SQCSim-srv/connection.h +++ b/SQCSim-srv/connection.h @@ -4,42 +4,24 @@ #include #include "../SQCSim-common/player.h" #include "../SQCSim-common/vector3.h" +#include "../SQCSim-common/serialization.h" #include "define.h" -struct Input { - Timestamp timestamp; - UINT8 keys; // 0bFBLRJS__ - Vector3f direction; -}; - -struct Output { - Timestamp timestamp; - UINT64 id = 0; - Vector3f position, direction; - bool is_shooting, is_jumping; -}; - -struct Sync { - Timestamp timestamp; - UINT64 sid = 0; - Vector3f position; -}; - class Connection { public: Connection( in_addr addr, std::string name, - UINT64 hash, - UINT64 self_hash, - UINT64 team_hash); + uint64_t hash, + uint64_t self_hash, + uint64_t team_hash); ~Connection(); Player* player = nullptr; in_addr GetAddr() const; - UINT64 GetHash(bool self = true) const; - UINT64 GetTeamHash() const; + uint64_t GetHash(bool self = true) const; + uint64_t GetTeamHash() const; std::string GetName() const; void AddInput(Input in); @@ -51,7 +33,7 @@ private: std::map m_input_manifest; std::map m_output_manifest; in_addr m_addr; - UINT64 m_id, + uint64_t m_id, m_sid, m_tid; std::string m_name; diff --git a/SQCSim-srv/define.h b/SQCSim-srv/define.h index 74c1c26..297bd15 100644 --- a/SQCSim-srv/define.h +++ b/SQCSim-srv/define.h @@ -6,38 +6,9 @@ #include #include #include -#include #define MAX_CONNECTIONS 16 typedef unsigned char LogDest; enum LOG_DEST { CONSOLE, LOGFILE, LOG_LAST }; -typedef std::chrono::system_clock::time_point Timestamp; - - -#ifdef _WIN32 - -#pragma comment(lib,"wsock32.lib") // Pour pouvoir faire fonctionner le linker sans le vcxproject - -#include -#include -#include - -#define popen _popen -#define pclose _pclose - -#else // Pas _WIN32 - -#include -#include -#include -#include -#include -#include - -#define SOCKET int -#define INVALID_SOCKET -1 -#define closesocket close - -#endif // _WIN32 #endif \ No newline at end of file diff --git a/SQCSim2021/SQCSim2021.vcxproj b/SQCSim2021/SQCSim2021.vcxproj index dfed398..ab87f31 100644 --- a/SQCSim2021/SQCSim2021.vcxproj +++ b/SQCSim2021/SQCSim2021.vcxproj @@ -25,6 +25,7 @@ + @@ -46,6 +47,7 @@ + diff --git a/SQCSim2021/SQCSim2021.vcxproj.filters b/SQCSim2021/SQCSim2021.vcxproj.filters index 4b2e6c9..8d96ac0 100644 --- a/SQCSim2021/SQCSim2021.vcxproj.filters +++ b/SQCSim2021/SQCSim2021.vcxproj.filters @@ -74,6 +74,9 @@ Fichiers d%27en-tête + + Fichiers d%27en-tête + @@ -127,5 +130,8 @@ Fichiers sources + + Fichiers sources + \ No newline at end of file diff --git a/SQCSim2021/chunk.cpp b/SQCSim2021/chunk.cpp index fe4c0a5..5d78859 100644 --- a/SQCSim2021/chunk.cpp +++ b/SQCSim2021/chunk.cpp @@ -1,13 +1,13 @@ #include "chunk.h" #include "world.h" -Chunk::Chunk(unsigned int x, unsigned int y) : m_posX(x), m_posY(y) { +Chunk::Chunk(unsigned int x, unsigned int y, int64_t seed) : m_posX(x), m_posY(y) { //std::ostringstream pos; // Vérifie l'existence d'un fichier .chunk avec sa position. //pos << CHUNK_PATH << x << '_' << y << ".chunk"; //std::ifstream input(pos.str(), std::fstream::binary); //if (input.fail()) { - OpenSimplexNoise::Noise simplex = OpenSimplexNoise::Noise(SEED); + OpenSimplexNoise::Noise simplex = OpenSimplexNoise::Noise(seed); m_blocks.Reset(BTYPE_AIR); for (int ix = 0; ix < CHUNK_SIZE_X; ++ix) // Montagnes diff --git a/SQCSim2021/chunk.h b/SQCSim2021/chunk.h index d4fdcb5..2ce15e8 100644 --- a/SQCSim2021/chunk.h +++ b/SQCSim2021/chunk.h @@ -25,7 +25,7 @@ class Chunk { void AddBlockToMesh(VertexBuffer::VertexData* vd, int& count, BlockType bt, int x, int y, int z, float u, float v, float s, World* world); public: - Chunk(unsigned int x, unsigned int y); + Chunk(unsigned int x, unsigned int y, int64_t seed); ~Chunk(); void RemoveBlock(int x, int y, int z, World* world); diff --git a/SQCSim2021/connector.cpp b/SQCSim2021/connector.cpp new file mode 100644 index 0000000..0bdaa56 --- /dev/null +++ b/SQCSim2021/connector.cpp @@ -0,0 +1,21 @@ +#include "connector.h" + +Connector::Connector() +{ +} + +Connector::~Connector() +{ +} + +int Connector::Init(sockaddr_in srv_addr) { + return 0; +} + +int Connector::Connect(std::string name) { + return 0; +} + +UINT64 Connector::getId() const { return m_sid; } + +unsigned int Connector::getSeed() const { return m_seed; } diff --git a/SQCSim2021/connector.h b/SQCSim2021/connector.h new file mode 100644 index 0000000..d2aee73 --- /dev/null +++ b/SQCSim2021/connector.h @@ -0,0 +1,46 @@ +#ifndef CONNECTOR_H__ +#define CONNECTOR_H__ + +#include "define.h" +#include "vector3.h" + +struct Input { // vers serveur + Timestamp timestamp; + UINT8 keys; // 0bFBLRJS__ + Vector3f direction; +}; + +struct Output { // autres joueurs du serveur + Timestamp timestamp; + UINT64 id = 0; + Vector3f position, direction; + bool is_shooting, is_jumping; +}; + +struct Sync { // du serveur + Timestamp timestamp; + UINT64 sid = 0; + Vector3f position; +}; + +class Connector { +public: + Connector(); + ~Connector(); + + int Init(sockaddr_in srv_addr); + int Connect(std::string name); + UINT64 getId() const; + unsigned int getSeed() const; + + //void SendInput(); + //int Sync(); +private: + SOCKET m_sock = 0; + std::string m_name = ""; + UINT64 m_sid = 0, + m_tid = 0; + unsigned int m_seed = 12345; + +}; +#endif diff --git a/SQCSim2021/define.h b/SQCSim2021/define.h index 660b62e..4e0c96f 100644 --- a/SQCSim2021/define.h +++ b/SQCSim2021/define.h @@ -4,6 +4,7 @@ #include #include #include +#include #ifdef _WIN32 #include @@ -12,6 +13,11 @@ #include #endif +#define NETWORK_TEST false +#define SRV_ADDR "127.0.0.1" +#define SRV_PORT 1025 +#define CLI_PORT 1026 + #define CHUNK_SIZE_X 4 #define CHUNK_SIZE_Y 64 #define CHUNK_SIZE_Z 4 @@ -35,9 +41,35 @@ #define TEXTURE_SIZE 512 #define MAX_BULLETS 512 +#ifdef _WIN32 + +#pragma comment(lib,"wsock32.lib") // Pour pouvoir faire fonctionner le linker sans le vcxproject + +#include +#include +#include + +#define popen _popen +#define pclose _pclose + +#else // Pas _WIN32 + +#include +#include +#include +#include +#include +#include + +#define SOCKET int +#define INVALID_SOCKET -1 +#define closesocket close + +#endif // _WIN32 typedef uint8_t BlockType; enum BLOCK_TYPE { BTYPE_AIR, BTYPE_DIRT, BTYPE_GRASS, BTYPE_METAL, BTYPE_ICE, BTYPE_LAST }; +typedef std::chrono::system_clock::time_point Timestamp; #define TEXTURE_PATH "./media/textures/" #define SHADER_PATH "./media/shaders/" diff --git a/SQCSim2021/engine.cpp b/SQCSim2021/engine.cpp index 1177fd3..79043ae 100644 --- a/SQCSim2021/engine.cpp +++ b/SQCSim2021/engine.cpp @@ -1,8 +1,4 @@ #include "engine.h" -#include -#include -#include "transformation.h" -#include "player.h" Engine::Engine() {} @@ -50,6 +46,28 @@ void Engine::Init() { for (int x = 0; x < MAX_BULLETS; ++x) m_bullets[x] = nullptr; + uint64_t seed = 12345; + std::string playname = "John Test"; + const char srvaddr[] = "127.0.0.1"; + if (NETWORK_TEST) { // Test connexion réseau. + sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(SRV_PORT); + addr.sin_addr.s_addr = inet_addr(srvaddr); + + if (!m_conn->Init(addr)) { + if (m_conn->Connect(playname)) { + // setup jeu en réseau. + seed = m_conn->getSeed(); + std::cout << "ID reçu du serveur: " << std::to_string(m_conn->getId()) << "!" << std::endl; + } + else std::cout << "Erreur de connexion." << std::endl; + } + else std::cout << "Erreur de création de socket." << std::endl; + } + + m_world.SetSeed(seed); + // Init Chunks m_world.GetChunks().Reset(nullptr); diff --git a/SQCSim2021/engine.h b/SQCSim2021/engine.h index e96a931..947df54 100644 --- a/SQCSim2021/engine.h +++ b/SQCSim2021/engine.h @@ -1,6 +1,8 @@ #ifndef ENGINE_H__ #define ENGINE_H__ +#include +#include #include "define.h" #include "openglcontext.h" #include "texture.h" @@ -15,6 +17,7 @@ #include "array2d.h" #include "world.h" #include "bullet.h" +#include "connector.h" class Engine : public OpenglContext { public: @@ -45,6 +48,7 @@ private: void DrawHud(float elapsedTime, BlockType bloc); void PrintText(float x, float y, float scale, const std::string& t); + Connector* m_conn = nullptr; Shader m_shader01; BlockInfo* m_blockinfo[BTYPE_LAST]; TextureAtlas m_textureAtlas = TextureAtlas(BTYPE_LAST); diff --git a/SQCSim2021/world.cpp b/SQCSim2021/world.cpp index bbd86c0..dc5e5b6 100644 --- a/SQCSim2021/world.cpp +++ b/SQCSim2021/world.cpp @@ -6,6 +6,10 @@ World::~World() {} Array2d& World::GetChunks() { return m_chunks; } +void World::SetSeed(uint64_t seed) { + m_seed = seed; +} + Chunk* World::ChunkAt(float x, float y, float z) const { int cx = (int)x / CHUNK_SIZE_X; int cz = (int)z / CHUNK_SIZE_Z; @@ -330,7 +334,12 @@ void World::UpdateWorld(Player& player, BlockInfo* blockinfo[BTYPE_LAST]) { chy = cy + ty * CHUNK_SIZE_Z; if (chx < WORLD_SIZE_X * CHUNK_SIZE_X && chy < WORLD_SIZE_Y * CHUNK_SIZE_Z && chx >= 0 && chy >= 0 && !ChunkAt(chx, 1, chy)) - genThList[threads++] = std::async(std::launch::async, [](unsigned int x, unsigned int y) { return new Chunk(x, y); }, chx / CHUNK_SIZE_X + m_center[0], chy / CHUNK_SIZE_Z + m_center[1]); + genThList[threads++] = std::async(std::launch::async, + [](unsigned int x, unsigned int y, uint64_t seed) { + return new Chunk(x, y, seed); }, + chx / CHUNK_SIZE_X + m_center[0], + chy / CHUNK_SIZE_Z + m_center[1], + m_seed); if (threads == THREADS_GENERATE_CHUNKS) frameGenerate = FRAMES_RENDER_CHUNKS; } for (; ty <= side; ++ty) { @@ -340,7 +349,12 @@ void World::UpdateWorld(Player& player, BlockInfo* blockinfo[BTYPE_LAST]) { chy = cy + ty * CHUNK_SIZE_Z; if (chx < WORLD_SIZE_X * CHUNK_SIZE_X && chy < WORLD_SIZE_Y * CHUNK_SIZE_Z && chx >= 0 && chy >= 0 && !ChunkAt(chx, 1, chy)) - genThList[threads++] = std::async(std::launch::async, [](unsigned int x, unsigned int y) { return new Chunk(x, y); }, chx / CHUNK_SIZE_X + m_center[0], chy / CHUNK_SIZE_Z + m_center[1]); + genThList[threads++] = std::async(std::launch::async, + [](unsigned int x, unsigned int y, uint64_t seed) { + return new Chunk(x, y, seed); }, + chx / CHUNK_SIZE_X + m_center[0], + chy / CHUNK_SIZE_Z + m_center[1], + m_seed); if (threads == THREADS_GENERATE_CHUNKS) frameGenerate = FRAMES_RENDER_CHUNKS; } for (; tx >= -side; --tx) { @@ -350,7 +364,12 @@ void World::UpdateWorld(Player& player, BlockInfo* blockinfo[BTYPE_LAST]) { chy = cy + ty * CHUNK_SIZE_Z; if (chx < WORLD_SIZE_X * CHUNK_SIZE_X && chy < WORLD_SIZE_Y * CHUNK_SIZE_Z && chx >= 0 && chy >= 0 && !ChunkAt(chx, 1, chy)) - genThList[threads++] = std::async(std::launch::async, [](unsigned int x, unsigned int y) { return new Chunk(x, y); }, chx / CHUNK_SIZE_X + m_center[0], chy / CHUNK_SIZE_Z + m_center[1]); + genThList[threads++] = std::async(std::launch::async, + [](unsigned int x, unsigned int y, uint64_t seed) { + return new Chunk(x, y, seed); }, + chx / CHUNK_SIZE_X + m_center[0], + chy / CHUNK_SIZE_Z + m_center[1], + m_seed); if (threads == THREADS_GENERATE_CHUNKS) frameGenerate = FRAMES_RENDER_CHUNKS; } for (; ty >= -side; --ty) { @@ -360,7 +379,12 @@ void World::UpdateWorld(Player& player, BlockInfo* blockinfo[BTYPE_LAST]) { chy = cy + ty * CHUNK_SIZE_Z; if (chx < WORLD_SIZE_X * CHUNK_SIZE_X && chy < WORLD_SIZE_Y * CHUNK_SIZE_Z && chx >= 0 && chy >= 0 && !ChunkAt(chx, 1, chy)) - genThList[threads++] = std::async(std::launch::async, [](unsigned int x, unsigned int y) { return new Chunk(x, y); }, chx / CHUNK_SIZE_X + m_center[0], chy / CHUNK_SIZE_Z + m_center[1]); + genThList[threads++] = std::async(std::launch::async, + [](unsigned int x, unsigned int y, uint64_t seed) { + return new Chunk(x, y, seed); }, + chx / CHUNK_SIZE_X + m_center[0], + chy / CHUNK_SIZE_Z + m_center[1], + m_seed); if (threads == THREADS_GENERATE_CHUNKS) frameGenerate = FRAMES_RENDER_CHUNKS; } if (frameGenerate) diff --git a/SQCSim2021/world.h b/SQCSim2021/world.h index d4b1545..55140e3 100644 --- a/SQCSim2021/world.h +++ b/SQCSim2021/world.h @@ -26,6 +26,8 @@ public: Array2d& GetChunks(); + void SetSeed(uint64_t seed); + Chunk* ChunkAt(float x, float y, float z) const; Chunk* ChunkAt(const Vector3f& pos) const; @@ -43,6 +45,7 @@ public: private: Array2d m_chunks = Array2d(WORLD_SIZE_X, WORLD_SIZE_Y); std::vector m_tbDeleted; + uint64_t m_seed = 0; unsigned int m_center[2] = { UINT16_MAX / 2 - WORLD_SIZE_X, UINT16_MAX / 2 - WORLD_SIZE_Y }; From 035436c63944e66c0f511d0eea9422204559556d Mon Sep 17 00:00:00 2001 From: MarcEricMartel Date: Mon, 25 Sep 2023 08:53:23 -0400 Subject: [PATCH 05/19] ajout errlog --- SQCSim-common/netprotocol.h | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/SQCSim-common/netprotocol.h b/SQCSim-common/netprotocol.h index 3609de9..a1beb1c 100644 --- a/SQCSim-common/netprotocol.h +++ b/SQCSim-common/netprotocol.h @@ -11,7 +11,7 @@ namespace netprot { ERR, INPUT, OUTPUT, SYNC, TEAMINF, SELFINF, PLAYINF, CHUNKMOD, PLAYERMOD, PICKUPMOD, - GAMEINFO, ENDINFO , CHAT + GAMEINFO, ENDINFO , CHAT, ERRLOG }; typedef struct { // cli -> srv UDP ~frame @@ -31,10 +31,10 @@ namespace netprot { typedef struct { // srv -> cli TCP ~second Timestamp timestamp; - uint64_t sid = 0, - timer = 0; - uint8_t hp = 0; + uint64_t sid = 0; + uint32_t timer = 0; uint16_t ammo = 0; + uint8_t hp = 0; Vector3f position; } Sync; @@ -54,11 +54,11 @@ namespace netprot { uint64_t id = 0, tid = 0; } PlayerInfo; - - typedef struct { + + typedef struct { // cli <-> srv TCP event (before game start)/ once uint64_t seed; - // uint8_t gameType; - // uint8_t time; + uint32_t countdown; + uint8_t gameType; // TOOD: enum. } GameInfo; typedef struct { // cli <-> srv TCP event @@ -68,21 +68,30 @@ namespace netprot { char mess[140]; // Good 'nough for twitr, good 'nough for me. } Chat; + typedef struct { // srv -> cli TCP event + char mess[140]; + bool is_fatal; + } ErrorLog; + inline void Serialize(Input* in, char* buf, uint32_t* buflen); // cli inline void Serialize(Output* out, char* buf, uint32_t* buflen); // srv inline void Serialize(Sync* sync, char* buf, uint32_t* buflen); // srv inline void Serialize(TeamInfo* tinfo, char* buf, uint32_t* buflen); // cli/srv inline void Serialize(SelfInfo* sinfo, char* buf, uint32_t* buflen); // cli/srv inline void Serialize(PlayerInfo* pinfo, char* buf, uint32_t* buflen); // srv + inline void Serialize(GameInfo* ginfo, char* buf, uint32_t* buflen); // cli/srv inline void Serialize(Chat* chat, char* buf, uint32_t* buflen); // cli/srv + inline void Serialize(ErrorLog* errlog, char* buf, uint32_t* buflen); // srv inline void Deserialize(Input* in, char* buf, uint32_t* buflen); // srv inline void Deserialize(Output* out, char* buf, uint32_t* buflen); // cli inline void Deserialize(Sync* sync, char* buf, uint32_t* buflen); // cli inline void Deserialize(TeamInfo* tinfo, char* buf, uint32_t* buflen); // cli/srv inline void Deserialize(SelfInfo* sinfo, char* buf, uint32_t* buflen); // cli/srv - inline void Deserialize(PlayerInfo* spinfoync, char* buf, uint32_t* buflen); // cli + inline void Deserialize(PlayerInfo* pinfo, char* buf, uint32_t* buflen); // cli + inline void Deserialize(GameInfo* ginfo, char* buf, uint32_t* buflen); // cli inline void Deserialize(Chat* chat, char* buf, uint32_t* buflen); // srv/cli + inline void Deserialize(ErrorLog* errlog, char* buf, uint32_t* buflen); // srv inline PacketType getType(char* buf, uint32_t* buflen); // srv/cli } From 4c2e8a12add2eca6bea79c2ae0d80d6ff371da45 Mon Sep 17 00:00:00 2001 From: MarcEricMartel Date: Mon, 25 Sep 2023 16:30:03 -0400 Subject: [PATCH 06/19] serialization --- SQCSim-common/SQCSim-common.vcxproj | 1 + SQCSim-common/SQCSim-common.vcxproj.filters | 3 + SQCSim-common/define.h | 2 +- SQCSim-common/netprotocol.cpp | 139 ++++++++++++++++++++ SQCSim-common/netprotocol.h | 97 +++++++------- SQCSim-srv/connection.cpp | 10 +- SQCSim-srv/connection.h | 12 +- SQCSim-srv/main.cpp | 1 - SQCSim-srv/server.cpp | 32 ++++- SQCSim-srv/server.h | 1 + 10 files changed, 239 insertions(+), 59 deletions(-) create mode 100644 SQCSim-common/netprotocol.cpp diff --git a/SQCSim-common/SQCSim-common.vcxproj b/SQCSim-common/SQCSim-common.vcxproj index 5f61a2a..15265fb 100644 --- a/SQCSim-common/SQCSim-common.vcxproj +++ b/SQCSim-common/SQCSim-common.vcxproj @@ -145,6 +145,7 @@ + diff --git a/SQCSim-common/SQCSim-common.vcxproj.filters b/SQCSim-common/SQCSim-common.vcxproj.filters index a6555fb..a98dd18 100644 --- a/SQCSim-common/SQCSim-common.vcxproj.filters +++ b/SQCSim-common/SQCSim-common.vcxproj.filters @@ -71,5 +71,8 @@ Fichiers sources + + Fichiers sources + \ No newline at end of file diff --git a/SQCSim-common/define.h b/SQCSim-common/define.h index 78284f5..45a7cbb 100644 --- a/SQCSim-common/define.h +++ b/SQCSim-common/define.h @@ -22,7 +22,7 @@ typedef uint8_t BlockType; enum BLOCK_TYPE { BTYPE_AIR, BTYPE_DIRT, BTYPE_GRASS, BTYPE_METAL, BTYPE_ICE, BTYPE_LAST }; -typedef std::chrono::system_clock::time_point Timestamp; +typedef uint64_t Timestamp; #ifdef _WIN32 diff --git a/SQCSim-common/netprotocol.cpp b/SQCSim-common/netprotocol.cpp new file mode 100644 index 0000000..5f8aef1 --- /dev/null +++ b/SQCSim-common/netprotocol.cpp @@ -0,0 +1,139 @@ +#include "netprotocol.h" + +void netprot::Serialize(Input* in, char* buf[], uint32_t* buflen) { + +} + +void netprot::Serialize(Output* out, char* buf[], uint32_t* buflen) { + +} + +void netprot::Serialize(Sync* sync, char* buf[], uint32_t* buflen) { + +} + +void netprot::Serialize(TeamInfo* tinfo, char* buf[], uint32_t* buflen) { + +} + +void netprot::Serialize(LoginInfo* linfo, char* buf[], uint32_t* buflen) { + *buf[0] = netprot::PACKET_TYPE::LOGINF; + void* nom = &linfo->name; + char* buff = *buf + 1; + + size_t namesize = 1; + + for (int x = 0; x < sizeof(linfo->name); ++x) + if (linfo->name[x] != (char)'\0') + ++namesize; + else break; + + memcpy(buff, nom, sizeof(linfo->name)); + uint64_t sid = linfo->sid; + char diff[sizeof(uint64_t)] = { (sid >> 56) & 0xFF, + (sid >> 48) & 0xFF, + (sid >> 40) & 0xFF, + (sid >> 32) & 0xFF, + (sid >> 24) & 0xFF, + (sid >> 16) & 0xFF, + (sid >> 8) & 0xFF, + sid & 0xFF }; + + void* ptrsid = *buf + namesize; + memcpy(ptrsid, &diff, sizeof(uint64_t)); + + *buflen = sizeof(LoginInfo) + 1; +} + +void netprot::Serialize(SelfInfo* sinfo, char* buf[], uint32_t* buflen) { + +} + +void netprot::Serialize(PlayerInfo* pinfo, char* buf[], uint32_t* buflen) { + +} + +void netprot::Serialize(GameInfo* ginfo, char* buf[], uint32_t* buflen) { + +} + +void netprot::Serialize(Chat* chat, char* buf[], uint32_t* buflen) { + +} + +void netprot::Serialize(ErrorLog* errlog, char* buf[], uint32_t* buflen) { + +} + + + +bool netprot::Deserialize(Input* in, char* buf, const uint32_t buflen) { + return false; +} + +bool netprot::Deserialize(Output* out, char* buf, const uint32_t buflen) { + return false; +} + +bool netprot::Deserialize(Sync* sync, char* buf, const uint32_t buflen) { + return false; +} + +bool netprot::Deserialize(TeamInfo* tinfo, char* buf, const uint32_t buflen) { + return false; +} + +bool netprot::Deserialize(LoginInfo* linfo, char* buf, const uint32_t buflen) { + if (buflen < sizeof(sizeof(LoginInfo)) + 1) + return false; + + size_t namesize = 0; + + for (int x = 0; x < sizeof(linfo->name); ++x) + if (buf[x] != (char)'\0') + ++namesize; + else break; + + memcpy((void*)&linfo->name, (void*)&buf[1], namesize); + + uint8_t diff[sizeof(uint64_t)] = { 0,0,0,0,0,0,0,0 }; + memcpy(&diff, &buf[namesize], sizeof(uint64_t)); + linfo->sid = (uint64_t)diff[0] << 56 | + (uint64_t)diff[1] << 48 | + (uint64_t)diff[2] << 40 | + (uint64_t)diff[3] << 32 | + (uint64_t)diff[4] << 24 | + (uint64_t)diff[5] << 16 | + (uint64_t)diff[6] << 8 | + (uint64_t)diff[7]; + + return true; +} + +bool netprot::Deserialize(SelfInfo* sinfo, char* buf, const uint32_t buflen) { + return false; +} + +bool netprot::Deserialize(PlayerInfo* pinfo, char* buf, const uint32_t buflen) { + return false; +} + +bool netprot::Deserialize(GameInfo* ginfo, char* buf, const uint32_t buflen) { + return false; +} + +bool netprot::Deserialize(Chat* chat, char* buf, const uint32_t buflen) { + return false; +} + +bool netprot::Deserialize(ErrorLog* errlog, char* buf, const uint32_t buflen) { + return false; +} + +netprot::PacketType netprot::getType(char* buf, const uint32_t buflen) { + if (buflen < 1 || + buf[0] >= netprot::PACKET_TYPE::LAST_PACK || + buf[0] <= netprot::PACKET_TYPE::ERR) + return netprot::PACKET_TYPE::ERR; + return buf[0]; +} diff --git a/SQCSim-common/netprotocol.h b/SQCSim-common/netprotocol.h index a1beb1c..421c960 100644 --- a/SQCSim-common/netprotocol.h +++ b/SQCSim-common/netprotocol.h @@ -1,98 +1,107 @@ #ifndef NETPROTOCOL_H__ #define NETPROTOCOL_H__ #include "define.h" +#include #include "vector3.h" /* Protocole Particulier de Partie à Plusieurs Personnes (PPPPP) */ +// Packet: packet[0] = PacketType, packet[1..n-1] = {packet} + namespace netprot { typedef uint8_t PacketType; enum PACKET_TYPE { ERR, INPUT, OUTPUT, SYNC, - TEAMINF, SELFINF, PLAYINF, + TEAMINF, SELFINF, PLAYINF, LOGINF, CHUNKMOD, PLAYERMOD, PICKUPMOD, - GAMEINFO, ENDINFO , CHAT, ERRLOG + GAMEINFO, ENDINFO , CHAT, ERRLOG, + LAST_PACK }; - typedef struct { // cli -> srv UDP ~frame + struct Input { // cli -> srv UDP ~frame Timestamp timestamp; - uint8_t keys; // 0bFBLRJS__ + uint8_t keys; // 0bFBLRJS__ bit-packing de bool. Vector3f direction; - } Input; + }; - typedef struct { // srv -> cli UDP ~frame + struct Output { // srv -> cli UDP ~frame Timestamp timestamp; uint64_t id = 0; Vector3f position, - direction; - bool is_shooting, - is_jumping; - } Output; + direction; + uint8_t states; // 0bJSH_____ bit-packing de bool. + }; - typedef struct { // srv -> cli TCP ~second + struct Sync { // srv -> cli TCP ~second Timestamp timestamp; uint64_t sid = 0; uint32_t timer = 0; uint16_t ammo = 0; uint8_t hp = 0; Vector3f position; - } Sync; + }; - typedef struct { // cli <-> srv TCP once + struct TeamInfo { // cli <-> srv TCP once char name[32]; uint64_t id = 0; - } TeamInfo; + }; - typedef struct { // cli <-> srv TCP once + struct LoginInfo { // cli <-> srv TCP once char name[32]; + uint64_t sid = 0; + }; + + struct SelfInfo { // cli <-> srv TCP once uint64_t sid = 0, tid = 0; - } SelfInfo; + }; - typedef struct { // cli <-> srv TCP once + struct PlayerInfo { // cli <-> srv TCP once char name[32]; uint64_t id = 0, tid = 0; - } PlayerInfo; - - typedef struct { // cli <-> srv TCP event (before game start)/ once + }; + + struct GameInfo { // cli <-> srv TCP event (before game start)/ once uint64_t seed; uint32_t countdown; uint8_t gameType; // TOOD: enum. - } GameInfo; + }; - typedef struct { // cli <-> srv TCP event + struct Chat { // cli <-> srv TCP event uint64_t src_id = 0, dest_id = 0, dest_team_id = 0; char mess[140]; // Good 'nough for twitr, good 'nough for me. - } Chat; + }; - typedef struct { // srv -> cli TCP event + struct ErrorLog { // srv -> cli TCP event char mess[140]; bool is_fatal; - } ErrorLog; + }; - inline void Serialize(Input* in, char* buf, uint32_t* buflen); // cli - inline void Serialize(Output* out, char* buf, uint32_t* buflen); // srv - inline void Serialize(Sync* sync, char* buf, uint32_t* buflen); // srv - inline void Serialize(TeamInfo* tinfo, char* buf, uint32_t* buflen); // cli/srv - inline void Serialize(SelfInfo* sinfo, char* buf, uint32_t* buflen); // cli/srv - inline void Serialize(PlayerInfo* pinfo, char* buf, uint32_t* buflen); // srv - inline void Serialize(GameInfo* ginfo, char* buf, uint32_t* buflen); // cli/srv - inline void Serialize(Chat* chat, char* buf, uint32_t* buflen); // cli/srv - inline void Serialize(ErrorLog* errlog, char* buf, uint32_t* buflen); // srv + void Serialize(Input* in, char* buf[], uint32_t* buflen); // cli + void Serialize(Output* out, char* buf[], uint32_t* buflen); // srv + void Serialize(Sync* sync, char* buf[], uint32_t* buflen); // srv + void Serialize(TeamInfo* tinfo, char* buf[], uint32_t* buflen); // cli/srv + void Serialize(LoginInfo* linfo, char* buf[], uint32_t* buflen); // cli/srv + void Serialize(SelfInfo* sinfo, char* buf[], uint32_t* buflen); // cli/srv + void Serialize(PlayerInfo* pinfo, char* buf[], uint32_t* buflen); // srv + void Serialize(GameInfo* ginfo, char* buf[], uint32_t* buflen); // cli/srv + void Serialize(Chat* chat, char* buf[], uint32_t* buflen); // cli/srv + void Serialize(ErrorLog* errlog, char* buf[], uint32_t* buflen); // srv - inline void Deserialize(Input* in, char* buf, uint32_t* buflen); // srv - inline void Deserialize(Output* out, char* buf, uint32_t* buflen); // cli - inline void Deserialize(Sync* sync, char* buf, uint32_t* buflen); // cli - inline void Deserialize(TeamInfo* tinfo, char* buf, uint32_t* buflen); // cli/srv - inline void Deserialize(SelfInfo* sinfo, char* buf, uint32_t* buflen); // cli/srv - inline void Deserialize(PlayerInfo* pinfo, char* buf, uint32_t* buflen); // cli - inline void Deserialize(GameInfo* ginfo, char* buf, uint32_t* buflen); // cli - inline void Deserialize(Chat* chat, char* buf, uint32_t* buflen); // srv/cli - inline void Deserialize(ErrorLog* errlog, char* buf, uint32_t* buflen); // srv + bool Deserialize(Input* in, char* buf, const uint32_t buflen); // srv + bool Deserialize(Output* out, char* buf, const uint32_t buflen); // cli + bool Deserialize(Sync* sync, char* buf, const uint32_t buflen); // cli + bool Deserialize(TeamInfo* tinfo, char* buf, const uint32_t buflen); // cli/srv + bool Deserialize(LoginInfo* linfo, char* buf, const uint32_t buflen); // cli/srv + bool Deserialize(SelfInfo* sinfo, char* buf, const uint32_t buflen); // cli/srv + bool Deserialize(PlayerInfo* pinfo, char* buf, const uint32_t buflen); // cli + bool Deserialize(GameInfo* ginfo, char* buf, const uint32_t buflen); // cli + bool Deserialize(Chat* chat, char* buf, const uint32_t buflen); // srv/cli + bool Deserialize(ErrorLog* errlog, char* buf, const uint32_t buflen); // srv - inline PacketType getType(char* buf, uint32_t* buflen); // srv/cli + PacketType getType(char* buf, uint32_t buflen); // srv/cli } #endif diff --git a/SQCSim-srv/connection.cpp b/SQCSim-srv/connection.cpp index 0e8d252..d6d25bf 100644 --- a/SQCSim-srv/connection.cpp +++ b/SQCSim-srv/connection.cpp @@ -25,19 +25,19 @@ uint64_t Connection::GetTeamHash() const { return m_tid; } std::string Connection::GetName() const { return m_name; } -void Connection::AddInput(Input in) { - m_input_manifest.insert({in.timestamp, in}); +void Connection::AddInput(netprot::Input in) { + m_input_manifest.insert({ in.timestamp, in }); } -Output* Connection::getOutput(Timestamp time) { +netprot::Output* Connection::getOutput(Timestamp time) { auto out = m_output_manifest.find(time); if (out != m_output_manifest.end()) return &out->second; return nullptr; } -Sync Connection::getSync(Timestamp time) { - Sync sync; +netprot::Sync Connection::getSync(Timestamp time) { + netprot::Sync sync; auto out = m_output_manifest.find(time); if (out != m_output_manifest.end()) { sync.timestamp = out->second.timestamp; diff --git a/SQCSim-srv/connection.h b/SQCSim-srv/connection.h index 6db8b06..3ccc285 100644 --- a/SQCSim-srv/connection.h +++ b/SQCSim-srv/connection.h @@ -4,7 +4,7 @@ #include #include "../SQCSim-common/player.h" #include "../SQCSim-common/vector3.h" -#include "../SQCSim-common/serialization.h" +#include "../SQCSim-common/netprotocol.h" #include "define.h" class Connection { @@ -24,14 +24,14 @@ public: uint64_t GetTeamHash() const; std::string GetName() const; - void AddInput(Input in); - Output* getOutput(Timestamp time); - Sync getSync(Timestamp time); + void AddInput(netprot::Input in); + netprot::Output* getOutput(Timestamp time); + netprot::Sync getSync(Timestamp time); void CleanInputManifest(Timestamp time); private: - std::map m_input_manifest; - std::map m_output_manifest; + std::map m_input_manifest; + std::map m_output_manifest; in_addr m_addr; uint64_t m_id, m_sid, diff --git a/SQCSim-srv/main.cpp b/SQCSim-srv/main.cpp index d67d836..a1e104d 100644 --- a/SQCSim-srv/main.cpp +++ b/SQCSim-srv/main.cpp @@ -1,4 +1,3 @@ -#include "define.h" #include "server.h" int main() { diff --git a/SQCSim-srv/server.cpp b/SQCSim-srv/server.cpp index e2740ba..5d391c8 100644 --- a/SQCSim-srv/server.cpp +++ b/SQCSim-srv/server.cpp @@ -79,14 +79,16 @@ int Server::Ready() { Log("À l'écoute sur le port: " + std::to_string(SRV_PORT), false, false); while (!readystart) { - - Log("trololo", false, false); + + // Listen/accept ici. + readystart = true; } return 0; } void Server::Run() { + Log("Partie en cours...", false, false); } @@ -131,6 +133,32 @@ void Server::Log(std::string str, bool is_error = false, bool is_fatal = false) } } +// Test serialize/deserialize: +/* + netprot::LoginInfo* log = new netprot::LoginInfo(); + char nom[] = "Jean Dujardin"; + memcpy(log->name, &nom, sizeof(nom)); + log->sid = 12345; + char* buf = new char[150]; + uint32_t buflen = 150; + + netprot::Serialize(log, &buf, &buflen); + + delete log; + log = new netprot::LoginInfo(); + + bool is_work = netprot::Deserialize(log, buf, buflen); + + std::string str; + + str.append(is_work ? "Y " : "N ").append(log->name).append(": ").append(std::to_string(log->sid)); + + Log(str, false, false); +*/ + + + + ///* Recevoir paquet */ //while (true) { // char buffer[2048]; diff --git a/SQCSim-srv/server.h b/SQCSim-srv/server.h index 2cb0618..298fc0f 100644 --- a/SQCSim-srv/server.h +++ b/SQCSim-srv/server.h @@ -7,6 +7,7 @@ #include "../SQCSim-common/world.h" #include "define.h" #include "connection.h" +#include "../SQCSim-common/netprotocol.h" class Server { public: From 8bc74624c362dac07632eb2ec88a3aabe37e1461 Mon Sep 17 00:00:00 2001 From: MarcEricMartel Date: Mon, 25 Sep 2023 17:17:17 -0400 Subject: [PATCH 07/19] =?UTF-8?q?Socket=20c=C3=B4t=C3=A9=20client!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SQCSim-common/define.h | 1 + SQCSim2021/SQCSim2021.vcxproj | 6 +- SQCSim2021/SQCSim2021.vcxproj.filters | 3 - SQCSim2021/audio.h | 2 +- SQCSim2021/connector.cpp | 49 +++++- SQCSim2021/connector.h | 31 +--- SQCSim2021/define.h | 58 ++----- SQCSim2021/engine.cpp | 8 +- SQCSim2021/engine.h | 2 +- SQCSim2021/matrix4.h | 2 +- SQCSim2021/player.h | 2 +- SQCSim2021/transformation.h | 2 +- SQCSim2021/vector3.h | 219 -------------------------- SQCSim2021/world.h | 2 +- 14 files changed, 74 insertions(+), 313 deletions(-) delete mode 100644 SQCSim2021/vector3.h diff --git a/SQCSim-common/define.h b/SQCSim-common/define.h index 45a7cbb..bafb39e 100644 --- a/SQCSim-common/define.h +++ b/SQCSim-common/define.h @@ -12,6 +12,7 @@ #define CHUNK_SIZE_Z 4 #define MAX_SELECTION_DISTANCE 5 #define SEED 12345 +#define COUNTDOWN 300 #define WORLD_SIZE_X 64 #define WORLD_SIZE_Y 64 diff --git a/SQCSim2021/SQCSim2021.vcxproj b/SQCSim2021/SQCSim2021.vcxproj index ab87f31..9cac270 100644 --- a/SQCSim2021/SQCSim2021.vcxproj +++ b/SQCSim2021/SQCSim2021.vcxproj @@ -38,7 +38,6 @@ - @@ -62,6 +61,11 @@ + + + {ee91ab12-4225-4a4d-931d-69d72f6d91fb} + + {A21FD938-1FEA-4687-AB86-0EABAC30877B} Win32Proj diff --git a/SQCSim2021/SQCSim2021.vcxproj.filters b/SQCSim2021/SQCSim2021.vcxproj.filters index 8d96ac0..60aa0a8 100644 --- a/SQCSim2021/SQCSim2021.vcxproj.filters +++ b/SQCSim2021/SQCSim2021.vcxproj.filters @@ -38,9 +38,6 @@ Fichiers d%27en-tête - - Fichiers d%27en-tête - Fichiers d%27en-tête diff --git a/SQCSim2021/audio.h b/SQCSim2021/audio.h index 9e8e3be..315bfae 100644 --- a/SQCSim2021/audio.h +++ b/SQCSim2021/audio.h @@ -4,7 +4,7 @@ #include #include #include "define.h" -#include "vector3.h" +#include "../SQCSim-common/vector3.h" class Audio { private: diff --git a/SQCSim2021/connector.cpp b/SQCSim2021/connector.cpp index 0bdaa56..4ccc9a0 100644 --- a/SQCSim2021/connector.cpp +++ b/SQCSim2021/connector.cpp @@ -1,18 +1,51 @@ #include "connector.h" -Connector::Connector() -{ -} +Connector::Connector() {} -Connector::~Connector() -{ -} +Connector::~Connector() {} + +int Connector::Init() { +#ifdef _WIN32 + if (WSAStartup(MAKEWORD(2, 2), &m_wsaData) != 0) { /* Initialisation de l'environnement reseau (Windows only) */ + std::cout << "Initialisation WinSock." << std::endl; + return 1; + } +#endif + + m_sock_udp = socket(AF_INET, SOCK_DGRAM, 0); + if (m_sock_udp == INVALID_SOCKET) { /* Creation du socket UDP */ + std::cout << "Creation Socket UDP." << std::endl; + return 2; + } + + m_sock_tcp = socket(AF_INET, SOCK_STREAM, 0); + if (m_sock_tcp == INVALID_SOCKET) { /* Creation du socket TCP */ + std::cout << "Creation Socket TCP." << std::endl; + return 3; + } + + /* Creation structure donnes descripteur du socket serveur */ + sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(SRV_PORT); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + if (bind(m_sock_udp, (sockaddr*)&addr, sizeof(addr)) != 0) { /* Associer le socket UDP au port */ + std::cout << "Association Socket UDP." << std::endl; + return 4; + } + + if (bind(m_sock_tcp, (sockaddr*)&addr, sizeof(addr)) != 0) { /* Associer le socket TCP au port */ + std::cout << "Association Socket TCP." << std::endl; + return 5; + } + + std::cout << "It is the voork!" << std::endl; -int Connector::Init(sockaddr_in srv_addr) { return 0; } -int Connector::Connect(std::string name) { +int Connector::Connect(sockaddr_in srv_addr, std::string name) { return 0; } diff --git a/SQCSim2021/connector.h b/SQCSim2021/connector.h index d2aee73..b9653ad 100644 --- a/SQCSim2021/connector.h +++ b/SQCSim2021/connector.h @@ -2,41 +2,26 @@ #define CONNECTOR_H__ #include "define.h" -#include "vector3.h" - -struct Input { // vers serveur - Timestamp timestamp; - UINT8 keys; // 0bFBLRJS__ - Vector3f direction; -}; - -struct Output { // autres joueurs du serveur - Timestamp timestamp; - UINT64 id = 0; - Vector3f position, direction; - bool is_shooting, is_jumping; -}; - -struct Sync { // du serveur - Timestamp timestamp; - UINT64 sid = 0; - Vector3f position; -}; +#include "../SQCSim-common/netprotocol.h" class Connector { public: Connector(); ~Connector(); - int Init(sockaddr_in srv_addr); - int Connect(std::string name); + int Init(); + int Connect(sockaddr_in srv_addr, std::string name); UINT64 getId() const; unsigned int getSeed() const; //void SendInput(); //int Sync(); private: - SOCKET m_sock = 0; +#ifdef _WIN32 + WSADATA m_wsaData; +#endif + SOCKET m_sock_udp = 0, + m_sock_tcp = 0; std::string m_name = ""; UINT64 m_sid = 0, m_tid = 0; diff --git a/SQCSim2021/define.h b/SQCSim2021/define.h index c26b096..533d6dc 100644 --- a/SQCSim2021/define.h +++ b/SQCSim2021/define.h @@ -1,11 +1,12 @@ -#ifndef DEFINE_H__ -#define DEFINE_H__ +#ifndef CLI_DEFINE_H__ +#define CLI_DEFINE_H__ #include #include #include #include #include +#include "../SQCSim-common/define.h" #ifdef _WIN32 #include @@ -14,21 +15,11 @@ #include #endif -#define NETWORK_TEST false + + +#define NETWORK_TEST true #define SRV_ADDR "127.0.0.1" -#define SRV_PORT 1025 -#define CLI_PORT 1026 - -#define CHUNK_SIZE_X 4 -#define CHUNK_SIZE_Y 64 -#define CHUNK_SIZE_Z 4 -#define MAX_SELECTION_DISTANCE 5 -#define BASE_WIDTH 640 -#define BASE_HEIGHT 480 -#define SEED 12345 - -#define WORLD_SIZE_X 64 -#define WORLD_SIZE_Y 64 +#define COUNTDOWN 300 #define FRAMES_RENDER_CHUNKS 1 #define FRAMES_UPDATE_CHUNKS 1 @@ -38,39 +29,8 @@ #define THREADS_UPDATE_CHUNKS 3 #define THREADS_DELETE_CHUNKS 3 -#define VIEW_DISTANCE 512 -#define TEXTURE_SIZE 512 -#define MAX_BULLETS 512 - -#ifdef _WIN32 - -#pragma comment(lib,"wsock32.lib") // Pour pouvoir faire fonctionner le linker sans le vcxproject - -#include -#include -#include - -#define popen _popen -#define pclose _pclose - -#else // Pas _WIN32 - -#include -#include -#include -#include -#include -#include - -#define SOCKET int -#define INVALID_SOCKET -1 -#define closesocket close - -#endif // _WIN32 - -typedef uint8_t BlockType; -enum BLOCK_TYPE { BTYPE_AIR, BTYPE_DIRT, BTYPE_GRASS, BTYPE_METAL, BTYPE_ICE, BTYPE_LAST }; -typedef std::chrono::system_clock::time_point Timestamp; +#define BASE_WIDTH 640 +#define BASE_HEIGHT 480 #define TEXTURE_PATH "./media/textures/" #define SHADER_PATH "./media/shaders/" diff --git a/SQCSim2021/engine.cpp b/SQCSim2021/engine.cpp index 3173dc0..bd618ae 100644 --- a/SQCSim2021/engine.cpp +++ b/SQCSim2021/engine.cpp @@ -55,11 +55,11 @@ void Engine::Init() { addr.sin_port = htons(SRV_PORT); addr.sin_addr.s_addr = inet_addr(srvaddr); - if (!m_conn->Init(addr)) { - if (m_conn->Connect(playname)) { + if (!m_conn.Init()) { + if (m_conn.Connect(addr, playname)) { // setup jeu en réseau. - seed = m_conn->getSeed(); - std::cout << "ID reçu du serveur: " << std::to_string(m_conn->getId()) << "!" << std::endl; + seed = m_conn.getSeed(); + std::cout << "ID reçu du serveur: " << std::to_string(m_conn.getId()) << "!" << std::endl; } else std::cout << "Erreur de connexion." << std::endl; } diff --git a/SQCSim2021/engine.h b/SQCSim2021/engine.h index faae19d..5ce52ec 100644 --- a/SQCSim2021/engine.h +++ b/SQCSim2021/engine.h @@ -49,7 +49,7 @@ private: void DrawHud(float elapsedTime, BlockType bloc); void PrintText(float x, float y, float scale, const std::string& t); - Connector* m_conn = nullptr; + Connector m_conn; Shader m_shader01; BlockInfo* m_blockinfo[BTYPE_LAST]; TextureAtlas m_textureAtlas = TextureAtlas(BTYPE_LAST); diff --git a/SQCSim2021/matrix4.h b/SQCSim2021/matrix4.h index a0d911a..bf5ac5b 100644 --- a/SQCSim2021/matrix4.h +++ b/SQCSim2021/matrix4.h @@ -6,7 +6,7 @@ #include #include "define.h" -#include "vector3.h" +#include "../SQCSim-common/vector3.h" #ifndef M_PI #define M_PI 3.14159265f diff --git a/SQCSim2021/player.h b/SQCSim2021/player.h index 3466d1a..239b550 100644 --- a/SQCSim2021/player.h +++ b/SQCSim2021/player.h @@ -1,6 +1,6 @@ #ifndef _PLAYER_H__ #define _PLAYER_H__ -#include "vector3.h" +#include "../SQCSim-common/vector3.h" #include "transformation.h" #include "audio.h" #include diff --git a/SQCSim2021/transformation.h b/SQCSim2021/transformation.h index d147a58..764a781 100644 --- a/SQCSim2021/transformation.h +++ b/SQCSim2021/transformation.h @@ -2,7 +2,7 @@ #define TRANSFORMATION_H__ #include "matrix4.h" -#include "vector3.h" +#include "../SQCSim-common/vector3.h" #include class Transformation diff --git a/SQCSim2021/vector3.h b/SQCSim2021/vector3.h deleted file mode 100644 index b191681..0000000 --- a/SQCSim2021/vector3.h +++ /dev/null @@ -1,219 +0,0 @@ -#ifndef VECTOR3_H__ -#define VECTOR3_H__ - -#include -#include - -template -class Vector3 -{ -public: - Vector3(); - Vector3(const T& x, const T& y, const T& z); - ~Vector3(); - - T Length() const; - void Normalize(); - void Zero(); - - T Dot(const Vector3& v) const; - Vector3 Cross(const Vector3& v) const; - - Vector3 operator+(const Vector3& v) const; - Vector3 operator-(const Vector3& v) const; - Vector3 operator-() const; - Vector3 operator+(const T& v) const; - Vector3 operator-(const T& v) const; - Vector3 operator/(const T& v) const; - Vector3 operator*(const T& v) const; - - Vector3& operator=(const Vector3& v); - - Vector3& operator+=(const Vector3& v); - Vector3& operator-=(const Vector3& v); - Vector3& operator+=(const T& v); - Vector3& operator-=(const T& v); - Vector3& operator/=(const T& v); - Vector3& operator*=(const T& v); - - bool operator==(const Vector3& v) const; - bool operator!=(const Vector3& v) const; - - void Afficher() const; - -public: - T x, y, z; -}; - -typedef Vector3 Vector3i; -typedef Vector3 Vector3f; - -template -inline std::ostream& operator<<(std::ostream& out, const Vector3& v) -{ - out << "[" << v.x << ", " << v.y << ", " << v.z << "]"; - return out; -} - - -template -Vector3::Vector3() -{ -} - -template -Vector3::Vector3(const T& x, const T& y, const T& z) : x(x), y(y), z(z) -{ -} - -template -Vector3::~Vector3() -{ -} - -template -T Vector3::Length() const -{ - return sqrt(x*x + y*y + z*z); -} - -template -void Vector3::Normalize() -{ - T len = Length(); - if (len != 0) - { - x /= len; - y /= len; - z /= len; - } -} - -template -void Vector3::Zero() -{ - x = y = z = 0; -} - -template -T Vector3::Dot(const Vector3& v) const -{ - return (x * v.x) + (y * v.y) + (z * v.z); -} - -template -Vector3 Vector3::Cross(const Vector3& v) const -{ - return Vector3( - y * v.z - v.y * z, - z * v.x - v.z * x, - x * v.y - v.x * y); -} - -template -Vector3 Vector3::operator+(const Vector3& v) const -{ - return Vector3(x + v.x, y + v.y, z + v.z); -} - -template -Vector3 Vector3::operator-(const Vector3& v) const -{ - return Vector3(x - v.x, y - v.y, z - v.z); -} - -template -Vector3 Vector3::operator-() const -{ - return Vector3(-x, -y, -z); -} - -template -Vector3 Vector3::operator+(const T& v) const -{ - return Vector3(x + v, y + v, z + v); -} - -template -Vector3 Vector3::operator-(const T& v) const -{ - return Vector3(x - v, y - v, z - v); -} - -template -Vector3 Vector3::operator/(const T& v) const -{ - return Vector3(x / v, y / v, z / v); -} - -template -Vector3 Vector3::operator*(const T& v) const -{ - return Vector3(x * v, y * v, z * v); -} - -template -Vector3& Vector3::operator=(const Vector3& v) -{ - x = v.x; - y = v.y; - z = v.z; - return *this; -} - -template -Vector3& Vector3::operator+=(const Vector3& v) -{ - return (*this = *this + v); -} - -template -Vector3& Vector3::operator-=(const Vector3& v) -{ - return (*this = *this - v); -} - -template -Vector3& Vector3::operator+=(const T& v) -{ - return (*this = *this + v); -} - -template -Vector3& Vector3::operator-=(const T& v) -{ - return (*this = *this - v); -} - -template -Vector3& Vector3::operator/=(const T& v) -{ - return (*this = *this / v); -} - -template -Vector3& Vector3::operator*=(const T& v) -{ - return (*this = *this * v); -} - -template -bool Vector3::operator==(const Vector3& v) const -{ - return (x == v.x && y == v.y && z == v.z); -} - -template -bool Vector3::operator!=(const Vector3& v) const -{ - return !(*this == v); -} - -template -void Vector3::Afficher() const -{ - std::cout << "[" << x << ", " << y << ", " << z << "]" << std::endl; -} - - -#endif // VECTOR3_H__ diff --git a/SQCSim2021/world.h b/SQCSim2021/world.h index 55140e3..486e2d2 100644 --- a/SQCSim2021/world.h +++ b/SQCSim2021/world.h @@ -8,7 +8,7 @@ #include "define.h" #include "chunk.h" #include "array2d.h" -#include "vector3.h" +#include "../SQCSim-common/vector3.h" #include "player.h" #include "transformation.h" #include "shader.h" From 4dbcb08a11ff3ea39b1e5d73b767a24b1dd7fc90 Mon Sep 17 00:00:00 2001 From: MarcEricMartel Date: Wed, 27 Sep 2023 11:24:41 -0400 Subject: [PATCH 08/19] OH YEAH. --- SQCSim-common/define.h | 3 +- SQCSim-common/netprotocol.cpp | 40 +++++++++++++------------- SQCSim-srv/server.cpp | 53 ++++++++++++++++++++++++----------- SQCSim-srv/server.h | 2 +- SQCSim2021/connector.cpp | 45 +++++++++++++++++++++++++---- SQCSim2021/connector.h | 9 +++--- SQCSim2021/define.h | 9 ++---- SQCSim2021/engine.cpp | 17 ++++------- 8 files changed, 112 insertions(+), 66 deletions(-) diff --git a/SQCSim-common/define.h b/SQCSim-common/define.h index bafb39e..968b71d 100644 --- a/SQCSim-common/define.h +++ b/SQCSim-common/define.h @@ -28,7 +28,8 @@ typedef uint64_t Timestamp; #ifdef _WIN32 #pragma comment(lib,"wsock32.lib") // Pour pouvoir faire fonctionner le linker sans le vcxproject - +#pragma comment(lib,"ws2_32.lib") +#include #include #include #include diff --git a/SQCSim-common/netprotocol.cpp b/SQCSim-common/netprotocol.cpp index 5f8aef1..5e95427 100644 --- a/SQCSim-common/netprotocol.cpp +++ b/SQCSim-common/netprotocol.cpp @@ -18,7 +18,6 @@ void netprot::Serialize(TeamInfo* tinfo, char* buf[], uint32_t* buflen) { void netprot::Serialize(LoginInfo* linfo, char* buf[], uint32_t* buflen) { *buf[0] = netprot::PACKET_TYPE::LOGINF; - void* nom = &linfo->name; char* buff = *buf + 1; size_t namesize = 1; @@ -27,22 +26,24 @@ void netprot::Serialize(LoginInfo* linfo, char* buf[], uint32_t* buflen) { if (linfo->name[x] != (char)'\0') ++namesize; else break; - - memcpy(buff, nom, sizeof(linfo->name)); + + memcpy(*buf + 1, &linfo->name, namesize); uint64_t sid = linfo->sid; - char diff[sizeof(uint64_t)] = { (sid >> 56) & 0xFF, + uint8_t diff[sizeof(uint64_t)] = { + (sid >> 56) & 0xFF, (sid >> 48) & 0xFF, (sid >> 40) & 0xFF, (sid >> 32) & 0xFF, (sid >> 24) & 0xFF, (sid >> 16) & 0xFF, (sid >> 8) & 0xFF, - sid & 0xFF }; + sid & 0xFF + }; - void* ptrsid = *buf + namesize; - memcpy(ptrsid, &diff, sizeof(uint64_t)); + char* ptrsid = *buf + namesize + 2; + memcpy(ptrsid, diff, sizeof(uint64_t)); - *buflen = sizeof(LoginInfo) + 1; + *buflen = namesize + sizeof(uint64_t) + 2; } void netprot::Serialize(SelfInfo* sinfo, char* buf[], uint32_t* buflen) { @@ -84,10 +85,10 @@ bool netprot::Deserialize(TeamInfo* tinfo, char* buf, const uint32_t buflen) { } bool netprot::Deserialize(LoginInfo* linfo, char* buf, const uint32_t buflen) { - if (buflen < sizeof(sizeof(LoginInfo)) + 1) + if (buflen < sizeof(LoginInfo) + 3) return false; - size_t namesize = 0; + size_t namesize = 1; for (int x = 0; x < sizeof(linfo->name); ++x) if (buf[x] != (char)'\0') @@ -97,15 +98,16 @@ bool netprot::Deserialize(LoginInfo* linfo, char* buf, const uint32_t buflen) { memcpy((void*)&linfo->name, (void*)&buf[1], namesize); uint8_t diff[sizeof(uint64_t)] = { 0,0,0,0,0,0,0,0 }; - memcpy(&diff, &buf[namesize], sizeof(uint64_t)); - linfo->sid = (uint64_t)diff[0] << 56 | - (uint64_t)diff[1] << 48 | - (uint64_t)diff[2] << 40 | - (uint64_t)diff[3] << 32 | - (uint64_t)diff[4] << 24 | - (uint64_t)diff[5] << 16 | - (uint64_t)diff[6] << 8 | - (uint64_t)diff[7]; + memcpy(diff, &buf[namesize + 1], sizeof(uint64_t)); + linfo->sid = + (uint64_t)diff[0] << 56 | + (uint64_t)diff[1] << 48 | + (uint64_t)diff[2] << 40 | + (uint64_t)diff[3] << 32 | + (uint64_t)diff[4] << 24 | + (uint64_t)diff[5] << 16 | + (uint64_t)diff[6] << 8 | + (uint64_t)diff[7]; return true; } diff --git a/SQCSim-srv/server.cpp b/SQCSim-srv/server.cpp index 5d391c8..7e7171a 100644 --- a/SQCSim-srv/server.cpp +++ b/SQCSim-srv/server.cpp @@ -73,16 +73,41 @@ int Server::Ready() { return 1; } - char buffer[2048]; + char* buf = new char[150]; + uint32_t buflen = 150; bool readystart = false; Log("À l'écoute sur le port: " + std::to_string(SRV_PORT), false, false); while (!readystart) { + sockaddr_in sockad; + int addrlen = sizeof(sockad); + SOCKET sock = accept(m_sock_tcp, (sockaddr*)&sockad, &addrlen); - // Listen/accept ici. + if (sock < 0) + Log("Erreur de connexion", true, false); + else if (sock > 0) { + char* strbuf = new char[150]; + uint32_t strbuflen = 150; + + std::cout << inet_ntop(AF_INET, &sockad.sin_addr, strbuf, strbuflen) << ':' << std::to_string(sockad.sin_port) << std::endl;; + if (recv(sock, buf, buflen, 0) > 0) { + netprot::LoginInfo log; + if (netprot::Deserialize(&log, buf, buflen)) { + std::string str; + log.sid = 8675309; // EIGHT SIX SEVENFIVE THREE AUGHT NIIIIIIIIIiiIIIIiINE! + + str.append(log.name).append(": ").append(std::to_string(log.sid)); + Log(str, false, false); - readystart = true; + netprot::Serialize(&log, &buf, &buflen); + send(sock, buf, buflen, 0); + + std::cin.getline(nullptr, 1); + readystart = true; + } + } + } } return 0; } @@ -118,7 +143,7 @@ void Server::Log(std::string str, bool is_error = false, bool is_fatal = false) break; case LOG_DEST::CONSOLE: default: - std::cout << Timestamp() << (is_fatal? "FATAL ": "") << (is_error ? "ERROR ": "") << str << std::endl; + std::cout << Timestamp() << (is_fatal ? "FATAL " : "") << (is_error ? "ERROR " : "") << str << std::endl; break; } @@ -135,30 +160,24 @@ void Server::Log(std::string str, bool is_error = false, bool is_fatal = false) // Test serialize/deserialize: /* - netprot::LoginInfo* log = new netprot::LoginInfo(); - char nom[] = "Jean Dujardin"; - memcpy(log->name, &nom, sizeof(nom)); - log->sid = 12345; + netprot::LoginInfo log, log2; + std::cout << "Nom? "; + std::cin.getline(log.name, 32); // NO! STD::CIN >> VARIABLE;! EVEEEEEERRRR!!! + log.sid = 12345; char* buf = new char[150]; uint32_t buflen = 150; - netprot::Serialize(log, &buf, &buflen); + netprot::Serialize(&log, &buf, &buflen); - delete log; - log = new netprot::LoginInfo(); - - bool is_work = netprot::Deserialize(log, buf, buflen); + bool is_work = netprot::Deserialize(&log2, buf, buflen); std::string str; - - str.append(is_work ? "Y " : "N ").append(log->name).append(": ").append(std::to_string(log->sid)); + str.append(is_work ? "Y " : "N ").append(log2.name).append(": ").append(std::to_string(log2.sid)); Log(str, false, false); */ - - ///* Recevoir paquet */ //while (true) { // char buffer[2048]; diff --git a/SQCSim-srv/server.h b/SQCSim-srv/server.h index 298fc0f..b79b805 100644 --- a/SQCSim-srv/server.h +++ b/SQCSim-srv/server.h @@ -5,9 +5,9 @@ #include #include #include "../SQCSim-common/world.h" +#include "../SQCSim-common/netprotocol.h" #include "define.h" #include "connection.h" -#include "../SQCSim-common/netprotocol.h" class Server { public: diff --git a/SQCSim2021/connector.cpp b/SQCSim2021/connector.cpp index 4ccc9a0..a2fd522 100644 --- a/SQCSim2021/connector.cpp +++ b/SQCSim2021/connector.cpp @@ -27,7 +27,7 @@ int Connector::Init() { /* Creation structure donnes descripteur du socket serveur */ sockaddr_in addr; addr.sin_family = AF_INET; - addr.sin_port = htons(SRV_PORT); + addr.sin_port = htons(CLI_PORT); addr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(m_sock_udp, (sockaddr*)&addr, sizeof(addr)) != 0) { /* Associer le socket UDP au port */ @@ -39,16 +39,49 @@ int Connector::Init() { std::cout << "Association Socket TCP." << std::endl; return 5; } - - std::cout << "It is the voork!" << std::endl; - return 0; } -int Connector::Connect(sockaddr_in srv_addr, std::string name) { +int Connector::Connect(char* srv_addr, std::string name) { + sockaddr_in add; + add.sin_family = AF_INET; + add.sin_port = htons(SRV_PORT); + + if (inet_pton(AF_INET, srv_addr, &add.sin_addr) <= 0) { + std::cout << "Addresse serveur invalide." << std::endl; + return 1; + } + + if (connect(m_sock_tcp, (sockaddr*)&add, sizeof(add)) < 0) { + std::cout << "Échec de la connexion." << std::endl; + } + + char* buf = new char[150]; + uint32_t buflen = 150; + netprot::LoginInfo log, retlog; + log.sid = 0; + memcpy(&log.name, name.c_str(), name.size() + 1); + + netprot::Serialize(&log, &buf, &buflen); + + int se = send(m_sock_tcp, buf, buflen, 0); + + delete[] buf; + buf = new char[150] {0}; + buflen = 150; + + while (recv(m_sock_tcp, buf, buflen, 0) < se) {} + + if (!netprot::Deserialize(&retlog, buf, buflen)) { + std::cout << "Packet invalide." << std::endl; + return 2; + } + + m_name = retlog.name; + m_sid = retlog.sid; return 0; } -UINT64 Connector::getId() const { return m_sid; } +uint64_t Connector::getId() const { return m_sid; } unsigned int Connector::getSeed() const { return m_seed; } diff --git a/SQCSim2021/connector.h b/SQCSim2021/connector.h index b9653ad..f3ed6bd 100644 --- a/SQCSim2021/connector.h +++ b/SQCSim2021/connector.h @@ -3,6 +3,7 @@ #include "define.h" #include "../SQCSim-common/netprotocol.h" +#include class Connector { public: @@ -10,8 +11,8 @@ public: ~Connector(); int Init(); - int Connect(sockaddr_in srv_addr, std::string name); - UINT64 getId() const; + int Connect(char* srv_addr, std::string name); + uint64_t getId() const; unsigned int getSeed() const; //void SendInput(); @@ -23,9 +24,9 @@ private: SOCKET m_sock_udp = 0, m_sock_tcp = 0; std::string m_name = ""; - UINT64 m_sid = 0, + uint64_t m_sid = 0, m_tid = 0; - unsigned int m_seed = 12345; + unsigned int m_seed = 0; }; #endif diff --git a/SQCSim2021/define.h b/SQCSim2021/define.h index 533d6dc..ceeb08c 100644 --- a/SQCSim2021/define.h +++ b/SQCSim2021/define.h @@ -1,23 +1,20 @@ #ifndef CLI_DEFINE_H__ #define CLI_DEFINE_H__ -#include -#include #include #include #include +#include +#include #include "../SQCSim-common/define.h" #ifdef _WIN32 -#include #include #include #include #endif - - -#define NETWORK_TEST true +#define NETWORK_TEST false #define SRV_ADDR "127.0.0.1" #define COUNTDOWN 300 diff --git a/SQCSim2021/engine.cpp b/SQCSim2021/engine.cpp index bd618ae..b30818f 100644 --- a/SQCSim2021/engine.cpp +++ b/SQCSim2021/engine.cpp @@ -40,26 +40,20 @@ void Engine::Init() { m_skybox.Init(0.2f); // Objet de musique! - m_audio.ToggleMusicState(); + //m_audio.ToggleMusicState(); // Array pour les balles. for (int x = 0; x < MAX_BULLETS; ++x) m_bullets[x] = nullptr; - uint64_t seed = 12345; - std::string playname = "John Test"; - const char srvaddr[] = "127.0.0.1"; + uint64_t seed = 0; + std::string playname = "La Chienne à Jacques"; if (NETWORK_TEST) { // Test connexion réseau. - sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_port = htons(SRV_PORT); - addr.sin_addr.s_addr = inet_addr(srvaddr); - if (!m_conn.Init()) { - if (m_conn.Connect(addr, playname)) { + if (!m_conn.Connect(SRV_ADDR, playname)) { // setup jeu en réseau. - seed = m_conn.getSeed(); std::cout << "ID reçu du serveur: " << std::to_string(m_conn.getId()) << "!" << std::endl; + //seed = m_conn.getSeed(); } else std::cout << "Erreur de connexion." << std::endl; } @@ -210,7 +204,6 @@ void Engine::DisplayHud(int timer) { ss << "Time: " << (int)(timer / 60) << ":" << std::setw(2) << std::setfill('0') << timer % 60; PrintText(Width() - Width() * 0.15, Height() - (Height() / 19.2), scale, ss.str()); - } void Engine::DisplayInfo(float elapsedTime, BlockType bloc) { From 7eabee38adeb3d2f7fcf9e536bdd11ed7e2b99b8 Mon Sep 17 00:00:00 2001 From: MarcEricMartel Date: Wed, 27 Sep 2023 15:44:56 -0400 Subject: [PATCH 09/19] Cleanup --- SQCSim-common/netprotocol.cpp | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/SQCSim-common/netprotocol.cpp b/SQCSim-common/netprotocol.cpp index 5e95427..fef687c 100644 --- a/SQCSim-common/netprotocol.cpp +++ b/SQCSim-common/netprotocol.cpp @@ -18,14 +18,8 @@ void netprot::Serialize(TeamInfo* tinfo, char* buf[], uint32_t* buflen) { void netprot::Serialize(LoginInfo* linfo, char* buf[], uint32_t* buflen) { *buf[0] = netprot::PACKET_TYPE::LOGINF; - char* buff = *buf + 1; - size_t namesize = 1; - - for (int x = 0; x < sizeof(linfo->name); ++x) - if (linfo->name[x] != (char)'\0') - ++namesize; - else break; + size_t namesize = std::strlen(linfo->name) + 1; memcpy(*buf + 1, &linfo->name, namesize); uint64_t sid = linfo->sid; @@ -40,8 +34,7 @@ void netprot::Serialize(LoginInfo* linfo, char* buf[], uint32_t* buflen) { sid & 0xFF }; - char* ptrsid = *buf + namesize + 2; - memcpy(ptrsid, diff, sizeof(uint64_t)); + memcpy(*buf + namesize + 2, diff, sizeof(uint64_t)); *buflen = namesize + sizeof(uint64_t) + 2; } @@ -88,14 +81,12 @@ bool netprot::Deserialize(LoginInfo* linfo, char* buf, const uint32_t buflen) { if (buflen < sizeof(LoginInfo) + 3) return false; - size_t namesize = 1; + size_t namesize = std::strlen(buf) + 1; - for (int x = 0; x < sizeof(linfo->name); ++x) - if (buf[x] != (char)'\0') - ++namesize; - else break; + if (namesize > 32) + return false; - memcpy((void*)&linfo->name, (void*)&buf[1], namesize); + memcpy(&linfo->name, &buf[1], namesize); uint8_t diff[sizeof(uint64_t)] = { 0,0,0,0,0,0,0,0 }; memcpy(diff, &buf[namesize + 1], sizeof(uint64_t)); From df52d4a084ee54bde25dc9f7ce4e88a518221fb7 Mon Sep 17 00:00:00 2001 From: MarcEricMartel Date: Wed, 27 Sep 2023 17:34:25 -0400 Subject: [PATCH 10/19] =?UTF-8?q?Redressage=20des=20classes;=20seed=20est?= =?UTF-8?q?=20re=C3=A7ue=20du=20serveur?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SQCSim-common/netprotocol.cpp | 118 +++++++++++++++++++++++++++++++--- SQCSim-common/netprotocol.h | 10 +-- SQCSim-srv/connection.cpp | 32 +++++---- SQCSim-srv/connection.h | 21 +++--- SQCSim-srv/server.cpp | 27 ++++++-- SQCSim2021/connector.cpp | 50 ++++++++------ SQCSim2021/connector.h | 14 ++-- SQCSim2021/engine.cpp | 3 +- 8 files changed, 198 insertions(+), 77 deletions(-) diff --git a/SQCSim-common/netprotocol.cpp b/SQCSim-common/netprotocol.cpp index fef687c..d7f7675 100644 --- a/SQCSim-common/netprotocol.cpp +++ b/SQCSim-common/netprotocol.cpp @@ -23,7 +23,7 @@ void netprot::Serialize(LoginInfo* linfo, char* buf[], uint32_t* buflen) { memcpy(*buf + 1, &linfo->name, namesize); uint64_t sid = linfo->sid; - uint8_t diff[sizeof(uint64_t)] = { + uint8_t sid8[sizeof(uint64_t)] = { (sid >> 56) & 0xFF, (sid >> 48) & 0xFF, (sid >> 40) & 0xFF, @@ -34,13 +34,23 @@ void netprot::Serialize(LoginInfo* linfo, char* buf[], uint32_t* buflen) { sid & 0xFF }; - memcpy(*buf + namesize + 2, diff, sizeof(uint64_t)); + memcpy(*buf + namesize + 2, sid8, sizeof(uint64_t)); - *buflen = namesize + sizeof(uint64_t) + 2; -} + uint64_t tid = linfo->tid; + uint8_t tid8[sizeof(uint64_t)] = { + (tid >> 56) & 0xFF, + (tid >> 48) & 0xFF, + (tid >> 40) & 0xFF, + (tid >> 32) & 0xFF, + (tid >> 24) & 0xFF, + (tid >> 16) & 0xFF, + (tid >> 8) & 0xFF, + tid & 0xFF + }; -void netprot::Serialize(SelfInfo* sinfo, char* buf[], uint32_t* buflen) { + memcpy(*buf + namesize + 2 + sizeof(uint64_t), tid8, sizeof(uint64_t)); + *buflen = namesize + sizeof(uint64_t) * 2 + 2; } void netprot::Serialize(PlayerInfo* pinfo, char* buf[], uint32_t* buflen) { @@ -48,7 +58,51 @@ void netprot::Serialize(PlayerInfo* pinfo, char* buf[], uint32_t* buflen) { } void netprot::Serialize(GameInfo* ginfo, char* buf[], uint32_t* buflen) { + *buf[0] = netprot::PACKET_TYPE::GAMEINFO; + uint64_t game = ginfo->seed; + uint8_t seed8[sizeof(uint64_t)] = { + (game >> 56) & 0xFF, + (game >> 48) & 0xFF, + (game >> 40) & 0xFF, + (game >> 32) & 0xFF, + (game >> 24) & 0xFF, + (game >> 16) & 0xFF, + (game >> 8) & 0xFF, + game & 0xFF + }; + + memcpy(*buf + 1, seed8, sizeof(uint64_t)); + + game = ginfo->countdown; + uint8_t count8[sizeof(uint64_t)] = { + (game >> 56) & 0xFF, + (game >> 48) & 0xFF, + (game >> 40) & 0xFF, + (game >> 32) & 0xFF, + (game >> 24) & 0xFF, + (game >> 16) & 0xFF, + (game >> 8) & 0xFF, + game & 0xFF + }; + + memcpy(*buf + sizeof(uint64_t) + 1, count8, sizeof(uint64_t)); + + game = ginfo->countdown; + uint8_t gtype8[sizeof(uint64_t)] = { + (game >> 56) & 0xFF, + (game >> 48) & 0xFF, + (game >> 40) & 0xFF, + (game >> 32) & 0xFF, + (game >> 24) & 0xFF, + (game >> 16) & 0xFF, + (game >> 8) & 0xFF, + game & 0xFF + }; + + memcpy(*buf + sizeof(uint64_t) + 1, gtype8, sizeof(uint64_t)); + + *buflen = sizeof(uint64_t) * 3 + 1; } void netprot::Serialize(Chat* chat, char* buf[], uint32_t* buflen) { @@ -100,11 +154,18 @@ bool netprot::Deserialize(LoginInfo* linfo, char* buf, const uint32_t buflen) { (uint64_t)diff[6] << 8 | (uint64_t)diff[7]; - return true; -} + memcpy(diff, &buf[namesize + sizeof(uint64_t) + 1], sizeof(uint64_t)); + linfo->tid = + (uint64_t)diff[0] << 56 | + (uint64_t)diff[1] << 48 | + (uint64_t)diff[2] << 40 | + (uint64_t)diff[3] << 32 | + (uint64_t)diff[4] << 24 | + (uint64_t)diff[5] << 16 | + (uint64_t)diff[6] << 8 | + (uint64_t)diff[7]; -bool netprot::Deserialize(SelfInfo* sinfo, char* buf, const uint32_t buflen) { - return false; + return true; } bool netprot::Deserialize(PlayerInfo* pinfo, char* buf, const uint32_t buflen) { @@ -112,7 +173,44 @@ bool netprot::Deserialize(PlayerInfo* pinfo, char* buf, const uint32_t buflen) { } bool netprot::Deserialize(GameInfo* ginfo, char* buf, const uint32_t buflen) { - return false; + if (buflen < sizeof(GameInfo) + 1) + return false; + + uint8_t diff[sizeof(uint64_t)] = { 0,0,0,0,0,0,0,0 }; + memcpy(diff, &buf[1], sizeof(uint64_t)); + ginfo->seed = + (uint64_t)diff[0] << 56 | + (uint64_t)diff[1] << 48 | + (uint64_t)diff[2] << 40 | + (uint64_t)diff[3] << 32 | + (uint64_t)diff[4] << 24 | + (uint64_t)diff[5] << 16 | + (uint64_t)diff[6] << 8 | + (uint64_t)diff[7]; + + memcpy(diff, &buf[sizeof(uint64_t) + 1], sizeof(uint64_t)); + ginfo->countdown = + (uint64_t)diff[0] << 56 | + (uint64_t)diff[1] << 48 | + (uint64_t)diff[2] << 40 | + (uint64_t)diff[3] << 32 | + (uint64_t)diff[4] << 24 | + (uint64_t)diff[5] << 16 | + (uint64_t)diff[6] << 8 | + (uint64_t)diff[7]; + + memcpy(diff, &buf[sizeof(uint64_t) * 2 + 1], sizeof(uint64_t)); + ginfo->gameType = + (uint64_t)diff[0] << 56 | + (uint64_t)diff[1] << 48 | + (uint64_t)diff[2] << 40 | + (uint64_t)diff[3] << 32 | + (uint64_t)diff[4] << 24 | + (uint64_t)diff[5] << 16 | + (uint64_t)diff[6] << 8 | + (uint64_t)diff[7]; + + return true; } bool netprot::Deserialize(Chat* chat, char* buf, const uint32_t buflen) { diff --git a/SQCSim-common/netprotocol.h b/SQCSim-common/netprotocol.h index 421c960..3e237f7 100644 --- a/SQCSim-common/netprotocol.h +++ b/SQCSim-common/netprotocol.h @@ -48,12 +48,8 @@ namespace netprot { struct LoginInfo { // cli <-> srv TCP once char name[32]; - uint64_t sid = 0; - }; - - struct SelfInfo { // cli <-> srv TCP once - uint64_t sid = 0, - tid = 0; + uint64_t sid = 0, + tid = 0; }; struct PlayerInfo { // cli <-> srv TCP once @@ -85,7 +81,6 @@ namespace netprot { void Serialize(Sync* sync, char* buf[], uint32_t* buflen); // srv void Serialize(TeamInfo* tinfo, char* buf[], uint32_t* buflen); // cli/srv void Serialize(LoginInfo* linfo, char* buf[], uint32_t* buflen); // cli/srv - void Serialize(SelfInfo* sinfo, char* buf[], uint32_t* buflen); // cli/srv void Serialize(PlayerInfo* pinfo, char* buf[], uint32_t* buflen); // srv void Serialize(GameInfo* ginfo, char* buf[], uint32_t* buflen); // cli/srv void Serialize(Chat* chat, char* buf[], uint32_t* buflen); // cli/srv @@ -96,7 +91,6 @@ namespace netprot { bool Deserialize(Sync* sync, char* buf, const uint32_t buflen); // cli bool Deserialize(TeamInfo* tinfo, char* buf, const uint32_t buflen); // cli/srv bool Deserialize(LoginInfo* linfo, char* buf, const uint32_t buflen); // cli/srv - bool Deserialize(SelfInfo* sinfo, char* buf, const uint32_t buflen); // cli/srv bool Deserialize(PlayerInfo* pinfo, char* buf, const uint32_t buflen); // cli bool Deserialize(GameInfo* ginfo, char* buf, const uint32_t buflen); // cli bool Deserialize(Chat* chat, char* buf, const uint32_t buflen); // srv/cli diff --git a/SQCSim-srv/connection.cpp b/SQCSim-srv/connection.cpp index d6d25bf..356396f 100644 --- a/SQCSim-srv/connection.cpp +++ b/SQCSim-srv/connection.cpp @@ -1,29 +1,27 @@ #include "connection.h" -Connection::Connection(in_addr addr, - std::string name, - uint64_t id, - uint64_t self_id, - uint64_t team_id): - m_addr(addr), - m_id(id), - m_sid(self_id), - m_tid(team_id), - m_name(name) { + + +Connection::Connection(SOCKET sock, + sockaddr_in sockaddr, + netprot::LoginInfo log, + netprot::PlayerInfo play): + m_sock(sock), + m_addr(sockaddr), + m_loginfo(log), + m_playinfo(play) { } Connection::~Connection() { - + closesocket(m_sock); } -in_addr Connection::GetAddr() const { return m_addr; } +uint64_t Connection::GetHash(bool self) const { return self? m_loginfo.sid: m_playinfo.id; } -uint64_t Connection::GetHash(bool self) const { return self? m_sid: m_id; } +uint64_t Connection::GetTeamHash() const { return m_loginfo.tid; } -uint64_t Connection::GetTeamHash() const { return m_tid; } - -std::string Connection::GetName() const { return m_name; } +std::string Connection::GetName() const { return m_loginfo.name; } void Connection::AddInput(netprot::Input in) { m_input_manifest.insert({ in.timestamp, in }); @@ -42,7 +40,7 @@ netprot::Sync Connection::getSync(Timestamp time) { if (out != m_output_manifest.end()) { sync.timestamp = out->second.timestamp; sync.position = out->second.position; - sync.sid = m_sid; + sync.sid = m_loginfo.sid; } return sync; } diff --git a/SQCSim-srv/connection.h b/SQCSim-srv/connection.h index 3ccc285..dc1df11 100644 --- a/SQCSim-srv/connection.h +++ b/SQCSim-srv/connection.h @@ -10,16 +10,14 @@ class Connection { public: Connection( - in_addr addr, - std::string name, - uint64_t hash, - uint64_t self_hash, - uint64_t team_hash); + SOCKET sock, + sockaddr_in sockaddr, + netprot::LoginInfo log, + netprot::PlayerInfo play); ~Connection(); Player* player = nullptr; - in_addr GetAddr() const; uint64_t GetHash(bool self = true) const; uint64_t GetTeamHash() const; std::string GetName() const; @@ -32,11 +30,12 @@ public: private: std::map m_input_manifest; std::map m_output_manifest; - in_addr m_addr; - uint64_t m_id, - m_sid, - m_tid; - std::string m_name; + std::map m_chatlog; + + SOCKET m_sock; + sockaddr_in m_addr; + netprot::LoginInfo m_loginfo; + netprot::PlayerInfo m_playinfo; }; #endif \ No newline at end of file diff --git a/SQCSim-srv/server.cpp b/SQCSim-srv/server.cpp index 7e7171a..d283e92 100644 --- a/SQCSim-srv/server.cpp +++ b/SQCSim-srv/server.cpp @@ -76,6 +76,7 @@ int Server::Ready() { char* buf = new char[150]; uint32_t buflen = 150; bool readystart = false; + srand(time(NULL)); Log("À l'écoute sur le port: " + std::to_string(SRV_PORT), false, false); @@ -87,23 +88,35 @@ int Server::Ready() { if (sock < 0) Log("Erreur de connexion", true, false); else if (sock > 0) { + std::string str = "Nouvelle connection provenant de: "; char* strbuf = new char[150]; uint32_t strbuflen = 150; - - std::cout << inet_ntop(AF_INET, &sockad.sin_addr, strbuf, strbuflen) << ':' << std::to_string(sockad.sin_port) << std::endl;; + + str.append(inet_ntop(AF_INET, &sockad.sin_addr, strbuf, strbuflen)).append(": ").append(std::to_string(sockad.sin_port)); + if (recv(sock, buf, buflen, 0) > 0) { netprot::LoginInfo log; if (netprot::Deserialize(&log, buf, buflen)) { - std::string str; - log.sid = 8675309; // EIGHT SIX SEVENFIVE THREE AUGHT NIIIIIIIIIiiIIIIiINE! + log.sid = ((uint64_t)rand() << 25) % 8675309; // EIGHT SIX SEVENFIVE THREE AUGHT NIIIIIIIIIiiIIIIiINE! + log.tid = 123456789; + str.append(" Nom: ").append(log.name); + Log(str, false, false); + str = ""; - str.append(log.name).append(": ").append(std::to_string(log.sid)); + str.append(log.name).append(" SID: [").append(std::to_string(log.sid).append("]")); Log(str, false, false); netprot::Serialize(&log, &buf, &buflen); send(sock, buf, buflen, 0); - - std::cin.getline(nullptr, 1); + buflen = 150; + Sleep(300); + netprot::GameInfo ginfo; + ginfo.countdown = 360; + ginfo.gameType = 1; + ginfo.seed = 9370707; + netprot::Serialize(&ginfo, &buf, &buflen); + send(sock, buf, buflen, 0); + Sleep(10000); readystart = true; } } diff --git a/SQCSim2021/connector.cpp b/SQCSim2021/connector.cpp index a2fd522..31e1b81 100644 --- a/SQCSim2021/connector.cpp +++ b/SQCSim2021/connector.cpp @@ -44,44 +44,58 @@ int Connector::Init() { int Connector::Connect(char* srv_addr, std::string name) { sockaddr_in add; - add.sin_family = AF_INET; - add.sin_port = htons(SRV_PORT); + m_srvsockaddr.sin_family = AF_INET; + m_srvsockaddr.sin_port = htons(SRV_PORT); - if (inet_pton(AF_INET, srv_addr, &add.sin_addr) <= 0) { + if (inet_pton(AF_INET, srv_addr, &m_srvsockaddr.sin_addr) <= 0) { std::cout << "Addresse serveur invalide." << std::endl; return 1; } - - if (connect(m_sock_tcp, (sockaddr*)&add, sizeof(add)) < 0) { + + if (connect(m_sock_tcp, (sockaddr*)&m_srvsockaddr, sizeof(m_srvsockaddr)) < 0) { std::cout << "Échec de la connexion." << std::endl; } - + char* buf = new char[150]; uint32_t buflen = 150; - netprot::LoginInfo log, retlog; - log.sid = 0; + netprot::LoginInfo log; memcpy(&log.name, name.c_str(), name.size() + 1); netprot::Serialize(&log, &buf, &buflen); - + int se = send(m_sock_tcp, buf, buflen, 0); delete[] buf; buf = new char[150] {0}; buflen = 150; + int rpack = 0; - while (recv(m_sock_tcp, buf, buflen, 0) < se) {} + while (rpack < 2) { + recv(m_sock_tcp, buf, buflen, 0); - if (!netprot::Deserialize(&retlog, buf, buflen)) { - std::cout << "Packet invalide." << std::endl; - return 2; + switch (netprot::getType(buf, buflen)) { + case netprot::PACKET_TYPE::LOGINF: + if (!netprot::Deserialize(&m_loginfo, buf, buflen)) { + std::cout << "Packet LoginInfo invalide." << std::endl; + return 2; + } + ++rpack; + break; + case netprot::PACKET_TYPE::GAMEINFO: + if (!netprot::Deserialize(&m_gameinfo, buf, buflen)) { + std::cout << "Packet GameInfo invalide." << std::endl; + return 3; + } + ++rpack; + break; + default: + std::cout << "Packet invalide." << std::endl; + break; + } } - - m_name = retlog.name; - m_sid = retlog.sid; return 0; } -uint64_t Connector::getId() const { return m_sid; } +uint64_t Connector::getId() const { return m_loginfo.sid; } -unsigned int Connector::getSeed() const { return m_seed; } +unsigned int Connector::getSeed() const { return m_gameinfo.seed; } diff --git a/SQCSim2021/connector.h b/SQCSim2021/connector.h index f3ed6bd..166c07b 100644 --- a/SQCSim2021/connector.h +++ b/SQCSim2021/connector.h @@ -21,12 +21,16 @@ private: #ifdef _WIN32 WSADATA m_wsaData; #endif + std::map m_inputmanifest; + std::map m_players; + std::map m_teams; + + netprot::LoginInfo m_loginfo; + netprot::GameInfo m_gameinfo; + + sockaddr_in m_srvsockaddr; SOCKET m_sock_udp = 0, - m_sock_tcp = 0; - std::string m_name = ""; - uint64_t m_sid = 0, - m_tid = 0; - unsigned int m_seed = 0; + m_sock_tcp = 0; }; #endif diff --git a/SQCSim2021/engine.cpp b/SQCSim2021/engine.cpp index b30818f..a20a720 100644 --- a/SQCSim2021/engine.cpp +++ b/SQCSim2021/engine.cpp @@ -53,7 +53,8 @@ void Engine::Init() { if (!m_conn.Connect(SRV_ADDR, playname)) { // setup jeu en réseau. std::cout << "ID reçu du serveur: " << std::to_string(m_conn.getId()) << "!" << std::endl; - //seed = m_conn.getSeed(); + std::cout << "Seed reçu du serveur: " << std::to_string(m_conn.getSeed()) << "!" << std::endl; + seed = m_conn.getSeed(); } else std::cout << "Erreur de connexion." << std::endl; } From 7e043eb9f87691fffe846693c533444bba1f801a Mon Sep 17 00:00:00 2001 From: MarcEricMartel Date: Thu, 28 Sep 2023 09:15:39 -0400 Subject: [PATCH 11/19] Cleanup --- SQCSim-srv/define.h | 3 +++ SQCSim-srv/server.cpp | 61 ++++++++++++++++++++++++++++++------------- SQCSim-srv/server.h | 20 +++++++++++++- 3 files changed, 65 insertions(+), 19 deletions(-) diff --git a/SQCSim-srv/define.h b/SQCSim-srv/define.h index 297bd15..77ca08e 100644 --- a/SQCSim-srv/define.h +++ b/SQCSim-srv/define.h @@ -8,6 +8,9 @@ #include #define MAX_CONNECTIONS 16 +#define ID_LIST_SIZE 127 +#define BUFFER_LENGTH 150 + typedef unsigned char LogDest; enum LOG_DEST { CONSOLE, LOGFILE, LOG_LAST }; diff --git a/SQCSim-srv/server.cpp b/SQCSim-srv/server.cpp index d283e92..17c5585 100644 --- a/SQCSim-srv/server.cpp +++ b/SQCSim-srv/server.cpp @@ -61,9 +61,6 @@ int Server::Init() { return 5; } - for (auto& conn : m_conn) - conn = nullptr; - return 0; } @@ -80,6 +77,12 @@ int Server::Ready() { Log("À l'écoute sur le port: " + std::to_string(SRV_PORT), false, false); + buildIdList(ID_LIST_SIZE); + + m_game.countdown = 360; + m_game.gameType = 1; + m_game.seed = 9370707; + while (!readystart) { sockaddr_in sockad; int addrlen = sizeof(sockad); @@ -89,16 +92,19 @@ int Server::Ready() { Log("Erreur de connexion", true, false); else if (sock > 0) { std::string str = "Nouvelle connection provenant de: "; - char* strbuf = new char[150]; - uint32_t strbuflen = 150; + char* strbuf = new char[BUFFER_LENGTH]; + uint32_t strbuflen = BUFFER_LENGTH; str.append(inet_ntop(AF_INET, &sockad.sin_addr, strbuf, strbuflen)).append(": ").append(std::to_string(sockad.sin_port)); if (recv(sock, buf, buflen, 0) > 0) { netprot::LoginInfo log; + netprot::PlayerInfo play; if (netprot::Deserialize(&log, buf, buflen)) { - log.sid = ((uint64_t)rand() << 25) % 8675309; // EIGHT SIX SEVENFIVE THREE AUGHT NIIIIIIIIIiiIIIIiINE! - log.tid = 123456789; + log.sid = getUniqueId(); + + log.tid = 0; + str.append(" Nom: ").append(log.name); Log(str, false, false); str = ""; @@ -106,17 +112,21 @@ int Server::Ready() { str.append(log.name).append(" SID: [").append(std::to_string(log.sid).append("]")); Log(str, false, false); - netprot::Serialize(&log, &buf, &buflen); - send(sock, buf, buflen, 0); - buflen = 150; - Sleep(300); - netprot::GameInfo ginfo; - ginfo.countdown = 360; - ginfo.gameType = 1; - ginfo.seed = 9370707; - netprot::Serialize(&ginfo, &buf, &buflen); - send(sock, buf, buflen, 0); - Sleep(10000); + //netprot::Serialize(&log, &buf, &buflen); + //send(sock, buf, buflen, 0); + //buflen = 150; + sendPack(sock, &log, &buf, &buflen); + + play.id = getUniqueId(); + memcpy(play.name, log.name, std::strlen(log.name) + 1); + play.tid = log.tid; + + //netprot::Serialize(&m_game, &buf, &buflen); + //send(sock, buf, buflen, 0); + sendPack(sock, &m_game, &buf, &buflen); + Connection* conn = new Connection(sock, sockad, log, play); + + m_players[log.sid] = conn; readystart = true; } } @@ -171,6 +181,21 @@ void Server::Log(std::string str, bool is_error = false, bool is_fatal = false) } } +void Server::buildIdList(size_t size) { + std::set lst; + + do lst.insert(((uint64_t)rand() << 25) % 8675309); // EIGHT SIX SEVENFIVE THREE AUGHT NIIIIIIiIIiiIiINE! + while (lst.size() < size); + + m_ids = std::vector(lst.begin(), lst.end()); +} + +uint64_t Server::getUniqueId() { + uint64_t id = m_ids.back(); + m_ids.pop_back(); + return id; +} + // Test serialize/deserialize: /* netprot::LoginInfo log, log2; diff --git a/SQCSim-srv/server.h b/SQCSim-srv/server.h index b79b805..425a996 100644 --- a/SQCSim-srv/server.h +++ b/SQCSim-srv/server.h @@ -3,6 +3,7 @@ #include #include +#include #include #include "../SQCSim-common/world.h" #include "../SQCSim-common/netprotocol.h" @@ -27,10 +28,27 @@ private: LogDest m_log; std::ofstream m_logfile; - Connection* m_conn[MAX_CONNECTIONS]; + std::map m_players; + std::map m_chatlog; + std::vector m_ids; + netprot::GameInfo m_game; + World* m_world = nullptr; std::string Timestamp(); void Log(std::string str, bool is_error, bool is_fatal); + void buildIdList(size_t size); + + uint64_t getUniqueId(); + template void sendPack(SOCKET sock, T* pack, char** buf, uint32_t* buflen); + }; + +template +void Server::sendPack(SOCKET sock, T* pack, char** buf, uint32_t* buflen) { + netprot::Serialize(pack, buf, buflen); + send(sock, *buf, *buflen, 0); + *buflen = BUFFER_LENGTH; +} + #endif From 954b976f2759f2aa2479f30f3109a99c2acf44b3 Mon Sep 17 00:00:00 2001 From: MarcEricMartel Date: Fri, 29 Sep 2023 12:04:08 -0400 Subject: [PATCH 12/19] cleanup de fichiers en double --- SQCSim-common/bullet.cpp | 4 + SQCSim-common/bullet.h | 1 + SQCSim-common/define.h | 2 +- SQCSim-common/netprotocol.cpp | 233 ++- SQCSim-common/netprotocol.h | 23 +- SQCSim2021/SQCSim2021.vcxproj | 7 - SQCSim2021/SQCSim2021.vcxproj.filters | 51 +- SQCSim2021/array2d.h | 61 - SQCSim2021/array3d.h | 55 - SQCSim2021/blockinfo.cpp | 42 - SQCSim2021/blockinfo.h | 32 - SQCSim2021/chunk.h | 8 +- SQCSim2021/connector.h | 4 +- SQCSim2021/engine.cpp | 2 +- SQCSim2021/engine.h | 6 +- SQCSim2021/matrix4.h | 571 ------ SQCSim2021/opensimplex.cpp | 2542 ------------------------- SQCSim2021/opensimplex.h | 51 - SQCSim2021/player.h | 4 +- SQCSim2021/transformation.h | 5 +- SQCSim2021/vertexbuffer.h | 2 +- SQCSim2021/world.h | 8 +- 22 files changed, 287 insertions(+), 3427 deletions(-) delete mode 100644 SQCSim2021/array2d.h delete mode 100644 SQCSim2021/array3d.h delete mode 100644 SQCSim2021/blockinfo.cpp delete mode 100644 SQCSim2021/blockinfo.h delete mode 100644 SQCSim2021/matrix4.h delete mode 100644 SQCSim2021/opensimplex.cpp delete mode 100644 SQCSim2021/opensimplex.h diff --git a/SQCSim-common/bullet.cpp b/SQCSim-common/bullet.cpp index c06c9e7..52824d5 100644 --- a/SQCSim-common/bullet.cpp +++ b/SQCSim-common/bullet.cpp @@ -30,3 +30,7 @@ void Bullet::Transpose(int& x, int& z) { m_startpos.x -= x * CHUNK_SIZE_X; m_startpos.z -= z * CHUNK_SIZE_Z; } + +Vector3f& Bullet::getPos() { + return m_currentpos; +} diff --git a/SQCSim-common/bullet.h b/SQCSim-common/bullet.h index de2e14c..599eab6 100644 --- a/SQCSim-common/bullet.h +++ b/SQCSim-common/bullet.h @@ -12,6 +12,7 @@ public: bool Update(World* world, float elapsedtime); void Transpose(int& x, int& z); + Vector3f& getPos(); private: Vector3f m_startpos; diff --git a/SQCSim-common/define.h b/SQCSim-common/define.h index 968b71d..0342359 100644 --- a/SQCSim-common/define.h +++ b/SQCSim-common/define.h @@ -11,7 +11,7 @@ #define CHUNK_SIZE_Y 64 #define CHUNK_SIZE_Z 4 #define MAX_SELECTION_DISTANCE 5 -#define SEED 12345 +#define SEED 0 #define COUNTDOWN 300 #define WORLD_SIZE_X 64 diff --git a/SQCSim-common/netprotocol.cpp b/SQCSim-common/netprotocol.cpp index d7f7675..b692201 100644 --- a/SQCSim-common/netprotocol.cpp +++ b/SQCSim-common/netprotocol.cpp @@ -13,7 +13,26 @@ void netprot::Serialize(Sync* sync, char* buf[], uint32_t* buflen) { } void netprot::Serialize(TeamInfo* tinfo, char* buf[], uint32_t* buflen) { + *buf[0] = netprot::PACKET_TYPE::TEAMINF; + size_t namesize = std::strlen(tinfo->name) + 1; + + memcpy(*buf + 1, &tinfo->name, namesize); + uint64_t tid = tinfo->id; + uint8_t tid8[sizeof(uint64_t)] = { + (tid >> 56) & 0xFF, + (tid >> 48) & 0xFF, + (tid >> 40) & 0xFF, + (tid >> 32) & 0xFF, + (tid >> 24) & 0xFF, + (tid >> 16) & 0xFF, + (tid >> 8) & 0xFF, + tid & 0xFF + }; + + memcpy(*buf + namesize + 2, tid8, sizeof(uint64_t)); + + *buflen = namesize + sizeof(uint64_t) + 2; } void netprot::Serialize(LoginInfo* linfo, char* buf[], uint32_t* buflen) { @@ -54,7 +73,40 @@ void netprot::Serialize(LoginInfo* linfo, char* buf[], uint32_t* buflen) { } void netprot::Serialize(PlayerInfo* pinfo, char* buf[], uint32_t* buflen) { + *buf[0] = netprot::PACKET_TYPE::PLAYINF; + size_t namesize = std::strlen(pinfo->name) + 1; + + memcpy(*buf + 1, &pinfo->name, namesize); + uint64_t id = pinfo->id; + uint8_t id8[sizeof(uint64_t)] = { + (id >> 56) & 0xFF, + (id >> 48) & 0xFF, + (id >> 40) & 0xFF, + (id >> 32) & 0xFF, + (id >> 24) & 0xFF, + (id >> 16) & 0xFF, + (id >> 8) & 0xFF, + id & 0xFF + }; + + memcpy(*buf + namesize + 2, id8, sizeof(uint64_t)); + + uint64_t tid = pinfo->tid; + uint8_t tid8[sizeof(uint64_t)] = { + (tid >> 56) & 0xFF, + (tid >> 48) & 0xFF, + (tid >> 40) & 0xFF, + (tid >> 32) & 0xFF, + (tid >> 24) & 0xFF, + (tid >> 16) & 0xFF, + (tid >> 8) & 0xFF, + tid & 0xFF + }; + + memcpy(*buf + namesize + 2 + sizeof(uint64_t), tid8, sizeof(uint64_t)); + + *buflen = namesize + sizeof(uint64_t) * 2 + 2; } void netprot::Serialize(GameInfo* ginfo, char* buf[], uint32_t* buflen) { @@ -106,11 +158,67 @@ void netprot::Serialize(GameInfo* ginfo, char* buf[], uint32_t* buflen) { } void netprot::Serialize(Chat* chat, char* buf[], uint32_t* buflen) { + *buf[0] = netprot::PACKET_TYPE::CHAT; + uint64_t src = chat->src_id; + uint8_t src8[sizeof(uint64_t)] = { + (src >> 56) & 0xFF, + (src >> 48) & 0xFF, + (src >> 40) & 0xFF, + (src >> 32) & 0xFF, + (src >> 24) & 0xFF, + (src >> 16) & 0xFF, + (src >> 8) & 0xFF, + src & 0xFF + }; + + memcpy(*buf + 1, src8, sizeof(uint64_t)); + + uint64_t dst = chat->dest_id; + uint8_t dst8[sizeof(uint64_t)] = { + (dst >> 56) & 0xFF, + (dst >> 48) & 0xFF, + (dst >> 40) & 0xFF, + (dst >> 32) & 0xFF, + (dst >> 24) & 0xFF, + (dst >> 16) & 0xFF, + (dst >> 8) & 0xFF, + dst & 0xFF + }; + + memcpy(*buf + 1 + sizeof(uint64_t), dst8, sizeof(uint64_t)); + + uint64_t dstteam = chat->dest_id; + uint8_t dstt8[sizeof(uint64_t)] = { + (dstteam >> 56) & 0xFF, + (dstteam >> 48) & 0xFF, + (dstteam >> 40) & 0xFF, + (dstteam >> 32) & 0xFF, + (dstteam >> 24) & 0xFF, + (dstteam >> 16) & 0xFF, + (dstteam >> 8) & 0xFF, + dstteam & 0xFF + }; + + memcpy(*buf + 1 + sizeof(uint64_t) * 2, dstt8, sizeof(uint64_t)); + + size_t messize = std::strlen(chat->mess) + 1; + + memcpy(*buf + 1 + sizeof(uint64_t) * 3, &chat->mess, messize); + + *buflen = messize + sizeof(uint64_t) * 3 + 2; } void netprot::Serialize(ErrorLog* errlog, char* buf[], uint32_t* buflen) { - + *buf[0] = netprot::PACKET_TYPE::ERRLOG; + + size_t messize = std::strlen(errlog->mess) + 1; + + memcpy(*buf + 1, &errlog->mess, messize); + + memcpy(*buf + 1 + messize, &errlog->is_fatal, sizeof(bool)); + + *buflen = messize + sizeof(bool) + 1; } @@ -128,11 +236,33 @@ bool netprot::Deserialize(Sync* sync, char* buf, const uint32_t buflen) { } bool netprot::Deserialize(TeamInfo* tinfo, char* buf, const uint32_t buflen) { - return false; + if (buflen <= sizeof(LoginInfo)) + return false; + + size_t namesize = std::strlen(buf) + 1; + + if (namesize > 32) + return false; + + memcpy(&tinfo->name, &buf[1], namesize); + + uint8_t diff[sizeof(uint64_t)] = { 0,0,0,0,0,0,0,0 }; + memcpy(diff, &buf[namesize + 1], sizeof(uint64_t)); + tinfo->id = + (uint64_t)diff[0] << 56 | + (uint64_t)diff[1] << 48 | + (uint64_t)diff[2] << 40 | + (uint64_t)diff[3] << 32 | + (uint64_t)diff[4] << 24 | + (uint64_t)diff[5] << 16 | + (uint64_t)diff[6] << 8 | + (uint64_t)diff[7]; + + return true; } bool netprot::Deserialize(LoginInfo* linfo, char* buf, const uint32_t buflen) { - if (buflen < sizeof(LoginInfo) + 3) + if (buflen <= sizeof(LoginInfo)) return false; size_t namesize = std::strlen(buf) + 1; @@ -169,11 +299,44 @@ bool netprot::Deserialize(LoginInfo* linfo, char* buf, const uint32_t buflen) { } bool netprot::Deserialize(PlayerInfo* pinfo, char* buf, const uint32_t buflen) { - return false; + if (buflen <= sizeof(PlayerInfo)) + return false; + + size_t namesize = std::strlen(buf) + 1; + + if (namesize > 32) + return false; + + memcpy(&pinfo->name, &buf[1], namesize); + + uint8_t diff[sizeof(uint64_t)] = { 0,0,0,0,0,0,0,0 }; + memcpy(diff, &buf[namesize + 1], sizeof(uint64_t)); + pinfo->id = + (uint64_t)diff[0] << 56 | + (uint64_t)diff[1] << 48 | + (uint64_t)diff[2] << 40 | + (uint64_t)diff[3] << 32 | + (uint64_t)diff[4] << 24 | + (uint64_t)diff[5] << 16 | + (uint64_t)diff[6] << 8 | + (uint64_t)diff[7]; + + memcpy(diff, &buf[namesize + sizeof(uint64_t) + 1], sizeof(uint64_t)); + pinfo->tid = + (uint64_t)diff[0] << 56 | + (uint64_t)diff[1] << 48 | + (uint64_t)diff[2] << 40 | + (uint64_t)diff[3] << 32 | + (uint64_t)diff[4] << 24 | + (uint64_t)diff[5] << 16 | + (uint64_t)diff[6] << 8 | + (uint64_t)diff[7]; + + return true; } bool netprot::Deserialize(GameInfo* ginfo, char* buf, const uint32_t buflen) { - if (buflen < sizeof(GameInfo) + 1) + if (buflen <= sizeof(GameInfo)) return false; uint8_t diff[sizeof(uint64_t)] = { 0,0,0,0,0,0,0,0 }; @@ -214,11 +377,67 @@ bool netprot::Deserialize(GameInfo* ginfo, char* buf, const uint32_t buflen) { } bool netprot::Deserialize(Chat* chat, char* buf, const uint32_t buflen) { - return false; + if (buflen <= sizeof(Chat)) + return false; + + uint8_t src[sizeof(uint64_t)] = { 0,0,0,0,0,0,0,0 }; + memcpy(src, &buf[1], sizeof(uint64_t)); + chat->src_id = + (uint64_t)src[0] << 56 | + (uint64_t)src[1] << 48 | + (uint64_t)src[2] << 40 | + (uint64_t)src[3] << 32 | + (uint64_t)src[4] << 24 | + (uint64_t)src[5] << 16 | + (uint64_t)src[6] << 8 | + (uint64_t)src[7]; + + uint8_t dst[sizeof(uint64_t)] = { 0,0,0,0,0,0,0,0 }; + memcpy(dst, &buf[1 + sizeof(uint64_t)], sizeof(uint64_t)); + chat->dest_id = + (uint64_t)dst[0] << 56 | + (uint64_t)dst[1] << 48 | + (uint64_t)dst[2] << 40 | + (uint64_t)dst[3] << 32 | + (uint64_t)dst[4] << 24 | + (uint64_t)dst[5] << 16 | + (uint64_t)dst[6] << 8 | + (uint64_t)dst[7]; + + uint8_t dstt[sizeof(uint64_t)] = { 0,0,0,0,0,0,0,0 }; + memcpy(dstt, &buf[1 + sizeof(uint64_t) * 2], sizeof(uint64_t)); + chat->dest_team_id = + (uint64_t)dstt[0] << 56 | + (uint64_t)dstt[1] << 48 | + (uint64_t)dstt[2] << 40 | + (uint64_t)dstt[3] << 32 | + (uint64_t)dstt[4] << 24 | + (uint64_t)dstt[5] << 16 | + (uint64_t)dstt[6] << 8 | + (uint64_t)dstt[7]; + + size_t messsize = std::strlen(buf + sizeof(uint64_t) * 3) + 1; + + if (messsize > 140) + return false; + + memcpy(&chat->mess, &buf[1 + sizeof(uint64_t) * 3], messsize); + return true; } bool netprot::Deserialize(ErrorLog* errlog, char* buf, const uint32_t buflen) { - return false; + if (buflen <= sizeof(ErrorLog)) + return false; + + size_t messsize = std::strlen(buf) + 1; + + if (messsize > 140) + return false; + + memcpy(&errlog->mess, &buf[1], messsize); + memcpy(&errlog->is_fatal, &buf[1 + messsize], sizeof(bool)); + + return true; } netprot::PacketType netprot::getType(char* buf, const uint32_t buflen) { diff --git a/SQCSim-common/netprotocol.h b/SQCSim-common/netprotocol.h index 3e237f7..7f93349 100644 --- a/SQCSim-common/netprotocol.h +++ b/SQCSim-common/netprotocol.h @@ -18,9 +18,26 @@ namespace netprot { LAST_PACK }; + struct Keys { + bool forward, + backward, + left, + right, + jump, + shoot; + }; + + struct States { + bool jumping, + shooting, + hit, + powerup; + }; + struct Input { // cli -> srv UDP ~frame Timestamp timestamp; - uint8_t keys; // 0bFBLRJS__ bit-packing de bool. + uint64_t sid = 0; + Keys keys; // 0bFBLRJS__ bit-packing de bool. Vector3f direction; }; @@ -28,8 +45,8 @@ namespace netprot { Timestamp timestamp; uint64_t id = 0; Vector3f position, - direction; - uint8_t states; // 0bJSH_____ bit-packing de bool. + direction; + States states; // 0bJSH_____ bit-packing de bool. }; struct Sync { // srv -> cli TCP ~second diff --git a/SQCSim2021/SQCSim2021.vcxproj b/SQCSim2021/SQCSim2021.vcxproj index 9cac270..005bcc5 100644 --- a/SQCSim2021/SQCSim2021.vcxproj +++ b/SQCSim2021/SQCSim2021.vcxproj @@ -19,18 +19,13 @@ - - - - - @@ -43,14 +38,12 @@ - - diff --git a/SQCSim2021/SQCSim2021.vcxproj.filters b/SQCSim2021/SQCSim2021.vcxproj.filters index 60aa0a8..ec7b7d6 100644 --- a/SQCSim2021/SQCSim2021.vcxproj.filters +++ b/SQCSim2021/SQCSim2021.vcxproj.filters @@ -11,12 +11,6 @@ - - Fichiers d%27en-tête - - - Fichiers d%27en-tête - Fichiers d%27en-tête @@ -26,12 +20,6 @@ Fichiers d%27en-tête - - Fichiers d%27en-tête - - - Fichiers d%27en-tête - Fichiers d%27en-tête @@ -44,15 +32,9 @@ Fichiers d%27en-tête - - Fichiers d%27en-tête - Fichiers d%27en-tête - - Fichiers d%27en-tête - Fichiers d%27en-tête @@ -65,20 +47,20 @@ Fichiers d%27en-tête - - Fichiers d%27en-tête - - - Fichiers d%27en-tête - Fichiers d%27en-tête + + Fichiers d%27en-tête + + + Fichiers d%27en-tête + + + Fichiers d%27en-tête + - - Fichiers sources - Fichiers sources @@ -103,9 +85,6 @@ Fichiers sources - - Fichiers sources - Fichiers sources @@ -121,14 +100,14 @@ Fichiers sources - - Fichiers sources - - - Fichiers sources - Fichiers sources + + Fichiers sources + + + Fichiers sources + \ No newline at end of file diff --git a/SQCSim2021/array2d.h b/SQCSim2021/array2d.h deleted file mode 100644 index 7451365..0000000 --- a/SQCSim2021/array2d.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef ARRAY2D_H__ -#define ARRAY2D_H__ - -#include "define.h" - -template -class Array2d { - public: - Array2d(int x, int y); - ~Array2d(); - Array2d(const Array2d& array); - - void Set(int x, int y, T type); - T Get(int x, int y) const; - T Remove(int x, int y); - - void Reset(T type); - - private: - int m_x, m_y; - T* m_array; - - int To1dIndex(int x, int y) const; -}; - -template -Array2d::Array2d(int x, int y) : m_x(x), m_y(y) { m_array = new T[m_x * m_y]; } - -template -Array2d::~Array2d() { delete[] m_array; } - -template -Array2d::Array2d(const Array2d& array) : m_x(array.m_x), m_y(array.m_y) { - m_array = new T[m_x * m_y]; - for (int i = 0; i < m_x * m_y; ++i) - m_array[i] = array.m_array[i]; -} - -template -void Array2d::Set(int x, int y, T type) { m_array[To1dIndex(x, y)] = type; } - -template -T Array2d::Get(int x, int y) const { return m_array[To1dIndex(x, y)]; } - -template -T Array2d::Remove(int x, int y) { - T thing = std::move(m_array[To1dIndex(x, y)]); - m_array[To1dIndex(x, y)] = nullptr; - return thing; -} - -template -void Array2d::Reset(T type) { - for (int i = 0; i < m_x * m_y; ++i) - m_array[i] = type; -} - -template -int Array2d::To1dIndex(int x, int y) const { return x + (y * m_x); } - -#endif // ARRAY2D_H__ diff --git a/SQCSim2021/array3d.h b/SQCSim2021/array3d.h deleted file mode 100644 index 97603ef..0000000 --- a/SQCSim2021/array3d.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef ARRAY3D_H__ -#define ARRAY3D_H__ - -#include "define.h" - -template -class Array3d { - public: - Array3d(int x, int y, int z); - ~Array3d(); - Array3d(const Array3d& array); - - void Set(int x, int y, int z, T type); - T Get(int x, int y, int z) const; - - void Reset(T type); - - private: - int m_x, m_y, m_z; - T* m_array; - - int To1dIndex(int x, int y, int z) const; -}; - -template -Array3d::Array3d(int x, int y, int z) : m_x(x), m_y(y), m_z(z) { m_array = new T[m_x * m_y * m_z]; } - -template -Array3d::~Array3d() { delete[] m_array; } - -template -Array3d::Array3d(const Array3d& array) : m_x(array.m_x), m_y(array.m_y), m_z(array.m_z) { - m_array = new T[m_x * m_y * m_z]; - for (int i = 0; i < m_x * m_y * m_z; ++i) - m_array[i] = array.m_array[i]; -} - -template -void Array3d::Set(int x, int y, int z, T type) { - m_array[To1dIndex(x, y, z)] = type; -} - -template -T Array3d::Get(int x, int y, int z) const { return m_array[To1dIndex(x, y, z)]; } - -template -void Array3d::Reset(T type) { - for (int i = 0; i < m_x * m_y * m_z; ++i) - m_array[i] = type; -} - -template -int Array3d::To1dIndex(int x, int y, int z) const { return x + (z * m_x) + (y * m_z * m_x); } - -#endif // ARRAY3D_H__ diff --git a/SQCSim2021/blockinfo.cpp b/SQCSim2021/blockinfo.cpp deleted file mode 100644 index 1e42825..0000000 --- a/SQCSim2021/blockinfo.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "blockinfo.h" -#include - -BlockInfo::BlockInfo(BlockType type, const std::string& name, float u, float v, float s, int dur) : m_type(type), m_name(name), m_u(u), m_v(v), m_s(s), m_durability(dur) -{ -} - -BlockInfo::~BlockInfo() -{ -} - -BlockType BlockInfo::GetType() const -{ - return m_type; -} - -void BlockInfo::SetDurability(int durability) -{ - m_durability = durability; -} - -int BlockInfo::GetDurability() const -{ - return m_durability; -} - -void BlockInfo::GetTexture(float& u, float& v, float& s) -{ - u = m_u; - v = m_v; - s = m_s; -} - -void BlockInfo::Show() const -{ - std::cout << "Type: " << m_type << std::endl; - std::cout << "Nom: " << m_name << std::endl; - std::cout << "Durabilite: " << m_durability << std::endl; - std::cout << "Coordonnees Texture: " << m_u << ", " << m_v << ", " << m_s << std::endl; -} - - diff --git a/SQCSim2021/blockinfo.h b/SQCSim2021/blockinfo.h deleted file mode 100644 index 8a2afe7..0000000 --- a/SQCSim2021/blockinfo.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef BLOCKINFO_H__ -#define BLOCKINFO_H__ - -#include -#include "define.h" - -class BlockInfo -{ - public: - BlockInfo(BlockType type, const std::string& name, float u, float v, float s, int dur); - ~BlockInfo(); - - BlockType GetType() const; - - void SetDurability(int durability); - int GetDurability() const; - - void GetTexture(float& u, float& v, float& s); - - void Show() const; - - private: - BlockType m_type; - float m_u; - float m_v; - float m_s; - std::string m_name; - int m_durability; - -}; - -#endif // BLOCKINFO_H__ diff --git a/SQCSim2021/chunk.h b/SQCSim2021/chunk.h index 2ce15e8..995b3f6 100644 --- a/SQCSim2021/chunk.h +++ b/SQCSim2021/chunk.h @@ -1,11 +1,11 @@ #ifndef CHUNK_H__ #define CHUNK_H__ #include "define.h" -#include "array3d.h" -#include "array2d.h" +#include "../SQCSim-common/array2d.h" +#include "../SQCSim-common/array3d.h" +#include "../SQCSim-common/blockinfo.h" +#include "../SQCSim-common/opensimplex.h" #include "vertexbuffer.h" -#include "blockinfo.h" -#include "opensimplex.h" class World; diff --git a/SQCSim2021/connector.h b/SQCSim2021/connector.h index 166c07b..297be6f 100644 --- a/SQCSim2021/connector.h +++ b/SQCSim2021/connector.h @@ -1,9 +1,9 @@ #ifndef CONNECTOR_H__ #define CONNECTOR_H__ -#include "define.h" -#include "../SQCSim-common/netprotocol.h" #include +#include "../SQCSim-common/netprotocol.h" +#include "define.h" class Connector { public: diff --git a/SQCSim2021/engine.cpp b/SQCSim2021/engine.cpp index a20a720..5f21e66 100644 --- a/SQCSim2021/engine.cpp +++ b/SQCSim2021/engine.cpp @@ -46,7 +46,7 @@ void Engine::Init() { for (int x = 0; x < MAX_BULLETS; ++x) m_bullets[x] = nullptr; - uint64_t seed = 0; + uint64_t seed = SEED; std::string playname = "La Chienne à Jacques"; if (NETWORK_TEST) { // Test connexion réseau. if (!m_conn.Init()) { diff --git a/SQCSim2021/engine.h b/SQCSim2021/engine.h index 5ce52ec..5220cf8 100644 --- a/SQCSim2021/engine.h +++ b/SQCSim2021/engine.h @@ -3,7 +3,10 @@ #include #include +#include "../SQCSim-common/array2d.h" +#include "../SQCSim-common/blockinfo.h" #include "define.h" +#include "bullet.h" #include "openglcontext.h" #include "texture.h" #include "transformation.h" @@ -13,10 +16,7 @@ #include "skybox.h" #include "audio.h" #include "textureatlas.h" -#include "blockinfo.h" -#include "array2d.h" #include "world.h" -#include "bullet.h" #include "connector.h" class Engine : public OpenglContext { diff --git a/SQCSim2021/matrix4.h b/SQCSim2021/matrix4.h deleted file mode 100644 index bf5ac5b..0000000 --- a/SQCSim2021/matrix4.h +++ /dev/null @@ -1,571 +0,0 @@ -#ifndef MATRIX4_H__ -#define MATRIX4_H__ - -#include -#include -#include - -#include "define.h" -#include "../SQCSim-common/vector3.h" - -#ifndef M_PI -#define M_PI 3.14159265f -#endif - -#define DEGTORAD(x) ((x * M_PI) / 180.f) -#define RADTODEG(x) ((180.f * x) / M_PI) - -template -class Matrix4 -{ - public: - typedef T Type; - - public: - static const Matrix4 ZERO; - static const Matrix4 IDENTITY; - - public: - Matrix4(); - Matrix4(const T& v); - Matrix4(const Matrix4& m); - Matrix4(const T& m_11, const T& m_12, const T& m_13, const T& m_14, - const T& m_21, const T& m_22, const T& m_23, const T& m_24, - const T& m_31, const T& m_32, const T& m_33, const T& m_34, - const T& m_41, const T& m_42, const T& m_43, const T& m_44); - - const T& Get11() const; - const T& Get12() const; - const T& Get13() const; - const T& Get14() const; - const T& Get21() const; - const T& Get22() const; - const T& Get23() const; - const T& Get24() const; - const T& Get31() const; - const T& Get32() const; - const T& Get33() const; - const T& Get34() const; - const T& Get41() const; - const T& Get42() const; - const T& Get43() const; - const T& Get44() const; - - Matrix4& operator=(const Matrix4& m); - - Matrix4 operator+(const Matrix4& m) const; - const Matrix4& operator+=(const Matrix4& m); - - Matrix4 operator-(const Matrix4& m) const; - Matrix4 operator-() const; - const Matrix4& operator-=(const Matrix4& m); - - Matrix4 operator*(const Matrix4& m) const; - Matrix4 operator*(const T& v) const; - const Matrix4& operator*=(const Matrix4& m); - const Matrix4& operator*=(const T& v); - - Matrix4 operator/(const T& v) const; - const Matrix4& operator/=(const T& v); - - bool operator==(const Matrix4& m) const; - bool operator!=(const Matrix4& m) const; - - void SetZero(); - void SetIdentity(); - void SetPerspectiveProjection(const T& fov, const T& aspect, const T& nearPlane, const T& farPlane); - void SetOrthographicProjection(const T& left, const T& right, const T& bottom, const T& top, const T& nearPlane, const T& farPlane); - - void SetLookAt(const Vector3& eyePosition, const Vector3& lookAtPosition, Vector3 upVector = Vector3(T(0), T(1), T(0))); - - bool IsZero() const; - bool IsIdentity() const; - - void ApplyTranslation(const T& x, const T& y, const T& z); - void ApplyRotation(const T& angle, const T& x, const T& y, const T& z); - void ApplyScale(const T& x, const T& y, const T& z); - - Vector3 GetTranslation() const; - - const T* GetInternalValues() const; - T* GetInternalValues(); - std::string ToString(const std::string& lineBegin = "|", const std::string& lineEnd = "|\n") const; - - private: - union { - // column-major matrix - struct - { - T m_11, m_21, m_31, m_41, m_12, m_22, m_32, m_42, m_13, m_23, m_33, m_43, m_14, m_24, m_34, m_44; - }; - T m_values[16]; - }; -}; - -typedef Matrix4 Matrix4i; -typedef Matrix4 Matrix4f; -typedef Matrix4 Matrix4d; - -template -const Matrix4 Matrix4::ZERO = Matrix4(0); - -template -const Matrix4 Matrix4::IDENTITY = Matrix4( - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1); - - template -std::ostream& operator<<(std::ostream& out, const Matrix4& m) -{ - out << m.ToString(); - return out; -} - - template -Matrix4::Matrix4() -{ - // Leave matrix uninitialized -} - - template -Matrix4::Matrix4(const T& v) -{ - for(int i = 0; i < 16; ++i) - m_values[i] = v; -} - - template -Matrix4::Matrix4(const Matrix4& m) -{ - for(int i = 0; i < 16; ++i) - m_values[i] = m.m_values[i]; -} - - template -Matrix4::Matrix4(const T& m_11, const T& m_12, const T& m_13, const T& m_14, - const T& m_21, const T& m_22, const T& m_23, const T& m_24, - const T& m_31, const T& m_32, const T& m_33, const T& m_34, - const T& m_41, const T& m_42, const T& m_43, const T& m_44) -{ - this->m_11 = m_11; - this->m_12 = m_12; - this->m_13 = m_13; - this->m_14 = m_14; - this->m_21 = m_21; - this->m_22 = m_22; - this->m_23 = m_23; - this->m_24 = m_24; - this->m_31 = m_31; - this->m_32 = m_32; - this->m_33 = m_33; - this->m_34 = m_34; - this->m_41 = m_41; - this->m_42 = m_42; - this->m_43 = m_43; - this->m_44 = m_44; -} - -template -const T& Matrix4::Get11() const -{ - return m_11; -} - -template -const T& Matrix4::Get12() const -{ - return m_12; -} - -template -const T& Matrix4::Get13() const -{ - return m_13; -} - -template -const T& Matrix4::Get14() const -{ - return m_14; -} - -template -const T& Matrix4::Get21() const -{ - return m_21; -} - -template -const T& Matrix4::Get22() const -{ - return m_22; -} - -template -const T& Matrix4::Get23() const -{ - return m_23; -} - -template -const T& Matrix4::Get24() const -{ - return m_24; -} - -template -const T& Matrix4::Get31() const -{ - return m_31; -} - -template -const T& Matrix4::Get32() const -{ - return m_32; -} - -template -const T& Matrix4::Get33() const -{ - return m_33; -} - -template -const T& Matrix4::Get34() const -{ - return m_34; -} - -template -const T& Matrix4::Get41() const -{ - return m_41; -} - -template -const T& Matrix4::Get42() const -{ - return m_42; -} - -template -const T& Matrix4::Get43() const -{ - return m_43; -} - -template -const T& Matrix4::Get44() const -{ - return m_44; -} - - template -Matrix4& Matrix4::operator=(const Matrix4& m) -{ - if(this != &m) - { - for(int i = 0; i < 16; ++i) - m_values[i] = m.m_values[i]; - } - - return *this; -} - -template -Matrix4 Matrix4::operator+(const Matrix4& m) const -{ - return Matrix4( - m_11 + m.m_11, m_12 + m.m_12, m_13 + m.m_13, m_14 + m.m_14, - m_21 + m.m_21, m_22 + m.m_22, m_23 + m.m_23, m_24 + m.m_24, - m_31 + m.m_31, m_32 + m.m_32, m_33 + m.m_33, m_34 + m.m_34, - m_41 + m.m_41, m_42 + m.m_42, m_43 + m.m_43, m_44 + m.m_44); -} - - template -const Matrix4& Matrix4::operator+=(const Matrix4& m) -{ - *this = *this + m; - return *this; -} - -template -Matrix4 Matrix4::operator-(const Matrix4& m) const -{ - return Matrix4( - m_11 - m.m_11, m_12 - m.m_12, m_13 - m.m_13, m_14 - m.m_14, - m_21 - m.m_21, m_22 - m.m_22, m_23 - m.m_23, m_24 - m.m_24, - m_31 - m.m_31, m_32 - m.m_32, m_33 - m.m_33, m_34 - m.m_34, - m_41 - m.m_41, m_42 - m.m_42, m_43 - m.m_43, m_44 - m.m_44); -} - -template -Matrix4 Matrix4::operator-() const -{ - return Matrix4( - -m_11, -m_12, -m_13, -m_14, - -m_21, -m_22, -m_23, -m_24, - -m_31, -m_32, -m_33, -m_34, - -m_41, -m_42, -m_43, -m_44); -} - - template -const Matrix4& Matrix4::operator-=(const Matrix4& m) -{ - *this = *this - m; - return *this; -} - -template -Matrix4 Matrix4::operator*(const Matrix4& m) const -{ - return Matrix4( - m_11 * m.m_11 + m_12 * m.m_21 + m_13 * m.m_31 + m_14 * m.m_41, - m_11 * m.m_12 + m_12 * m.m_22 + m_13 * m.m_32 + m_14 * m.m_42, - m_11 * m.m_13 + m_12 * m.m_23 + m_13 * m.m_33 + m_14 * m.m_43, - m_11 * m.m_14 + m_12 * m.m_24 + m_13 * m.m_34 + m_14 * m.m_44, - - m_21 * m.m_11 + m_22 * m.m_21 + m_23 * m.m_31 + m_24 * m.m_41, - m_21 * m.m_12 + m_22 * m.m_22 + m_23 * m.m_32 + m_24 * m.m_42, - m_21 * m.m_13 + m_22 * m.m_23 + m_23 * m.m_33 + m_24 * m.m_43, - m_21 * m.m_14 + m_22 * m.m_24 + m_23 * m.m_34 + m_24 * m.m_44, - - m_31 * m.m_11 + m_32 * m.m_21 + m_33 * m.m_31 + m_34 * m.m_41, - m_31 * m.m_12 + m_32 * m.m_22 + m_33 * m.m_32 + m_34 * m.m_42, - m_31 * m.m_13 + m_32 * m.m_23 + m_33 * m.m_33 + m_34 * m.m_43, - m_31 * m.m_14 + m_32 * m.m_24 + m_33 * m.m_34 + m_34 * m.m_44, - - m_41 * m.m_11 + m_42 * m.m_21 + m_43 * m.m_31 + m_44 * m.m_41, - m_41 * m.m_12 + m_42 * m.m_22 + m_43 * m.m_32 + m_44 * m.m_42, - m_41 * m.m_13 + m_42 * m.m_23 + m_43 * m.m_33 + m_44 * m.m_43, - m_41 * m.m_14 + m_42 * m.m_24 + m_43 * m.m_34 + m_44 * m.m_44); -} - -template -Matrix4 Matrix4::operator*(const T& v) const -{ - return Matrix4( - m_11 * v, m_12 * v, m_13 * v, m_14 * v, - m_21 * v, m_22 * v, m_23 * v, m_24 * v, - m_31 * v, m_32 * v, m_33 * v, m_34 * v, - m_41 * v, m_42 * v, m_43 * v, m_44 * v); -} - - template -const Matrix4& Matrix4::operator*=(const Matrix4& m) -{ - *this = *this * m; - return *this; -} - - template -const Matrix4& Matrix4::operator*=(const T& v) -{ - *this = *this * v; - return *this; -} - -template -Matrix4 Matrix4::operator/(const T& v) const -{ - return Matrix4( - m_11 / v, m_12 / v, m_13 / v, m_14 / v, - m_21 / v, m_22 / v, m_23 / v, m_24 / v, - m_31 / v, m_32 / v, m_33 / v, m_34 / v, - m_41 / v, m_42 / v, m_43 / v, m_44 / v); -} - - template -const Matrix4& Matrix4::operator/=(const T& v) -{ - *this = *this / v; - return *this; -} - -template -bool Matrix4::operator==(const Matrix4& m) const -{ - for(int i = 0; i < 16; ++i) - if(m_values[i] != m.m_values[i]) - return false; - - return true; -} - -template -bool Matrix4::operator!=(const Matrix4& m) const -{ - return !(*this == m); -} - - template -void Matrix4::SetZero() -{ - *this = ZERO; -} - - template -void Matrix4::SetIdentity() -{ - *this = IDENTITY; -} - - template -void Matrix4::SetPerspectiveProjection(const T& fov, const T& aspect, const T& nearPlane, const T& farPlane) -{ - const float h = T(1) / tan(fov * T(M_PI / 360.f)); - T negDepth = nearPlane - farPlane; - - SetZero(); - - m_11 = h / aspect; - m_22 = h; - m_33 = (farPlane + nearPlane) / negDepth; - m_34 = T(2) * (nearPlane * farPlane) / negDepth; - m_43 = -T(1); -} - - template -void Matrix4::SetOrthographicProjection(const T& left, const T& right, const T& bottom, const T& top, const T& nearPlane, const T& farPlane) -{ - m_11 = T(2) / (right - left); - m_12 = T(0); - m_13 = T(0); - m_14 = -(right + left) / (right - left); - - m_21 = T(0); - m_22 = T(2) / (top - bottom); - m_23 = T(0); - m_24 = -(top + bottom) / (top - bottom); - - m_31 = T(0); - m_32 = T(0); - m_33 = -T(2) / (farPlane - nearPlane); - m_34 = -(farPlane + nearPlane) / (farPlane - nearPlane); - - m_41 = T(0); - m_42 = T(0); - m_43 = T(0); - m_44 = T(1); -} - - template -void Matrix4::SetLookAt(const Vector3& eyePosition, const Vector3& lookAtPosition, Vector3 upVector) -{ - Vector3f L = lookAtPosition - eyePosition; - L.Normalize(); - - upVector.Normalize(); - Vector3f S = L.Cross(upVector); - S.Normalize(); - - Vector3f U = S.Cross(L); - - Matrix4 M; - M.m_11 = S.x; - M.m_12 = S.y; - M.m_13 = S.z; - M.m_14 = 0; - - M.m_21 = U.x; - M.m_22 = U.y; - M.m_23 = U.z; - M.m_24 = 0; - - M.m_31 = -L.x; - M.m_32 = -L.y; - M.m_33 = -L.z; - M.m_34 = 0; - - M.m_41 = 0; - M.m_42 = 0; - M.m_43 = 0; - M.m_44 = 1.f; - - SetIdentity(); - *this *= M; - ApplyTranslation(-eyePosition.x, -eyePosition.y, -eyePosition.z); -} - - template -void Matrix4::ApplyTranslation(const T& x, const T& y, const T& z) -{ - Matrix4 tmp( - 1, 0, 0, x, - 0, 1, 0, y, - 0, 0, 1, z, - 0, 0, 0, 1); - - *this *= tmp; -} - - template -void Matrix4::ApplyRotation(const T& angle, const T& x, const T& y, const T& z) -{ - // TODO axis (x, y, z) must be normalized... - - T s = sin(DEGTORAD(angle)); - T c = cos(DEGTORAD(angle)); - T ic = T(1) - c; - - Matrix4 tmp( - x * x * ic + c, y * x * ic + (z * s), z * x * ic - (y * s), 0, - x * y * ic - (z * s), y * y * ic + c, z * y * ic + (x * s), 0, - x * z * ic + (y * s), y * z * ic - (x * s), z * z * ic + c, 0, - 0, 0, 0, 1); - - *this *= tmp; -} - - template -void Matrix4::ApplyScale(const T& x, const T& y, const T& z) -{ - Matrix4 tmp( - x, 0, 0, 0, - 0, y, 0, 0, - 0, 0, z, 0, - 0, 0, 0, 1); - - *this *= tmp; -} - -template -Vector3 Matrix4::GetTranslation() const -{ - // NOTE: Works only if the matrix doesn't contains scale information (only rotation and translation) - // Reference: http://www.gamedev.net/topic/397751-how-to-get-camera-position/ - T x = -(m_11 * m_14 + m_21 * m_24 + m_31 * m_34); - T y = -(m_12 * m_14 + m_22 * m_24 + m_32 * m_34); - T z = -(m_13 * m_14 + m_23 * m_24 + m_33 * m_34); - - return Vector3(x, y, z); -} - - template -T* Matrix4::GetInternalValues() -{ - return m_values; -} - -template -const T* Matrix4::GetInternalValues() const -{ - return m_values; -} - -template -std::string Matrix4::ToString(const std::string& lineBegin, const std::string& lineEnd) const -{ - std::ostringstream ss; - ss << lineBegin << m_11 << " " << m_12 << " " << m_13 << " " << m_14 << lineEnd; - ss << lineBegin << m_21 << " " << m_22 << " " << m_23 << " " << m_24 << lineEnd; - ss << lineBegin << m_31 << " " << m_32 << " " << m_33 << " " << m_34 << lineEnd; - ss << lineBegin << m_41 << " " << m_42 << " " << m_43 << " " << m_44 << lineEnd; - - return ss.str(); -} - -#endif // MATRIX4_H__ diff --git a/SQCSim2021/opensimplex.cpp b/SQCSim2021/opensimplex.cpp deleted file mode 100644 index a64d31f..0000000 --- a/SQCSim2021/opensimplex.cpp +++ /dev/null @@ -1,2542 +0,0 @@ -#include "opensimplex.h" - -#include -namespace OpenSimplexNoise -{ - using namespace std; - - Noise::Noise() - : m_stretch2d(-0.211324865405187) //(1/Math.sqrt(2+1)-1)/2; - , m_squish2d(0.366025403784439) //(Math.sqrt(2+1)-1)/2; - , m_stretch3d(-1.0 / 6) //(1/Math.sqrt(3+1)-1)/3; - , m_squish3d(1.0 / 3) //(Math.sqrt(3+1)-1)/3; - , m_stretch4d(-0.138196601125011) //(1/Math.sqrt(4+1)-1)/4; - , m_squish4d(0.309016994374947) //(Math.sqrt(4+1)-1)/4; - , m_norm2d(47) - , m_norm3d(103) - , m_norm4d(30) - , m_defaultSeed(0) - , m_perm{0} - , m_permGradIndex3d{0} - , m_gradients2d{ 5, 2, 2, 5, - -5, 2, -2, 5, - 5, -2, 2, -5, - -5, -2, -2, -5, } - , m_gradients3d{-11, 4, 4, -4, 11, 4, -4, 4, 11, - 11, 4, 4, 4, 11, 4, 4, 4, 11, - -11, -4, 4, -4, -11, 4, -4, -4, 11, - 11, -4, 4, 4, -11, 4, 4, -4, 11, - -11, 4, -4, -4, 11, -4, -4, 4, -11, - 11, 4, -4, 4, 11, -4, 4, 4, -11, - -11, -4, -4, -4, -11, -4, -4, -4, -11, - 11, -4, -4, 4, -11, -4, 4, -4, -11, } - , m_gradients4d{ 3, 1, 1, 1, 1, 3, 1, 1, 1, 1, 3, 1, 1, 1, 1, 3, - -3, 1, 1, 1, -1, 3, 1, 1, -1, 1, 3, 1, -1, 1, 1, 3, - 3, -1, 1, 1, 1, -3, 1, 1, 1, -1, 3, 1, 1, -1, 1, 3, - -3, -1, 1, 1, -1, -3, 1, 1, -1, -1, 3, 1, -1, -1, 1, 3, - 3, 1, -1, 1, 1, 3, -1, 1, 1, 1, -3, 1, 1, 1, -1, 3, - -3, 1, -1, 1, -1, 3, -1, 1, -1, 1, -3, 1, -1, 1, -1, 3, - 3, -1, -1, 1, 1, -3, -1, 1, 1, -1, -3, 1, 1, -1, -1, 3, - -3, -1, -1, 1, -1, -3, -1, 1, -1, -1, -3, 1, -1, -1, -1, 3, - 3, 1, 1, -1, 1, 3, 1, -1, 1, 1, 3, -1, 1, 1, 1, -3, - -3, 1, 1, -1, -1, 3, 1, -1, -1, 1, 3, -1, -1, 1, 1, -3, - 3, -1, 1, -1, 1, -3, 1, -1, 1, -1, 3, -1, 1, -1, 1, -3, - -3, -1, 1, -1, -1, -3, 1, -1, -1, -1, 3, -1, -1, -1, 1, -3, - 3, 1, -1, -1, 1, 3, -1, -1, 1, 1, -3, -1, 1, 1, -1, -3, - -3, 1, -1, -1, -1, 3, -1, -1, -1, 1, -3, -1, -1, 1, -1, -3, - 3, -1, -1, -1, 1, -3, -1, -1, 1, -1, -3, -1, 1, -1, -1, -3, - -3, -1, -1, -1, -1, -3, -1, -1, -1, -1, -3, -1, -1, -1, -1, -3, } - { - } - - Noise::Noise(int64_t seed) - : Noise() - { - short source[256]; - for (short i = 0; i < 256; i++) - { - source[i] = i; - } - seed = seed * 6364136223846793005l + 1442695040888963407l; - seed = seed * 6364136223846793005l + 1442695040888963407l; - seed = seed * 6364136223846793005l + 1442695040888963407l; - for (int i = 255; i >= 0; i--) - { - seed = seed * 6364136223846793005l + 1442695040888963407l; - int r = static_cast((seed + 31) % (i + 1)); - if (r < 0) - { - r += (i + 1); - } - m_perm[i] = source[r]; - m_permGradIndex3d[i] = static_cast((m_perm[i] % (m_gradients3d.size() / 3)) * 3); - source[r] = source[i]; - } - } - - double Noise::eval(double x, double y) const - { - //Place input coordinates onto grid. - double stretchOffset = (x + y) * m_stretch2d; - double xs = x + stretchOffset; - double ys = y + stretchOffset; - - //Floor to get grid coordinates of rhombus (stretched square) super-cell origin. - int xsb = static_cast(floor(xs)); - int ysb = static_cast(floor(ys)); - - //Skew out to get actual coordinates of rhombus origin. We'll need these later. - double squishOffset = (xsb + ysb) * m_squish2d; - double xb = xsb + squishOffset; - double yb = ysb + squishOffset; - - //Compute grid coordinates relative to rhombus origin. - double xins = xs - xsb; - double yins = ys - ysb; - - //Sum those together to get a value that determines which region we're in. - double inSum = xins + yins; - - //Positions relative to origin point. - double dx0 = x - xb; - double dy0 = y - yb; - - //We'll be defining these inside the next block and using them afterwards. - double dx_ext, dy_ext; - int xsv_ext, ysv_ext; - - double value = 0; - - //Contribution (1,0) - double dx1 = dx0 - 1 - m_squish2d; - double dy1 = dy0 - 0 - m_squish2d; - double attn1 = 2 - dx1 * dx1 - dy1 * dy1; - if (attn1 > 0) - { - attn1 *= attn1; - value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, dx1, dy1); - } - - //Contribution (0,1) - double dx2 = dx0 - 0 - m_squish2d; - double dy2 = dy0 - 1 - m_squish2d; - double attn2 = 2 - dx2 * dx2 - dy2 * dy2; - if (attn2 > 0) - { - attn2 *= attn2; - value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, dx2, dy2); - } - - if (inSum <= 1) - { //We're inside the triangle (2-Simplex) at (0,0) - double zins = 1 - inSum; - if (zins > xins || zins > yins) - { //(0,0) is one of the closest two triangular vertices - if (xins > yins) - { - xsv_ext = xsb + 1; - ysv_ext = ysb - 1; - dx_ext = dx0 - 1; - dy_ext = dy0 + 1; - } - else - { - xsv_ext = xsb - 1; - ysv_ext = ysb + 1; - dx_ext = dx0 + 1; - dy_ext = dy0 - 1; - } - } - else - { //(1,0) and (0,1) are the closest two vertices. - xsv_ext = xsb + 1; - ysv_ext = ysb + 1; - dx_ext = dx0 - 1 - 2 * m_squish2d; - dy_ext = dy0 - 1 - 2 * m_squish2d; - } - } - else - { //We're inside the triangle (2-Simplex) at (1,1) - double zins = 2 - inSum; - if (zins < xins || zins < yins) - { //(0,0) is one of the closest two triangular vertices - if (xins > yins) - { - xsv_ext = xsb + 2; - ysv_ext = ysb + 0; - dx_ext = dx0 - 2 - 2 * m_squish2d; - dy_ext = dy0 + 0 - 2 * m_squish2d; - } - else - { - xsv_ext = xsb + 0; - ysv_ext = ysb + 2; - dx_ext = dx0 + 0 - 2 * m_squish2d; - dy_ext = dy0 - 2 - 2 * m_squish2d; - } - } - else - { //(1,0) and (0,1) are the closest two vertices. - dx_ext = dx0; - dy_ext = dy0; - xsv_ext = xsb; - ysv_ext = ysb; - } - xsb += 1; - ysb += 1; - dx0 = dx0 - 1 - 2 * m_squish2d; - dy0 = dy0 - 1 - 2 * m_squish2d; - } - - //Contribution (0,0) or (1,1) - double attn0 = 2 - dx0 * dx0 - dy0 * dy0; - if (attn0 > 0) - { - attn0 *= attn0; - value += attn0 * attn0 * extrapolate(xsb, ysb, dx0, dy0); - } - - //Extra Vertex - double attn_ext = 2 - dx_ext * dx_ext - dy_ext * dy_ext; - if (attn_ext > 0) - { - attn_ext *= attn_ext; - value += attn_ext * attn_ext * extrapolate(xsv_ext, ysv_ext, dx_ext, dy_ext); - } - - return value / m_norm2d; - } - - double Noise::eval(double x, double y, double z) const - { - //Place input coordinates on simplectic honeycomb. - double stretchOffset = (x + y + z) * m_stretch3d; - double xs = x + stretchOffset; - double ys = y + stretchOffset; - double zs = z + stretchOffset; - - //static_cast(floor to get simplectic honeycomb coordinates of rhombohedron (stretched cube) super-cell origin. - int xsb = static_cast(floor(xs)); - int ysb = static_cast(floor(ys)); - int zsb = static_cast(floor(zs)); - - //Skew out to get actual coordinates of rhombohedron origin. We'll need these later. - double squishOffset = (xsb + ysb + zsb) * m_squish3d; - double xb = xsb + squishOffset; - double yb = ysb + squishOffset; - double zb = zsb + squishOffset; - - //Compute simplectic honeycomb coordinates relative to rhombohedral origin. - double xins = xs - xsb; - double yins = ys - ysb; - double zins = zs - zsb; - - //Sum those together to get a value that determines which region we're in. - double inSum = xins + yins + zins; - - //Positions relative to origin point. - double dx0 = x - xb; - double dy0 = y - yb; - double dz0 = z - zb; - - //We'll be defining these inside the next block and using them afterwards. - double dx_ext0, dy_ext0, dz_ext0; - double dx_ext1, dy_ext1, dz_ext1; - int xsv_ext0, ysv_ext0, zsv_ext0; - int xsv_ext1, ysv_ext1, zsv_ext1; - - double value = 0; - if (inSum <= 1) - { //We're inside the tetrahedron (3-Simplex) at (0,0,0) - -//Determine which two of (0,0,1), (0,1,0), (1,0,0) are closest. - char aPoint = 0x01; - double aScore = xins; - char bPoint = 0x02; - double bScore = yins; - if (aScore >= bScore && zins > bScore) - { - bScore = zins; - bPoint = 0x04; - } - else if (aScore < bScore && zins > aScore) - { - aScore = zins; - aPoint = 0x04; - } - - //Now we determine the two lattice points not part of the tetrahedron that may contribute. - //This depends on the closest two tetrahedral vertices, including (0,0,0) - double wins = 1 - inSum; - if (wins > aScore || wins > bScore) - { //(0,0,0) is one of the closest two tetrahedral vertices. - char c = (bScore > aScore ? bPoint : aPoint); //Our other closest vertex is the closest out of a and b. - - if ((c & 0x01) == 0) - { - xsv_ext0 = xsb - 1; - xsv_ext1 = xsb; - dx_ext0 = dx0 + 1; - dx_ext1 = dx0; - } - else - { - xsv_ext0 = xsv_ext1 = xsb + 1; - dx_ext0 = dx_ext1 = dx0 - 1; - } - - if ((c & 0x02) == 0) - { - ysv_ext0 = ysv_ext1 = ysb; - dy_ext0 = dy_ext1 = dy0; - if ((c & 0x01) == 0) - { - ysv_ext1 -= 1; - dy_ext1 += 1; - } - else - { - ysv_ext0 -= 1; - dy_ext0 += 1; - } - } - else - { - ysv_ext0 = ysv_ext1 = ysb + 1; - dy_ext0 = dy_ext1 = dy0 - 1; - } - - if ((c & 0x04) == 0) - { - zsv_ext0 = zsb; - zsv_ext1 = zsb - 1; - dz_ext0 = dz0; - dz_ext1 = dz0 + 1; - } - else - { - zsv_ext0 = zsv_ext1 = zsb + 1; - dz_ext0 = dz_ext1 = dz0 - 1; - } - } - else - { //(0,0,0) is not one of the closest two tetrahedral vertices. - char c = static_cast(aPoint | bPoint); //Our two extra vertices are determined by the closest two. - - if ((c & 0x01) == 0) - { - xsv_ext0 = xsb; - xsv_ext1 = xsb - 1; - dx_ext0 = dx0 - 2 * m_squish3d; - dx_ext1 = dx0 + 1 - m_squish3d; - } - else - { - xsv_ext0 = xsv_ext1 = xsb + 1; - dx_ext0 = dx0 - 1 - 2 * m_squish3d; - dx_ext1 = dx0 - 1 - m_squish3d; - } - - if ((c & 0x02) == 0) - { - ysv_ext0 = ysb; - ysv_ext1 = ysb - 1; - dy_ext0 = dy0 - 2 * m_squish3d; - dy_ext1 = dy0 + 1 - m_squish3d; - } - else - { - ysv_ext0 = ysv_ext1 = ysb + 1; - dy_ext0 = dy0 - 1 - 2 * m_squish3d; - dy_ext1 = dy0 - 1 - m_squish3d; - } - - if ((c & 0x04) == 0) - { - zsv_ext0 = zsb; - zsv_ext1 = zsb - 1; - dz_ext0 = dz0 - 2 * m_squish3d; - dz_ext1 = dz0 + 1 - m_squish3d; - } - else - { - zsv_ext0 = zsv_ext1 = zsb + 1; - dz_ext0 = dz0 - 1 - 2 * m_squish3d; - dz_ext1 = dz0 - 1 - m_squish3d; - } - } - - //Contribution (0,0,0) - double attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0; - if (attn0 > 0) - { - attn0 *= attn0; - value += attn0 * attn0 * extrapolate(xsb + 0, ysb + 0, zsb + 0, dx0, dy0, dz0); - } - - //Contribution (1,0,0) - double dx1 = dx0 - 1 - m_squish3d; - double dy1 = dy0 - 0 - m_squish3d; - double dz1 = dz0 - 0 - m_squish3d; - double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1; - if (attn1 > 0) - { - attn1 *= attn1; - value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, zsb + 0, dx1, dy1, dz1); - } - - //Contribution (0,1,0) - double dx2 = dx0 - 0 - m_squish3d; - double dy2 = dy0 - 1 - m_squish3d; - double dz2 = dz1; - double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2; - if (attn2 > 0) - { - attn2 *= attn2; - value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, zsb + 0, dx2, dy2, dz2); - } - - //Contribution (0,0,1) - double dx3 = dx2; - double dy3 = dy1; - double dz3 = dz0 - 1 - m_squish3d; - double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3; - if (attn3 > 0) - { - attn3 *= attn3; - value += attn3 * attn3 * extrapolate(xsb + 0, ysb + 0, zsb + 1, dx3, dy3, dz3); - } - } - else if (inSum >= 2) - { //We're inside the tetrahedron (3-Simplex) at (1,1,1) - -//Determine which two tetrahedral vertices are the closest, out of (1,1,0), (1,0,1), (0,1,1) but not (1,1,1). - char aPoint = 0x06; - double aScore = xins; - char bPoint = 0x05; - double bScore = yins; - if (aScore <= bScore && zins < bScore) - { - bScore = zins; - bPoint = 0x03; - } - else if (aScore > bScore && zins < aScore) - { - aScore = zins; - aPoint = 0x03; - } - - //Now we determine the two lattice points not part of the tetrahedron that may contribute. - //This depends on the closest two tetrahedral vertices, including (1,1,1) - double wins = 3 - inSum; - if (wins < aScore || wins < bScore) - { //(1,1,1) is one of the closest two tetrahedral vertices. - char c = (bScore < aScore ? bPoint : aPoint); //Our other closest vertex is the closest out of a and b. - - if ((c & 0x01) != 0) - { - xsv_ext0 = xsb + 2; - xsv_ext1 = xsb + 1; - dx_ext0 = dx0 - 2 - 3 * m_squish3d; - dx_ext1 = dx0 - 1 - 3 * m_squish3d; - } - else - { - xsv_ext0 = xsv_ext1 = xsb; - dx_ext0 = dx_ext1 = dx0 - 3 * m_squish3d; - } - - if ((c & 0x02) != 0) - { - ysv_ext0 = ysv_ext1 = ysb + 1; - dy_ext0 = dy_ext1 = dy0 - 1 - 3 * m_squish3d; - if ((c & 0x01) != 0) - { - ysv_ext1 += 1; - dy_ext1 -= 1; - } - else - { - ysv_ext0 += 1; - dy_ext0 -= 1; - } - } - else - { - ysv_ext0 = ysv_ext1 = ysb; - dy_ext0 = dy_ext1 = dy0 - 3 * m_squish3d; - } - - if ((c & 0x04) != 0) - { - zsv_ext0 = zsb + 1; - zsv_ext1 = zsb + 2; - dz_ext0 = dz0 - 1 - 3 * m_squish3d; - dz_ext1 = dz0 - 2 - 3 * m_squish3d; - } - else - { - zsv_ext0 = zsv_ext1 = zsb; - dz_ext0 = dz_ext1 = dz0 - 3 * m_squish3d; - } - } - else - { //(1,1,1) is not one of the closest two tetrahedral vertices. - char c = static_cast(aPoint & bPoint); //Our two extra vertices are determined by the closest two. - - if ((c & 0x01) != 0) - { - xsv_ext0 = xsb + 1; - xsv_ext1 = xsb + 2; - dx_ext0 = dx0 - 1 - m_squish3d; - dx_ext1 = dx0 - 2 - 2 * m_squish3d; - } - else - { - xsv_ext0 = xsv_ext1 = xsb; - dx_ext0 = dx0 - m_squish3d; - dx_ext1 = dx0 - 2 * m_squish3d; - } - - if ((c & 0x02) != 0) - { - ysv_ext0 = ysb + 1; - ysv_ext1 = ysb + 2; - dy_ext0 = dy0 - 1 - m_squish3d; - dy_ext1 = dy0 - 2 - 2 * m_squish3d; - } - else - { - ysv_ext0 = ysv_ext1 = ysb; - dy_ext0 = dy0 - m_squish3d; - dy_ext1 = dy0 - 2 * m_squish3d; - } - - if ((c & 0x04) != 0) - { - zsv_ext0 = zsb + 1; - zsv_ext1 = zsb + 2; - dz_ext0 = dz0 - 1 - m_squish3d; - dz_ext1 = dz0 - 2 - 2 * m_squish3d; - } - else - { - zsv_ext0 = zsv_ext1 = zsb; - dz_ext0 = dz0 - m_squish3d; - dz_ext1 = dz0 - 2 * m_squish3d; - } - } - - //Contribution (1,1,0) - double dx3 = dx0 - 1 - 2 * m_squish3d; - double dy3 = dy0 - 1 - 2 * m_squish3d; - double dz3 = dz0 - 0 - 2 * m_squish3d; - double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3; - if (attn3 > 0) - { - attn3 *= attn3; - value += attn3 * attn3 * extrapolate(xsb + 1, ysb + 1, zsb + 0, dx3, dy3, dz3); - } - - //Contribution (1,0,1) - double dx2 = dx3; - double dy2 = dy0 - 0 - 2 * m_squish3d; - double dz2 = dz0 - 1 - 2 * m_squish3d; - double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2; - if (attn2 > 0) - { - attn2 *= attn2; - value += attn2 * attn2 * extrapolate(xsb + 1, ysb + 0, zsb + 1, dx2, dy2, dz2); - } - - //Contribution (0,1,1) - double dx1 = dx0 - 0 - 2 * m_squish3d; - double dy1 = dy3; - double dz1 = dz2; - double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1; - if (attn1 > 0) - { - attn1 *= attn1; - value += attn1 * attn1 * extrapolate(xsb + 0, ysb + 1, zsb + 1, dx1, dy1, dz1); - } - - //Contribution (1,1,1) - dx0 = dx0 - 1 - 3 * m_squish3d; - dy0 = dy0 - 1 - 3 * m_squish3d; - dz0 = dz0 - 1 - 3 * m_squish3d; - double attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0; - if (attn0 > 0) - { - attn0 *= attn0; - value += attn0 * attn0 * extrapolate(xsb + 1, ysb + 1, zsb + 1, dx0, dy0, dz0); - } - } - else - { //We're inside the octahedron (Rectified 3-Simplex) in between. - double aScore; - char aPoint; - bool aIsFurtherSide; - double bScore; - char bPoint; - bool bIsFurtherSide; - - //Decide between point (0,0,1) and (1,1,0) as closest - double p1 = xins + yins; - if (p1 > 1) - { - aScore = p1 - 1; - aPoint = 0x03; - aIsFurtherSide = true; - } - else - { - aScore = 1 - p1; - aPoint = 0x04; - aIsFurtherSide = false; - } - - //Decide between point (0,1,0) and (1,0,1) as closest - double p2 = xins + zins; - if (p2 > 1) - { - bScore = p2 - 1; - bPoint = 0x05; - bIsFurtherSide = true; - } - else - { - bScore = 1 - p2; - bPoint = 0x02; - bIsFurtherSide = false; - } - - //The closest out of the two (1,0,0) and (0,1,1) will replace the furthest out of the two decided above, if closer. - double p3 = yins + zins; - if (p3 > 1) - { - double score = p3 - 1; - if (aScore <= bScore && aScore < score) - { - aScore = score; - aPoint = 0x06; - aIsFurtherSide = true; - } - else if (aScore > bScore && bScore < score) - { - bScore = score; - bPoint = 0x06; - bIsFurtherSide = true; - } - } - else - { - double score = 1 - p3; - if (aScore <= bScore && aScore < score) - { - aScore = score; - aPoint = 0x01; - aIsFurtherSide = false; - } - else if (aScore > bScore && bScore < score) - { - bScore = score; - bPoint = 0x01; - bIsFurtherSide = false; - } - } - - //Where each of the two closest points are determines how the extra two vertices are calculated. - if (aIsFurtherSide == bIsFurtherSide) - { - if (aIsFurtherSide) - { //Both closest points on (1,1,1) side - -//One of the two extra points is (1,1,1) - dx_ext0 = dx0 - 1 - 3 * m_squish3d; - dy_ext0 = dy0 - 1 - 3 * m_squish3d; - dz_ext0 = dz0 - 1 - 3 * m_squish3d; - xsv_ext0 = xsb + 1; - ysv_ext0 = ysb + 1; - zsv_ext0 = zsb + 1; - - //Other extra point is based on the shared axis. - char c = static_cast(aPoint & bPoint); - if ((c & 0x01) != 0) - { - dx_ext1 = dx0 - 2 - 2 * m_squish3d; - dy_ext1 = dy0 - 2 * m_squish3d; - dz_ext1 = dz0 - 2 * m_squish3d; - xsv_ext1 = xsb + 2; - ysv_ext1 = ysb; - zsv_ext1 = zsb; - } - else if ((c & 0x02) != 0) - { - dx_ext1 = dx0 - 2 * m_squish3d; - dy_ext1 = dy0 - 2 - 2 * m_squish3d; - dz_ext1 = dz0 - 2 * m_squish3d; - xsv_ext1 = xsb; - ysv_ext1 = ysb + 2; - zsv_ext1 = zsb; - } - else - { - dx_ext1 = dx0 - 2 * m_squish3d; - dy_ext1 = dy0 - 2 * m_squish3d; - dz_ext1 = dz0 - 2 - 2 * m_squish3d; - xsv_ext1 = xsb; - ysv_ext1 = ysb; - zsv_ext1 = zsb + 2; - } - } - else - {//Both closest points on (0,0,0) side - - //One of the two extra points is (0,0,0) - dx_ext0 = dx0; - dy_ext0 = dy0; - dz_ext0 = dz0; - xsv_ext0 = xsb; - ysv_ext0 = ysb; - zsv_ext0 = zsb; - - //Other extra point is based on the omitted axis. - char c = static_cast(aPoint | bPoint); - if ((c & 0x01) == 0) - { - dx_ext1 = dx0 + 1 - m_squish3d; - dy_ext1 = dy0 - 1 - m_squish3d; - dz_ext1 = dz0 - 1 - m_squish3d; - xsv_ext1 = xsb - 1; - ysv_ext1 = ysb + 1; - zsv_ext1 = zsb + 1; - } - else if ((c & 0x02) == 0) - { - dx_ext1 = dx0 - 1 - m_squish3d; - dy_ext1 = dy0 + 1 - m_squish3d; - dz_ext1 = dz0 - 1 - m_squish3d; - xsv_ext1 = xsb + 1; - ysv_ext1 = ysb - 1; - zsv_ext1 = zsb + 1; - } - else - { - dx_ext1 = dx0 - 1 - m_squish3d; - dy_ext1 = dy0 - 1 - m_squish3d; - dz_ext1 = dz0 + 1 - m_squish3d; - xsv_ext1 = xsb + 1; - ysv_ext1 = ysb + 1; - zsv_ext1 = zsb - 1; - } - } - } - else - { //One point on (0,0,0) side, one point on (1,1,1) side - char c1, c2; - if (aIsFurtherSide) - { - c1 = aPoint; - c2 = bPoint; - } - else - { - c1 = bPoint; - c2 = aPoint; - } - - //One contribution is a permutation of (1,1,-1) - if ((c1 & 0x01) == 0) - { - dx_ext0 = dx0 + 1 - m_squish3d; - dy_ext0 = dy0 - 1 - m_squish3d; - dz_ext0 = dz0 - 1 - m_squish3d; - xsv_ext0 = xsb - 1; - ysv_ext0 = ysb + 1; - zsv_ext0 = zsb + 1; - } - else if ((c1 & 0x02) == 0) - { - dx_ext0 = dx0 - 1 - m_squish3d; - dy_ext0 = dy0 + 1 - m_squish3d; - dz_ext0 = dz0 - 1 - m_squish3d; - xsv_ext0 = xsb + 1; - ysv_ext0 = ysb - 1; - zsv_ext0 = zsb + 1; - } - else - { - dx_ext0 = dx0 - 1 - m_squish3d; - dy_ext0 = dy0 - 1 - m_squish3d; - dz_ext0 = dz0 + 1 - m_squish3d; - xsv_ext0 = xsb + 1; - ysv_ext0 = ysb + 1; - zsv_ext0 = zsb - 1; - } - - //One contribution is a permutation of (0,0,2) - dx_ext1 = dx0 - 2 * m_squish3d; - dy_ext1 = dy0 - 2 * m_squish3d; - dz_ext1 = dz0 - 2 * m_squish3d; - xsv_ext1 = xsb; - ysv_ext1 = ysb; - zsv_ext1 = zsb; - if ((c2 & 0x01) != 0) - { - dx_ext1 -= 2; - xsv_ext1 += 2; - } - else if ((c2 & 0x02) != 0) - { - dy_ext1 -= 2; - ysv_ext1 += 2; - } - else - { - dz_ext1 -= 2; - zsv_ext1 += 2; - } - } - - //Contribution (1,0,0) - double dx1 = dx0 - 1 - m_squish3d; - double dy1 = dy0 - 0 - m_squish3d; - double dz1 = dz0 - 0 - m_squish3d; - double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1; - if (attn1 > 0) - { - attn1 *= attn1; - value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, zsb + 0, dx1, dy1, dz1); - } - - //Contribution (0,1,0) - double dx2 = dx0 - 0 - m_squish3d; - double dy2 = dy0 - 1 - m_squish3d; - double dz2 = dz1; - double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2; - if (attn2 > 0) - { - attn2 *= attn2; - value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, zsb + 0, dx2, dy2, dz2); - } - - //Contribution (0,0,1) - double dx3 = dx2; - double dy3 = dy1; - double dz3 = dz0 - 1 - m_squish3d; - double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3; - if (attn3 > 0) - { - attn3 *= attn3; - value += attn3 * attn3 * extrapolate(xsb + 0, ysb + 0, zsb + 1, dx3, dy3, dz3); - } - - //Contribution (1,1,0) - double dx4 = dx0 - 1 - 2 * m_squish3d; - double dy4 = dy0 - 1 - 2 * m_squish3d; - double dz4 = dz0 - 0 - 2 * m_squish3d; - double attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4; - if (attn4 > 0) - { - attn4 *= attn4; - value += attn4 * attn4 * extrapolate(xsb + 1, ysb + 1, zsb + 0, dx4, dy4, dz4); - } - - //Contribution (1,0,1) - double dx5 = dx4; - double dy5 = dy0 - 0 - 2 * m_squish3d; - double dz5 = dz0 - 1 - 2 * m_squish3d; - double attn5 = 2 - dx5 * dx5 - dy5 * dy5 - dz5 * dz5; - if (attn5 > 0) - { - attn5 *= attn5; - value += attn5 * attn5 * extrapolate(xsb + 1, ysb + 0, zsb + 1, dx5, dy5, dz5); - } - - //Contribution (0,1,1) - double dx6 = dx0 - 0 - 2 * m_squish3d; - double dy6 = dy4; - double dz6 = dz5; - double attn6 = 2 - dx6 * dx6 - dy6 * dy6 - dz6 * dz6; - if (attn6 > 0) - { - attn6 *= attn6; - value += attn6 * attn6 * extrapolate(xsb + 0, ysb + 1, zsb + 1, dx6, dy6, dz6); - } - } - - //First extra vertex - double attn_ext0 = 2 - dx_ext0 * dx_ext0 - dy_ext0 * dy_ext0 - dz_ext0 * dz_ext0; - if (attn_ext0 > 0) - { - attn_ext0 *= attn_ext0; - value += attn_ext0 * attn_ext0 * extrapolate(xsv_ext0, ysv_ext0, zsv_ext0, dx_ext0, dy_ext0, dz_ext0); - } - - //Second extra vertex - double attn_ext1 = 2 - dx_ext1 * dx_ext1 - dy_ext1 * dy_ext1 - dz_ext1 * dz_ext1; - if (attn_ext1 > 0) - { - attn_ext1 *= attn_ext1; - value += attn_ext1 * attn_ext1 * extrapolate(xsv_ext1, ysv_ext1, zsv_ext1, dx_ext1, dy_ext1, dz_ext1); - } - - return value / m_norm3d; - } - - double Noise::eval(double x, double y, double z, double w) const - { - //Place input coordinates on simplectic honeycomb. - double stretchOffset = (x + y + z + w) * m_stretch4d; - double xs = x + stretchOffset; - double ys = y + stretchOffset; - double zs = z + stretchOffset; - double ws = w + stretchOffset; - - //static_cast(floor to get simplectic honeycomb coordinates of rhombo-hypercube super-cell origin. - int xsb = static_cast(floor(xs)); - int ysb = static_cast(floor(ys)); - int zsb = static_cast(floor(zs)); - int wsb = static_cast(floor(ws)); - - //Skew out to get actual coordinates of stretched rhombo-hypercube origin. We'll need these later. - double squishOffset = (xsb + ysb + zsb + wsb) * m_squish4d; - double xb = xsb + squishOffset; - double yb = ysb + squishOffset; - double zb = zsb + squishOffset; - double wb = wsb + squishOffset; - - //Compute simplectic honeycomb coordinates relative to rhombo-hypercube origin. - double xins = xs - xsb; - double yins = ys - ysb; - double zins = zs - zsb; - double wins = ws - wsb; - - //Sum those together to get a value that determines which region we're in. - double inSum = xins + yins + zins + wins; - - //Positions relative to origin point. - double dx0 = x - xb; - double dy0 = y - yb; - double dz0 = z - zb; - double dw0 = w - wb; - - //We'll be defining these inside the next block and using them afterwards. - double dx_ext0, dy_ext0, dz_ext0, dw_ext0; - double dx_ext1, dy_ext1, dz_ext1, dw_ext1; - double dx_ext2, dy_ext2, dz_ext2, dw_ext2; - int xsv_ext0, ysv_ext0, zsv_ext0, wsv_ext0; - int xsv_ext1, ysv_ext1, zsv_ext1, wsv_ext1; - int xsv_ext2, ysv_ext2, zsv_ext2, wsv_ext2; - - double value = 0; - if (inSum <= 1) - { //We're inside the pentachoron (4-Simplex) at (0,0,0,0) - -//Determine which two of (0,0,0,1), (0,0,1,0), (0,1,0,0), (1,0,0,0) are closest. - char aPoint = 0x01; - double aScore = xins; - char bPoint = 0x02; - double bScore = yins; - if (aScore >= bScore && zins > bScore) - { - bScore = zins; - bPoint = 0x04; - } - else if (aScore < bScore && zins > aScore) - { - aScore = zins; - aPoint = 0x04; - } - if (aScore >= bScore && wins > bScore) - { - bScore = wins; - bPoint = 0x08; - } - else if (aScore < bScore && wins > aScore) - { - aScore = wins; - aPoint = 0x08; - } - - //Now we determine the three lattice points not part of the pentachoron that may contribute. - //This depends on the closest two pentachoron vertices, including (0,0,0,0) - double uins = 1 - inSum; - if (uins > aScore || uins > bScore) - { //(0,0,0,0) is one of the closest two pentachoron vertices. - char c = (bScore > aScore ? bPoint : aPoint); //Our other closest vertex is the closest out of a and b. - if ((c & 0x01) == 0) - { - xsv_ext0 = xsb - 1; - xsv_ext1 = xsv_ext2 = xsb; - dx_ext0 = dx0 + 1; - dx_ext1 = dx_ext2 = dx0; - } - else - { - xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb + 1; - dx_ext0 = dx_ext1 = dx_ext2 = dx0 - 1; - } - - if ((c & 0x02) == 0) - { - ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb; - dy_ext0 = dy_ext1 = dy_ext2 = dy0; - if ((c & 0x01) == 0x01) - { - ysv_ext0 -= 1; - dy_ext0 += 1; - } - else - { - ysv_ext1 -= 1; - dy_ext1 += 1; - } - } - else - { - ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1; - dy_ext0 = dy_ext1 = dy_ext2 = dy0 - 1; - } - - if ((c & 0x04) == 0) - { - zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb; - dz_ext0 = dz_ext1 = dz_ext2 = dz0; - if ((c & 0x03) != 0) - { - if ((c & 0x03) == 0x03) - { - zsv_ext0 -= 1; - dz_ext0 += 1; - } - else - { - zsv_ext1 -= 1; - dz_ext1 += 1; - } - } - else - { - zsv_ext2 -= 1; - dz_ext2 += 1; - } - } - else - { - zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1; - dz_ext0 = dz_ext1 = dz_ext2 = dz0 - 1; - } - - if ((c & 0x08) == 0) - { - wsv_ext0 = wsv_ext1 = wsb; - wsv_ext2 = wsb - 1; - dw_ext0 = dw_ext1 = dw0; - dw_ext2 = dw0 + 1; - } - else - { - wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb + 1; - dw_ext0 = dw_ext1 = dw_ext2 = dw0 - 1; - } - } - else - { //(0,0,0,0) is not one of the closest two pentachoron vertices. - char c = static_cast(aPoint | bPoint); //Our three extra vertices are determined by the closest two. - - if ((c & 0x01) == 0) - { - xsv_ext0 = xsv_ext2 = xsb; - xsv_ext1 = xsb - 1; - dx_ext0 = dx0 - 2 * m_squish4d; - dx_ext1 = dx0 + 1 - m_squish4d; - dx_ext2 = dx0 - m_squish4d; - } - else - { - xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb + 1; - dx_ext0 = dx0 - 1 - 2 * m_squish4d; - dx_ext1 = dx_ext2 = dx0 - 1 - m_squish4d; - } - - if ((c & 0x02) == 0) - { - ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb; - dy_ext0 = dy0 - 2 * m_squish4d; - dy_ext1 = dy_ext2 = dy0 - m_squish4d; - if ((c & 0x01) == 0x01) - { - ysv_ext1 -= 1; - dy_ext1 += 1; - } - else - { - ysv_ext2 -= 1; - dy_ext2 += 1; - } - } - else - { - ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1; - dy_ext0 = dy0 - 1 - 2 * m_squish4d; - dy_ext1 = dy_ext2 = dy0 - 1 - m_squish4d; - } - - if ((c & 0x04) == 0) - { - zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb; - dz_ext0 = dz0 - 2 * m_squish4d; - dz_ext1 = dz_ext2 = dz0 - m_squish4d; - if ((c & 0x03) == 0x03) - { - zsv_ext1 -= 1; - dz_ext1 += 1; - } - else - { - zsv_ext2 -= 1; - dz_ext2 += 1; - } - } - else - { - zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1; - dz_ext0 = dz0 - 1 - 2 * m_squish4d; - dz_ext1 = dz_ext2 = dz0 - 1 - m_squish4d; - } - - if ((c & 0x08) == 0) - { - wsv_ext0 = wsv_ext1 = wsb; - wsv_ext2 = wsb - 1; - dw_ext0 = dw0 - 2 * m_squish4d; - dw_ext1 = dw0 - m_squish4d; - dw_ext2 = dw0 + 1 - m_squish4d; - } - else - { - wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb + 1; - dw_ext0 = dw0 - 1 - 2 * m_squish4d; - dw_ext1 = dw_ext2 = dw0 - 1 - m_squish4d; - } - } - - //Contribution (0,0,0,0) - double attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0 - dw0 * dw0; - if (attn0 > 0) - { - attn0 *= attn0; - value += attn0 * attn0 * extrapolate(xsb + 0, ysb + 0, zsb + 0, wsb + 0, dx0, dy0, dz0, dw0); - } - - //Contribution (1,0,0,0) - double dx1 = dx0 - 1 - m_squish4d; - double dy1 = dy0 - 0 - m_squish4d; - double dz1 = dz0 - 0 - m_squish4d; - double dw1 = dw0 - 0 - m_squish4d; - double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1; - if (attn1 > 0) - { - attn1 *= attn1; - value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, zsb + 0, wsb + 0, dx1, dy1, dz1, dw1); - } - - //Contribution (0,1,0,0) - double dx2 = dx0 - 0 - m_squish4d; - double dy2 = dy0 - 1 - m_squish4d; - double dz2 = dz1; - double dw2 = dw1; - double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2; - if (attn2 > 0) - { - attn2 *= attn2; - value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, zsb + 0, wsb + 0, dx2, dy2, dz2, dw2); - } - - //Contribution (0,0,1,0) - double dx3 = dx2; - double dy3 = dy1; - double dz3 = dz0 - 1 - m_squish4d; - double dw3 = dw1; - double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3; - if (attn3 > 0) - { - attn3 *= attn3; - value += attn3 * attn3 * extrapolate(xsb + 0, ysb + 0, zsb + 1, wsb + 0, dx3, dy3, dz3, dw3); - } - - //Contribution (0,0,0,1) - double dx4 = dx2; - double dy4 = dy1; - double dz4 = dz1; - double dw4 = dw0 - 1 - m_squish4d; - double attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4; - if (attn4 > 0) - { - attn4 *= attn4; - value += attn4 * attn4 * extrapolate(xsb + 0, ysb + 0, zsb + 0, wsb + 1, dx4, dy4, dz4, dw4); - } - } - else if (inSum >= 3) - { //We're inside the pentachoron (4-Simplex) at (1,1,1,1) -//Determine which two of (1,1,1,0), (1,1,0,1), (1,0,1,1), (0,1,1,1) are closest. - char aPoint = 0x0E; - double aScore = xins; - char bPoint = 0x0D; - double bScore = yins; - if (aScore <= bScore && zins < bScore) - { - bScore = zins; - bPoint = 0x0B; - } - else if (aScore > bScore && zins < aScore) - { - aScore = zins; - aPoint = 0x0B; - } - if (aScore <= bScore && wins < bScore) - { - bScore = wins; - bPoint = 0x07; - } - else if (aScore > bScore && wins < aScore) - { - aScore = wins; - aPoint = 0x07; - } - - //Now we determine the three lattice points not part of the pentachoron that may contribute. - //This depends on the closest two pentachoron vertices, including (0,0,0,0) - double uins = 4 - inSum; - if (uins < aScore || uins < bScore) - { //(1,1,1,1) is one of the closest two pentachoron vertices. - char c = (bScore < aScore ? bPoint : aPoint); //Our other closest vertex is the closest out of a and b. - - if ((c & 0x01) != 0) - { - xsv_ext0 = xsb + 2; - xsv_ext1 = xsv_ext2 = xsb + 1; - dx_ext0 = dx0 - 2 - 4 * m_squish4d; - dx_ext1 = dx_ext2 = dx0 - 1 - 4 * m_squish4d; - } - else - { - xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb; - dx_ext0 = dx_ext1 = dx_ext2 = dx0 - 4 * m_squish4d; - } - - if ((c & 0x02) != 0) - { - ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1; - dy_ext0 = dy_ext1 = dy_ext2 = dy0 - 1 - 4 * m_squish4d; - if ((c & 0x01) != 0) - { - ysv_ext1 += 1; - dy_ext1 -= 1; - } - else - { - ysv_ext0 += 1; - dy_ext0 -= 1; - } - } - else - { - ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb; - dy_ext0 = dy_ext1 = dy_ext2 = dy0 - 4 * m_squish4d; - } - - if ((c & 0x04) != 0) - { - zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1; - dz_ext0 = dz_ext1 = dz_ext2 = dz0 - 1 - 4 * m_squish4d; - if ((c & 0x03) != 0x03) - { - if ((c & 0x03) == 0) - { - zsv_ext0 += 1; - dz_ext0 -= 1; - } - else - { - zsv_ext1 += 1; - dz_ext1 -= 1; - } - } - else - { - zsv_ext2 += 1; - dz_ext2 -= 1; - } - } - else - { - zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb; - dz_ext0 = dz_ext1 = dz_ext2 = dz0 - 4 * m_squish4d; - } - - if ((c & 0x08) != 0) - { - wsv_ext0 = wsv_ext1 = wsb + 1; - wsv_ext2 = wsb + 2; - dw_ext0 = dw_ext1 = dw0 - 1 - 4 * m_squish4d; - dw_ext2 = dw0 - 2 - 4 * m_squish4d; - } - else - { - wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb; - dw_ext0 = dw_ext1 = dw_ext2 = dw0 - 4 * m_squish4d; - } - } - else - { //(1,1,1,1) is not one of the closest two pentachoron vertices. - char c = static_cast(aPoint & bPoint); //Our three extra vertices are determined by the closest two. - - if ((c & 0x01) != 0) - { - xsv_ext0 = xsv_ext2 = xsb + 1; - xsv_ext1 = xsb + 2; - dx_ext0 = dx0 - 1 - 2 * m_squish4d; - dx_ext1 = dx0 - 2 - 3 * m_squish4d; - dx_ext2 = dx0 - 1 - 3 * m_squish4d; - } - else - { - xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb; - dx_ext0 = dx0 - 2 * m_squish4d; - dx_ext1 = dx_ext2 = dx0 - 3 * m_squish4d; - } - - if ((c & 0x02) != 0) - { - ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1; - dy_ext0 = dy0 - 1 - 2 * m_squish4d; - dy_ext1 = dy_ext2 = dy0 - 1 - 3 * m_squish4d; - if ((c & 0x01) != 0) - { - ysv_ext2 += 1; - dy_ext2 -= 1; - } - else - { - ysv_ext1 += 1; - dy_ext1 -= 1; - } - } - else - { - ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb; - dy_ext0 = dy0 - 2 * m_squish4d; - dy_ext1 = dy_ext2 = dy0 - 3 * m_squish4d; - } - - if ((c & 0x04) != 0) - { - zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1; - dz_ext0 = dz0 - 1 - 2 * m_squish4d; - dz_ext1 = dz_ext2 = dz0 - 1 - 3 * m_squish4d; - if ((c & 0x03) != 0) - { - zsv_ext2 += 1; - dz_ext2 -= 1; - } - else - { - zsv_ext1 += 1; - dz_ext1 -= 1; - } - } - else - { - zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb; - dz_ext0 = dz0 - 2 * m_squish4d; - dz_ext1 = dz_ext2 = dz0 - 3 * m_squish4d; - } - - if ((c & 0x08) != 0) - { - wsv_ext0 = wsv_ext1 = wsb + 1; - wsv_ext2 = wsb + 2; - dw_ext0 = dw0 - 1 - 2 * m_squish4d; - dw_ext1 = dw0 - 1 - 3 * m_squish4d; - dw_ext2 = dw0 - 2 - 3 * m_squish4d; - } - else - { - wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb; - dw_ext0 = dw0 - 2 * m_squish4d; - dw_ext1 = dw_ext2 = dw0 - 3 * m_squish4d; - } - } - - //Contribution (1,1,1,0) - double dx4 = dx0 - 1 - 3 * m_squish4d; - double dy4 = dy0 - 1 - 3 * m_squish4d; - double dz4 = dz0 - 1 - 3 * m_squish4d; - double dw4 = dw0 - 3 * m_squish4d; - double attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4; - if (attn4 > 0) - { - attn4 *= attn4; - value += attn4 * attn4 * extrapolate(xsb + 1, ysb + 1, zsb + 1, wsb + 0, dx4, dy4, dz4, dw4); - } - - //Contribution (1,1,0,1) - double dx3 = dx4; - double dy3 = dy4; - double dz3 = dz0 - 3 * m_squish4d; - double dw3 = dw0 - 1 - 3 * m_squish4d; - double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3; - if (attn3 > 0) - { - attn3 *= attn3; - value += attn3 * attn3 * extrapolate(xsb + 1, ysb + 1, zsb + 0, wsb + 1, dx3, dy3, dz3, dw3); - } - - //Contribution (1,0,1,1) - double dx2 = dx4; - double dy2 = dy0 - 3 * m_squish4d; - double dz2 = dz4; - double dw2 = dw3; - double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2; - if (attn2 > 0) - { - attn2 *= attn2; - value += attn2 * attn2 * extrapolate(xsb + 1, ysb + 0, zsb + 1, wsb + 1, dx2, dy2, dz2, dw2); - } - - //Contribution (0,1,1,1) - double dx1 = dx0 - 3 * m_squish4d; - double dz1 = dz4; - double dy1 = dy4; - double dw1 = dw3; - double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1; - if (attn1 > 0) - { - attn1 *= attn1; - value += attn1 * attn1 * extrapolate(xsb + 0, ysb + 1, zsb + 1, wsb + 1, dx1, dy1, dz1, dw1); - } - - //Contribution (1,1,1,1) - dx0 = dx0 - 1 - 4 * m_squish4d; - dy0 = dy0 - 1 - 4 * m_squish4d; - dz0 = dz0 - 1 - 4 * m_squish4d; - dw0 = dw0 - 1 - 4 * m_squish4d; - double attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0 - dw0 * dw0; - if (attn0 > 0) - { - attn0 *= attn0; - value += attn0 * attn0 * extrapolate(xsb + 1, ysb + 1, zsb + 1, wsb + 1, dx0, dy0, dz0, dw0); - } - } - else if (inSum <= 2) - { //We're inside the first dispentachoron (Rectified 4-Simplex) - double aScore; - char aPoint; - bool aIsBiggerSide = true; - double bScore; - char bPoint; - bool bIsBiggerSide = true; - - //Decide between (1,1,0,0) and (0,0,1,1) - if (xins + yins > zins + wins) - { - aScore = xins + yins; - aPoint = 0x03; - } - else - { - aScore = zins + wins; - aPoint = 0x0C; - } - - //Decide between (1,0,1,0) and (0,1,0,1) - if (xins + zins > yins + wins) - { - bScore = xins + zins; - bPoint = 0x05; - } - else - { - bScore = yins + wins; - bPoint = 0x0A; - } - - //Closer between (1,0,0,1) and (0,1,1,0) will replace the further of a and b, if closer. - if (xins + wins > yins + zins) - { - double score = xins + wins; - if (aScore >= bScore && score > bScore) - { - bScore = score; - bPoint = 0x09; - } - else if (aScore < bScore && score > aScore) - { - aScore = score; - aPoint = 0x09; - } - } - else - { - double score = yins + zins; - if (aScore >= bScore && score > bScore) - { - bScore = score; - bPoint = 0x06; - } - else if (aScore < bScore && score > aScore) - { - aScore = score; - aPoint = 0x06; - } - } - - //Decide if (1,0,0,0) is closer. - double p1 = 2 - inSum + xins; - if (aScore >= bScore && p1 > bScore) - { - bScore = p1; - bPoint = 0x01; - bIsBiggerSide = false; - } - else if (aScore < bScore && p1 > aScore) - { - aScore = p1; - aPoint = 0x01; - aIsBiggerSide = false; - } - - //Decide if (0,1,0,0) is closer. - double p2 = 2 - inSum + yins; - if (aScore >= bScore && p2 > bScore) - { - bScore = p2; - bPoint = 0x02; - bIsBiggerSide = false; - } - else if (aScore < bScore && p2 > aScore) - { - aScore = p2; - aPoint = 0x02; - aIsBiggerSide = false; - } - - //Decide if (0,0,1,0) is closer. - double p3 = 2 - inSum + zins; - if (aScore >= bScore && p3 > bScore) - { - bScore = p3; - bPoint = 0x04; - bIsBiggerSide = false; - } - else if (aScore < bScore && p3 > aScore) - { - aScore = p3; - aPoint = 0x04; - aIsBiggerSide = false; - } - - //Decide if (0,0,0,1) is closer. - double p4 = 2 - inSum + wins; - if (aScore >= bScore && p4 > bScore) - { - bScore = p4; - bPoint = 0x08; - bIsBiggerSide = false; - } - else if (aScore < bScore && p4 > aScore) - { - aScore = p4; - aPoint = 0x08; - aIsBiggerSide = false; - } - - //Where each of the two closest points are determines how the extra three vertices are calculated. - if (aIsBiggerSide == bIsBiggerSide) - { - if (aIsBiggerSide) - { //Both closest points on the bigger side - char c1 = static_cast(aPoint | bPoint); - char c2 = static_cast(aPoint & bPoint); - if ((c1 & 0x01) == 0) - { - xsv_ext0 = xsb; - xsv_ext1 = xsb - 1; - dx_ext0 = dx0 - 3 * m_squish4d; - dx_ext1 = dx0 + 1 - 2 * m_squish4d; - } - else - { - xsv_ext0 = xsv_ext1 = xsb + 1; - dx_ext0 = dx0 - 1 - 3 * m_squish4d; - dx_ext1 = dx0 - 1 - 2 * m_squish4d; - } - - if ((c1 & 0x02) == 0) - { - ysv_ext0 = ysb; - ysv_ext1 = ysb - 1; - dy_ext0 = dy0 - 3 * m_squish4d; - dy_ext1 = dy0 + 1 - 2 * m_squish4d; - } - else - { - ysv_ext0 = ysv_ext1 = ysb + 1; - dy_ext0 = dy0 - 1 - 3 * m_squish4d; - dy_ext1 = dy0 - 1 - 2 * m_squish4d; - } - - if ((c1 & 0x04) == 0) - { - zsv_ext0 = zsb; - zsv_ext1 = zsb - 1; - dz_ext0 = dz0 - 3 * m_squish4d; - dz_ext1 = dz0 + 1 - 2 * m_squish4d; - } - else - { - zsv_ext0 = zsv_ext1 = zsb + 1; - dz_ext0 = dz0 - 1 - 3 * m_squish4d; - dz_ext1 = dz0 - 1 - 2 * m_squish4d; - } - - if ((c1 & 0x08) == 0) - { - wsv_ext0 = wsb; - wsv_ext1 = wsb - 1; - dw_ext0 = dw0 - 3 * m_squish4d; - dw_ext1 = dw0 + 1 - 2 * m_squish4d; - } - else - { - wsv_ext0 = wsv_ext1 = wsb + 1; - dw_ext0 = dw0 - 1 - 3 * m_squish4d; - dw_ext1 = dw0 - 1 - 2 * m_squish4d; - } - - //One combination is a permutation of (0,0,0,2) based on c2 - xsv_ext2 = xsb; - ysv_ext2 = ysb; - zsv_ext2 = zsb; - wsv_ext2 = wsb; - dx_ext2 = dx0 - 2 * m_squish4d; - dy_ext2 = dy0 - 2 * m_squish4d; - dz_ext2 = dz0 - 2 * m_squish4d; - dw_ext2 = dw0 - 2 * m_squish4d; - if ((c2 & 0x01) != 0) - { - xsv_ext2 += 2; - dx_ext2 -= 2; - } - else if ((c2 & 0x02) != 0) - { - ysv_ext2 += 2; - dy_ext2 -= 2; - } - else if ((c2 & 0x04) != 0) - { - zsv_ext2 += 2; - dz_ext2 -= 2; - } - else - { - wsv_ext2 += 2; - dw_ext2 -= 2; - } - - } - else - { //Both closest points on the smaller side - //One of the two extra points is (0,0,0,0) - xsv_ext2 = xsb; - ysv_ext2 = ysb; - zsv_ext2 = zsb; - wsv_ext2 = wsb; - dx_ext2 = dx0; - dy_ext2 = dy0; - dz_ext2 = dz0; - dw_ext2 = dw0; - - //Other two points are based on the omitted axes. - char c = static_cast(aPoint | bPoint); - - if ((c & 0x01) == 0) - { - xsv_ext0 = xsb - 1; - xsv_ext1 = xsb; - dx_ext0 = dx0 + 1 - m_squish4d; - dx_ext1 = dx0 - m_squish4d; - } - else - { - xsv_ext0 = xsv_ext1 = xsb + 1; - dx_ext0 = dx_ext1 = dx0 - 1 - m_squish4d; - } - - if ((c & 0x02) == 0) - { - ysv_ext0 = ysv_ext1 = ysb; - dy_ext0 = dy_ext1 = dy0 - m_squish4d; - if ((c & 0x01) == 0x01) - { - ysv_ext0 -= 1; - dy_ext0 += 1; - } - else - { - ysv_ext1 -= 1; - dy_ext1 += 1; - } - } - else - { - ysv_ext0 = ysv_ext1 = ysb + 1; - dy_ext0 = dy_ext1 = dy0 - 1 - m_squish4d; - } - - if ((c & 0x04) == 0) - { - zsv_ext0 = zsv_ext1 = zsb; - dz_ext0 = dz_ext1 = dz0 - m_squish4d; - if ((c & 0x03) == 0x03) - { - zsv_ext0 -= 1; - dz_ext0 += 1; - } - else - { - zsv_ext1 -= 1; - dz_ext1 += 1; - } - } - else - { - zsv_ext0 = zsv_ext1 = zsb + 1; - dz_ext0 = dz_ext1 = dz0 - 1 - m_squish4d; - } - - if ((c & 0x08) == 0) - { - wsv_ext0 = wsb; - wsv_ext1 = wsb - 1; - dw_ext0 = dw0 - m_squish4d; - dw_ext1 = dw0 + 1 - m_squish4d; - } - else - { - wsv_ext0 = wsv_ext1 = wsb + 1; - dw_ext0 = dw_ext1 = dw0 - 1 - m_squish4d; - } - - } - } - else - { //One point on each "side" - char c1, c2; - if (aIsBiggerSide) - { - c1 = aPoint; - c2 = bPoint; - } - else - { - c1 = bPoint; - c2 = aPoint; - } - - //Two contributions are the bigger-sided point with each 0 replaced with -1. - if ((c1 & 0x01) == 0) - { - xsv_ext0 = xsb - 1; - xsv_ext1 = xsb; - dx_ext0 = dx0 + 1 - m_squish4d; - dx_ext1 = dx0 - m_squish4d; - } - else - { - xsv_ext0 = xsv_ext1 = xsb + 1; - dx_ext0 = dx_ext1 = dx0 - 1 - m_squish4d; - } - - if ((c1 & 0x02) == 0) - { - ysv_ext0 = ysv_ext1 = ysb; - dy_ext0 = dy_ext1 = dy0 - m_squish4d; - if ((c1 & 0x01) == 0x01) - { - ysv_ext0 -= 1; - dy_ext0 += 1; - } - else - { - ysv_ext1 -= 1; - dy_ext1 += 1; - } - } - else - { - ysv_ext0 = ysv_ext1 = ysb + 1; - dy_ext0 = dy_ext1 = dy0 - 1 - m_squish4d; - } - - if ((c1 & 0x04) == 0) - { - zsv_ext0 = zsv_ext1 = zsb; - dz_ext0 = dz_ext1 = dz0 - m_squish4d; - if ((c1 & 0x03) == 0x03) - { - zsv_ext0 -= 1; - dz_ext0 += 1; - } - else - { - zsv_ext1 -= 1; - dz_ext1 += 1; - } - } - else - { - zsv_ext0 = zsv_ext1 = zsb + 1; - dz_ext0 = dz_ext1 = dz0 - 1 - m_squish4d; - } - - if ((c1 & 0x08) == 0) - { - wsv_ext0 = wsb; - wsv_ext1 = wsb - 1; - dw_ext0 = dw0 - m_squish4d; - dw_ext1 = dw0 + 1 - m_squish4d; - } - else - { - wsv_ext0 = wsv_ext1 = wsb + 1; - dw_ext0 = dw_ext1 = dw0 - 1 - m_squish4d; - } - - //One contribution is a permutation of (0,0,0,2) based on the smaller-sided point - xsv_ext2 = xsb; - ysv_ext2 = ysb; - zsv_ext2 = zsb; - wsv_ext2 = wsb; - dx_ext2 = dx0 - 2 * m_squish4d; - dy_ext2 = dy0 - 2 * m_squish4d; - dz_ext2 = dz0 - 2 * m_squish4d; - dw_ext2 = dw0 - 2 * m_squish4d; - if ((c2 & 0x01) != 0) - { - xsv_ext2 += 2; - dx_ext2 -= 2; - } - else if ((c2 & 0x02) != 0) - { - ysv_ext2 += 2; - dy_ext2 -= 2; - } - else if ((c2 & 0x04) != 0) - { - zsv_ext2 += 2; - dz_ext2 -= 2; - } - else - { - wsv_ext2 += 2; - dw_ext2 -= 2; - } - } - - //Contribution (1,0,0,0) - double dx1 = dx0 - 1 - m_squish4d; - double dy1 = dy0 - 0 - m_squish4d; - double dz1 = dz0 - 0 - m_squish4d; - double dw1 = dw0 - 0 - m_squish4d; - double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1; - if (attn1 > 0) - { - attn1 *= attn1; - value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, zsb + 0, wsb + 0, dx1, dy1, dz1, dw1); - } - - //Contribution (0,1,0,0) - double dx2 = dx0 - 0 - m_squish4d; - double dy2 = dy0 - 1 - m_squish4d; - double dz2 = dz1; - double dw2 = dw1; - double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2; - if (attn2 > 0) - { - attn2 *= attn2; - value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, zsb + 0, wsb + 0, dx2, dy2, dz2, dw2); - } - - //Contribution (0,0,1,0) - double dx3 = dx2; - double dy3 = dy1; - double dz3 = dz0 - 1 - m_squish4d; - double dw3 = dw1; - double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3; - if (attn3 > 0) - { - attn3 *= attn3; - value += attn3 * attn3 * extrapolate(xsb + 0, ysb + 0, zsb + 1, wsb + 0, dx3, dy3, dz3, dw3); - } - - //Contribution (0,0,0,1) - double dx4 = dx2; - double dy4 = dy1; - double dz4 = dz1; - double dw4 = dw0 - 1 - m_squish4d; - double attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4; - if (attn4 > 0) - { - attn4 *= attn4; - value += attn4 * attn4 * extrapolate(xsb + 0, ysb + 0, zsb + 0, wsb + 1, dx4, dy4, dz4, dw4); - } - - //Contribution (1,1,0,0) - double dx5 = dx0 - 1 - 2 * m_squish4d; - double dy5 = dy0 - 1 - 2 * m_squish4d; - double dz5 = dz0 - 0 - 2 * m_squish4d; - double dw5 = dw0 - 0 - 2 * m_squish4d; - double attn5 = 2 - dx5 * dx5 - dy5 * dy5 - dz5 * dz5 - dw5 * dw5; - if (attn5 > 0) - { - attn5 *= attn5; - value += attn5 * attn5 * extrapolate(xsb + 1, ysb + 1, zsb + 0, wsb + 0, dx5, dy5, dz5, dw5); - } - - //Contribution (1,0,1,0) - double dx6 = dx0 - 1 - 2 * m_squish4d; - double dy6 = dy0 - 0 - 2 * m_squish4d; - double dz6 = dz0 - 1 - 2 * m_squish4d; - double dw6 = dw0 - 0 - 2 * m_squish4d; - double attn6 = 2 - dx6 * dx6 - dy6 * dy6 - dz6 * dz6 - dw6 * dw6; - if (attn6 > 0) - { - attn6 *= attn6; - value += attn6 * attn6 * extrapolate(xsb + 1, ysb + 0, zsb + 1, wsb + 0, dx6, dy6, dz6, dw6); - } - - //Contribution (1,0,0,1) - double dx7 = dx0 - 1 - 2 * m_squish4d; - double dy7 = dy0 - 0 - 2 * m_squish4d; - double dz7 = dz0 - 0 - 2 * m_squish4d; - double dw7 = dw0 - 1 - 2 * m_squish4d; - double attn7 = 2 - dx7 * dx7 - dy7 * dy7 - dz7 * dz7 - dw7 * dw7; - if (attn7 > 0) - { - attn7 *= attn7; - value += attn7 * attn7 * extrapolate(xsb + 1, ysb + 0, zsb + 0, wsb + 1, dx7, dy7, dz7, dw7); - } - - //Contribution (0,1,1,0) - double dx8 = dx0 - 0 - 2 * m_squish4d; - double dy8 = dy0 - 1 - 2 * m_squish4d; - double dz8 = dz0 - 1 - 2 * m_squish4d; - double dw8 = dw0 - 0 - 2 * m_squish4d; - double attn8 = 2 - dx8 * dx8 - dy8 * dy8 - dz8 * dz8 - dw8 * dw8; - if (attn8 > 0) - { - attn8 *= attn8; - value += attn8 * attn8 * extrapolate(xsb + 0, ysb + 1, zsb + 1, wsb + 0, dx8, dy8, dz8, dw8); - } - - //Contribution (0,1,0,1) - double dx9 = dx0 - 0 - 2 * m_squish4d; - double dy9 = dy0 - 1 - 2 * m_squish4d; - double dz9 = dz0 - 0 - 2 * m_squish4d; - double dw9 = dw0 - 1 - 2 * m_squish4d; - double attn9 = 2 - dx9 * dx9 - dy9 * dy9 - dz9 * dz9 - dw9 * dw9; - if (attn9 > 0) - { - attn9 *= attn9; - value += attn9 * attn9 * extrapolate(xsb + 0, ysb + 1, zsb + 0, wsb + 1, dx9, dy9, dz9, dw9); - } - - //Contribution (0,0,1,1) - double dx10 = dx0 - 0 - 2 * m_squish4d; - double dy10 = dy0 - 0 - 2 * m_squish4d; - double dz10 = dz0 - 1 - 2 * m_squish4d; - double dw10 = dw0 - 1 - 2 * m_squish4d; - double attn10 = 2 - dx10 * dx10 - dy10 * dy10 - dz10 * dz10 - dw10 * dw10; - if (attn10 > 0) - { - attn10 *= attn10; - value += attn10 * attn10 * extrapolate(xsb + 0, ysb + 0, zsb + 1, wsb + 1, dx10, dy10, dz10, dw10); - } - } - else - { //We're inside the second dispentachoron (Rectified 4-Simplex) - double aScore; - char aPoint; - bool aIsBiggerSide = true; - double bScore; - char bPoint; - bool bIsBiggerSide = true; - - //Decide between (0,0,1,1) and (1,1,0,0) - if (xins + yins < zins + wins) - { - aScore = xins + yins; - aPoint = 0x0C; - } - else - { - aScore = zins + wins; - aPoint = 0x03; - } - - //Decide between (0,1,0,1) and (1,0,1,0) - if (xins + zins < yins + wins) - { - bScore = xins + zins; - bPoint = 0x0A; - } - else - { - bScore = yins + wins; - bPoint = 0x05; - } - - //Closer between (0,1,1,0) and (1,0,0,1) will replace the further of a and b, if closer. - if (xins + wins < yins + zins) - { - double score = xins + wins; - if (aScore <= bScore && score < bScore) - { - bScore = score; - bPoint = 0x06; - } - else if (aScore > bScore && score < aScore) - { - aScore = score; - aPoint = 0x06; - } - } - else - { - double score = yins + zins; - if (aScore <= bScore && score < bScore) - { - bScore = score; - bPoint = 0x09; - } - else if (aScore > bScore && score < aScore) - { - aScore = score; - aPoint = 0x09; - } - } - - //Decide if (0,1,1,1) is closer. - double p1 = 3 - inSum + xins; - if (aScore <= bScore && p1 < bScore) - { - bScore = p1; - bPoint = 0x0E; - bIsBiggerSide = false; - } - else if (aScore > bScore && p1 < aScore) - { - aScore = p1; - aPoint = 0x0E; - aIsBiggerSide = false; - } - - //Decide if (1,0,1,1) is closer. - double p2 = 3 - inSum + yins; - if (aScore <= bScore && p2 < bScore) - { - bScore = p2; - bPoint = 0x0D; - bIsBiggerSide = false; - } - else if (aScore > bScore && p2 < aScore) - { - aScore = p2; - aPoint = 0x0D; - aIsBiggerSide = false; - } - - //Decide if (1,1,0,1) is closer. - double p3 = 3 - inSum + zins; - if (aScore <= bScore && p3 < bScore) - { - bScore = p3; - bPoint = 0x0B; - bIsBiggerSide = false; - } - else if (aScore > bScore && p3 < aScore) - { - aScore = p3; - aPoint = 0x0B; - aIsBiggerSide = false; - } - - //Decide if (1,1,1,0) is closer. - double p4 = 3 - inSum + wins; - if (aScore <= bScore && p4 < bScore) - { - bScore = p4; - bPoint = 0x07; - bIsBiggerSide = false; - } - else if (aScore > bScore && p4 < aScore) - { - aScore = p4; - aPoint = 0x07; - aIsBiggerSide = false; - } - - //Where each of the two closest points are determines how the extra three vertices are calculated. - if (aIsBiggerSide == bIsBiggerSide) - { - if (aIsBiggerSide) - { //Both closest points on the bigger side - char c1 = static_cast(aPoint & bPoint); - char c2 = static_cast(aPoint | bPoint); - - //Two contributions are permutations of (0,0,0,1) and (0,0,0,2) based on c1 - xsv_ext0 = xsv_ext1 = xsb; - ysv_ext0 = ysv_ext1 = ysb; - zsv_ext0 = zsv_ext1 = zsb; - wsv_ext0 = wsv_ext1 = wsb; - dx_ext0 = dx0 - m_squish4d; - dy_ext0 = dy0 - m_squish4d; - dz_ext0 = dz0 - m_squish4d; - dw_ext0 = dw0 - m_squish4d; - dx_ext1 = dx0 - 2 * m_squish4d; - dy_ext1 = dy0 - 2 * m_squish4d; - dz_ext1 = dz0 - 2 * m_squish4d; - dw_ext1 = dw0 - 2 * m_squish4d; - if ((c1 & 0x01) != 0) - { - xsv_ext0 += 1; - dx_ext0 -= 1; - xsv_ext1 += 2; - dx_ext1 -= 2; - } - else if ((c1 & 0x02) != 0) - { - ysv_ext0 += 1; - dy_ext0 -= 1; - ysv_ext1 += 2; - dy_ext1 -= 2; - } - else if ((c1 & 0x04) != 0) - { - zsv_ext0 += 1; - dz_ext0 -= 1; - zsv_ext1 += 2; - dz_ext1 -= 2; - } - else - { - wsv_ext0 += 1; - dw_ext0 -= 1; - wsv_ext1 += 2; - dw_ext1 -= 2; - } - - //One contribution is a permutation of (1,1,1,-1) based on c2 - xsv_ext2 = xsb + 1; - ysv_ext2 = ysb + 1; - zsv_ext2 = zsb + 1; - wsv_ext2 = wsb + 1; - dx_ext2 = dx0 - 1 - 2 * m_squish4d; - dy_ext2 = dy0 - 1 - 2 * m_squish4d; - dz_ext2 = dz0 - 1 - 2 * m_squish4d; - dw_ext2 = dw0 - 1 - 2 * m_squish4d; - if ((c2 & 0x01) == 0) - { - xsv_ext2 -= 2; - dx_ext2 += 2; - } - else if ((c2 & 0x02) == 0) - { - ysv_ext2 -= 2; - dy_ext2 += 2; - } - else if ((c2 & 0x04) == 0) - { - zsv_ext2 -= 2; - dz_ext2 += 2; - } - else - { - wsv_ext2 -= 2; - dw_ext2 += 2; - } - } - else - { //Both closest points on the smaller side - //One of the two extra points is (1,1,1,1) - xsv_ext2 = xsb + 1; - ysv_ext2 = ysb + 1; - zsv_ext2 = zsb + 1; - wsv_ext2 = wsb + 1; - dx_ext2 = dx0 - 1 - 4 * m_squish4d; - dy_ext2 = dy0 - 1 - 4 * m_squish4d; - dz_ext2 = dz0 - 1 - 4 * m_squish4d; - dw_ext2 = dw0 - 1 - 4 * m_squish4d; - - //Other two points are based on the shared axes. - char c = static_cast(aPoint & bPoint); - - if ((c & 0x01) != 0) - { - xsv_ext0 = xsb + 2; - xsv_ext1 = xsb + 1; - dx_ext0 = dx0 - 2 - 3 * m_squish4d; - dx_ext1 = dx0 - 1 - 3 * m_squish4d; - } - else - { - xsv_ext0 = xsv_ext1 = xsb; - dx_ext0 = dx_ext1 = dx0 - 3 * m_squish4d; - } - - if ((c & 0x02) != 0) - { - ysv_ext0 = ysv_ext1 = ysb + 1; - dy_ext0 = dy_ext1 = dy0 - 1 - 3 * m_squish4d; - if ((c & 0x01) == 0) - { - ysv_ext0 += 1; - dy_ext0 -= 1; - } - else - { - ysv_ext1 += 1; - dy_ext1 -= 1; - } - } - else - { - ysv_ext0 = ysv_ext1 = ysb; - dy_ext0 = dy_ext1 = dy0 - 3 * m_squish4d; - } - - if ((c & 0x04) != 0) - { - zsv_ext0 = zsv_ext1 = zsb + 1; - dz_ext0 = dz_ext1 = dz0 - 1 - 3 * m_squish4d; - if ((c & 0x03) == 0) - { - zsv_ext0 += 1; - dz_ext0 -= 1; - } - else - { - zsv_ext1 += 1; - dz_ext1 -= 1; - } - } - else - { - zsv_ext0 = zsv_ext1 = zsb; - dz_ext0 = dz_ext1 = dz0 - 3 * m_squish4d; - } - - if ((c & 0x08) != 0) - { - wsv_ext0 = wsb + 1; - wsv_ext1 = wsb + 2; - dw_ext0 = dw0 - 1 - 3 * m_squish4d; - dw_ext1 = dw0 - 2 - 3 * m_squish4d; - } - else - { - wsv_ext0 = wsv_ext1 = wsb; - dw_ext0 = dw_ext1 = dw0 - 3 * m_squish4d; - } - } - } - else - { //One point on each "side" - char c1, c2; - if (aIsBiggerSide) - { - c1 = aPoint; - c2 = bPoint; - } - else - { - c1 = bPoint; - c2 = aPoint; - } - - //Two contributions are the bigger-sided point with each 1 replaced with 2. - if ((c1 & 0x01) != 0) - { - xsv_ext0 = xsb + 2; - xsv_ext1 = xsb + 1; - dx_ext0 = dx0 - 2 - 3 * m_squish4d; - dx_ext1 = dx0 - 1 - 3 * m_squish4d; - } - else - { - xsv_ext0 = xsv_ext1 = xsb; - dx_ext0 = dx_ext1 = dx0 - 3 * m_squish4d; - } - - if ((c1 & 0x02) != 0) - { - ysv_ext0 = ysv_ext1 = ysb + 1; - dy_ext0 = dy_ext1 = dy0 - 1 - 3 * m_squish4d; - if ((c1 & 0x01) == 0) - { - ysv_ext0 += 1; - dy_ext0 -= 1; - } - else - { - ysv_ext1 += 1; - dy_ext1 -= 1; - } - } - else - { - ysv_ext0 = ysv_ext1 = ysb; - dy_ext0 = dy_ext1 = dy0 - 3 * m_squish4d; - } - - if ((c1 & 0x04) != 0) - { - zsv_ext0 = zsv_ext1 = zsb + 1; - dz_ext0 = dz_ext1 = dz0 - 1 - 3 * m_squish4d; - if ((c1 & 0x03) == 0) - { - zsv_ext0 += 1; - dz_ext0 -= 1; - } - else - { - zsv_ext1 += 1; - dz_ext1 -= 1; - } - } - else - { - zsv_ext0 = zsv_ext1 = zsb; - dz_ext0 = dz_ext1 = dz0 - 3 * m_squish4d; - } - - if ((c1 & 0x08) != 0) - { - wsv_ext0 = wsb + 1; - wsv_ext1 = wsb + 2; - dw_ext0 = dw0 - 1 - 3 * m_squish4d; - dw_ext1 = dw0 - 2 - 3 * m_squish4d; - } - else - { - wsv_ext0 = wsv_ext1 = wsb; - dw_ext0 = dw_ext1 = dw0 - 3 * m_squish4d; - } - - //One contribution is a permutation of (1,1,1,-1) based on the smaller-sided point - xsv_ext2 = xsb + 1; - ysv_ext2 = ysb + 1; - zsv_ext2 = zsb + 1; - wsv_ext2 = wsb + 1; - dx_ext2 = dx0 - 1 - 2 * m_squish4d; - dy_ext2 = dy0 - 1 - 2 * m_squish4d; - dz_ext2 = dz0 - 1 - 2 * m_squish4d; - dw_ext2 = dw0 - 1 - 2 * m_squish4d; - if ((c2 & 0x01) == 0) - { - xsv_ext2 -= 2; - dx_ext2 += 2; - } - else if ((c2 & 0x02) == 0) - { - ysv_ext2 -= 2; - dy_ext2 += 2; - } - else if ((c2 & 0x04) == 0) - { - zsv_ext2 -= 2; - dz_ext2 += 2; - } - else - { - wsv_ext2 -= 2; - dw_ext2 += 2; - } - } - - //Contribution (1,1,1,0) - double dx4 = dx0 - 1 - 3 * m_squish4d; - double dy4 = dy0 - 1 - 3 * m_squish4d; - double dz4 = dz0 - 1 - 3 * m_squish4d; - double dw4 = dw0 - 3 * m_squish4d; - double attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4; - if (attn4 > 0) - { - attn4 *= attn4; - value += attn4 * attn4 * extrapolate(xsb + 1, ysb + 1, zsb + 1, wsb + 0, dx4, dy4, dz4, dw4); - } - - //Contribution (1,1,0,1) - double dx3 = dx4; - double dy3 = dy4; - double dz3 = dz0 - 3 * m_squish4d; - double dw3 = dw0 - 1 - 3 * m_squish4d; - double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3; - if (attn3 > 0) - { - attn3 *= attn3; - value += attn3 * attn3 * extrapolate(xsb + 1, ysb + 1, zsb + 0, wsb + 1, dx3, dy3, dz3, dw3); - } - - //Contribution (1,0,1,1) - double dx2 = dx4; - double dy2 = dy0 - 3 * m_squish4d; - double dz2 = dz4; - double dw2 = dw3; - double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2; - if (attn2 > 0) - { - attn2 *= attn2; - value += attn2 * attn2 * extrapolate(xsb + 1, ysb + 0, zsb + 1, wsb + 1, dx2, dy2, dz2, dw2); - } - - //Contribution (0,1,1,1) - double dx1 = dx0 - 3 * m_squish4d; - double dz1 = dz4; - double dy1 = dy4; - double dw1 = dw3; - double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1; - if (attn1 > 0) - { - attn1 *= attn1; - value += attn1 * attn1 * extrapolate(xsb + 0, ysb + 1, zsb + 1, wsb + 1, dx1, dy1, dz1, dw1); - } - - //Contribution (1,1,0,0) - double dx5 = dx0 - 1 - 2 * m_squish4d; - double dy5 = dy0 - 1 - 2 * m_squish4d; - double dz5 = dz0 - 0 - 2 * m_squish4d; - double dw5 = dw0 - 0 - 2 * m_squish4d; - double attn5 = 2 - dx5 * dx5 - dy5 * dy5 - dz5 * dz5 - dw5 * dw5; - if (attn5 > 0) - { - attn5 *= attn5; - value += attn5 * attn5 * extrapolate(xsb + 1, ysb + 1, zsb + 0, wsb + 0, dx5, dy5, dz5, dw5); - } - - //Contribution (1,0,1,0) - double dx6 = dx0 - 1 - 2 * m_squish4d; - double dy6 = dy0 - 0 - 2 * m_squish4d; - double dz6 = dz0 - 1 - 2 * m_squish4d; - double dw6 = dw0 - 0 - 2 * m_squish4d; - double attn6 = 2 - dx6 * dx6 - dy6 * dy6 - dz6 * dz6 - dw6 * dw6; - if (attn6 > 0) - { - attn6 *= attn6; - value += attn6 * attn6 * extrapolate(xsb + 1, ysb + 0, zsb + 1, wsb + 0, dx6, dy6, dz6, dw6); - } - - //Contribution (1,0,0,1) - double dx7 = dx0 - 1 - 2 * m_squish4d; - double dy7 = dy0 - 0 - 2 * m_squish4d; - double dz7 = dz0 - 0 - 2 * m_squish4d; - double dw7 = dw0 - 1 - 2 * m_squish4d; - double attn7 = 2 - dx7 * dx7 - dy7 * dy7 - dz7 * dz7 - dw7 * dw7; - if (attn7 > 0) - { - attn7 *= attn7; - value += attn7 * attn7 * extrapolate(xsb + 1, ysb + 0, zsb + 0, wsb + 1, dx7, dy7, dz7, dw7); - } - - //Contribution (0,1,1,0) - double dx8 = dx0 - 0 - 2 * m_squish4d; - double dy8 = dy0 - 1 - 2 * m_squish4d; - double dz8 = dz0 - 1 - 2 * m_squish4d; - double dw8 = dw0 - 0 - 2 * m_squish4d; - double attn8 = 2 - dx8 * dx8 - dy8 * dy8 - dz8 * dz8 - dw8 * dw8; - if (attn8 > 0) - { - attn8 *= attn8; - value += attn8 * attn8 * extrapolate(xsb + 0, ysb + 1, zsb + 1, wsb + 0, dx8, dy8, dz8, dw8); - } - - //Contribution (0,1,0,1) - double dx9 = dx0 - 0 - 2 * m_squish4d; - double dy9 = dy0 - 1 - 2 * m_squish4d; - double dz9 = dz0 - 0 - 2 * m_squish4d; - double dw9 = dw0 - 1 - 2 * m_squish4d; - double attn9 = 2 - dx9 * dx9 - dy9 * dy9 - dz9 * dz9 - dw9 * dw9; - if (attn9 > 0) - { - attn9 *= attn9; - value += attn9 * attn9 * extrapolate(xsb + 0, ysb + 1, zsb + 0, wsb + 1, dx9, dy9, dz9, dw9); - } - - //Contribution (0,0,1,1) - double dx10 = dx0 - 0 - 2 * m_squish4d; - double dy10 = dy0 - 0 - 2 * m_squish4d; - double dz10 = dz0 - 1 - 2 * m_squish4d; - double dw10 = dw0 - 1 - 2 * m_squish4d; - double attn10 = 2 - dx10 * dx10 - dy10 * dy10 - dz10 * dz10 - dw10 * dw10; - if (attn10 > 0) - { - attn10 *= attn10; - value += attn10 * attn10 * extrapolate(xsb + 0, ysb + 0, zsb + 1, wsb + 1, dx10, dy10, dz10, dw10); - } - } - - //First extra vertex - double attn_ext0 = 2 - dx_ext0 * dx_ext0 - dy_ext0 * dy_ext0 - dz_ext0 * dz_ext0 - dw_ext0 * dw_ext0; - if (attn_ext0 > 0) - { - attn_ext0 *= attn_ext0; - value += attn_ext0 * attn_ext0 * extrapolate(xsv_ext0, ysv_ext0, zsv_ext0, wsv_ext0, dx_ext0, dy_ext0, dz_ext0, dw_ext0); - } - - //Second extra vertex - double attn_ext1 = 2 - dx_ext1 * dx_ext1 - dy_ext1 * dy_ext1 - dz_ext1 * dz_ext1 - dw_ext1 * dw_ext1; - if (attn_ext1 > 0) - { - attn_ext1 *= attn_ext1; - value += attn_ext1 * attn_ext1 * extrapolate(xsv_ext1, ysv_ext1, zsv_ext1, wsv_ext1, dx_ext1, dy_ext1, dz_ext1, dw_ext1); - } - - //Third extra vertex - double attn_ext2 = 2 - dx_ext2 * dx_ext2 - dy_ext2 * dy_ext2 - dz_ext2 * dz_ext2 - dw_ext2 * dw_ext2; - if (attn_ext2 > 0) - { - attn_ext2 *= attn_ext2; - value += attn_ext2 * attn_ext2 * extrapolate(xsv_ext2, ysv_ext2, zsv_ext2, wsv_ext2, dx_ext2, dy_ext2, dz_ext2, dw_ext2); - } - - return value / m_norm4d; - } - - double Noise::extrapolate(int xsb, int ysb, double dx, double dy) const - { - int index = m_perm[(m_perm[xsb & 0xFF] + ysb) & 0xFF] & 0x0E; - return m_gradients2d[index] * dx - + m_gradients2d[index + 1] * dy; - } - - double Noise::extrapolate(int xsb, int ysb, int zsb, double dx, double dy, double dz) const - { - int index = m_permGradIndex3d[(m_perm[(m_perm[xsb & 0xFF] + ysb) & 0xFF] + zsb) & 0xFF]; - return m_gradients3d[index] * dx - + m_gradients3d[index + 1] * dy - + m_gradients3d[index + 2] * dz; - } - - double Noise::extrapolate(int xsb, int ysb, int zsb, int wsb, double dx, double dy, double dz, double dw) const - { - int index = m_perm[(m_perm[(m_perm[(m_perm[xsb & 0xFF] + ysb) & 0xFF] + zsb) & 0xFF] + wsb) & 0xFF] & 0xFC; - return m_gradients4d[index] * dx - + m_gradients4d[index + 1] * dy - + m_gradients4d[index + 2] * dz - + m_gradients4d[index + 3] * dw; - } - -} \ No newline at end of file diff --git a/SQCSim2021/opensimplex.h b/SQCSim2021/opensimplex.h deleted file mode 100644 index 4b1b1f4..0000000 --- a/SQCSim2021/opensimplex.h +++ /dev/null @@ -1,51 +0,0 @@ -/** - Open Simple Noise for C++ - - Port to C++ from https://gist.github.com/KdotJPG/b1270127455a94ac5d19 - by Rickard Lundberg, 2019. -*/ -#ifndef _OPENSIMPLEX_H__ -#define _OPENSIMPLEX_H__ - -#include -#include - -namespace OpenSimplexNoise -{ - class Noise - { - public: - Noise(); - Noise(int64_t seed); - //2D Open Simplex Noise. - double eval(const double x, const double y) const; - //3D Open Simplex Noise. - double eval(double x, double y, double z) const; - //4D Open Simplex Noise. - double eval(double x, double y, double z, double w) const; - private: - const double m_stretch2d; - const double m_squish2d; - const double m_stretch3d; - const double m_squish3d; - const double m_stretch4d; - const double m_squish4d; - - const double m_norm2d; - const double m_norm3d; - const double m_norm4d; - - const long m_defaultSeed; - - std::array m_perm; - std::array m_permGradIndex3d; - std::array m_gradients2d; - std::array m_gradients3d; - std::array m_gradients4d; - double extrapolate(int xsb, int ysb, double dx, double dy) const; - double extrapolate(int xsb, int ysb, int zsb, double dx, double dy, double dz) const; - double extrapolate(int xsb, int ysb, int zsb, int wsb, double dx, double dy, double dz, double dw) const; - }; -} - -#endif // _OPENSIMPLEX_H__ \ No newline at end of file diff --git a/SQCSim2021/player.h b/SQCSim2021/player.h index 239b550..d8f46f3 100644 --- a/SQCSim2021/player.h +++ b/SQCSim2021/player.h @@ -1,5 +1,5 @@ -#ifndef _PLAYER_H__ -#define _PLAYER_H__ +#ifndef CLI_PLAYER_H__ +#define CLI_PLAYER_H__ #include "../SQCSim-common/vector3.h" #include "transformation.h" #include "audio.h" diff --git a/SQCSim2021/transformation.h b/SQCSim2021/transformation.h index 764a781..72e0690 100644 --- a/SQCSim2021/transformation.h +++ b/SQCSim2021/transformation.h @@ -1,9 +1,10 @@ #ifndef TRANSFORMATION_H__ #define TRANSFORMATION_H__ -#include "matrix4.h" -#include "../SQCSim-common/vector3.h" #include +#include "../SQCSim-common/matrix4.h" +#include "../SQCSim-common/vector3.h" +#include "define.h" class Transformation { diff --git a/SQCSim2021/vertexbuffer.h b/SQCSim2021/vertexbuffer.h index b44f8f8..845eefe 100644 --- a/SQCSim2021/vertexbuffer.h +++ b/SQCSim2021/vertexbuffer.h @@ -1,8 +1,8 @@ #ifndef VERTEXBUFFER_H__ #define VERTEXBUFFER_H__ -#include "define.h" #include +#include "define.h" class VertexBuffer { diff --git a/SQCSim2021/world.h b/SQCSim2021/world.h index 486e2d2..d2b0dc1 100644 --- a/SQCSim2021/world.h +++ b/SQCSim2021/world.h @@ -5,14 +5,14 @@ #include #include #include -#include "define.h" -#include "chunk.h" -#include "array2d.h" #include "../SQCSim-common/vector3.h" +#include "../SQCSim-common/array2d.h" +#include "bullet.h" +#include "define.h" #include "player.h" +#include "chunk.h" #include "transformation.h" #include "shader.h" -#include "bullet.h" #include "textureatlas.h" class Chunk; From 6bea176979f662d83af5fa8bea734838cdec16e5 Mon Sep 17 00:00:00 2001 From: MarcEricMartel Date: Fri, 29 Sep 2023 17:02:57 -0400 Subject: [PATCH 13/19] Cleanup de l'objet Bullet --- SQCSim-common/bullet.cpp | 13 ++++++----- SQCSim-common/bullet.h | 17 ++++++++------ SQCSim2021/SQCSim2021.vcxproj | 2 -- SQCSim2021/SQCSim2021.vcxproj.filters | 6 ----- SQCSim2021/bullet.cpp | 32 --------------------------- SQCSim2021/bullet.h | 27 ---------------------- SQCSim2021/engine.cpp | 6 ++--- SQCSim2021/engine.h | 2 +- SQCSim2021/world.h | 2 +- 9 files changed, 23 insertions(+), 84 deletions(-) delete mode 100644 SQCSim2021/bullet.cpp delete mode 100644 SQCSim2021/bullet.h diff --git a/SQCSim-common/bullet.cpp b/SQCSim-common/bullet.cpp index 52824d5..5c4e6c4 100644 --- a/SQCSim-common/bullet.cpp +++ b/SQCSim-common/bullet.cpp @@ -1,10 +1,9 @@ #include "bullet.h" #include "world.h" -Bullet::Bullet(Player& player) { - m_startpos = m_currentpos = player.GetPOV() + player.GetDirection(); - m_velocity = player.GetDirection(); -} +Bullet::Bullet(Vector3f pos, Vector3f dir) : m_startpos(pos), m_currentpos(pos), m_velocity(dir) {} + +Bullet::Bullet(Vector3f pos, Vector3f dir, uint64_t tid): m_startpos(pos), m_currentpos(pos), m_velocity(dir), m_tid(tid) {} Bullet::~Bullet() {} @@ -31,6 +30,10 @@ void Bullet::Transpose(int& x, int& z) { m_startpos.z -= z * CHUNK_SIZE_Z; } -Vector3f& Bullet::getPos() { +Vector3f Bullet::getPos() { return m_currentpos; } + +uint64_t Bullet::getTeamID(){ + return m_tid; +} diff --git a/SQCSim-common/bullet.h b/SQCSim-common/bullet.h index 599eab6..d17000e 100644 --- a/SQCSim-common/bullet.h +++ b/SQCSim-common/bullet.h @@ -1,23 +1,26 @@ #ifndef BULLET_H__ #define BULLET_H__ - -#include "player.h" +#include "define.h" +#include "vector3.h" class World; class Bullet { public: - Bullet(Player& player); + Bullet(Vector3f pos, Vector3f dir); + Bullet(Vector3f pos, Vector3f dir, uint64_t tid); ~Bullet(); bool Update(World* world, float elapsedtime); void Transpose(int& x, int& z); - Vector3f& getPos(); + Vector3f getPos(); + uint64_t getTeamID(); private: - Vector3f m_startpos; - Vector3f m_currentpos; - Vector3f m_velocity; + Vector3f m_startpos, + m_currentpos, + m_velocity; + uint64_t m_tid = 0; }; #endif // BULLET_H__ diff --git a/SQCSim2021/SQCSim2021.vcxproj b/SQCSim2021/SQCSim2021.vcxproj index 005bcc5..02c2b51 100644 --- a/SQCSim2021/SQCSim2021.vcxproj +++ b/SQCSim2021/SQCSim2021.vcxproj @@ -20,7 +20,6 @@ - @@ -38,7 +37,6 @@ - diff --git a/SQCSim2021/SQCSim2021.vcxproj.filters b/SQCSim2021/SQCSim2021.vcxproj.filters index ec7b7d6..0e371d0 100644 --- a/SQCSim2021/SQCSim2021.vcxproj.filters +++ b/SQCSim2021/SQCSim2021.vcxproj.filters @@ -56,9 +56,6 @@ Fichiers d%27en-tête - - Fichiers d%27en-tête - @@ -106,8 +103,5 @@ Fichiers sources - - Fichiers sources - \ No newline at end of file diff --git a/SQCSim2021/bullet.cpp b/SQCSim2021/bullet.cpp deleted file mode 100644 index 38f4901..0000000 --- a/SQCSim2021/bullet.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "bullet.h" -#include "world.h" - -Bullet::Bullet(Player& player) { - m_startpos = m_currentpos = player.GetPOV() + player.GetDirection(); - m_velocity = player.GetDirection(); -} - -Bullet::~Bullet() {} - -bool Bullet::Update(World* world, Transformation& tran, float elapsedtime) { - for (int x = 0; x < 1000; ++x) { - m_currentpos += m_velocity * elapsedtime; - - if (!world->ChunkAt(m_currentpos)) - return true; - else if (world->BlockAt(m_currentpos) != BTYPE_AIR) { - world->ChangeBlockAtPosition(BTYPE_AIR, m_currentpos); - return true; - } - else if ((m_currentpos - m_startpos).Length() > VIEW_DISTANCE) return true; - } - - return false; -} - -void Bullet::Transpose(int& x, int& z) { - m_currentpos.x -= x * CHUNK_SIZE_X; - m_currentpos.z -= z * CHUNK_SIZE_Z; - m_startpos.x -= x * CHUNK_SIZE_X; - m_startpos.z -= z * CHUNK_SIZE_Z; -} diff --git a/SQCSim2021/bullet.h b/SQCSim2021/bullet.h deleted file mode 100644 index ce706f8..0000000 --- a/SQCSim2021/bullet.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef BULLET_H__ -#define BULLET_H__ - -#include "audio.h" -#include "player.h" -#include "vertexbuffer.h" -#include "texture.h" - -class World; - -class Bullet { -public: - Bullet(Player& player); - ~Bullet(); - - bool Update(World* world, Transformation& tran, float elapsedtime); - void Transpose(int& x, int& z); - -private: - Vector3f m_startpos; - Vector3f m_currentpos; - Vector3f m_velocity; - -}; - -#endif // BULLET_H__ - diff --git a/SQCSim2021/engine.cpp b/SQCSim2021/engine.cpp index 5f21e66..6599890 100644 --- a/SQCSim2021/engine.cpp +++ b/SQCSim2021/engine.cpp @@ -374,12 +374,12 @@ void Engine::Render(float elapsedTime) { else if (bulletTime <= 0.f) { for (int x = 0; x < MAX_BULLETS; ++x) // Ajouter une balle dans l'array (aussi connu sous le nom de "faire pow pow"). if (!m_bullets[x]) { - m_bullets[x] = new Bullet(m_player); + m_bullets[x] = new Bullet(m_player.GetPOV() + m_player.GetDirection(), m_player.GetDirection()); break; } else if (x == MAX_BULLETS - 1) { // S'il y a pas d'espace dans l'array, prendre la place de la première balle de l'array. m_bullets[0]->~Bullet(); - m_bullets[0] = new Bullet(m_player); + m_bullets[0] = new Bullet(m_player.GetPOV() + m_player.GetDirection(), m_player.GetDirection()); } bulletTime = .1f; m_audio.Create3DAudioObj(m_powpow, AUDIO_PATH "windowsaccount.wav", m_player.GetPOV(), m_player.GetDirection() * 10, .5f); @@ -396,7 +396,7 @@ void Engine::Render(float elapsedTime) { for (int x = 0; x < MAX_BULLETS; ++x) // Array de bullets en jeu. if (m_bullets[x]) - if (m_bullets[x]->Update(&m_world, all, elapsedTime)) { + if (m_bullets[x]->Update(&m_world, elapsedTime)) { m_bullets[x]->~Bullet(); m_bullets[x] = nullptr; } diff --git a/SQCSim2021/engine.h b/SQCSim2021/engine.h index 5220cf8..8c525b6 100644 --- a/SQCSim2021/engine.h +++ b/SQCSim2021/engine.h @@ -5,8 +5,8 @@ #include #include "../SQCSim-common/array2d.h" #include "../SQCSim-common/blockinfo.h" +#include "../SQCSim-common/bullet.h" #include "define.h" -#include "bullet.h" #include "openglcontext.h" #include "texture.h" #include "transformation.h" diff --git a/SQCSim2021/world.h b/SQCSim2021/world.h index d2b0dc1..0b88748 100644 --- a/SQCSim2021/world.h +++ b/SQCSim2021/world.h @@ -7,7 +7,7 @@ #include #include "../SQCSim-common/vector3.h" #include "../SQCSim-common/array2d.h" -#include "bullet.h" +#include "../SQCSim-common/bullet.h" #include "define.h" #include "player.h" #include "chunk.h" From ace555a93e4b74e4f2e051664ce44d032ab719d1 Mon Sep 17 00:00:00 2001 From: MarcEricMartel Date: Sat, 30 Sep 2023 10:27:05 -0400 Subject: [PATCH 14/19] Ajouts dans netprotocol --- SQCSim-common/netprotocol.cpp | 57 +++++++++++++++++++++++++++++++++++ SQCSim-common/netprotocol.h | 3 +- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/SQCSim-common/netprotocol.cpp b/SQCSim-common/netprotocol.cpp index b692201..d4edca2 100644 --- a/SQCSim-common/netprotocol.cpp +++ b/SQCSim-common/netprotocol.cpp @@ -1,7 +1,64 @@ #include "netprotocol.h" void netprot::Serialize(Input* in, char* buf[], uint32_t* buflen) { + *buf[0] = netprot::PACKET_TYPE::INPUT; + uint64_t time = in->timestamp; + uint8_t time8[sizeof(uint64_t)] = {(time >> 56) & 0xFF, + (time >> 48) & 0xFF, + (time >> 40) & 0xFF, + (time >> 32) & 0xFF, + (time >> 24) & 0xFF, + (time >> 16) & 0xFF, + (time >> 8 ) & 0xFF, + time & 0xFF}; + + memcpy(*buf + 1, time8, sizeof(uint64_t)); + + uint64_t sid = in->sid; + uint8_t sid8[sizeof(uint64_t)] = {(sid >> 56) & 0xFF, + (sid >> 48) & 0xFF, + (sid >> 40) & 0xFF, + (sid >> 32) & 0xFF, + (sid >> 24) & 0xFF, + (sid >> 16) & 0xFF, + (sid >> 8 ) & 0xFF, + sid & 0xFF}; + + memcpy(*buf + sizeof(uint64_t) + 1, sid8, sizeof(uint64_t)); + + Keys keys = in->keys; + uint8_t keys8 = // Reste un bit. + keys.forward & 0b10000000 | + keys.backward & 0b01000000 | + keys.left & 0b00100000 | + keys.right & 0b00010000 | + keys.jump & 0b00001000 | + keys.shoot & 0b00000100 | + keys.block & 0b00000010 ; + + memcpy(*buf + sizeof(uint64_t) + 2, &keys8, sizeof(uint8_t)); + + uint32_t vec[3]; + memcpy(vec, &in->direction, sizeof(Vector3f)); // Pour dénaturer les floats. + + uint8_t vec8[3 * sizeof(uint32_t)] = { + (vec[0] >> 24) & 0xFF, + (vec[0] >> 16) & 0xFF, + (vec[0] >> 8) & 0xFF, + vec[0] & 0xFF, + (vec[1] >> 24) & 0xFF, + (vec[1] >> 16) & 0xFF, + (vec[1] >> 8) & 0xFF, + vec[1] & 0xFF, + (vec[2] >> 24) & 0xFF, + (vec[2] >> 16) & 0xFF, + (vec[2] >> 8) & 0xFF, + vec[2] & 0xFF}; + + memcpy(*buf + sizeof(uint64_t) + 3, vec8, sizeof(uint32_t) * 3); + + *buflen = sizeof(uint64_t) + 3 + sizeof(uint32_t) * 3; } void netprot::Serialize(Output* out, char* buf[], uint32_t* buflen) { diff --git a/SQCSim-common/netprotocol.h b/SQCSim-common/netprotocol.h index 7f93349..bdaf0a7 100644 --- a/SQCSim-common/netprotocol.h +++ b/SQCSim-common/netprotocol.h @@ -24,7 +24,8 @@ namespace netprot { left, right, jump, - shoot; + shoot, + block; }; struct States { From 033365c9615e62fee7cb2e4e9ef1d3ac06087606 Mon Sep 17 00:00:00 2001 From: MarcEricMartel Date: Sat, 30 Sep 2023 14:46:54 -0400 Subject: [PATCH 15/19] =?UTF-8?q?Ind=C3=A9pendance!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SQCSim-common/SQCSim-common.vcxproj | 2 + SQCSim-common/SQCSim-common.vcxproj.filters | 6 + SQCSim-common/chunk.cpp | 35 +- SQCSim-common/chunk.h | 11 +- SQCSim-common/define.h | 10 +- SQCSim-common/player.cpp | 40 +- SQCSim-common/player.h | 22 +- .../transformation.cpp | 8 +- .../transformation.h | 6 +- SQCSim-common/world.cpp | 321 ++++++++++- SQCSim-common/world.h | 20 +- SQCSim2021/SQCSim2021.vcxproj | 12 +- SQCSim2021/SQCSim2021.vcxproj.filters | 36 +- SQCSim2021/audio.h | 2 +- SQCSim2021/chunk.cpp | 234 -------- SQCSim2021/chunk.h | 49 -- SQCSim2021/define.h | 8 - SQCSim2021/engine.cpp | 44 +- SQCSim2021/engine.h | 10 +- SQCSim2021/mesh.cpp | 120 +++++ SQCSim2021/mesh.h | 32 ++ SQCSim2021/openglcontext.h | 3 +- SQCSim2021/player.cpp | 209 -------- SQCSim2021/player.h | 43 -- SQCSim2021/skybox.cpp | 2 +- SQCSim2021/skybox.h | 2 +- SQCSim2021/texture.h | 4 +- SQCSim2021/textureatlas.h | 2 +- SQCSim2021/world.cpp | 506 ------------------ SQCSim2021/world.h | 59 -- SQCSim2021/worldrenderer.cpp | 213 ++++++++ SQCSim2021/worldrenderer.h | 23 + 32 files changed, 890 insertions(+), 1204 deletions(-) rename {SQCSim2021 => SQCSim-common}/transformation.cpp (91%) rename {SQCSim2021 => SQCSim-common}/transformation.h (87%) delete mode 100644 SQCSim2021/chunk.cpp delete mode 100644 SQCSim2021/chunk.h create mode 100644 SQCSim2021/mesh.cpp create mode 100644 SQCSim2021/mesh.h delete mode 100644 SQCSim2021/player.cpp delete mode 100644 SQCSim2021/player.h delete mode 100644 SQCSim2021/world.cpp delete mode 100644 SQCSim2021/world.h create mode 100644 SQCSim2021/worldrenderer.cpp create mode 100644 SQCSim2021/worldrenderer.h diff --git a/SQCSim-common/SQCSim-common.vcxproj b/SQCSim-common/SQCSim-common.vcxproj index 15265fb..991cd1d 100644 --- a/SQCSim-common/SQCSim-common.vcxproj +++ b/SQCSim-common/SQCSim-common.vcxproj @@ -138,6 +138,7 @@ + @@ -148,6 +149,7 @@ + diff --git a/SQCSim-common/SQCSim-common.vcxproj.filters b/SQCSim-common/SQCSim-common.vcxproj.filters index a98dd18..26c6c05 100644 --- a/SQCSim-common/SQCSim-common.vcxproj.filters +++ b/SQCSim-common/SQCSim-common.vcxproj.filters @@ -51,6 +51,9 @@ Fichiers d%27en-tête + + Fichiers d%27en-tête + @@ -74,5 +77,8 @@ Fichiers sources + + Fichiers sources + \ No newline at end of file diff --git a/SQCSim-common/chunk.cpp b/SQCSim-common/chunk.cpp index 7bfc690..cb93218 100644 --- a/SQCSim-common/chunk.cpp +++ b/SQCSim-common/chunk.cpp @@ -1,13 +1,13 @@ #include "chunk.h" #include "world.h" -Chunk::Chunk(unsigned int x, unsigned int y) : m_posX(x), m_posY(y) { +Chunk::Chunk(unsigned int x, unsigned int y, int64_t seed) : m_posX(x), m_posY(y) { //std::ostringstream pos; // Vérifie l'existence d'un fichier .chunk avec sa position. //pos << CHUNK_PATH << x << '_' << y << ".chunk"; //std::ifstream input(pos.str(), std::fstream::binary); //if (input.fail()) { - OpenSimplexNoise::Noise simplex = OpenSimplexNoise::Noise(SEED); + OpenSimplexNoise::Noise simplex = OpenSimplexNoise::Noise(seed); m_blocks.Reset(BTYPE_AIR); for (int ix = 0; ix < CHUNK_SIZE_X; ++ix) // Montagnes @@ -98,14 +98,45 @@ Chunk::~Chunk() { void Chunk::RemoveBlock(int x, int y, int z, World* world) { m_blocks.Set(x, y, z, BTYPE_AIR); + CheckNeighbors(x, y, world); + m_isDirty = true; } void Chunk::SetBlock(int x, int y, int z, BlockType type, World* world) { m_blocks.Set(x, y, z, type); + if (world) CheckNeighbors(x, z, world); // Si nullptr, ne pas vérifier les chunks voisines. + m_isDirty = true; } BlockType Chunk::GetBlock(int x, int y, int z) { return m_blocks.Get(x, y, z); } +void Chunk::CheckNeighbors(unsigned int x, unsigned int z, World* world) { + unsigned int cx, cy; + + world->GetScope(cx, cy); + + if (x == 0 && m_posX - cx >= 0 && + world->ChunkAt((m_posX - cx - 1) * CHUNK_SIZE_X, 1, (m_posY - cy) * CHUNK_SIZE_Z)) + world->ChunkAt((m_posX - cx - 1) * CHUNK_SIZE_X, 1, (m_posY - cy) * CHUNK_SIZE_Z)->MakeDirty(); + else if (x == CHUNK_SIZE_X - 1 && m_posX - cx < WORLD_SIZE_X && + world->ChunkAt((m_posX - cx + 1) * CHUNK_SIZE_X, 1, (m_posY - cy) * CHUNK_SIZE_Z)) + world->ChunkAt((m_posX - cx + 1) * CHUNK_SIZE_X, 1, (m_posY - cy) * CHUNK_SIZE_Z)->MakeDirty(); + + if (z == 0 && m_posY - cy >= 0 && + world->ChunkAt((m_posX - cx) * CHUNK_SIZE_X, 1, (m_posY - cy - 1) * CHUNK_SIZE_Z)) + world->ChunkAt((m_posX - cx) * CHUNK_SIZE_X, 1, (m_posY - cy - 1) * CHUNK_SIZE_Z)->MakeDirty(); + else if (z == CHUNK_SIZE_X - 1 && m_posY - cy < WORLD_SIZE_Y && + world->ChunkAt((m_posX - cx) * CHUNK_SIZE_X, 1, (m_posY - cy + 1) * CHUNK_SIZE_Z)) + world->ChunkAt((m_posX - cx) * CHUNK_SIZE_X, 1, (m_posY - cy + 1) * CHUNK_SIZE_Z)->MakeDirty(); +} + void Chunk::GetPosition(unsigned int& x, unsigned int& y) const { x = m_posX; y = m_posY; } +bool Chunk::IsDirty() const { return m_isDirty; } + +void Chunk::MakeDirty() { m_isDirty = true; } + +void Chunk::MakeClean() { m_isDirty = false; } + void Chunk::MakeModified() { m_isModified = true; } + diff --git a/SQCSim-common/chunk.h b/SQCSim-common/chunk.h index ec015ba..ea4a949 100644 --- a/SQCSim-common/chunk.h +++ b/SQCSim-common/chunk.h @@ -1,30 +1,35 @@ #ifndef CHUNK_H__ #define CHUNK_H__ #include "define.h" -#include "array3d.h" #include "array2d.h" +#include "array3d.h" #include "blockinfo.h" #include "opensimplex.h" class World; class Chunk { - protected: + private: Array3d m_blocks = Array3d(CHUNK_SIZE_X, CHUNK_SIZE_Y, CHUNK_SIZE_Z); + bool m_isDirty = true; bool m_isModified = false; unsigned int m_posX; // Position du chunk dans l'array constituant le monde. unsigned int m_posY; public: - Chunk(unsigned int x, unsigned int y); + Chunk(unsigned int x, unsigned int y, int64_t seed); ~Chunk(); void RemoveBlock(int x, int y, int z, World* world); void SetBlock(int x, int y, int z, BlockType type, World* world); BlockType GetBlock(int x, int y, int z); + void CheckNeighbors(unsigned int x, unsigned int z, World* world); void GetPosition(unsigned int& x, unsigned int& y) const; + bool IsDirty() const; + void MakeDirty(); + void MakeClean(); void MakeModified(); }; diff --git a/SQCSim-common/define.h b/SQCSim-common/define.h index 0342359..f02d719 100644 --- a/SQCSim-common/define.h +++ b/SQCSim-common/define.h @@ -17,7 +17,15 @@ #define WORLD_SIZE_X 64 #define WORLD_SIZE_Y 64 -#define VIEW_DISTANCE 512 +#define FRAMES_RENDER_CHUNKS 1 +#define FRAMES_UPDATE_CHUNKS 1 +#define FRAMES_DELETE_CHUNKS 1 + +#define THREADS_GENERATE_CHUNKS 8 +#define THREADS_UPDATE_CHUNKS 3 +#define THREADS_DELETE_CHUNKS 3 + +#define VIEW_DISTANCE 512 // Si les chunks arrêtent de s'afficher pendant une game et qu'il y a un access violation quand tu quitte, il faut augmenter ce chiffre. #define TEXTURE_SIZE 512 #define MAX_BULLETS 512 diff --git a/SQCSim-common/player.cpp b/SQCSim-common/player.cpp index b42968a..2043dd9 100644 --- a/SQCSim-common/player.cpp +++ b/SQCSim-common/player.cpp @@ -4,6 +4,8 @@ Player::Player(const Vector3f& position, float rotX, float rotY) : m_position(position), m_rotX(rotX), m_rotY(rotY) { m_velocity = Vector3f(0, 0, 0); m_airborne = true; + m_hp = 0.75f; //TODO: Remettre à 1.0f + m_username = "Zelda Bee-Bop"; } void Player::TurnLeftRight(float value) { @@ -64,7 +66,8 @@ Vector3f Player::GetInput(bool front, bool back, bool left, bool right, bool jum return delta; } -void Player::ApplyPhysics(Vector3f input, World* world, float elapsedTime) { +Player::Sound Player::ApplyPhysics(Vector3f input, World* world, float elapsedTime) { + Player::Sound snd = Player::Sound::NOSOUND; static float timing = 0.f; /* Gestion de collisions */ BlockType bt1, bt2, bt3; @@ -85,6 +88,7 @@ void Player::ApplyPhysics(Vector3f input, World* world, float elapsedTime) { if (bt3 != BTYPE_AIR) { m_velocity.y = 0; if (timing == 0.f) { + if (m_airborne) snd = Player::Sound::FALL; timing = .3f; } m_airborne = false; @@ -137,10 +141,10 @@ void Player::ApplyPhysics(Vector3f input, World* world, float elapsedTime) { m_velocity.x += input.x * 2.f * elapsedTime; m_velocity.z += input.z * 2.f * elapsedTime; - if (input.x == 0.f) + if (input.x == 0.f) m_velocity.x *= .8f; - - if (input.z == 0.f) + + if (input.z == 0.f) m_velocity.z *= .8f; } else { @@ -160,6 +164,30 @@ void Player::ApplyPhysics(Vector3f input, World* world, float elapsedTime) { m_velocity.y = vy; m_position += m_velocity; + + static float bobbingtime = 0; // Gestion de la caméra + static bool leftright = false; + static bool isStep = false; + if (bobbingtime <= 360.f) + bobbingtime += elapsedTime * 20.f; else bobbingtime = 0; + + if ((sin(bobbingtime) - 0.5f) * (abs(m_velocity.x) + abs(m_velocity.z)) < -.2f && !m_airborne) { + if (!isStep) { + snd = Player::Sound::STEP; + } + isStep = true; + } + else isStep = false; + m_POV = m_position.y; + m_POV += m_airborne ? 0 : (sin(bobbingtime) - 0.5f) * (abs(m_velocity.x) + abs(m_velocity.z)) * .2f; + + return snd; +} + +void Player::ApplyTransformation(Transformation& transformation, bool rel) const { + transformation.ApplyRotation(-m_rotX, 1, 0, 0); + transformation.ApplyRotation(-m_rotY, 0, 1, 0); + if (rel) transformation.ApplyTranslation(-GetPOV()); } Vector3f Player::GetPosition() const { return Vector3f(m_position.x + CHUNK_SIZE_X * WORLD_SIZE_X / 2, m_position.y, m_position.z + CHUNK_SIZE_Z * WORLD_SIZE_Y / 2); } @@ -170,6 +198,10 @@ Vector3f Player::GetPOV() const { return Vector3f(GetPosition().x, m_POV, GetPos Vector3f Player::GetDirection() const { return m_direction; } +std::string Player::GetUsername() const { return m_username; } + +float Player::GetHP() const { return m_hp; } + void Player::Teleport(int& x, int& z) { m_position.x -= x * CHUNK_SIZE_X; m_position.z -= z * CHUNK_SIZE_Z; diff --git a/SQCSim-common/player.h b/SQCSim-common/player.h index f889b68..3856ced 100644 --- a/SQCSim-common/player.h +++ b/SQCSim-common/player.h @@ -1,33 +1,43 @@ -#ifndef _PLAYER_H__ -#define _PLAYER_H__ -#include "vector3.h" +#ifndef CLI_PLAYER_H__ +#define CLI_PLAYER_H__ #include +#include "transformation.h" +#include "vector3.h" -class World; +class World; class Player { public: + enum Sound { NOSOUND, STEP, FALL }; + Player(const Vector3f& position, float rotX = 0, float rotY = 0); void TurnLeftRight(float value); void TurnTopBottom(float value); Vector3f GetInput(bool front, bool back, bool left, bool right, bool jump, bool dash, float elapsedTime); - void ApplyPhysics(Vector3f input, World* world, float elapsedTime); + Sound ApplyPhysics(Vector3f input, World* world, float elapsedTime); + void ApplyTransformation(Transformation& transformation, bool rel = true) const; Vector3f GetPosition() const; Vector3f GetDirection() const; Vector3f GetVelocity() const; Vector3f GetPOV() const; + std::string GetUsername() const; + float GetHP() const; void Teleport(int& x, int& z); -protected: +private: Vector3f m_position; Vector3f m_velocity; Vector3f m_direction; + std::string m_username; + float m_rotX = 0; float m_rotY = 0; float m_POV; + float m_hp; + bool m_airborne; }; #endif //_PLAYER_H__ diff --git a/SQCSim2021/transformation.cpp b/SQCSim-common/transformation.cpp similarity index 91% rename from SQCSim2021/transformation.cpp rename to SQCSim-common/transformation.cpp index 7629f52..c4169a3 100644 --- a/SQCSim2021/transformation.cpp +++ b/SQCSim-common/transformation.cpp @@ -51,10 +51,10 @@ void Transformation::ApplyScale(const Vector3f& v) ApplyScale(v.x, v.y, v.z); } -void Transformation::Use() const -{ - glLoadMatrixf(m_stack.top().GetInternalValues()); -} +//void Transformation::Use() const +//{ +// glLoadMatrixf(m_stack.top().GetInternalValues()); +//} const Matrix4f& Transformation::GetMatrix() const { diff --git a/SQCSim2021/transformation.h b/SQCSim-common/transformation.h similarity index 87% rename from SQCSim2021/transformation.h rename to SQCSim-common/transformation.h index 72e0690..f6ac105 100644 --- a/SQCSim2021/transformation.h +++ b/SQCSim-common/transformation.h @@ -2,9 +2,9 @@ #define TRANSFORMATION_H__ #include -#include "../SQCSim-common/matrix4.h" -#include "../SQCSim-common/vector3.h" #include "define.h" +#include "matrix4.h" +#include "vector3.h" class Transformation { @@ -25,7 +25,7 @@ class Transformation void ApplyScale(float x, float y, float z); void ApplyScale(const Vector3f& v); - void Use() const; + //void Use() const; const Matrix4f& GetMatrix() const; diff --git a/SQCSim-common/world.cpp b/SQCSim-common/world.cpp index 6b4ccf6..4d7c292 100644 --- a/SQCSim-common/world.cpp +++ b/SQCSim-common/world.cpp @@ -6,6 +6,10 @@ World::~World() {} Array2d& World::GetChunks() { return m_chunks; } +void World::SetSeed(uint64_t seed) { + m_seed = seed; +} + Chunk* World::ChunkAt(float x, float y, float z) const { int cx = (int)x / CHUNK_SIZE_X; int cz = (int)z / CHUNK_SIZE_Z; @@ -38,23 +42,112 @@ BlockType World::BlockAt(const Vector3f& pos, BlockType defaultBlockType) const return BlockAt(pos.x, pos.y, pos.z, defaultBlockType); } +void World::TransposeWorld(Vector3f& player, Bullet* bullets[MAX_BULLETS]) { + int x = 0, y = 0; + + if (player.x > (WORLD_SIZE_X * CHUNK_SIZE_X) * .6f) ++x; + else if (player.x < (WORLD_SIZE_X * CHUNK_SIZE_X) * .4f) --x; + if (player.z > (WORLD_SIZE_Y * CHUNK_SIZE_Z) * .6f) ++y; + else if (player.z < (WORLD_SIZE_Y * CHUNK_SIZE_Z) * .4f) --y; + + if (!x && !y) return; + + if (x > 0) { + for (int ax = 0; ax < WORLD_SIZE_X; ++ax) + for (int ay = 0; ay < WORLD_SIZE_Y; ++ay) + if (ax - x >= 0) { + m_chunks.Set(ax - x, ay, + m_chunks.Remove(ax, ay)); + if (ax == WORLD_SIZE_X - 1 && m_chunks.Get(ax - x, ay)) + m_chunks.Get(ax - x, ay)->MakeDirty(); + } + else if (m_chunks.Get(ax, ay)) m_tbDeleted.emplace_back(m_chunks.Remove(ax, ay)); + } + else if (x < 0) { + for (int ax = WORLD_SIZE_X - 1; ax >= 0; --ax) + for (int ay = WORLD_SIZE_Y - 1; ay >= 0; --ay) + if (ax - x < WORLD_SIZE_X) { + m_chunks.Set(ax - x, ay, + m_chunks.Remove(ax, ay)); + if (ax == 0 && m_chunks.Get(ax - x, ay)) + m_chunks.Get(ax - x, ay)->MakeDirty(); + } + else if (m_chunks.Get(ax, ay)) m_tbDeleted.emplace_back(m_chunks.Remove(ax, ay)); + } + + if (y > 0) { + for (int ax = 0; ax < WORLD_SIZE_X; ++ax) + for (int ay = 0; ay < WORLD_SIZE_Y; ++ay) + if (ay - y >= 0) { + m_chunks.Set(ax, ay - y, + m_chunks.Remove(ax, ay)); + if (ay == WORLD_SIZE_Y - 1 && m_chunks.Get(ax, ay - y)) + m_chunks.Get(ax, ay - y)->MakeDirty(); + } + else if (m_chunks.Get(ax, ay)) m_tbDeleted.emplace_back(m_chunks.Remove(ax, ay)); + } + else if (y < 0) { + for (int ax = WORLD_SIZE_X - 1; ax >= 0; --ax) + for (int ay = WORLD_SIZE_Y - 1; ay >= 0; --ay) + if (ay - y < WORLD_SIZE_Y) { + m_chunks.Set(ax, ay - y, + m_chunks.Remove(ax, ay)); + if (ay == 0 && m_chunks.Get(ax, ay - y)) + m_chunks.Get(ax, ay - y)->MakeDirty(); + } + else if (m_chunks.Get(ax, ay)) m_tbDeleted.emplace_back(m_chunks.Remove(ax, ay)); + } + + m_center[0] += x; m_center[1] += y; + player.x -= x * CHUNK_SIZE_X; + player.z -= y * CHUNK_SIZE_Z; + + for (int index = 0; index < MAX_BULLETS; ++index) + if (bullets[index]) bullets[index]->Transpose(x, y); +} + +void World::CleanUpWorld(int& deleteframes, bool clear = false) { + if (clear) { + while (m_tbDeleted.size() > 0) { + delete m_tbDeleted.back(); + m_tbDeleted.pop_back(); + } + } + if (!m_tbDeleted.empty() && !deleteframes) { + int deleted = 0; + while (deleted < THREADS_DELETE_CHUNKS) { + + + } + delete m_tbDeleted.back(); + m_tbDeleted.pop_back(); + deleteframes = FRAMES_DELETE_CHUNKS; + } +} + void World::GetScope(unsigned int& x, unsigned int& y) { x = m_center[0]; y = m_center[1]; } -void World::ChangeBlockAtPosition(BlockType blockType, Vector3f pos) { - int bx = (int)pos.x % CHUNK_SIZE_X; - int by = (int)pos.y % CHUNK_SIZE_Y; - int bz = (int)pos.z % CHUNK_SIZE_Z; - - ChunkAt(pos)->SetBlock(bx, by, bz, blockType, this); +void World::Update(Bullet* bullets[MAX_BULLETS], Vector3f& player_pos, BlockInfo* blockinfo[BTYPE_LAST]) { + UpdateWorld(player_pos, blockinfo); + //TransposeWorld(player_pos, bullets); } +// +//void World::UpdateChunk(int& updates, unsigned int chx, unsigned int chy, BlockInfo* blockinfo[BTYPE_LAST]) { +// if (updates == 0 && ChunkAt(chx, 1, chy) && +// ChunkAt(chx, 1, chy)->IsDirty()) { +// ChunkAt(chx, 1, chy)->Update(blockinfo, this); +// updates = FRAMES_UPDATE_CHUNKS; +// } +// +//} -void World::ChangeBlockAtCursor(BlockType blockType, Player& player, bool& block) { - Vector3f currentPos = player.GetPosition(); +void World::ChangeBlockAtCursor(BlockType blockType, const Vector3f& player_pos, const Vector3f& player_dir, bool& block) { + Vector3f currentPos = player_pos; Vector3f currentBlock = currentPos; - Vector3f ray = player.GetDirection(); + Vector3f ray = player_dir; bool found = false; if (block) return; @@ -76,7 +169,7 @@ void World::ChangeBlockAtCursor(BlockType blockType, Player& player, bool& block BlockType bt = BlockAt(currentBlock); - if (bt == BTYPE_AIR) { // V?rification pour ?tre s?r que le bloc ? changer n'est pas dans le joueur. + if (bt == BTYPE_AIR) { // Vérification pour être sûr que le bloc à changer n'est pas dans le joueur. int Bx = (int)currentBlock.x; int By = (int)currentBlock.y; int Bz = (int)currentBlock.z; @@ -107,3 +200,211 @@ void World::ChangeBlockAtCursor(BlockType blockType, Player& player, bool& block block = true; } } + +void World::ChangeBlockAtPosition(BlockType blockType, Vector3f pos) { + int bx = (int)pos.x % CHUNK_SIZE_X; + int by = (int)pos.y % CHUNK_SIZE_Y; + int bz = (int)pos.z % CHUNK_SIZE_Z; + + ChunkAt(pos)->SetBlock(bx, by, bz, blockType, this); + ChunkAt(pos)->MakeModified(); +} + +void World::UpdateWorld(const Vector3f& player, BlockInfo* blockinfo[BTYPE_LAST]) { + int cx = player.x; + int cy = player.z; + static int frameGenerate = 1; + static int frameUpdate = 2; + static int frameDelete = 3; + int side = 0; + int threads = 0; + std::future genThList[THREADS_GENERATE_CHUNKS]; + //std::future delThList[THREADS_DELETE_CHUNKS]; + + if (frameGenerate > 0) --frameGenerate; + if (frameUpdate > 0) --frameUpdate; + if (frameDelete > 0) --frameDelete; + + if (!frameGenerate) + while (side * CHUNK_SIZE_X <= VIEW_DISTANCE * 2 + CHUNK_SIZE_X) { + int tx = -side, ty = -side; + int chx = 0; + int chy = 0; + + for (; tx <= side; ++tx) { + if (frameGenerate) + break; + chx = cx + tx * CHUNK_SIZE_X; + chy = cy + ty * CHUNK_SIZE_Z; + if (chx < WORLD_SIZE_X * CHUNK_SIZE_X && chy < WORLD_SIZE_Y * CHUNK_SIZE_Z && + chx >= 0 && chy >= 0 && !ChunkAt(chx, 1, chy)) + genThList[threads++] = std::async(std::launch::async, + [](unsigned int x, unsigned int y, uint64_t seed) { + return new Chunk(x, y, seed); }, + chx / CHUNK_SIZE_X + m_center[0], + chy / CHUNK_SIZE_Z + m_center[1], + m_seed); + if (threads == THREADS_GENERATE_CHUNKS) frameGenerate = FRAMES_RENDER_CHUNKS; + } + for (; ty <= side; ++ty) { + if (frameGenerate) + break; + chx = cx + tx * CHUNK_SIZE_X; + chy = cy + ty * CHUNK_SIZE_Z; + if (chx < WORLD_SIZE_X * CHUNK_SIZE_X && chy < WORLD_SIZE_Y * CHUNK_SIZE_Z && + chx >= 0 && chy >= 0 && !ChunkAt(chx, 1, chy)) + genThList[threads++] = std::async(std::launch::async, + [](unsigned int x, unsigned int y, uint64_t seed) { + return new Chunk(x, y, seed); }, + chx / CHUNK_SIZE_X + m_center[0], + chy / CHUNK_SIZE_Z + m_center[1], + m_seed); + if (threads == THREADS_GENERATE_CHUNKS) frameGenerate = FRAMES_RENDER_CHUNKS; + } + for (; tx >= -side; --tx) { + if (frameGenerate) + break; + chx = cx + tx * CHUNK_SIZE_X; + chy = cy + ty * CHUNK_SIZE_Z; + if (chx < WORLD_SIZE_X * CHUNK_SIZE_X && chy < WORLD_SIZE_Y * CHUNK_SIZE_Z && + chx >= 0 && chy >= 0 && !ChunkAt(chx, 1, chy)) + genThList[threads++] = std::async(std::launch::async, + [](unsigned int x, unsigned int y, uint64_t seed) { + return new Chunk(x, y, seed); }, + chx / CHUNK_SIZE_X + m_center[0], + chy / CHUNK_SIZE_Z + m_center[1], + m_seed); + if (threads == THREADS_GENERATE_CHUNKS) frameGenerate = FRAMES_RENDER_CHUNKS; + } + for (; ty >= -side; --ty) { + if (frameGenerate) + break; + chx = cx + tx * CHUNK_SIZE_X; + chy = cy + ty * CHUNK_SIZE_Z; + if (chx < WORLD_SIZE_X * CHUNK_SIZE_X && chy < WORLD_SIZE_Y * CHUNK_SIZE_Z && + chx >= 0 && chy >= 0 && !ChunkAt(chx, 1, chy)) + genThList[threads++] = std::async(std::launch::async, + [](unsigned int x, unsigned int y, uint64_t seed) { + return new Chunk(x, y, seed); }, + chx / CHUNK_SIZE_X + m_center[0], + chy / CHUNK_SIZE_Z + m_center[1], + m_seed); + if (threads == THREADS_GENERATE_CHUNKS) frameGenerate = FRAMES_RENDER_CHUNKS; + } + if (frameGenerate) + break; + ++side; + } + + if (threads > 0) { + for (int i = 0; i < threads; ++i) + genThList[i].wait(); + + for (int i = 0; i < threads; ++i) { + unsigned int x, y; + Chunk* chunk = genThList[i].get(); + chunk->GetPosition(x, y); + m_chunks.Set(x - m_center[0], y - m_center[1], chunk); + } + } + + side = 0; + threads = 0; + + //if (!frameUpdate) + // while (side * CHUNK_SIZE_X <= VIEW_DISTANCE * 2) { + // int tx = -side, ty = -side; + + // for (; tx <= side; ++tx) { + // if (frameUpdate) + // break; + // unsigned int chx = cx + tx * CHUNK_SIZE_X, chy = cy + ty * CHUNK_SIZE_Z; + // if (ChunkAt(chx, 1, chy) && + // ChunkAt(chx, 1, chy)->IsDirty()) { + // updateThList[threads++] = + // std::async(std::launch::async, + // [](Chunk* chunk, BlockInfo* blockinfo[BTYPE_LAST], World* world) { + // chunk->Update(blockinfo, world); return chunk; }, ChunkAt(chx, 1, chy), blockinfo, this); + // if (threads == THREADS_UPDATE_CHUNKS) frameUpdate = FRAMES_UPDATE_CHUNKS; + // } + // } + // for (; ty <= side; ++ty) { + // if (frameUpdate) + // break; + // unsigned int chx = cx + tx * CHUNK_SIZE_X, chy = cy + ty * CHUNK_SIZE_Z; + // if (ChunkAt(chx, 1, chy) && + // ChunkAt(chx, 1, chy)->IsDirty()) { + // updateThList[threads++] = + // std::async(std::launch::async, + // [](Chunk* chunk, BlockInfo* blockinfo[BTYPE_LAST], World* world) { + // chunk->Update(blockinfo, world); return chunk; }, ChunkAt(chx, 1, chy), blockinfo, this); + // if (threads == THREADS_UPDATE_CHUNKS) frameUpdate = FRAMES_UPDATE_CHUNKS; + // } + // } + // for (; tx >= -side; --tx) { + // if (frameUpdate) + // break; + // unsigned int chx = cx + tx * CHUNK_SIZE_X, chy = cy + ty * CHUNK_SIZE_Z; + // if (ChunkAt(chx, 1, chy) && + // ChunkAt(chx, 1, chy)->IsDirty()) { + // updateThList[threads++] = + // std::async(std::launch::async, + // [](Chunk* chunk, BlockInfo* blockinfo[BTYPE_LAST], World* world) { + // chunk->Update(blockinfo, world); return chunk; }, ChunkAt(chx, 1, chy), blockinfo, this); + // if (threads == THREADS_UPDATE_CHUNKS) frameUpdate = FRAMES_UPDATE_CHUNKS; + // } + // } + // for (; ty >= -side; --ty) { + // if (frameUpdate) + // break; + // unsigned int chx = cx + tx * CHUNK_SIZE_X, chy = cy + ty * CHUNK_SIZE_Z; + // if (ChunkAt(chx, 1, chy) && + // ChunkAt(chx, 1, chy)->IsDirty()) { + // updateThList[threads++] = + // std::async(std::launch::async, + // [](Chunk* chunk, BlockInfo* blockinfo[BTYPE_LAST], World* world) { + // chunk->Update(blockinfo, world); return chunk; }, ChunkAt(chx, 1, chy), blockinfo, this); + // if (threads == THREADS_UPDATE_CHUNKS) frameUpdate = FRAMES_UPDATE_CHUNKS; + // } + // } + // if (frameUpdate) + // break; + // ++side; + // } + + //if (threads > 0) { + // for (int i = 0; i < threads; ++i) { + // updateThList[i].wait(); + // Chunk* chunk = updateThList[i].get(); + // chunk->FlushMeshToVBO(); + // } + //} + + threads = 0; + + //int del = THREADS_DELETE_CHUNKS; + //while (!m_tbDeleted.empty() && del--) { // Moins rapide que le bout en dessous, mais -beaucoup- plus stable. + // m_tbDeleted.back()->FlushVBO(); + // m_tbDeleted.back()->~Chunk(); + // m_tbDeleted.pop_back(); + //} + + /*while (!m_tbDeleted.empty() && !frameDelete) { + if (m_tbDeleted.back()) { + m_tbDeleted.back()->FlushVBO(); + delThList[threads] = + std::async(std::launch::async, + [](Chunk* chunk) { delete chunk; }, m_tbDeleted.back()); + m_tbDeleted.pop_back(); + if (++threads > THREADS_DELETE_CHUNKS) frameDelete = FRAMES_DELETE_CHUNKS; + } + else m_tbDeleted.pop_back(); + }*/ + + /*for (int x = 0; x < threads; ++x) { + delThList[x].wait(); + delThList[x].get(); + }*/ +} + +int World::GettbDeleted() const { return m_tbDeleted.size(); } \ No newline at end of file diff --git a/SQCSim-common/world.h b/SQCSim-common/world.h index dd9967a..69937aa 100644 --- a/SQCSim-common/world.h +++ b/SQCSim-common/world.h @@ -6,14 +6,12 @@ #include #include #include "define.h" -#include "chunk.h" -#include "array2d.h" #include "vector3.h" -#include "player.h" +#include "array2d.h" #include "bullet.h" +#include "chunk.h" class Chunk; -class Player; class Bullet; class World { @@ -23,23 +21,33 @@ public: Array2d& GetChunks(); + void SetSeed(uint64_t seed); + Chunk* ChunkAt(float x, float y, float z) const; Chunk* ChunkAt(const Vector3f& pos) const; BlockType BlockAt(float x, float y, float z, BlockType defaultBlockType = BTYPE_AIR) const; BlockType BlockAt(const Vector3f& pos, BlockType defaultBlockType = BTYPE_AIR) const; + void Update(Bullet* bullets[MAX_BULLETS], Vector3f& player_pos, BlockInfo* blockinfo[BTYPE_LAST]); + void GetScope(unsigned int& x, unsigned int& y); - void ChangeBlockAtCursor(BlockType blockType, Player& player, bool& block); + void ChangeBlockAtCursor(BlockType blockType, const Vector3f& player_pos, const Vector3f& player_dir, bool& block); void ChangeBlockAtPosition(BlockType blockType, Vector3f pos); - + void CleanUpWorld(int& deleteframes, bool clear); + int GettbDeleted() const; private: Array2d m_chunks = Array2d(WORLD_SIZE_X, WORLD_SIZE_Y); std::vector m_tbDeleted; + uint64_t m_seed = 0; unsigned int m_center[2] = { UINT16_MAX / 2 - WORLD_SIZE_X, UINT16_MAX / 2 - WORLD_SIZE_Y }; + void UpdateChunk(int& updates, unsigned int chx, unsigned int chy, BlockInfo* blockinfo[BTYPE_LAST]); + void UpdateWorld(const Vector3f& player, BlockInfo* blockinfo[BTYPE_LAST]); + void TransposeWorld(Vector3f& player, Bullet* bullets[MAX_BULLETS]); + }; #endif // WORLD_H__ diff --git a/SQCSim2021/SQCSim2021.vcxproj b/SQCSim2021/SQCSim2021.vcxproj index 02c2b51..8fb814c 100644 --- a/SQCSim2021/SQCSim2021.vcxproj +++ b/SQCSim2021/SQCSim2021.vcxproj @@ -20,37 +20,33 @@ - + - - - + - + - - - + diff --git a/SQCSim2021/SQCSim2021.vcxproj.filters b/SQCSim2021/SQCSim2021.vcxproj.filters index 0e371d0..3bf0928 100644 --- a/SQCSim2021/SQCSim2021.vcxproj.filters +++ b/SQCSim2021/SQCSim2021.vcxproj.filters @@ -11,9 +11,6 @@ - - Fichiers d%27en-tête - Fichiers d%27en-tête @@ -23,12 +20,6 @@ Fichiers d%27en-tête - - Fichiers d%27en-tête - - - Fichiers d%27en-tête - Fichiers d%27en-tête @@ -44,9 +35,6 @@ Fichiers d%27en-tête - - Fichiers d%27en-tête - Fichiers d%27en-tête @@ -56,11 +44,14 @@ Fichiers d%27en-tête + + Fichiers d%27en-tête + + + Fichiers d%27en-tête + - - Fichiers sources - Fichiers sources @@ -73,12 +64,6 @@ Fichiers sources - - Fichiers sources - - - Fichiers sources - Fichiers sources @@ -94,14 +79,17 @@ Fichiers sources - - Fichiers sources - Fichiers sources Fichiers sources + + Fichiers sources + + + Fichiers sources + \ No newline at end of file diff --git a/SQCSim2021/audio.h b/SQCSim2021/audio.h index 315bfae..b0ff044 100644 --- a/SQCSim2021/audio.h +++ b/SQCSim2021/audio.h @@ -3,8 +3,8 @@ #include #include -#include "define.h" #include "../SQCSim-common/vector3.h" +#include "define.h" class Audio { private: diff --git a/SQCSim2021/chunk.cpp b/SQCSim2021/chunk.cpp deleted file mode 100644 index 5d78859..0000000 --- a/SQCSim2021/chunk.cpp +++ /dev/null @@ -1,234 +0,0 @@ -#include "chunk.h" -#include "world.h" - -Chunk::Chunk(unsigned int x, unsigned int y, int64_t seed) : m_posX(x), m_posY(y) { - //std::ostringstream pos; // Vérifie l'existence d'un fichier .chunk avec sa position. - //pos << CHUNK_PATH << x << '_' << y << ".chunk"; - //std::ifstream input(pos.str(), std::fstream::binary); - - //if (input.fail()) { - OpenSimplexNoise::Noise simplex = OpenSimplexNoise::Noise(seed); - m_blocks.Reset(BTYPE_AIR); - - for (int ix = 0; ix < CHUNK_SIZE_X; ++ix) // Montagnes - for (int iz = 0; iz < CHUNK_SIZE_Z; ++iz) { - float xnoiz, ynoiz; - xnoiz = (double)(ix + x * CHUNK_SIZE_X) / 4096.; - ynoiz = (double)(iz + y * CHUNK_SIZE_Z) / 4096.; - double height = 0; - for (int x = 0; x < 39; ++x) { - height += simplex.eval(xnoiz, ynoiz); - height *= .79; - xnoiz *= 1.139; - ynoiz *= 1.139; - } - height = height * 2000. * simplex.eval((double)(ix + x * CHUNK_SIZE_X) / 512., (double)(iz + y * CHUNK_SIZE_Z) / 512.); - height /= (CHUNK_SIZE_Y / 1.9); - height += 15.; - for (int iy = 0; iy <= (int)height % CHUNK_SIZE_Y; ++iy) - SetBlock(ix, iy, iz, BTYPE_METAL, nullptr); - } - for (int ix = 0; ix < CHUNK_SIZE_X; ++ix) // Collines - for (int iz = 0; iz < CHUNK_SIZE_Z; ++iz) { - float xnoiz, ynoiz; - xnoiz = (double)(ix + x * CHUNK_SIZE_X) / 512.; - ynoiz = (double)(iz + y * CHUNK_SIZE_Z) / 512.; - float height = simplex.eval(xnoiz, ynoiz) * 50.f;// +1.f; - for (int iy = 0; iy <= (int)height % CHUNK_SIZE_Y; ++iy) { - if (GetBlock(ix, iy, iz) == BTYPE_AIR) - SetBlock(ix, iy, iz, BTYPE_GRASS, nullptr); - } - } - for (int ix = 0; ix < CHUNK_SIZE_X; ++ix) // "Lacs" - for (int iz = 0; iz < CHUNK_SIZE_Z; ++iz) { - for (int iy = 0; iy < 13; ++iy) { - if (GetBlock(ix, iy, iz) == BTYPE_AIR) - SetBlock(ix, iy, iz, BTYPE_ICE, nullptr); - } - } - //for (int ix = 0; ix < CHUNK_SIZE_X; ++ix) // "Arbres" - // for (int iz = 0; iz < CHUNK_SIZE_Z; ++iz) { - // float xnoiz, ynoiz; - // xnoiz = (double)(iz * CHUNK_SIZE_Y + x * CHUNK_SIZE_X) / 256.; - // ynoiz = (double)(ix * CHUNK_SIZE_Y + y * CHUNK_SIZE_Z) / 256.; - // bool tree = (int)(abs(simplex.eval(xnoiz, ynoiz)) * 17933.f) % CHUNK_SIZE_Y > 126 ? true : false; - // for (int iy = 0; iy < CHUNK_SIZE_Y - 10; ++iy) - // if (GetBlock(ix, iy, iz) == BTYPE_AIR) - // if (GetBlock(ix, iy - 1, iz) == BTYPE_GRASS) - // if (tree) { - // for (int i = 0; i < (int)(abs(simplex.eval(xnoiz, ynoiz) * 4)) % 42 + 1; ++i) - // SetBlock(ix, iy + i, iz, BTYPE_DIRT, nullptr); - // break; - // } - // } - /* } - else { - input.seekg(0, std::ios_base::end); - int size = input.tellg(); - input.seekg(0, std::ios_base::beg); - - char data[CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z]; - input.read(data, size); - input.close(); - - for (int ix = 0; ix < CHUNK_SIZE_X; ++ix) - for (int iz = 0; iz < CHUNK_SIZE_Z; ++iz) - for (int iy = 0; iy < CHUNK_SIZE_Y; ++iy) - m_blocks.Set(ix, iy, iz, data[ix + (iz * CHUNK_SIZE_X) + (iy * CHUNK_SIZE_Z * CHUNK_SIZE_X)]); - }*/ -} - -Chunk::~Chunk() { - /*if (m_isModified) { - char data[CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z]; - - for (int x = 0; x < CHUNK_SIZE_X; ++x) - for (int z = 0; z < CHUNK_SIZE_Z; ++z) - for (int y = 0; y < CHUNK_SIZE_Y; ++y) - data[x + (z * CHUNK_SIZE_X) + (y * CHUNK_SIZE_Z * CHUNK_SIZE_X)] = (char)GetBlock(x, y, z); - - std::ostringstream pos; - pos << CHUNK_PATH << m_posX << '_' << m_posY << ".chunk"; - - std::ofstream output(pos.str(), std::fstream::binary); - output.write(data, sizeof(data)); - output.close(); - }*/ -} - -void Chunk::RemoveBlock(int x, int y, int z, World* world) { - m_blocks.Set(x, y, z, BTYPE_AIR); - CheckNeighbors(x, y, world); - m_isDirty = true; -} - -void Chunk::SetBlock(int x, int y, int z, BlockType type, World* world) { - m_blocks.Set(x, y, z, type); - if (world) CheckNeighbors(x, z, world); // Si nullptr, ne pas vérifier les chunks voisines. - m_isDirty = true; -} - -BlockType Chunk::GetBlock(int x, int y, int z) { return m_blocks.Get(x, y, z); } - -void Chunk::CheckNeighbors(unsigned int x, unsigned int z, World* world) { - unsigned int cx, cy; - - world->GetScope(cx, cy); - - if (x == 0 && m_posX - cx >= 0 && - world->ChunkAt((m_posX - cx - 1) * CHUNK_SIZE_X, 1, (m_posY - cy) * CHUNK_SIZE_Z)) - world->ChunkAt((m_posX - cx - 1) * CHUNK_SIZE_X, 1, (m_posY - cy) * CHUNK_SIZE_Z)->MakeDirty(); - else if (x == CHUNK_SIZE_X - 1 && m_posX - cx < WORLD_SIZE_X && - world->ChunkAt((m_posX - cx + 1) * CHUNK_SIZE_X, 1, (m_posY - cy) * CHUNK_SIZE_Z)) - world->ChunkAt((m_posX - cx + 1) * CHUNK_SIZE_X, 1, (m_posY - cy) * CHUNK_SIZE_Z)->MakeDirty(); - - if (z == 0 && m_posY - cy >= 0 && - world->ChunkAt((m_posX - cx) * CHUNK_SIZE_X, 1, (m_posY - cy - 1) * CHUNK_SIZE_Z)) - world->ChunkAt((m_posX - cx) * CHUNK_SIZE_X, 1, (m_posY - cy - 1) * CHUNK_SIZE_Z)->MakeDirty(); - else if (z == CHUNK_SIZE_X - 1 && m_posY - cy < WORLD_SIZE_Y && - world->ChunkAt((m_posX - cx) * CHUNK_SIZE_X, 1, (m_posY - cy + 1) * CHUNK_SIZE_Z)) - world->ChunkAt((m_posX - cx) * CHUNK_SIZE_X, 1, (m_posY - cy + 1) * CHUNK_SIZE_Z)->MakeDirty(); -} - -void Chunk::GetPosition(unsigned int& x, unsigned int& y) const { x = m_posX; y = m_posY; } - -void Chunk::FlushMeshToVBO() { - m_vertexBuffer.SetMeshData(m_vd, m_vcount); - m_vcount = 0; - delete[] m_vd; -} - -void Chunk::FlushVBO() { - m_vertexBuffer.Flush(); -} - -void Chunk::Update(BlockInfo* blockinfo[BTYPE_LAST], World* world) { - float u, v, s; - // Update mesh - if (m_isDirty) { - int maxVertexCount = (CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z) * (6 * 4); - m_vd = new VertexBuffer::VertexData[maxVertexCount]; - m_vcount = 0; - for (int x = 0; x < CHUNK_SIZE_X; ++x) { - for (int z = 0; z < CHUNK_SIZE_Z; ++z) { - for (int y = 0; y < CHUNK_SIZE_Y; ++y) { - if (m_vcount > USHRT_MAX) - break; - - BlockType bt = GetBlock(x, y, z); - - if (bt != BTYPE_AIR) { - blockinfo[bt]->GetTexture(u, v, s); - AddBlockToMesh(m_vd, m_vcount, bt, x, y, z, u, v, s, world); - } - } - } - } - if (m_vcount > USHRT_MAX) { - m_vcount = USHRT_MAX; - std::cout << "[ Chunk :: Update ] Chunk data truncaned , too much vertices to have a 16 bit index " << std::endl; - } - } - m_isDirty = false; -} - -void Chunk::AddBlockToMesh(VertexBuffer::VertexData* vd, int& count, BlockType bt, - int x, int y, int z, float u, float v, float s, World* world) { - - unsigned int cex, cey; - - world->GetScope(cex, cey); - - int cx = x + (m_posX - cex) * CHUNK_SIZE_X, cy = z + (m_posY - cey) * CHUNK_SIZE_Z; - - if (y == CHUNK_SIZE_Y - 1 || GetBlock(x, y + 1, z) == BTYPE_AIR) { // y - vd[count++] = VertexBuffer::VertexData(x, y + 1.f, z, .8f, .8f, .8f, u, v); - vd[count++] = VertexBuffer::VertexData(x, y + 1.f, z + 1.f, .8f, .8f, .8f, u, v + s); - vd[count++] = VertexBuffer::VertexData(x + 1.f, y + 1.f, z + 1.f, .8f, .8f, .8f, u + s, v + s); - vd[count++] = VertexBuffer::VertexData(x + 1.f, y + 1.f, z, .8f, .8f, .8f, u + s, v); - } - - if (y == 0 || GetBlock(x, y - 1, z) == BTYPE_AIR) { // -y - vd[count++] = VertexBuffer::VertexData(x, y, z + 1.f, .2f, .2f, .2f, u, v); - vd[count++] = VertexBuffer::VertexData(x, y, z, .2f, .2f, .2f, u, v + s); - vd[count++] = VertexBuffer::VertexData(x + 1.f, y, z, .2f, .2f, .2f, u + s, v + s); - vd[count++] = VertexBuffer::VertexData(x + 1.f, y, z + 1.f, .2f, .2f, .2f, u + s, v); - } - - if (world->BlockAt(cx + 1, y, cy) == BTYPE_AIR) { // x - vd[count++] = VertexBuffer::VertexData(x + 1.f, y, z, .9f, .9f, .9f, u, v); - vd[count++] = VertexBuffer::VertexData(x + 1.f, y + 1.f, z, .9f, .9f, .9f, u, v + s); - vd[count++] = VertexBuffer::VertexData(x + 1.f, y + 1.f, z + 1.f, .9f, .9f, .9f, u + s, v + s); - vd[count++] = VertexBuffer::VertexData(x + 1.f, y, z + 1.f, .9f, .9f, .9f, u + s, v); - } - - if (world->BlockAt(cx - 1, y, cy) == BTYPE_AIR) { // -x - vd[count++] = VertexBuffer::VertexData(x, y + 1.f, z + 1.f, .5f, .5f, .5f, u, v + s); - vd[count++] = VertexBuffer::VertexData(x, y + 1.f, z, .5f, .5f, .5f, u + s, v + s); - vd[count++] = VertexBuffer::VertexData(x, y, z, .5f, .5f, .5f, u + s, v); - vd[count++] = VertexBuffer::VertexData(x, y, z + 1.f, .5f, .5f, .5f, u, v); - } - - if (world->BlockAt(cx, y, cy + 1) == BTYPE_AIR) { // z - vd[count++] = VertexBuffer::VertexData(x, y, z + 1.f, .4f, .4f, .4f, u, v); - vd[count++] = VertexBuffer::VertexData(x + 1.f, y, z + 1.f, .4f, .4f, .4f, u + s, v); - vd[count++] = VertexBuffer::VertexData(x + 1.f, y + 1.f, z + 1.f, .4f, .4f, .4f, u + s, v + s); - vd[count++] = VertexBuffer::VertexData(x, y + 1.f, z + 1.f, .4f, .4f, .4f, u, v + s); - } - - if (world->BlockAt(cx, y, cy - 1) == BTYPE_AIR) { // -z - vd[count++] = VertexBuffer::VertexData(x, y + 1.f, z, 1.f, 1.f, 1.f, u, v + s); - vd[count++] = VertexBuffer::VertexData(x + 1.f, y + 1.f, z, 1.f, 1.f, 1.f, u + s, v + s); - vd[count++] = VertexBuffer::VertexData(x + 1.f, y, z , 1.f, 1.f, 1.f, u + s, v); - vd[count++] = VertexBuffer::VertexData(x, y, z , 1.f, 1.f, 1.f, u, v); - } -} - -void Chunk::Render() const { m_vertexBuffer.Render(); } - -bool Chunk::IsDirty() const { return m_isDirty; } - -void Chunk::MakeDirty() { m_isDirty = true; } - -void Chunk::MakeModified() { m_isModified = true; } - diff --git a/SQCSim2021/chunk.h b/SQCSim2021/chunk.h deleted file mode 100644 index 995b3f6..0000000 --- a/SQCSim2021/chunk.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef CHUNK_H__ -#define CHUNK_H__ -#include "define.h" -#include "../SQCSim-common/array2d.h" -#include "../SQCSim-common/array3d.h" -#include "../SQCSim-common/blockinfo.h" -#include "../SQCSim-common/opensimplex.h" -#include "vertexbuffer.h" - -class World; - -class Chunk { - private: - Array3d m_blocks = Array3d(CHUNK_SIZE_X, CHUNK_SIZE_Y, CHUNK_SIZE_Z); - VertexBuffer m_vertexBuffer; - bool m_isDirty = true; - bool m_isModified = false; - - unsigned int m_posX; // Position du chunk dans l'array constituant le monde. - unsigned int m_posY; - - VertexBuffer::VertexData* m_vd; - int m_vcount; - - void AddBlockToMesh(VertexBuffer::VertexData* vd, int& count, BlockType bt, int x, int y, int z, float u, float v, float s, World* world); - - public: - Chunk(unsigned int x, unsigned int y, int64_t seed); - ~Chunk(); - - void RemoveBlock(int x, int y, int z, World* world); - void SetBlock(int x, int y, int z, BlockType type, World* world); - BlockType GetBlock(int x, int y, int z); - void CheckNeighbors(unsigned int x, unsigned int z, World* world); - void GetPosition(unsigned int& x, unsigned int& y) const; - - - void Update(BlockInfo* blockinfo[BTYPE_LAST], World* world); - void FlushMeshToVBO(); - - void FlushVBO(); - - void Render() const; - bool IsDirty() const; - void MakeDirty(); - void MakeModified(); -}; - -#endif // CHUNK_H__ diff --git a/SQCSim2021/define.h b/SQCSim2021/define.h index ceeb08c..8d51188 100644 --- a/SQCSim2021/define.h +++ b/SQCSim2021/define.h @@ -18,14 +18,6 @@ #define SRV_ADDR "127.0.0.1" #define COUNTDOWN 300 -#define FRAMES_RENDER_CHUNKS 1 -#define FRAMES_UPDATE_CHUNKS 1 -#define FRAMES_DELETE_CHUNKS 1 - -#define THREADS_GENERATE_CHUNKS 8 -#define THREADS_UPDATE_CHUNKS 3 -#define THREADS_DELETE_CHUNKS 3 - #define BASE_WIDTH 640 #define BASE_HEIGHT 480 diff --git a/SQCSim2021/engine.cpp b/SQCSim2021/engine.cpp index 6599890..5c670b3 100644 --- a/SQCSim2021/engine.cpp +++ b/SQCSim2021/engine.cpp @@ -17,11 +17,11 @@ void Engine::Init() { abort(); } - //glDisable(GL_FRAMEBUFFER_SRGB); - //glEnable(GL_DEPTH_TEST); - //glEnable(GL_STENCIL_TEST); - //glEnable(GL_POINT_SMOOTH); - //glEnable(GL_BLEND); + glDisable(GL_FRAMEBUFFER_SRGB); + glEnable(GL_DEPTH_TEST); + glEnable(GL_STENCIL_TEST); + glEnable(GL_POINT_SMOOTH); + glEnable(GL_BLEND); glEnable(GL_CULL_FACE); glEnable(GL_TEXTURE_2D); @@ -31,10 +31,10 @@ void Engine::Init() { gluPerspective(45.0f, (float)Width() / (float)Height(), 0.1f, VIEW_DISTANCE); glShadeModel(GL_SMOOTH); - //glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); - //glDisable(GL_BLEND); - //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - //glBlendEquation(GL_FUNC_SUBTRACT); + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + glDisable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendEquation(GL_FUNC_SUBTRACT); // // Objet de skybox avec sa propre texture et son propre shader! m_skybox.Init(0.2f); @@ -330,6 +330,7 @@ int Engine::GetCountdown(float elapsedTime) { void Engine::Render(float elapsedTime) { //static float gameTime = elapsedTime; + static irrklang::ISound* step; // Pour les sons de pas. static float pollTime = 0; static float bulletTime = 0; static BlockType bloc = 1; @@ -341,6 +342,7 @@ void Engine::Render(float elapsedTime) { Transformation all; Transformation skybox; + Vector3f vstep; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // Transformations initiales @@ -350,8 +352,22 @@ void Engine::Render(float elapsedTime) { if (bulletTime > 0.f) bulletTime -= elapsedTime; if (bulletTime < 0.f) bulletTime = 0.f; + static bool leftright = false; if (pollTime >= .005f) { - m_player.ApplyPhysics(m_player.GetInput(m_keyW, m_keyS, m_keyA, m_keyD, m_keySpace, (bloc == BTYPE_LAST && bulletTime <= 0.f && m_mouseL), elapsedTime), &m_world, elapsedTime, &m_audio); + Player::Sound snd = m_player.ApplyPhysics(m_player.GetInput(m_keyW, m_keyS, m_keyA, m_keyD, m_keySpace, (bloc == BTYPE_LAST && bulletTime <= 0.f && m_mouseL), elapsedTime), &m_world, elapsedTime); + switch (snd) { + case Player::Sound::STEP: + if (leftright) + vstep = Vector3f(m_player.GetPosition().x + m_player.GetDirection().z, m_player.GetPosition().y - 1.7f, m_player.GetPosition().z + m_player.GetDirection().x); + else vstep = Vector3f(m_player.GetPosition().x - m_player.GetDirection().z, m_player.GetPosition().y - 1.7f, m_player.GetPosition().z - m_player.GetDirection().x); + m_audio.Create3DAudioObj(step, AUDIO_PATH "step.wav", vstep, m_player.GetVelocity(), .8f); + leftright = !leftright; + break; + case Player::Sound::FALL: + m_audio.Create3DAudioObj(step, AUDIO_PATH "hit.wav", m_player.GetPosition(), m_player.GetVelocity(), 1.f); + break; + default: break; + } m_audio.Update3DAudio(m_player.GetPOV(), m_player.GetDirection(), m_player.GetVelocity()); // Ajustement du positionnement 3D avec les coordonnées du joueur et // son vecteur de vélocité (pour l'effet Doppler) pollTime = 0; @@ -370,7 +386,7 @@ void Engine::Render(float elapsedTime) { if (m_mouseL) { if (bloc != BTYPE_LAST) - m_world.ChangeBlockAtCursor(bloc, m_player, m_block); + m_world.ChangeBlockAtCursor(bloc, m_player.GetPosition(), m_player.GetDirection(), m_block); else if (bulletTime <= 0.f) { for (int x = 0; x < MAX_BULLETS; ++x) // Ajouter une balle dans l'array (aussi connu sous le nom de "faire pow pow"). if (!m_bullets[x]) { @@ -392,7 +408,7 @@ void Engine::Render(float elapsedTime) { } } else if (m_mouseR) - m_world.ChangeBlockAtCursor(BTYPE_AIR, m_player, m_block); + m_world.ChangeBlockAtCursor(BTYPE_AIR, m_player.GetPosition(), m_player.GetDirection(), m_block); for (int x = 0; x < MAX_BULLETS; ++x) // Array de bullets en jeu. if (m_bullets[x]) @@ -401,7 +417,9 @@ void Engine::Render(float elapsedTime) { m_bullets[x] = nullptr; } - m_world.Update(m_renderCount, m_bullets, m_player, all, m_shader01, m_textureAtlas, m_blockinfo); + m_wrenderer.RenderWorld(&m_world, m_renderCount, m_player.GetPosition(), m_player.GetDirection(), all, m_shader01, m_textureAtlas); + m_world.Update(m_bullets, m_player.GetPosition(), m_blockinfo); + m_wrenderer.UpdateWorld(&m_world, m_player.GetPosition(), m_blockinfo); if (m_isSkybox) m_skybox.Render(skybox); diff --git a/SQCSim2021/engine.h b/SQCSim2021/engine.h index 8c525b6..e2a291f 100644 --- a/SQCSim2021/engine.h +++ b/SQCSim2021/engine.h @@ -6,18 +6,19 @@ #include "../SQCSim-common/array2d.h" #include "../SQCSim-common/blockinfo.h" #include "../SQCSim-common/bullet.h" +#include "../SQCSim-common/chunk.h" +#include "../SQCSim-common/world.h" +#include "../SQCSim-common/transformation.h" +#include "../SQCSim-common/player.h" #include "define.h" #include "openglcontext.h" #include "texture.h" -#include "transformation.h" #include "shader.h" -#include "player.h" -#include "chunk.h" #include "skybox.h" #include "audio.h" #include "textureatlas.h" -#include "world.h" #include "connector.h" +#include "worldrenderer.h" class Engine : public OpenglContext { public: @@ -55,6 +56,7 @@ private: TextureAtlas m_textureAtlas = TextureAtlas(BTYPE_LAST); World m_world = World(); + WorldRenderer m_wrenderer = WorldRenderer(); Texture m_textureSkybox; Texture m_textureFont; diff --git a/SQCSim2021/mesh.cpp b/SQCSim2021/mesh.cpp new file mode 100644 index 0000000..c6f6b9b --- /dev/null +++ b/SQCSim2021/mesh.cpp @@ -0,0 +1,120 @@ +#include "mesh.h" + +Mesh::Mesh(Chunk* chunk): m_chunk(chunk) {} + +Mesh::~Mesh() { + FlushVBO(); +} + +void Mesh::FlushMeshToVBO() { + m_vertexBuffer.SetMeshData(m_vd, m_vcount); + m_vcount = 0; + delete[] m_vd; +} + +void Mesh::FlushVBO() { + m_vertexBuffer.Flush(); +} + +void Mesh::Update(BlockInfo* blockinfo[BTYPE_LAST], World* world) { + float u, v, s; + // Update mesh + if (m_chunk->IsDirty()) { + int maxVertexCount = (CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z) * (6 * 4); + m_vd = new VertexBuffer::VertexData[maxVertexCount]; + m_vcount = 0; + for (int x = 0; x < CHUNK_SIZE_X; ++x) { + for (int z = 0; z < CHUNK_SIZE_Z; ++z) { + for (int y = 0; y < CHUNK_SIZE_Y; ++y) { + if (m_vcount > USHRT_MAX) + break; + + BlockType bt = m_chunk->GetBlock(x, y, z); + + if (bt != BTYPE_AIR) { + blockinfo[bt]->GetTexture(u, v, s); + AddBlockToMesh(m_vd, m_vcount, bt, x, y, z, u, v, s, world); + } + } + } + } + if (m_vcount > USHRT_MAX) { + m_vcount = USHRT_MAX; + std::cout << "[ Chunk :: Update ] Chunk data truncaned , too much vertices to have a 16 bit index " << std::endl; + } + } + m_chunk->MakeClean(); +} + +void Mesh::AddBlockToMesh(VertexBuffer::VertexData* vd, int& count, BlockType bt, + int x, int y, int z, float u, float v, float s, World* world) { + + unsigned int cex, cey, cpx, cpy; + + m_chunk->GetPosition(cpx, cpy); + + world->GetScope(cex, cey); + + int cx = x + (cpx - cex) * CHUNK_SIZE_X, cy = z + (cpy - cey) * CHUNK_SIZE_Z; + + if (y == CHUNK_SIZE_Y - 1 || m_chunk->GetBlock(x, y + 1, z) == BTYPE_AIR) { // y + vd[count++] = VertexBuffer::VertexData(x, y + 1.f, z, .8f, .8f, .8f, u, v); + vd[count++] = VertexBuffer::VertexData(x, y + 1.f, z + 1.f, .8f, .8f, .8f, u, v + s); + vd[count++] = VertexBuffer::VertexData(x + 1.f, y + 1.f, z + 1.f, .8f, .8f, .8f, u + s, v + s); + vd[count++] = VertexBuffer::VertexData(x + 1.f, y + 1.f, z, .8f, .8f, .8f, u + s, v); + } + + if (y == 0 || m_chunk->GetBlock(x, y - 1, z) == BTYPE_AIR) { // -y + vd[count++] = VertexBuffer::VertexData(x, y, z + 1.f, .2f, .2f, .2f, u, v); + vd[count++] = VertexBuffer::VertexData(x, y, z, .2f, .2f, .2f, u, v + s); + vd[count++] = VertexBuffer::VertexData(x + 1.f, y, z, .2f, .2f, .2f, u + s, v + s); + vd[count++] = VertexBuffer::VertexData(x + 1.f, y, z + 1.f, .2f, .2f, .2f, u + s, v); + } + + if (world->BlockAt(cx + 1, y, cy) == BTYPE_AIR) { // x + vd[count++] = VertexBuffer::VertexData(x + 1.f, y, z, .9f, .9f, .9f, u, v); + vd[count++] = VertexBuffer::VertexData(x + 1.f, y + 1.f, z, .9f, .9f, .9f, u, v + s); + vd[count++] = VertexBuffer::VertexData(x + 1.f, y + 1.f, z + 1.f, .9f, .9f, .9f, u + s, v + s); + vd[count++] = VertexBuffer::VertexData(x + 1.f, y, z + 1.f, .9f, .9f, .9f, u + s, v); + } + + if (world->BlockAt(cx - 1, y, cy) == BTYPE_AIR) { // -x + vd[count++] = VertexBuffer::VertexData(x, y + 1.f, z + 1.f, .5f, .5f, .5f, u, v + s); + vd[count++] = VertexBuffer::VertexData(x, y + 1.f, z, .5f, .5f, .5f, u + s, v + s); + vd[count++] = VertexBuffer::VertexData(x, y, z, .5f, .5f, .5f, u + s, v); + vd[count++] = VertexBuffer::VertexData(x, y, z + 1.f, .5f, .5f, .5f, u, v); + } + + if (world->BlockAt(cx, y, cy + 1) == BTYPE_AIR) { // z + vd[count++] = VertexBuffer::VertexData(x, y, z + 1.f, .4f, .4f, .4f, u, v); + vd[count++] = VertexBuffer::VertexData(x + 1.f, y, z + 1.f, .4f, .4f, .4f, u + s, v); + vd[count++] = VertexBuffer::VertexData(x + 1.f, y + 1.f, z + 1.f, .4f, .4f, .4f, u + s, v + s); + vd[count++] = VertexBuffer::VertexData(x, y + 1.f, z + 1.f, .4f, .4f, .4f, u, v + s); + } + + if (world->BlockAt(cx, y, cy - 1) == BTYPE_AIR) { // -z + vd[count++] = VertexBuffer::VertexData(x, y + 1.f, z, 1.f, 1.f, 1.f, u, v + s); + vd[count++] = VertexBuffer::VertexData(x + 1.f, y + 1.f, z, 1.f, 1.f, 1.f, u + s, v + s); + vd[count++] = VertexBuffer::VertexData(x + 1.f, y, z, 1.f, 1.f, 1.f, u + s, v); + vd[count++] = VertexBuffer::VertexData(x, y, z, 1.f, 1.f, 1.f, u, v); + } +} + +void Mesh::Render() const { m_vertexBuffer.Render(); } + +bool Mesh::IsDirty() const { return m_chunk->IsDirty(); } + +bool Mesh::IsNew() { + bool bl = m_new; + if (m_new) + m_new = false; + return bl; +} + +void Mesh::GetPosition(unsigned int& x, unsigned int& y, World* world) const { + unsigned int sx, sy; + world->GetScope(sx, sy); + m_chunk->GetPosition(x, y); + x -= sx; + y -= sy; +} diff --git a/SQCSim2021/mesh.h b/SQCSim2021/mesh.h new file mode 100644 index 0000000..0a070ff --- /dev/null +++ b/SQCSim2021/mesh.h @@ -0,0 +1,32 @@ +#ifndef MESH_H__ +#define MESH_H__ + +#include "../SQCSim-common/chunk.h" +#include "../SQCSim-common/world.h" +#include "define.h" +#include "vertexbuffer.h" + +class Mesh { +private: + VertexBuffer m_vertexBuffer; + VertexBuffer::VertexData* m_vd = nullptr; + int m_vcount = 0; + bool m_new = true; + Chunk* m_chunk; // NE PAS DÉTRUIRE ICI. + + void AddBlockToMesh(VertexBuffer::VertexData* vd, int& count, BlockType bt, int x, int y, int z, float u, float v, float s, World* world); + +public: + Mesh(Chunk* chunk); + ~Mesh(); + + void FlushMeshToVBO(); + void FlushVBO(); + void Update(BlockInfo* blockinfo[BTYPE_LAST], World* world); + void Render() const; + + bool IsDirty() const; + bool IsNew(); + void GetPosition(unsigned int& x, unsigned int& y, World* world) const; +}; +#endif \ No newline at end of file diff --git a/SQCSim2021/openglcontext.h b/SQCSim2021/openglcontext.h index 5b116f4..b82c683 100644 --- a/SQCSim2021/openglcontext.h +++ b/SQCSim2021/openglcontext.h @@ -1,10 +1,9 @@ #ifndef OPENGLCONTEXT_H__ #define OPENGLCONTEXT_H__ -#include "define.h" - #include #include +#include "define.h" // Documentation de SFML: http://www.sfml-dev.org/documentation/index-fr.php class OpenglContext diff --git a/SQCSim2021/player.cpp b/SQCSim2021/player.cpp deleted file mode 100644 index f5313c7..0000000 --- a/SQCSim2021/player.cpp +++ /dev/null @@ -1,209 +0,0 @@ -#include "player.h" -#include "world.h" - -Player::Player(const Vector3f& position, float rotX, float rotY) : m_position(position), m_rotX(rotX), m_rotY(rotY) { - m_velocity = Vector3f(0, 0, 0); - m_airborne = true; - m_hp = 0.75f; //TODO: Remettre à 1.0f - m_username = "Zelda Bee-Bop"; -} - -void Player::TurnLeftRight(float value) { - m_rotY += value; - if (m_rotY > 360) m_rotY = 0; - else if (m_rotY < -360) m_rotY = 0; -} - -void Player::TurnTopBottom(float value) { - m_rotX += value; - if (m_rotX > 80) m_rotX = 80; - else if (m_rotX < -80) m_rotX = -80; -} - -Vector3f Player::GetInput(bool front, bool back, bool left, bool right, bool jump, bool shoot, float elapsedTime) { - - Vector3f delta = Vector3f(0, 0, 0); - - float yrotrad = (m_rotY / 57.2957795056f); // 180/Pi = 57.295... - float xrotrad = (m_rotX / 57.2957795056f); - - m_direction = Vector3f(cos(xrotrad) * sin(yrotrad), - -sin(xrotrad), - cos(xrotrad) * -cos(yrotrad)); - - m_direction.Normalize(); - - if (front) { - delta.x += float(sin(yrotrad)) * elapsedTime * 10.f; - delta.z += float(-cos(yrotrad)) * elapsedTime * 10.f; - } - else if (back) { - delta.x += float(-sin(yrotrad)) * elapsedTime * 10.f; - delta.z += float(cos(yrotrad)) * elapsedTime * 10.f; - } - - if (left) { - delta.x += float(-cos(yrotrad)) * elapsedTime * 10.f; - delta.z += float(-sin(yrotrad)) * elapsedTime * 10.f; - } - else if (right) { - delta.x += float(cos(yrotrad)) * elapsedTime * 10.f; - delta.z += float(sin(yrotrad)) * elapsedTime * 10.f; - } - - delta.Normalize(); - delta.x *= .6f; - delta.z *= .6f; - - if ((jump || shoot ) && !m_airborne) { - delta.y += jump? .32f: shoot? .1f : 0.f; - m_airborne = true; - } - - if (shoot) // Recoil! - TurnTopBottom(-1); - - return delta; -} - -void Player::ApplyPhysics(Vector3f input, World* world, float elapsedTime, Audio* audio) { - static irrklang::ISound* step; // Pour les sons de pas. - static float timing = 0.f; - /* Gestion de collisions */ - BlockType bt1, bt2, bt3; - - bt1 = world->BlockAt(GetPosition().x, GetPosition().y + input.y, GetPosition().z); - bt2 = world->BlockAt(GetPosition().x, GetPosition().y + input.y - 0.9f, GetPosition().z); - bt3 = world->BlockAt(GetPosition().x, GetPosition().y + input.y - 1.7f, GetPosition().z); - if ((bt1 != BTYPE_AIR || bt2 != BTYPE_AIR || bt3 != BTYPE_AIR) && m_position.y < 129.7f) { - bt1 = world->BlockAt(GetPosition().x, GetPosition().y + .3f, GetPosition().z); - if (bt1 == BTYPE_AIR) m_position.y = (int)m_position.y + .7f; - m_velocity.y = input.y = 0; - m_airborne = false; - } - else { - if (abs(m_velocity.y) < 1.1f) m_velocity.y += input.y - 1.1f * elapsedTime; - bt3 = world->BlockAt(GetPosition().x, GetPosition().y + m_velocity.y - 1.7f, GetPosition().z); - bt1 = world->BlockAt(GetPosition().x, GetPosition().y + .3f, GetPosition().z); - if (bt3 != BTYPE_AIR) { - m_velocity.y = 0; - if (timing == 0.f) { - if (m_airborne) audio->Create3DAudioObj(step, AUDIO_PATH "hit.wav", GetPosition(), GetVelocity(), 1.f); - timing = .3f; - } - m_airborne = false; - } - else if (bt1 != BTYPE_AIR) { - m_velocity.y = -.1f; - } - else m_airborne = true; - } - - if (timing > 0.f) timing -= elapsedTime; - if (timing < 0.f) timing = 0.f; - - bt1 = world->BlockAt(GetPosition().x + input.x, GetPosition().y, GetPosition().z); - bt2 = world->BlockAt(GetPosition().x + input.x, GetPosition().y - 0.9f, GetPosition().z); - bt3 = world->BlockAt(GetPosition().x + input.x, GetPosition().y - 1.7f, GetPosition().z); - if (bt1 == BTYPE_AIR && bt2 != BTYPE_AIR && bt3 != BTYPE_AIR) { - if (input.x > 0) - input.x = m_velocity.x = 0.5f; - else - input.x = m_velocity.x = -0.5f; - m_velocity.y = 0.3; - m_velocity.z *= .5f; - } else if (bt1 != BTYPE_AIR || bt2 != BTYPE_AIR || bt3 != BTYPE_AIR) { - input.x = m_velocity.x = 0; - m_velocity.z *= .5f; - } - - bt1 = world->BlockAt(GetPosition().x, GetPosition().y, GetPosition().z + input.z); - bt2 = world->BlockAt(GetPosition().x, GetPosition().y - 0.9f, GetPosition().z + input.z); - bt3 = world->BlockAt(GetPosition().x, GetPosition().y - 1.7f, GetPosition().z + input.z); - if (bt1 == BTYPE_AIR && bt2 != BTYPE_AIR && bt3 != BTYPE_AIR) { - if (input.z > 0) - input.z = m_velocity.z = 0.5f; - else - input.z = m_velocity.z = -0.5f; - m_velocity.y = 0.3; - m_velocity.x *= .5f; - } else if (bt1 != BTYPE_AIR || bt2 != BTYPE_AIR || bt3 != BTYPE_AIR) { - input.z = m_velocity.z = 0; - m_velocity.x *= .5f; - } - - /* Fin gestion de collisions */ - /* Gestion de la friction */ - - if (!m_airborne) { - m_velocity.x += input.x * 2.f * elapsedTime; - m_velocity.z += input.z * 2.f * elapsedTime; - - if (input.x == 0.f) - m_velocity.x *= .8f; - - if (input.z == 0.f) - m_velocity.z *= .8f; - } - else { - m_velocity.x += input.x * .4f * elapsedTime; // Techniquement contre les lois de la physique, mais c'est beaucoup moins chiant pour grimper sur les blocs. - m_velocity.z += input.z * .4f * elapsedTime; - m_velocity.x *= .99f; - m_velocity.z *= .99f; - } - - /* Fin gestion de la friction */ - - float vy = m_velocity.y; - m_velocity.y = 1.f; // Padding pour limiter le x et z lors du Normalize(). - if (m_velocity.Length() >= 1.f) m_velocity.Normalize(); // Limiteur de vitesse en x/z. - m_velocity.y = 0; - if (m_velocity.Length() < .005f) m_velocity.Zero(); // Threshold en x/z. - m_velocity.y = vy; - - m_position += m_velocity; - - static float bobbingtime = 0; // Gestion de la caméra - static bool leftright = false; - static bool isStep = false; - if (bobbingtime <= 360.f) - bobbingtime += elapsedTime * 20.f; else bobbingtime = 0; - - if ((sin(bobbingtime) - 0.5f) * (abs(m_velocity.x) + abs(m_velocity.z)) < -.2f && !m_airborne) { - Vector3f vstep; - if (leftright) - vstep = Vector3f(GetPosition().x + GetDirection().z, GetPosition().y - 1.7f, GetPosition().z + GetDirection().x); - else vstep = Vector3f(GetPosition().x - GetDirection().z, GetPosition().y - 1.7f, GetPosition().z - GetDirection().x); - if (!isStep) { - audio->Create3DAudioObj(step, AUDIO_PATH "step.wav", vstep, GetVelocity(), .8f); - leftright = !leftright; - } - isStep = true; - } - else isStep = false; - m_POV = m_position.y; - m_POV += m_airborne ? 0 : (sin(bobbingtime) - 0.5f) * (abs(m_velocity.x) + abs(m_velocity.z)) * .2f; -} - -void Player::ApplyTransformation(Transformation& transformation, bool rel) const { - transformation.ApplyRotation(-m_rotX, 1, 0, 0); - transformation.ApplyRotation(-m_rotY, 0, 1, 0); - if (rel) transformation.ApplyTranslation(-GetPOV()); -} - -Vector3f Player::GetPosition() const { return Vector3f(m_position.x + CHUNK_SIZE_X * WORLD_SIZE_X / 2, m_position.y, m_position.z + CHUNK_SIZE_Z * WORLD_SIZE_Y / 2); } - -Vector3f Player::GetVelocity() const { return m_velocity; } - -Vector3f Player::GetPOV() const { return Vector3f(GetPosition().x, m_POV, GetPosition().z); } - -Vector3f Player::GetDirection() const { return m_direction; } - -std::string Player::GetUsername() const { return m_username; } - -float Player::GetHP() const { return m_hp; } - -void Player::Teleport(int& x, int& z) { - m_position.x -= x * CHUNK_SIZE_X; - m_position.z -= z * CHUNK_SIZE_Z; -} diff --git a/SQCSim2021/player.h b/SQCSim2021/player.h deleted file mode 100644 index d8f46f3..0000000 --- a/SQCSim2021/player.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef CLI_PLAYER_H__ -#define CLI_PLAYER_H__ -#include "../SQCSim-common/vector3.h" -#include "transformation.h" -#include "audio.h" -#include - -class World; - -class Player { -public: - Player(const Vector3f& position, float rotX = 0, float rotY = 0); - void TurnLeftRight(float value); - void TurnTopBottom(float value); - Vector3f GetInput(bool front, bool back, bool left, bool right, bool jump, bool dash, float elapsedTime); - void ApplyPhysics(Vector3f input, World* world, float elapsedTime, Audio* audio); - void ApplyTransformation(Transformation& transformation, bool rel = true) const; - - Vector3f GetPosition() const; - Vector3f GetDirection() const; - Vector3f GetVelocity() const; - Vector3f GetPOV() const; - std::string GetUsername() const; - float GetHP() const; - void Teleport(int& x, int& z); - -private: - Vector3f m_position; - Vector3f m_velocity; - Vector3f m_direction; - - std::string m_username; - - float m_rotX = 0; - float m_rotY = 0; - float m_POV; - - float m_hp; - - bool m_airborne; -}; -#endif //_PLAYER_H__ - diff --git a/SQCSim2021/skybox.cpp b/SQCSim2021/skybox.cpp index 947535c..a024ae7 100644 --- a/SQCSim2021/skybox.cpp +++ b/SQCSim2021/skybox.cpp @@ -45,7 +45,7 @@ void Skybox::Init(float size){ void Skybox::Render(Transformation tran) const { glDisable(GL_DEPTH_TEST); glDisable(GL_BLEND); - tran.Use(); + glLoadMatrixf(tran.GetMatrix().GetInternalValues()); m_texture.Bind(); m_shader.Use(); m_vertexBuffer.Render(); diff --git a/SQCSim2021/skybox.h b/SQCSim2021/skybox.h index a596416..6df8aab 100644 --- a/SQCSim2021/skybox.h +++ b/SQCSim2021/skybox.h @@ -1,10 +1,10 @@ #ifndef SKYBOX_H__ #define SKYBOX_H__ +#include "../SQCSim-common/transformation.h" #include "define.h" #include "vertexbuffer.h" #include "texture.h" -#include "transformation.h" #include "shader.h" class Skybox { diff --git a/SQCSim2021/texture.h b/SQCSim2021/texture.h index 1a37395..fa549fd 100644 --- a/SQCSim2021/texture.h +++ b/SQCSim2021/texture.h @@ -1,9 +1,9 @@ #ifndef TEXTURE_H__ #define TEXTURE_H__ -#include "define.h" -#include #include +#include +#include "define.h" class Texture { diff --git a/SQCSim2021/textureatlas.h b/SQCSim2021/textureatlas.h index 1d717e1..fab0665 100644 --- a/SQCSim2021/textureatlas.h +++ b/SQCSim2021/textureatlas.h @@ -1,9 +1,9 @@ #ifndef TEXTUREATLAS_H__ #define TEXTUREATLAS_H__ -#include "define.h" #include #include #include +#include "define.h" class TextureAtlas { diff --git a/SQCSim2021/world.cpp b/SQCSim2021/world.cpp deleted file mode 100644 index dc5e5b6..0000000 --- a/SQCSim2021/world.cpp +++ /dev/null @@ -1,506 +0,0 @@ -#include "world.h" - -World::World() {} - -World::~World() {} - -Array2d& World::GetChunks() { return m_chunks; } - -void World::SetSeed(uint64_t seed) { - m_seed = seed; -} - -Chunk* World::ChunkAt(float x, float y, float z) const { - int cx = (int)x / CHUNK_SIZE_X; - int cz = (int)z / CHUNK_SIZE_Z; - - if (x < 0 || y < 0 || z < 0 || - x >= WORLD_SIZE_X * CHUNK_SIZE_X || - z >= CHUNK_SIZE_Z * WORLD_SIZE_Y || - y > CHUNK_SIZE_Y) - return 0; - - return m_chunks.Get(cx, cz); -} - -Chunk* World::ChunkAt(const Vector3f& pos) const { return ChunkAt(pos.x, pos.y, pos.z); } - -BlockType World::BlockAt(float x, float y, float z, BlockType defaultBlockType) const { - Chunk* c = ChunkAt(x, y, z); - - if (!c) - return defaultBlockType; - - int bx = (int)x % CHUNK_SIZE_X; - int by = (int)y % CHUNK_SIZE_Y; - int bz = (int)z % CHUNK_SIZE_Z; - - return c->GetBlock(bx, by, bz); -} - -BlockType World::BlockAt(const Vector3f& pos, BlockType defaultBlockType) const { - return BlockAt(pos.x, pos.y, pos.z, defaultBlockType); -} - -void World::TransposeWorld(Player& player, Bullet* bullets[MAX_BULLETS]) { - int x = 0, y = 0; - - if (player.GetPosition().x > (WORLD_SIZE_X * CHUNK_SIZE_X) * .6f) ++x; - else if (player.GetPosition().x < (WORLD_SIZE_X * CHUNK_SIZE_X) * .4f) --x; - if (player.GetPosition().z > (WORLD_SIZE_Y * CHUNK_SIZE_Z) * .6f) ++y; - else if (player.GetPosition().z < (WORLD_SIZE_Y * CHUNK_SIZE_Z) * .4f) --y; - - if (!x && !y) return; - - if (x > 0) { - for (int ax = 0; ax < WORLD_SIZE_X; ++ax) - for (int ay = 0; ay < WORLD_SIZE_Y; ++ay) - if (ax - x >= 0) { - m_chunks.Set(ax - x, ay, - m_chunks.Remove(ax, ay)); - if (ax == WORLD_SIZE_X - 1 && m_chunks.Get(ax - x, ay)) - m_chunks.Get(ax - x, ay)->MakeDirty(); - } - else if (m_chunks.Get(ax, ay)) m_tbDeleted.emplace_back(m_chunks.Remove(ax, ay)); - } - else if (x < 0) { - for (int ax = WORLD_SIZE_X - 1; ax >= 0; --ax) - for (int ay = WORLD_SIZE_Y - 1; ay >= 0; --ay) - if (ax - x < WORLD_SIZE_X) { - m_chunks.Set(ax - x, ay, - m_chunks.Remove(ax, ay)); - if (ax == 0 && m_chunks.Get(ax - x, ay)) - m_chunks.Get(ax - x, ay)->MakeDirty(); - } - else if (m_chunks.Get(ax, ay)) m_tbDeleted.emplace_back(m_chunks.Remove(ax, ay)); - } - - if (y > 0) { - for (int ax = 0; ax < WORLD_SIZE_X; ++ax) - for (int ay = 0; ay < WORLD_SIZE_Y; ++ay) - if (ay - y >= 0) { - m_chunks.Set(ax, ay - y, - m_chunks.Remove(ax, ay)); - if (ay == WORLD_SIZE_Y - 1 && m_chunks.Get(ax, ay - y)) - m_chunks.Get(ax, ay - y)->MakeDirty(); - } - else if (m_chunks.Get(ax, ay)) m_tbDeleted.emplace_back(m_chunks.Remove(ax, ay)); - } - else if (y < 0) { - for (int ax = WORLD_SIZE_X - 1; ax >= 0; --ax) - for (int ay = WORLD_SIZE_Y - 1; ay >= 0; --ay) - if (ay - y < WORLD_SIZE_Y) { - m_chunks.Set(ax, ay - y, - m_chunks.Remove(ax, ay)); - if (ay == 0 && m_chunks.Get(ax, ay - y)) - m_chunks.Get(ax, ay - y)->MakeDirty(); - } - else if (m_chunks.Get(ax, ay)) m_tbDeleted.emplace_back(m_chunks.Remove(ax, ay)); - } - - m_center[0] += x; m_center[1] += y; - player.Teleport(x, y); - - for (int index = 0; index < MAX_BULLETS; ++index) - if (bullets[index]) bullets[index]->Transpose(x, y); -} - -void World::CleanUpWorld(int& deleteframes, bool clear = false) { - if (clear) { - while (m_tbDeleted.size() > 0) { - delete m_tbDeleted.back(); - m_tbDeleted.pop_back(); - } - } - if (!m_tbDeleted.empty() && !deleteframes) { - int deleted = 0; - while (deleted < THREADS_DELETE_CHUNKS) { - - - } - delete m_tbDeleted.back(); - m_tbDeleted.pop_back(); - deleteframes = FRAMES_DELETE_CHUNKS; - } -} - -void World::GetScope(unsigned int& x, unsigned int& y) { - x = m_center[0]; - y = m_center[1]; -} - -void World::Update(int& rendercount, Bullet* bullets[MAX_BULLETS], Player& player, Transformation& world, Shader& shader, TextureAtlas& atlas, BlockInfo* blockinfo[BTYPE_LAST]) { - glStencilFunc(GL_EQUAL, 1, 0x00); - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - atlas.Bind(); - RenderWorld(rendercount, player, world, shader); - UpdateWorld(player, blockinfo); - //TransposeWorld(player, bullets); - shader.Disable(); - glStencilFunc(GL_GREATER, 1, 0xFF); -} - -void World::UpdateChunk(int& updates, unsigned int chx, unsigned int chy, BlockInfo* blockinfo[BTYPE_LAST]) { - if (updates == 0 && ChunkAt(chx, 1, chy) && - ChunkAt(chx, 1, chy)->IsDirty()) { - ChunkAt(chx, 1, chy)->Update(blockinfo, this); - updates = FRAMES_UPDATE_CHUNKS; - } - -} - -void World::ChangeBlockAtCursor(BlockType blockType, Player& player, bool& block) { - Vector3f currentPos = player.GetPosition(); - Vector3f currentBlock = currentPos; - Vector3f ray = player.GetDirection(); - bool found = false; - - if (block) return; - - while ((currentPos - currentBlock).Length() <= MAX_SELECTION_DISTANCE && !found) { - currentBlock += ray / 10.f; - - BlockType bt = BlockAt(currentBlock); - - if (bt != BTYPE_AIR) - found = true; - } - - if (found) - if (blockType != BTYPE_AIR) { - found = false; - while ((currentPos - currentBlock).Length() >= 1.7f && !found) { - currentBlock -= ray / 10.f; - - BlockType bt = BlockAt(currentBlock); - - if (bt == BTYPE_AIR) { // Vérification pour être sûr que le bloc à changer n'est pas dans le joueur. - int Bx = (int)currentBlock.x; - int By = (int)currentBlock.y; - int Bz = (int)currentBlock.z; - - int Px = (int)currentPos.x; - int PyA = (int)currentPos.y; - int PyB = (int)(currentPos.y - .9f); - int PyC = (int)(currentPos.y - 1.7f); - int Pz = (int)currentPos.z; - - if (!(Bx == Px && - (By == PyA || - By == PyB || - By == PyC) && - Bz == Pz)) - found = true; - } - } - } - - if (found && (int)currentBlock.y < CHUNK_SIZE_Y) { - int bx = (int)currentBlock.x % CHUNK_SIZE_X; - int by = (int)currentBlock.y % CHUNK_SIZE_Y; - int bz = (int)currentBlock.z % CHUNK_SIZE_Z; - - ChunkAt(currentBlock)->SetBlock(bx, by, bz, blockType, this); - ChunkAt(currentBlock)->MakeModified(); - block = true; - } -} - -void World::ChangeBlockAtPosition(BlockType blockType, Vector3f pos) { - int bx = (int)pos.x % CHUNK_SIZE_X; - int by = (int)pos.y % CHUNK_SIZE_Y; - int bz = (int)pos.z % CHUNK_SIZE_Z; - - ChunkAt(pos)->SetBlock(bx, by, bz, blockType, this); - ChunkAt(pos)->MakeModified(); -} - -void World::RenderWorld(int& rendercount, Player& player, Transformation& world, Shader& shader) { - shader.Use(); - rendercount = 0; - Vector3f angle; - Vector3f cursor; - Vector3f direct = player.GetDirection(); - Vector3f pos = player.GetPosition() - direct; - - direct.y = 0; - direct.Normalize(); - pos.y = 1; - - static Vector3 renderManifest[VIEW_DISTANCE * 8]; // Nombre de Chunks maximal à être rendus. - - //for (int dist = VIEW_DISTANCE; dist >= 0; dist -= CHUNK_SIZE_X) { - for (int dist = 0; dist <= VIEW_DISTANCE; dist += CHUNK_SIZE_X) { - // Configuration du radar. - float sinus, cosinus; - int echantillons; - - if (dist > VIEW_DISTANCE * .1f) { - sinus = .00872653549f; // sin(1/2 degré) - cosinus = .99996192306; // cos(1/2 degré) - echantillons = 180; - } - //else if (dist > VIEW_DISTANCE * .3f) { - // sinus = .01151891831f; // sin(2/3 degré) - // cosinus = .99993365506; // cos(2/3 degré) - // echantillons = 120; - //} - //else if (dist > VIEW_DISTANCE * .2f) { - // sinus = .01745240643; // sin(1 degré) - // cosinus = .99984769515; // cos(1 degré) - // echantillons = 90; - //} - //else if (dist > VIEW_DISTANCE * .1f) { - // sinus = .0261769483; - // cosinus = .99965732497; - // echantillons = 60; - //} - else { - sinus = .0348994967; - cosinus = .99939082701; - echantillons = 45; - } - - angle.x = direct.z + direct.x; - angle.z = direct.z - direct.x; - angle.y = 0; - angle.Normalize(); - - for (int radar = 0; radar < echantillons; ++radar) { - float x = angle.x; - - angle.x = angle.x * cosinus - angle.z * sinus; - angle.z = angle.z * cosinus + x * sinus; - angle.Normalize(); - - cursor = pos - direct * CHUNK_SIZE_X * 2 + angle * dist; - if (cursor.y >= 128.f || cursor.y >= 0.f) cursor.y = CHUNK_SIZE_Y / 2.f; - - - if (ChunkAt(cursor)) { - bool valide = true; - unsigned int chx, chy; - ChunkAt(cursor)->GetPosition(chx, chy); - for (int index = 0; index < rendercount; ++index) // Permet de vérifier seulement contre celles ajoutées dans la frame, et ne pas avoir à refaire l'array à chaque frame. - if (renderManifest[index].x == chx && renderManifest[index].z == chy) - valide = false; - - if (valide) renderManifest[rendercount++] = Vector3(chx, - (VIEW_DISTANCE - (pos - cursor).Length() * 3.f + 256.f) < 0.f? 0: - (VIEW_DISTANCE - (pos - cursor).Length() * 3.f + 256.f) * 1000, - chy); - } - } - } - for (int index = 0; index < rendercount; ++index) { - int chx = (renderManifest[index].x - m_center[0]) * CHUNK_SIZE_X, chy = (renderManifest[index].z - m_center[1]) * CHUNK_SIZE_Z; - - world.ApplyTranslation(chx, 0, chy); - world.Use(); - float blcolor = renderManifest[index].y / (VIEW_DISTANCE / 50.f); - glBlendColor(blcolor, blcolor, blcolor, 1.f); - ChunkAt(chx, 1, chy)->Render(); - world.ApplyTranslation(-chx, 0, -chy); - } - shader.Disable(); -}; - -void World::UpdateWorld(Player& player, BlockInfo* blockinfo[BTYPE_LAST]) { - int cx = player.GetPosition().x; - int cy = player.GetPosition().z; - static int frameGenerate = 1; - static int frameUpdate = 2; - static int frameDelete = 3; - int side = 0; - int threads = 0; - std::future genThList[THREADS_GENERATE_CHUNKS]; - std::future updateThList[THREADS_UPDATE_CHUNKS]; - std::future delThList[THREADS_DELETE_CHUNKS]; - - if (frameGenerate > 0) --frameGenerate; - if (frameUpdate > 0) --frameUpdate; - if (frameDelete > 0) --frameDelete; - - if (!frameGenerate) - while (side * CHUNK_SIZE_X <= VIEW_DISTANCE * 2 + CHUNK_SIZE_X) { - int tx = -side, ty = -side; - int chx = 0; - int chy = 0; - - for (; tx <= side; ++tx) { - if (frameGenerate) - break; - chx = cx + tx * CHUNK_SIZE_X; - chy = cy + ty * CHUNK_SIZE_Z; - if (chx < WORLD_SIZE_X * CHUNK_SIZE_X && chy < WORLD_SIZE_Y * CHUNK_SIZE_Z && - chx >= 0 && chy >= 0 && !ChunkAt(chx, 1, chy)) - genThList[threads++] = std::async(std::launch::async, - [](unsigned int x, unsigned int y, uint64_t seed) { - return new Chunk(x, y, seed); }, - chx / CHUNK_SIZE_X + m_center[0], - chy / CHUNK_SIZE_Z + m_center[1], - m_seed); - if (threads == THREADS_GENERATE_CHUNKS) frameGenerate = FRAMES_RENDER_CHUNKS; - } - for (; ty <= side; ++ty) { - if (frameGenerate) - break; - chx = cx + tx * CHUNK_SIZE_X; - chy = cy + ty * CHUNK_SIZE_Z; - if (chx < WORLD_SIZE_X * CHUNK_SIZE_X && chy < WORLD_SIZE_Y * CHUNK_SIZE_Z && - chx >= 0 && chy >= 0 && !ChunkAt(chx, 1, chy)) - genThList[threads++] = std::async(std::launch::async, - [](unsigned int x, unsigned int y, uint64_t seed) { - return new Chunk(x, y, seed); }, - chx / CHUNK_SIZE_X + m_center[0], - chy / CHUNK_SIZE_Z + m_center[1], - m_seed); - if (threads == THREADS_GENERATE_CHUNKS) frameGenerate = FRAMES_RENDER_CHUNKS; - } - for (; tx >= -side; --tx) { - if (frameGenerate) - break; - chx = cx + tx * CHUNK_SIZE_X; - chy = cy + ty * CHUNK_SIZE_Z; - if (chx < WORLD_SIZE_X * CHUNK_SIZE_X && chy < WORLD_SIZE_Y * CHUNK_SIZE_Z && - chx >= 0 && chy >= 0 && !ChunkAt(chx, 1, chy)) - genThList[threads++] = std::async(std::launch::async, - [](unsigned int x, unsigned int y, uint64_t seed) { - return new Chunk(x, y, seed); }, - chx / CHUNK_SIZE_X + m_center[0], - chy / CHUNK_SIZE_Z + m_center[1], - m_seed); - if (threads == THREADS_GENERATE_CHUNKS) frameGenerate = FRAMES_RENDER_CHUNKS; - } - for (; ty >= -side; --ty) { - if (frameGenerate) - break; - chx = cx + tx * CHUNK_SIZE_X; - chy = cy + ty * CHUNK_SIZE_Z; - if (chx < WORLD_SIZE_X * CHUNK_SIZE_X && chy < WORLD_SIZE_Y * CHUNK_SIZE_Z && - chx >= 0 && chy >= 0 && !ChunkAt(chx, 1, chy)) - genThList[threads++] = std::async(std::launch::async, - [](unsigned int x, unsigned int y, uint64_t seed) { - return new Chunk(x, y, seed); }, - chx / CHUNK_SIZE_X + m_center[0], - chy / CHUNK_SIZE_Z + m_center[1], - m_seed); - if (threads == THREADS_GENERATE_CHUNKS) frameGenerate = FRAMES_RENDER_CHUNKS; - } - if (frameGenerate) - break; - ++side; - } - - if (threads > 0) { - for (int i = 0; i < threads; ++i) - genThList[i].wait(); - - for (int i = 0; i < threads; ++i) { - unsigned int x, y; - Chunk* chunk = genThList[i].get(); - chunk->GetPosition(x, y); - m_chunks.Set(x - m_center[0], y - m_center[1], chunk); - } - } - - side = 0; - threads = 0; - - if (!frameUpdate) - while (side * CHUNK_SIZE_X <= VIEW_DISTANCE * 2) { - int tx = -side, ty = -side; - - for (; tx <= side; ++tx) { - if (frameUpdate) - break; - unsigned int chx = cx + tx * CHUNK_SIZE_X, chy = cy + ty * CHUNK_SIZE_Z; - if (ChunkAt(chx, 1, chy) && - ChunkAt(chx, 1, chy)->IsDirty()) { - updateThList[threads++] = - std::async(std::launch::async, - [](Chunk* chunk, BlockInfo* blockinfo[BTYPE_LAST], World* world) { - chunk->Update(blockinfo, world); return chunk; }, ChunkAt(chx, 1, chy), blockinfo, this); - if (threads == THREADS_UPDATE_CHUNKS) frameUpdate = FRAMES_UPDATE_CHUNKS; - } - } - for (; ty <= side; ++ty) { - if (frameUpdate) - break; - unsigned int chx = cx + tx * CHUNK_SIZE_X, chy = cy + ty * CHUNK_SIZE_Z; - if (ChunkAt(chx, 1, chy) && - ChunkAt(chx, 1, chy)->IsDirty()) { - updateThList[threads++] = - std::async(std::launch::async, - [](Chunk* chunk, BlockInfo* blockinfo[BTYPE_LAST], World* world) { - chunk->Update(blockinfo, world); return chunk; }, ChunkAt(chx, 1, chy), blockinfo, this); - if (threads == THREADS_UPDATE_CHUNKS) frameUpdate = FRAMES_UPDATE_CHUNKS; - } - } - for (; tx >= -side; --tx) { - if (frameUpdate) - break; - unsigned int chx = cx + tx * CHUNK_SIZE_X, chy = cy + ty * CHUNK_SIZE_Z; - if (ChunkAt(chx, 1, chy) && - ChunkAt(chx, 1, chy)->IsDirty()) { - updateThList[threads++] = - std::async(std::launch::async, - [](Chunk* chunk, BlockInfo* blockinfo[BTYPE_LAST], World* world) { - chunk->Update(blockinfo, world); return chunk; }, ChunkAt(chx, 1, chy), blockinfo, this); - if (threads == THREADS_UPDATE_CHUNKS) frameUpdate = FRAMES_UPDATE_CHUNKS; - } - } - for (; ty >= -side; --ty) { - if (frameUpdate) - break; - unsigned int chx = cx + tx * CHUNK_SIZE_X, chy = cy + ty * CHUNK_SIZE_Z; - if (ChunkAt(chx, 1, chy) && - ChunkAt(chx, 1, chy)->IsDirty()) { - updateThList[threads++] = - std::async(std::launch::async, - [](Chunk* chunk, BlockInfo* blockinfo[BTYPE_LAST], World* world) { - chunk->Update(blockinfo, world); return chunk; }, ChunkAt(chx, 1, chy), blockinfo, this); - if (threads == THREADS_UPDATE_CHUNKS) frameUpdate = FRAMES_UPDATE_CHUNKS; - } - } - if (frameUpdate) - break; - ++side; - } - - if (threads > 0) { - for (int i = 0; i < threads; ++i) { - updateThList[i].wait(); - Chunk* chunk = updateThList[i].get(); - chunk->FlushMeshToVBO(); - } - } - - threads = 0; - - int del = THREADS_DELETE_CHUNKS; - while (!m_tbDeleted.empty() && del--) { // Moins rapide que le bout en dessous, mais -beaucoup- plus stable. - m_tbDeleted.back()->FlushVBO(); - m_tbDeleted.back()->~Chunk(); - m_tbDeleted.pop_back(); - } - - /*while (!m_tbDeleted.empty() && !frameDelete) { - if (m_tbDeleted.back()) { - m_tbDeleted.back()->FlushVBO(); - delThList[threads] = - std::async(std::launch::async, - [](Chunk* chunk) { delete chunk; }, m_tbDeleted.back()); - m_tbDeleted.pop_back(); - if (++threads > THREADS_DELETE_CHUNKS) frameDelete = FRAMES_DELETE_CHUNKS; - } - else m_tbDeleted.pop_back(); - }*/ - - for (int x = 0; x < threads; ++x) { - delThList[x].wait(); - delThList[x].get(); - } -} - -int World::GettbDeleted() const { return m_tbDeleted.size(); } \ No newline at end of file diff --git a/SQCSim2021/world.h b/SQCSim2021/world.h deleted file mode 100644 index 0b88748..0000000 --- a/SQCSim2021/world.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef WORLD_H__ -#define WORLD_H__ -#include -#include -#include -#include -#include -#include "../SQCSim-common/vector3.h" -#include "../SQCSim-common/array2d.h" -#include "../SQCSim-common/bullet.h" -#include "define.h" -#include "player.h" -#include "chunk.h" -#include "transformation.h" -#include "shader.h" -#include "textureatlas.h" - -class Chunk; -class Player; -class Bullet; - -class World { -public: - World(); - ~World(); - - Array2d& GetChunks(); - - void SetSeed(uint64_t seed); - - Chunk* ChunkAt(float x, float y, float z) const; - Chunk* ChunkAt(const Vector3f& pos) const; - - BlockType BlockAt(float x, float y, float z, BlockType defaultBlockType = BTYPE_AIR) const; - BlockType BlockAt(const Vector3f& pos, BlockType defaultBlockType = BTYPE_AIR) const; - - void Update(int& rendercount, Bullet* bullets[MAX_BULLETS], Player& player, Transformation& world, Shader& shader, TextureAtlas& atlas, BlockInfo* blockinfo[BTYPE_LAST]); - - void GetScope(unsigned int& x, unsigned int& y); - - void ChangeBlockAtCursor(BlockType blockType, Player& player, bool& block); - void ChangeBlockAtPosition(BlockType blockType, Vector3f pos); - void CleanUpWorld(int& deleteframes, bool clear); - int GettbDeleted() const; -private: - Array2d m_chunks = Array2d(WORLD_SIZE_X, WORLD_SIZE_Y); - std::vector m_tbDeleted; - uint64_t m_seed = 0; - - unsigned int m_center[2] = { UINT16_MAX / 2 - WORLD_SIZE_X, UINT16_MAX / 2 - WORLD_SIZE_Y }; - - void UpdateChunk(int& updates, unsigned int chx, unsigned int chy, BlockInfo* blockinfo[BTYPE_LAST]); - void RenderWorld(int& rendercount, Player& player, Transformation& world, Shader& shader); - void UpdateWorld(Player& player, BlockInfo* blockinfo[BTYPE_LAST]); - void TransposeWorld(Player& player, Bullet* bullets[MAX_BULLETS]); - -}; -#endif // WORLD_H__ - diff --git a/SQCSim2021/worldrenderer.cpp b/SQCSim2021/worldrenderer.cpp new file mode 100644 index 0000000..dcf6d45 --- /dev/null +++ b/SQCSim2021/worldrenderer.cpp @@ -0,0 +1,213 @@ +#include "worldrenderer.h" + +WorldRenderer::WorldRenderer() { + m_meshes.Reset(nullptr); +} + +WorldRenderer::~WorldRenderer() { +} + +void WorldRenderer::RenderWorld(World* origin, int& rendercount, const Vector3f& player_pos, const Vector3f& player_dir, Transformation& world, Shader& shader, TextureAtlas& atlas) { + rendercount = 0; + Vector3f angle; + Vector3f cursor; + Vector3f direct = player_dir; + Vector3f pos = player_pos - direct; + + direct.y = 0; + direct.Normalize(); + pos.y = 1; + + static Vector3 renderManifest[VIEW_DISTANCE * 8]; // Nombre de Chunks maximal à être rendus. + + //for (int dist = VIEW_DISTANCE; dist >= 0; dist -= CHUNK_SIZE_X) { + for (int dist = 0; dist <= VIEW_DISTANCE; dist += CHUNK_SIZE_X) { + // Configuration du radar. + float sinus, cosinus; + int echantillons; + + if (dist > VIEW_DISTANCE * .1f) { + sinus = .00872653549f; // sin(1/2 degré) + cosinus = .99996192306; // cos(1/2 degré) + echantillons = 180; + } + //else {//if (dist > VIEW_DISTANCE * .3f) { + // sinus = .01151891831f; // sin(2/3 degré) + // cosinus = .99993365506; // cos(2/3 degré) + // echantillons = 120; + //} + //else if (dist > VIEW_DISTANCE * .2f) { + // sinus = .01745240643; // sin(1 degré) + // cosinus = .99984769515; // cos(1 degré) + // echantillons = 90; + //} + //else if (dist > VIEW_DISTANCE * .1f) { + // sinus = .0261769483; + // cosinus = .99965732497; + // echantillons = 60; + //} + else { + sinus = .0348994967; + cosinus = .99939082701; + echantillons = 45; + } + + angle.x = direct.z + direct.x; + angle.z = direct.z - direct.x; + angle.y = 0; + angle.Normalize(); + + for (int radar = 0; radar < echantillons; ++radar) { + float x = angle.x; + + angle.x = angle.x * cosinus - angle.z * sinus; + angle.z = angle.z * cosinus + x * sinus; + angle.Normalize(); + + cursor = pos - direct * CHUNK_SIZE_X * 4 + angle * dist; + if (cursor.y >= 128.f || cursor.y >= 0.f) cursor.y = CHUNK_SIZE_Y / 4.f; + + + if (origin->ChunkAt(cursor)) { + bool valide = true; + unsigned int chx, chy; + origin->ChunkAt(cursor)->GetPosition(chx, chy); + for (int index = 0; index < rendercount; ++index) // Permet de vérifier seulement contre celles ajoutées dans la frame, et ne pas avoir à refaire l'array à chaque frame. + if (renderManifest[index].x == chx && renderManifest[index].z == chy) + valide = false; + + if (valide) renderManifest[rendercount++] = Vector3(chx, + (VIEW_DISTANCE - (pos - cursor).Length() * 3.f + 256.f) < 0.f ? 0 : + (VIEW_DISTANCE - (pos - cursor).Length() * 3.f + 256.f) * 1000, + chy); + } + } + } + shader.Use(); + atlas.Bind(); + glStencilFunc(GL_EQUAL, 1, 0x00); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + + unsigned int sx, sy, cx, cy; + origin->GetScope(sx,sy); + + for (int index = 0; index < rendercount; ++index) { + int chx = (renderManifest[index].x - sx) * CHUNK_SIZE_X, chy = (renderManifest[index].z - sy) * CHUNK_SIZE_Z; + + world.ApplyTranslation(chx, 0, chy); + glLoadMatrixf(world.GetMatrix().GetInternalValues()); + float blcolor = renderManifest[index].y / (VIEW_DISTANCE / 50.f); + glBlendColor(blcolor, blcolor, blcolor, 1.f); + origin->ChunkAt(chx, 1, chy)->GetPosition(cx,cy); + if (m_meshes.Get(cx - sx, cy - sy)) + m_meshes.Get(cx - sx, cy -sy)->Render(); + world.ApplyTranslation(-chx, 0, -chy); + } + shader.Disable(); + glStencilFunc(GL_GREATER, 1, 0xFF); +}; + +void WorldRenderer::UpdateWorld(World* origin, const Vector3f& player, BlockInfo* blockinfo[BTYPE_LAST]) { + int cx = player.x; + int cy = player.z; + static int frameUpdate = 2; + int side = 0; + int threads = 0; + std::future updateThList[THREADS_UPDATE_CHUNKS]; + + unsigned int mx = 0 , my = 0, sx, sy; + + origin->GetScope(sx, sy); + + if (frameUpdate > 0) --frameUpdate; + + if (!frameUpdate) + while (side * CHUNK_SIZE_X <= VIEW_DISTANCE * 2) { + int tx = -side, ty = -side; + + for (; tx <= side; ++tx) { + if (frameUpdate) + break; + unsigned int chx = cx + tx * CHUNK_SIZE_X, chy = cy + ty * CHUNK_SIZE_Z; + if (origin->ChunkAt(chx, 1, chy) && + origin->ChunkAt(chx, 1, chy)->IsDirty()) { + origin->ChunkAt(chx, 1, chy)->GetPosition(mx, my); + if (m_meshes.Get(mx - sx, my - sy)) + updateThList[threads++] = + std::async(std::launch::async, + [](Mesh* mesh, BlockInfo* blockinfo[BTYPE_LAST], World* world) { + mesh->Update(blockinfo, world); return mesh; }, m_meshes.Get(mx - sx, my - sy), blockinfo, origin); + else updateThList[threads++] = std::async(std::launch::async, + [](Chunk* chunk) { return new Mesh(chunk); }, origin->ChunkAt(chx, 1, chy)); + if (threads == THREADS_UPDATE_CHUNKS) frameUpdate = FRAMES_UPDATE_CHUNKS; + } + } + for (; ty <= side; ++ty) { + if (frameUpdate) + break; + unsigned int chx = cx + tx * CHUNK_SIZE_X, chy = cy + ty * CHUNK_SIZE_Z; + if (origin->ChunkAt(chx, 1, chy) && + origin->ChunkAt(chx, 1, chy)->IsDirty()) { + origin->ChunkAt(chx, 1, chy)->GetPosition(mx, my); + if (m_meshes.Get(mx - sx, my - sy)) + updateThList[threads++] = + std::async(std::launch::async, + [](Mesh* mesh, BlockInfo* blockinfo[BTYPE_LAST], World* world) { + mesh->Update(blockinfo, world); return mesh; }, m_meshes.Get(mx - sx, my - sy), blockinfo, origin); + else updateThList[threads++] = std::async(std::launch::async, + [](Chunk* chunk) { return new Mesh(chunk); }, origin->ChunkAt(chx, 1, chy)); + if (threads == THREADS_UPDATE_CHUNKS) frameUpdate = FRAMES_UPDATE_CHUNKS; + } + } + for (; tx >= -side; --tx) { + if (frameUpdate) + break; + unsigned int chx = cx + tx * CHUNK_SIZE_X, chy = cy + ty * CHUNK_SIZE_Z; + if (origin->ChunkAt(chx, 1, chy) && + origin->ChunkAt(chx, 1, chy)->IsDirty()) { + origin->ChunkAt(chx, 1, chy)->GetPosition(mx, my); + if (m_meshes.Get(mx - sx, my - sy)) + updateThList[threads++] = + std::async(std::launch::async, + [](Mesh* mesh, BlockInfo* blockinfo[BTYPE_LAST], World* world) { + mesh->Update(blockinfo, world); return mesh; }, m_meshes.Get(mx - sx, my - sy), blockinfo, origin); + else updateThList[threads++] = std::async(std::launch::async, + [](Chunk* chunk) { return new Mesh(chunk); }, origin->ChunkAt(chx, 1, chy)); + if (threads == THREADS_UPDATE_CHUNKS) frameUpdate = FRAMES_UPDATE_CHUNKS; + } + } + for (; ty >= -side; --ty) { + if (frameUpdate) + break; + unsigned int chx = cx + tx * CHUNK_SIZE_X, chy = cy + ty * CHUNK_SIZE_Z; + if (origin->ChunkAt(chx, 1, chy) && + origin->ChunkAt(chx, 1, chy)->IsDirty()) { + origin->ChunkAt(chx, 1, chy)->GetPosition(mx, my); + if (m_meshes.Get(mx - sx, my - sy)) + updateThList[threads++] = + std::async(std::launch::async, + [](Mesh* mesh, BlockInfo* blockinfo[BTYPE_LAST], World* world) { + mesh->Update(blockinfo, world); return mesh; }, m_meshes.Get(mx - sx, my - sy), blockinfo, origin); + else updateThList[threads++] = std::async(std::launch::async, + [](Chunk* chunk) { return new Mesh(chunk); }, origin->ChunkAt(chx, 1, chy)); + if (threads == THREADS_UPDATE_CHUNKS) frameUpdate = FRAMES_UPDATE_CHUNKS; + } + } + if (frameUpdate) + break; + ++side; + } + + if (threads > 0) { + for (int i = 0; i < threads; ++i) { + updateThList[i].wait(); + Mesh* mesh = updateThList[i].get(); + if (mesh->IsNew()) { + unsigned int x, y; + mesh->GetPosition(x, y, origin); + m_meshes.Set(x, y, mesh); + } + mesh->FlushMeshToVBO(); + } + } +} diff --git a/SQCSim2021/worldrenderer.h b/SQCSim2021/worldrenderer.h new file mode 100644 index 0000000..46e6624 --- /dev/null +++ b/SQCSim2021/worldrenderer.h @@ -0,0 +1,23 @@ +#ifndef WORLDRENDERER_H__ +#define WORLDRENDERER_H__ +#include +#include +#include "../SQCSim-common/world.h" +#include "../SQCSim-common/transformation.h" +#include "define.h" +#include "mesh.h" +#include "textureatlas.h" +#include "shader.h" + +class WorldRenderer { +private: + Array2d m_meshes = Array2d(WORLD_SIZE_X, WORLD_SIZE_Y); + +public: + WorldRenderer(); + ~WorldRenderer(); + + void RenderWorld(World* origin, int& rendercount, const Vector3f& player_pos, const Vector3f& player_dir, Transformation& world, Shader& shader, TextureAtlas& atlas); + void UpdateWorld(World* origin, const Vector3f& player, BlockInfo* blockinfo[BTYPE_LAST]); +}; +#endif \ No newline at end of file From ef8a05054560eebe09a00942cb8bd94f93f49418 Mon Sep 17 00:00:00 2001 From: MarcEricMartel Date: Sat, 30 Sep 2023 14:54:39 -0400 Subject: [PATCH 16/19] Cleanup un peu TOC --- SQCSim-common/bullet.h | 1 + SQCSim-common/chunk.h | 1 + SQCSim-common/netprotocol.h | 1 + SQCSim-common/player.h | 5 +++-- SQCSim-common/world.h | 1 + SQCSim-srv/connection.h | 1 + 6 files changed, 8 insertions(+), 2 deletions(-) diff --git a/SQCSim-common/bullet.h b/SQCSim-common/bullet.h index d17000e..e3ae989 100644 --- a/SQCSim-common/bullet.h +++ b/SQCSim-common/bullet.h @@ -1,5 +1,6 @@ #ifndef BULLET_H__ #define BULLET_H__ + #include "define.h" #include "vector3.h" diff --git a/SQCSim-common/chunk.h b/SQCSim-common/chunk.h index ea4a949..b053928 100644 --- a/SQCSim-common/chunk.h +++ b/SQCSim-common/chunk.h @@ -1,5 +1,6 @@ #ifndef CHUNK_H__ #define CHUNK_H__ + #include "define.h" #include "array2d.h" #include "array3d.h" diff --git a/SQCSim-common/netprotocol.h b/SQCSim-common/netprotocol.h index bdaf0a7..d7c7a93 100644 --- a/SQCSim-common/netprotocol.h +++ b/SQCSim-common/netprotocol.h @@ -1,5 +1,6 @@ #ifndef NETPROTOCOL_H__ #define NETPROTOCOL_H__ + #include "define.h" #include #include "vector3.h" diff --git a/SQCSim-common/player.h b/SQCSim-common/player.h index 3856ced..e439fa2 100644 --- a/SQCSim-common/player.h +++ b/SQCSim-common/player.h @@ -1,5 +1,6 @@ -#ifndef CLI_PLAYER_H__ -#define CLI_PLAYER_H__ +#ifndef PLAYER_H__ +#define PLAYER_H__ + #include #include "transformation.h" #include "vector3.h" diff --git a/SQCSim-common/world.h b/SQCSim-common/world.h index 69937aa..d341ee8 100644 --- a/SQCSim-common/world.h +++ b/SQCSim-common/world.h @@ -1,5 +1,6 @@ #ifndef WORLD_H__ #define WORLD_H__ + #include #include #include diff --git a/SQCSim-srv/connection.h b/SQCSim-srv/connection.h index dc1df11..6dee988 100644 --- a/SQCSim-srv/connection.h +++ b/SQCSim-srv/connection.h @@ -1,5 +1,6 @@ #ifndef CONNECTION_H__ #define CONNECTION_H__ + #include #include #include "../SQCSim-common/player.h" From 09dd3d332feebfa820c9fe1bd19b7b077c2eb390 Mon Sep 17 00:00:00 2001 From: MarcEricMartel Date: Sat, 30 Sep 2023 15:01:20 -0400 Subject: [PATCH 17/19] Update SQCSim2021.vcxproj --- SQCSim2021/SQCSim2021.vcxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SQCSim2021/SQCSim2021.vcxproj b/SQCSim2021/SQCSim2021.vcxproj index 8fb814c..abbdbc9 100644 --- a/SQCSim2021/SQCSim2021.vcxproj +++ b/SQCSim2021/SQCSim2021.vcxproj @@ -168,8 +168,8 @@ Fast - Console - true + Windows + false true true irrKlang.lib;sfml-main.lib;sfml-system.lib;sfml-window.lib;sfml-graphics.lib;GlU32.Lib;OpenGL32.Lib;DevIL.lib;ILU.lib;ILUT.lib;glew32.lib;%(AdditionalDependencies) @@ -190,8 +190,8 @@ Fast - Console - true + Windows + false true true irrKlang.lib;sfml-main.lib;sfml-system.lib;sfml-window.lib;sfml-graphics.lib;GlU32.Lib;OpenGL32.Lib;DevIL.lib;ILU.lib;ILUT.lib;glew32.lib;%(AdditionalDependencies) From 8b3baa9063837a77fb55b250121fb0187c9bc2f1 Mon Sep 17 00:00:00 2001 From: MarcEricMartel Date: Sat, 30 Sep 2023 18:33:23 -0400 Subject: [PATCH 18/19] Corrections fichiers pour avoir la version x86 fonctionnelle --- Debug/glew32.dll | Bin 331776 -> 389632 bytes Debug/openal32.dll | Bin 423936 -> 630784 bytes Debug/sfml-audio-d-2.dll | Bin 1388544 -> 1372672 bytes Debug/sfml-graphics-d-2.dll | Bin 1359872 -> 1435136 bytes Debug/sfml-network-d-2.dll | Bin 332800 -> 330752 bytes Debug/sfml-system-d-2.dll | Bin 202752 -> 188928 bytes Debug/sfml-window-d-2.dll | Bin 281600 -> 339968 bytes Release/glew32.dll | Bin 331776 -> 389632 bytes SQCSim-common/define.h | 2 +- SQCSim2021/SQCSim2021.vcxproj | 8 +- SQCSim2021/define.h | 2 + .../doc/html/AlResource_8hpp_source.html | 58 + .../doc/html/Audio_2Export_8hpp_source.html | 56 + .../doc/html/Audio_8hpp_source.html | 56 + .../doc/html/BlendMode_8hpp_source.html | 77 + .../doc/html/CircleShape_8hpp_source.html | 60 + .../doc/html/Clipboard_8hpp_source.html | 59 + .../doc/html/Clock_8hpp_source.html | 59 + .../doc/html/Color_8hpp_source.html | 71 + .../doc/html/Config_8hpp_source.html | 57 + .../doc/html/ContextSettings_8hpp_source.html | 70 + .../doc/html/Context_8hpp_source.html | 61 + .../doc/html/ConvexShape_8hpp_source.html | 60 + .../doc/html/Cursor_8hpp_source.html | 74 + .../doc/html/Drawable_8hpp_source.html | 61 + .../sfml251-32/doc/html/Err_8hpp_source.html | 58 + .../doc/html/Event_8hpp_source.html | 146 ++ .../doc/html/FileInputStream_8hpp_source.html | 60 + .../sfml251-32/doc/html/Font_8hpp_source.html | 63 + .../sfml251-32/doc/html/Ftp_8hpp_source.html | 70 + .../doc/html/GlResource_8hpp_source.html | 60 + .../sfml251-32/doc/html/Glsl_8hpp_source.html | 70 + .../doc/html/Glyph_8hpp_source.html | 63 + .../doc/html/GpuPreference_8hpp.html | 98 + .../doc/html/GpuPreference_8hpp_source.html | 56 + .../html/Graphics_2Export_8hpp_source.html | 56 + .../doc/html/Graphics_8hpp_source.html | 56 + .../sfml251-32/doc/html/Http_8hpp_source.html | 71 + .../doc/html/Image_8hpp_source.html | 62 + .../doc/html/InputSoundFile_8hpp_source.html | 62 + .../doc/html/InputStream_8hpp_source.html | 59 + .../doc/html/IpAddress_8hpp_source.html | 64 + .../doc/html/Joystick_8hpp_source.html | 71 + .../doc/html/Keyboard_8hpp_source.html | 160 ++ .../doc/html/Listener_8hpp_source.html | 59 + .../sfml251-32/doc/html/Lock_8hpp_source.html | 60 + .../sfml251-32/doc/html/Main_8hpp_source.html | 56 + .../html/MemoryInputStream_8hpp_source.html | 59 + .../doc/html/Mouse_8hpp_source.html | 68 + .../doc/html/Music_8hpp_source.html | 69 + .../doc/html/Mutex_8hpp_source.html | 59 + .../doc/html/NativeActivity_8hpp_source.html | 58 + .../doc/html/Network_2Export_8hpp_source.html | 56 + .../doc/html/Network_8hpp_source.html | 56 + .../doc/html/NonCopyable_8hpp_source.html | 60 + .../doc/html/OpenGL_8hpp_source.html | 56 + .../doc/html/OutputSoundFile_8hpp_source.html | 60 + .../doc/html/Packet_8hpp_source.html | 61 + .../doc/html/PrimitiveType_8hpp_source.html | 68 + .../sfml251-32/doc/html/Rect_8hpp_source.html | 66 + .../doc/html/RectangleShape_8hpp_source.html | 60 + .../doc/html/RenderStates_8hpp_source.html | 67 + .../doc/html/RenderTarget_8hpp_source.html | 73 + .../doc/html/RenderTexture_8hpp_source.html | 62 + .../doc/html/RenderWindow_8hpp_source.html | 67 + .../doc/html/Sensor_8hpp_source.html | 66 + .../doc/html/Shader_8hpp_source.html | 76 + .../doc/html/Shape_8hpp_source.html | 67 + .../doc/html/Sleep_8hpp_source.html | 58 + .../doc/html/SocketHandle_8hpp_source.html | 57 + .../doc/html/SocketSelector_8hpp_source.html | 61 + .../doc/html/Socket_8hpp_source.html | 67 + .../html/SoundBufferRecorder_8hpp_source.html | 60 + .../doc/html/SoundBuffer_8hpp_source.html | 63 + .../html/SoundFileFactory_8hpp_source.html | 61 + .../doc/html/SoundFileReader_8hpp_source.html | 64 + .../doc/html/SoundFileWriter_8hpp_source.html | 59 + .../doc/html/SoundRecorder_8hpp_source.html | 61 + .../doc/html/SoundSource_8hpp_source.html | 64 + .../doc/html/SoundStream_8hpp_source.html | 63 + .../doc/html/Sound_8hpp_source.html | 62 + .../doc/html/Sprite_8hpp_source.html | 66 + .../doc/html/String_8hpp_source.html | 61 + .../doc/html/System_2Export_8hpp_source.html | 56 + .../doc/html/System_8hpp_source.html | 56 + .../doc/html/TcpListener_8hpp_source.html | 63 + .../doc/html/TcpSocket_8hpp_source.html | 65 + .../sfml251-32/doc/html/Text_8hpp_source.html | 69 + .../doc/html/Texture_8hpp_source.html | 69 + .../doc/html/ThreadLocalPtr_8hpp_source.html | 63 + .../doc/html/ThreadLocal_8hpp_source.html | 59 + .../doc/html/Thread_8hpp_source.html | 59 + .../sfml251-32/doc/html/Time_8hpp_source.html | 59 + .../doc/html/Touch_8hpp_source.html | 60 + .../doc/html/Transform_8hpp_source.html | 61 + .../doc/html/Transformable_8hpp_source.html | 60 + .../doc/html/UdpSocket_8hpp_source.html | 62 + .../sfml251-32/doc/html/Utf_8hpp_source.html | 61 + .../doc/html/Vector2_8hpp_source.html | 61 + .../doc/html/Vector3_8hpp_source.html | 62 + .../doc/html/VertexArray_8hpp_source.html | 64 + .../doc/html/VertexBuffer_8hpp_source.html | 67 + .../doc/html/Vertex_8hpp_source.html | 63 + .../doc/html/VideoMode_8hpp_source.html | 61 + .../sfml251-32/doc/html/View_8hpp_source.html | 61 + .../doc/html/WindowHandle_8hpp_source.html | 58 + .../doc/html/WindowStyle_8hpp_source.html | 63 + .../doc/html/Window_2Export_8hpp_source.html | 56 + .../doc/html/Window_2Window_8hpp_source.html | 70 + .../doc/html/Window_8hpp_source.html | 56 + .../sfml251-32/doc/html/annotated.html | 162 ++ .../external/sfml251-32/doc/html/bc_s.png | Bin 0 -> 676 bytes .../external/sfml251-32/doc/html/bdwn.png | Bin 0 -> 147 bytes .../external/sfml251-32/doc/html/classes.html | 130 + .../html/classsf_1_1AlResource-members.html | 63 + .../doc/html/classsf_1_1AlResource.html | 154 ++ .../doc/html/classsf_1_1AlResource.png | Bin 0 -> 2327 bytes .../html/classsf_1_1CircleShape-members.html | 103 + .../doc/html/classsf_1_1CircleShape.html | 1478 ++++++++++++ .../doc/html/classsf_1_1CircleShape.png | Bin 0 -> 1009 bytes .../html/classsf_1_1Clipboard-members.html | 63 + .../doc/html/classsf_1_1Clipboard.html | 152 ++ .../doc/html/classsf_1_1Clock-members.html | 64 + .../sfml251-32/doc/html/classsf_1_1Clock.html | 154 ++ .../doc/html/classsf_1_1Color-members.html | 86 + .../sfml251-32/doc/html/classsf_1_1Color.html | 987 ++++++++ .../doc/html/classsf_1_1Context-members.html | 75 + .../doc/html/classsf_1_1Context.html | 392 +++ .../doc/html/classsf_1_1Context.png | Bin 0 -> 734 bytes .../html/classsf_1_1ConvexShape-members.html | 102 + .../doc/html/classsf_1_1ConvexShape.html | 1456 ++++++++++++ .../doc/html/classsf_1_1ConvexShape.png | Bin 0 -> 1022 bytes .../doc/html/classsf_1_1Cursor-members.html | 82 + .../doc/html/classsf_1_1Cursor.html | 335 +++ .../sfml251-32/doc/html/classsf_1_1Cursor.png | Bin 0 -> 506 bytes .../doc/html/classsf_1_1Drawable-members.html | 64 + .../doc/html/classsf_1_1Drawable.html | 191 ++ .../doc/html/classsf_1_1Drawable.png | Bin 0 -> 2175 bytes .../doc/html/classsf_1_1Event-members.html | 99 + .../sfml251-32/doc/html/classsf_1_1Event.html | 505 ++++ .../classsf_1_1FileInputStream-members.html | 71 + .../doc/html/classsf_1_1FileInputStream.html | 334 +++ .../doc/html/classsf_1_1FileInputStream.png | Bin 0 -> 842 bytes .../doc/html/classsf_1_1Font-members.html | 75 + .../sfml251-32/doc/html/classsf_1_1Font.html | 568 +++++ .../doc/html/classsf_1_1Ftp-members.html | 85 + .../sfml251-32/doc/html/classsf_1_1Ftp.html | 745 ++++++ .../sfml251-32/doc/html/classsf_1_1Ftp.png | Bin 0 -> 488 bytes ...f_1_1Ftp_1_1DirectoryResponse-members.html | 111 + .../classsf_1_1Ftp_1_1DirectoryResponse.html | 411 ++++ .../classsf_1_1Ftp_1_1DirectoryResponse.png | Bin 0 -> 686 bytes ...ssf_1_1Ftp_1_1ListingResponse-members.html | 111 + .../classsf_1_1Ftp_1_1ListingResponse.html | 422 ++++ .../classsf_1_1Ftp_1_1ListingResponse.png | Bin 0 -> 659 bytes .../classsf_1_1Ftp_1_1Response-members.html | 109 + .../doc/html/classsf_1_1Ftp_1_1Response.html | 377 +++ .../doc/html/classsf_1_1Ftp_1_1Response.png | Bin 0 -> 970 bytes .../html/classsf_1_1GlResource-members.html | 64 + .../doc/html/classsf_1_1GlResource.html | 214 ++ .../doc/html/classsf_1_1GlResource.png | Bin 0 -> 1764 bytes ...ource_1_1TransientContextLock-members.html | 65 + ...1_1GlResource_1_1TransientContextLock.html | 131 + ..._1_1GlResource_1_1TransientContextLock.png | Bin 0 -> 769 bytes .../doc/html/classsf_1_1Glyph-members.html | 65 + .../sfml251-32/doc/html/classsf_1_1Glyph.html | 185 ++ .../doc/html/classsf_1_1Http-members.html | 67 + .../sfml251-32/doc/html/classsf_1_1Http.html | 263 ++ .../sfml251-32/doc/html/classsf_1_1Http.png | Bin 0 -> 489 bytes .../classsf_1_1Http_1_1Request-members.html | 74 + .../doc/html/classsf_1_1Http_1_1Request.html | 352 +++ .../classsf_1_1Http_1_1Response-members.html | 92 + .../doc/html/classsf_1_1Http_1_1Response.html | 345 +++ .../doc/html/classsf_1_1Image-members.html | 77 + .../sfml251-32/doc/html/classsf_1_1Image.html | 650 +++++ .../classsf_1_1InputSoundFile-members.html | 77 + .../doc/html/classsf_1_1InputSoundFile.html | 481 ++++ .../doc/html/classsf_1_1InputSoundFile.png | Bin 0 -> 559 bytes .../html/classsf_1_1InputStream-members.html | 66 + .../doc/html/classsf_1_1InputStream.html | 281 +++ .../doc/html/classsf_1_1InputStream.png | Bin 0 -> 911 bytes .../html/classsf_1_1IpAddress-members.html | 75 + .../doc/html/classsf_1_1IpAddress.html | 561 +++++ .../doc/html/classsf_1_1Joystick-members.html | 80 + .../doc/html/classsf_1_1Joystick.html | 478 ++++ .../doc/html/classsf_1_1Keyboard-members.html | 172 ++ .../doc/html/classsf_1_1Keyboard.html | 539 +++++ .../doc/html/classsf_1_1Listener-members.html | 72 + .../doc/html/classsf_1_1Listener.html | 537 +++++ .../doc/html/classsf_1_1Lock-members.html | 65 + .../sfml251-32/doc/html/classsf_1_1Lock.html | 154 ++ .../sfml251-32/doc/html/classsf_1_1Lock.png | Bin 0 -> 491 bytes .../classsf_1_1MemoryInputStream-members.html | 68 + .../html/classsf_1_1MemoryInputStream.html | 313 +++ .../doc/html/classsf_1_1MemoryInputStream.png | Bin 0 -> 657 bytes .../doc/html/classsf_1_1Mouse-members.html | 76 + .../sfml251-32/doc/html/classsf_1_1Mouse.html | 357 +++ .../doc/html/classsf_1_1Music-members.html | 109 + .../sfml251-32/doc/html/classsf_1_1Music.html | 1445 +++++++++++ .../sfml251-32/doc/html/classsf_1_1Music.png | Bin 0 -> 919 bytes .../doc/html/classsf_1_1Mutex-members.html | 67 + .../sfml251-32/doc/html/classsf_1_1Mutex.html | 186 ++ .../sfml251-32/doc/html/classsf_1_1Mutex.png | Bin 0 -> 511 bytes .../html/classsf_1_1NonCopyable-members.html | 63 + .../doc/html/classsf_1_1NonCopyable.html | 172 ++ .../doc/html/classsf_1_1NonCopyable.png | Bin 0 -> 6259 bytes .../classsf_1_1OutputSoundFile-members.html | 67 + .../doc/html/classsf_1_1OutputSoundFile.html | 225 ++ .../doc/html/classsf_1_1OutputSoundFile.png | Bin 0 -> 576 bytes .../doc/html/classsf_1_1Packet-members.html | 105 + .../doc/html/classsf_1_1Packet.html | 1156 +++++++++ .../doc/html/classsf_1_1Rect-members.html | 75 + .../sfml251-32/doc/html/classsf_1_1Rect.html | 630 +++++ .../classsf_1_1RectangleShape-members.html | 102 + .../doc/html/classsf_1_1RectangleShape.html | 1434 +++++++++++ .../doc/html/classsf_1_1RectangleShape.png | Bin 0 -> 1060 bytes .../html/classsf_1_1RenderStates-members.html | 72 + .../doc/html/classsf_1_1RenderStates.html | 409 ++++ .../html/classsf_1_1RenderTarget-members.html | 84 + .../doc/html/classsf_1_1RenderTarget.html | 822 +++++++ .../doc/html/classsf_1_1RenderTarget.png | Bin 0 -> 1080 bytes .../classsf_1_1RenderTexture-members.html | 94 + .../doc/html/classsf_1_1RenderTexture.html | 1264 ++++++++++ .../doc/html/classsf_1_1RenderTexture.png | Bin 0 -> 814 bytes .../html/classsf_1_1RenderWindow-members.html | 120 + .../doc/html/classsf_1_1RenderWindow.html | 2116 +++++++++++++++++ .../doc/html/classsf_1_1RenderWindow.png | Bin 0 -> 1416 bytes .../doc/html/classsf_1_1Sensor-members.html | 72 + .../doc/html/classsf_1_1Sensor.html | 262 ++ .../doc/html/classsf_1_1Shader-members.html | 118 + .../doc/html/classsf_1_1Shader.html | 2092 ++++++++++++++++ .../sfml251-32/doc/html/classsf_1_1Shader.png | Bin 0 -> 748 bytes .../doc/html/classsf_1_1Shape-members.html | 99 + .../sfml251-32/doc/html/classsf_1_1Shape.html | 1328 +++++++++++ .../sfml251-32/doc/html/classsf_1_1Shape.png | Bin 0 -> 1486 bytes .../doc/html/classsf_1_1Socket-members.html | 82 + .../doc/html/classsf_1_1Socket.html | 476 ++++ .../sfml251-32/doc/html/classsf_1_1Socket.png | Bin 0 -> 1245 bytes .../classsf_1_1SocketSelector-members.html | 70 + .../doc/html/classsf_1_1SocketSelector.html | 346 +++ .../doc/html/classsf_1_1Sound-members.html | 99 + .../sfml251-32/doc/html/classsf_1_1Sound.html | 1123 +++++++++ .../sfml251-32/doc/html/classsf_1_1Sound.png | Bin 0 -> 691 bytes .../html/classsf_1_1SoundBuffer-members.html | 78 + .../doc/html/classsf_1_1SoundBuffer.html | 524 ++++ .../doc/html/classsf_1_1SoundBuffer.png | Bin 0 -> 509 bytes ...lasssf_1_1SoundBufferRecorder-members.html | 79 + .../html/classsf_1_1SoundBufferRecorder.html | 645 +++++ .../html/classsf_1_1SoundBufferRecorder.png | Bin 0 -> 903 bytes .../classsf_1_1SoundFileFactory-members.html | 69 + .../doc/html/classsf_1_1SoundFileFactory.html | 389 +++ .../classsf_1_1SoundFileReader-members.html | 65 + .../doc/html/classsf_1_1SoundFileReader.html | 259 ++ .../classsf_1_1SoundFileWriter-members.html | 64 + .../doc/html/classsf_1_1SoundFileWriter.html | 219 ++ .../classsf_1_1SoundRecorder-members.html | 79 + .../doc/html/classsf_1_1SoundRecorder.html | 618 +++++ .../doc/html/classsf_1_1SoundRecorder.png | Bin 0 -> 906 bytes .../html/classsf_1_1SoundSource-members.html | 89 + .../doc/html/classsf_1_1SoundSource.html | 804 +++++++ .../doc/html/classsf_1_1SoundSource.png | Bin 0 -> 1194 bytes .../html/classsf_1_1SoundStream-members.html | 100 + .../doc/html/classsf_1_1SoundStream.html | 1227 ++++++++++ .../doc/html/classsf_1_1SoundStream.png | Bin 0 -> 926 bytes .../doc/html/classsf_1_1Sprite-members.html | 93 + .../doc/html/classsf_1_1Sprite.html | 1133 +++++++++ .../sfml251-32/doc/html/classsf_1_1Sprite.png | Bin 0 -> 734 bytes .../doc/html/classsf_1_1String-members.html | 112 + .../doc/html/classsf_1_1String.html | 1789 ++++++++++++++ .../html/classsf_1_1TcpListener-members.html | 83 + .../doc/html/classsf_1_1TcpListener.html | 547 +++++ .../doc/html/classsf_1_1TcpListener.png | Bin 0 -> 715 bytes .../html/classsf_1_1TcpSocket-members.html | 91 + .../doc/html/classsf_1_1TcpSocket.html | 819 +++++++ .../doc/html/classsf_1_1TcpSocket.png | Bin 0 -> 722 bytes .../doc/html/classsf_1_1Text-members.html | 113 + .../sfml251-32/doc/html/classsf_1_1Text.html | 1557 ++++++++++++ .../sfml251-32/doc/html/classsf_1_1Text.png | Bin 0 -> 710 bytes .../doc/html/classsf_1_1Texture-members.html | 100 + .../doc/html/classsf_1_1Texture.html | 1201 ++++++++++ .../doc/html/classsf_1_1Texture.png | Bin 0 -> 475 bytes .../doc/html/classsf_1_1Thread-members.html | 70 + .../doc/html/classsf_1_1Thread.html | 316 +++ .../sfml251-32/doc/html/classsf_1_1Thread.png | Bin 0 -> 501 bytes .../html/classsf_1_1ThreadLocal-members.html | 67 + .../doc/html/classsf_1_1ThreadLocal.html | 194 ++ .../doc/html/classsf_1_1ThreadLocal.png | Bin 0 -> 878 bytes .../classsf_1_1ThreadLocalPtr-members.html | 71 + .../doc/html/classsf_1_1ThreadLocalPtr.html | 277 +++ .../doc/html/classsf_1_1ThreadLocalPtr.png | Bin 0 -> 597 bytes .../doc/html/classsf_1_1Time-members.html | 96 + .../sfml251-32/doc/html/classsf_1_1Time.html | 1516 ++++++++++++ .../doc/html/classsf_1_1Touch-members.html | 64 + .../sfml251-32/doc/html/classsf_1_1Touch.html | 211 ++ .../html/classsf_1_1Transform-members.html | 84 + .../doc/html/classsf_1_1Transform.html | 1038 ++++++++ .../classsf_1_1Transformable-members.html | 81 + .../doc/html/classsf_1_1Transformable.html | 723 ++++++ .../doc/html/classsf_1_1Transformable.png | Bin 0 -> 1671 bytes .../html/classsf_1_1UdpSocket-members.html | 88 + .../doc/html/classsf_1_1UdpSocket.html | 793 ++++++ .../doc/html/classsf_1_1UdpSocket.png | Bin 0 -> 723 bytes .../sfml251-32/doc/html/classsf_1_1Utf.html | 80 + .../classsf_1_1Utf_3_0116_01_4-members.html | 74 + .../doc/html/classsf_1_1Utf_3_0116_01_4.html | 883 +++++++ .../classsf_1_1Utf_3_0132_01_4-members.html | 78 + .../doc/html/classsf_1_1Utf_3_0132_01_4.html | 1105 +++++++++ .../classsf_1_1Utf_3_018_01_4-members.html | 74 + .../doc/html/classsf_1_1Utf_3_018_01_4.html | 883 +++++++ .../doc/html/classsf_1_1Vector2-members.html | 78 + .../doc/html/classsf_1_1Vector2.html | 878 +++++++ .../doc/html/classsf_1_1Vector3-members.html | 79 + .../doc/html/classsf_1_1Vector3.html | 907 +++++++ .../doc/html/classsf_1_1Vertex-members.html | 69 + .../doc/html/classsf_1_1Vertex.html | 333 +++ .../html/classsf_1_1VertexArray-members.html | 73 + .../doc/html/classsf_1_1VertexArray.html | 413 ++++ .../doc/html/classsf_1_1VertexArray.png | Bin 0 -> 512 bytes .../html/classsf_1_1VertexBuffer-members.html | 89 + .../doc/html/classsf_1_1VertexBuffer.html | 772 ++++++ .../doc/html/classsf_1_1VertexBuffer.png | Bin 0 -> 753 bytes .../html/classsf_1_1VideoMode-members.html | 75 + .../doc/html/classsf_1_1VideoMode.html | 615 +++++ .../doc/html/classsf_1_1View-members.html | 81 + .../sfml251-32/doc/html/classsf_1_1View.html | 703 ++++++ .../doc/html/classsf_1_1Window-members.html | 98 + .../doc/html/classsf_1_1Window.html | 1130 +++++++++ .../sfml251-32/doc/html/classsf_1_1Window.png | Bin 0 -> 1018 bytes .../external/sfml251-32/doc/html/closed.png | Bin 0 -> 132 bytes .../sfml251-32/doc/html/deprecated.html | 94 + .../dir_5cf786e58cbf7297a26339ae6e44357c.html | 56 + .../dir_83d50c0b1f1eceb6f182949162e90861.html | 56 + .../dir_89e9fb32471ae291b179a889144513db.html | 56 + .../dir_c0a853e81d6f1c1f0a3eb7a27dc24256.html | 66 + .../dir_d44c64559bbebec7f509842c48db8b23.html | 60 + .../dir_dd49ddb3ba8035e4a328f8c5f31cda7e.html | 56 + .../dir_e68e8157741866f444e17edd764ebbae.html | 56 + .../dir_e71ec51a9abd604c65f6abb639f6ea75.html | 56 + .../external/sfml251-32/doc/html/doc.png | Bin 0 -> 746 bytes .../external/sfml251-32/doc/html/doxygen.css | 1450 +++++++++++ .../external/sfml251-32/doc/html/doxygen.png | Bin 0 -> 3779 bytes .../sfml251-32/doc/html/dynsections.js | 127 + .../external/sfml251-32/doc/html/files.html | 155 ++ .../sfml251-32/doc/html/folderclosed.png | Bin 0 -> 616 bytes .../sfml251-32/doc/html/folderopen.png | Bin 0 -> 597 bytes .../sfml251-32/doc/html/functions.html | 182 ++ .../sfml251-32/doc/html/functions_0x7e.html | 213 ++ .../sfml251-32/doc/html/functions_b.html | 170 ++ .../sfml251-32/doc/html/functions_c.html | 257 ++ .../sfml251-32/doc/html/functions_d.html | 191 ++ .../sfml251-32/doc/html/functions_e.html | 146 ++ .../sfml251-32/doc/html/functions_enum.html | 114 + .../sfml251-32/doc/html/functions_eval.html | 123 + .../sfml251-32/doc/html/functions_eval_b.html | 129 + .../sfml251-32/doc/html/functions_eval_c.html | 137 ++ .../sfml251-32/doc/html/functions_eval_d.html | 147 ++ .../sfml251-32/doc/html/functions_eval_e.html | 119 + .../sfml251-32/doc/html/functions_eval_f.html | 164 ++ .../sfml251-32/doc/html/functions_eval_g.html | 116 + .../sfml251-32/doc/html/functions_eval_h.html | 119 + .../sfml251-32/doc/html/functions_eval_i.html | 117 + .../sfml251-32/doc/html/functions_eval_j.html | 113 + .../sfml251-32/doc/html/functions_eval_k.html | 107 + .../sfml251-32/doc/html/functions_eval_l.html | 126 + .../sfml251-32/doc/html/functions_eval_m.html | 143 ++ .../sfml251-32/doc/html/functions_eval_n.html | 200 ++ .../sfml251-32/doc/html/functions_eval_o.html | 123 + .../sfml251-32/doc/html/functions_eval_p.html | 149 ++ .../sfml251-32/doc/html/functions_eval_q.html | 101 + .../sfml251-32/doc/html/functions_eval_r.html | 139 ++ .../sfml251-32/doc/html/functions_eval_s.html | 168 ++ .../sfml251-32/doc/html/functions_eval_t.html | 125 + .../sfml251-32/doc/html/functions_eval_u.html | 117 + .../sfml251-32/doc/html/functions_eval_v.html | 108 + .../sfml251-32/doc/html/functions_eval_w.html | 101 + .../sfml251-32/doc/html/functions_eval_x.html | 105 + .../sfml251-32/doc/html/functions_eval_y.html | 99 + .../sfml251-32/doc/html/functions_eval_z.html | 102 + .../sfml251-32/doc/html/functions_f.html | 216 ++ .../sfml251-32/doc/html/functions_func.html | 114 + .../doc/html/functions_func_0x7e.html | 209 ++ .../sfml251-32/doc/html/functions_func_b.html | 104 + .../sfml251-32/doc/html/functions_func_c.html | 178 ++ .../sfml251-32/doc/html/functions_func_d.html | 127 + .../sfml251-32/doc/html/functions_func_e.html | 112 + .../sfml251-32/doc/html/functions_func_f.html | 134 ++ .../sfml251-32/doc/html/functions_func_g.html | 452 ++++ .../sfml251-32/doc/html/functions_func_h.html | 101 + .../sfml251-32/doc/html/functions_func_i.html | 168 ++ .../sfml251-32/doc/html/functions_func_k.html | 95 + .../sfml251-32/doc/html/functions_func_l.html | 143 ++ .../sfml251-32/doc/html/functions_func_m.html | 117 + .../sfml251-32/doc/html/functions_func_n.html | 100 + .../sfml251-32/doc/html/functions_func_o.html | 287 +++ .../sfml251-32/doc/html/functions_func_p.html | 117 + .../sfml251-32/doc/html/functions_func_r.html | 169 ++ .../sfml251-32/doc/html/functions_func_s.html | 400 ++++ .../sfml251-32/doc/html/functions_func_t.html | 188 ++ .../sfml251-32/doc/html/functions_func_u.html | 116 + .../sfml251-32/doc/html/functions_func_v.html | 113 + .../sfml251-32/doc/html/functions_func_w.html | 106 + .../sfml251-32/doc/html/functions_func_z.html | 95 + .../sfml251-32/doc/html/functions_g.html | 483 ++++ .../sfml251-32/doc/html/functions_h.html | 134 ++ .../sfml251-32/doc/html/functions_i.html | 203 ++ .../sfml251-32/doc/html/functions_j.html | 128 + .../sfml251-32/doc/html/functions_k.html | 117 + .../sfml251-32/doc/html/functions_l.html | 187 ++ .../sfml251-32/doc/html/functions_m.html | 196 ++ .../sfml251-32/doc/html/functions_n.html | 215 ++ .../sfml251-32/doc/html/functions_o.html | 323 +++ .../sfml251-32/doc/html/functions_p.html | 182 ++ .../sfml251-32/doc/html/functions_q.html | 102 + .../sfml251-32/doc/html/functions_r.html | 223 ++ .../sfml251-32/doc/html/functions_rela.html | 66 + .../sfml251-32/doc/html/functions_s.html | 517 ++++ .../sfml251-32/doc/html/functions_t.html | 261 ++ .../sfml251-32/doc/html/functions_type.html | 69 + .../sfml251-32/doc/html/functions_u.html | 148 ++ .../sfml251-32/doc/html/functions_v.html | 131 + .../sfml251-32/doc/html/functions_vars.html | 478 ++++ .../sfml251-32/doc/html/functions_w.html | 130 + .../sfml251-32/doc/html/functions_x.html | 118 + .../sfml251-32/doc/html/functions_y.html | 115 + .../sfml251-32/doc/html/functions_z.html | 113 + .../external/sfml251-32/doc/html/globals.html | 59 + .../sfml251-32/doc/html/globals_defs.html | 59 + .../sfml251-32/doc/html/group__audio.html | 99 + .../sfml251-32/doc/html/group__graphics.html | 202 ++ .../sfml251-32/doc/html/group__network.html | 84 + .../sfml251-32/doc/html/group__system.html | 192 ++ .../sfml251-32/doc/html/group__window.html | 187 ++ .../sfml251-32/doc/html/hierarchy.html | 175 ++ .../external/sfml251-32/doc/html/index.html | 53 + .../external/sfml251-32/doc/html/jquery.js | 115 + .../doc/html/mainpage_8hpp_source.html | 56 + .../external/sfml251-32/doc/html/menudata.js | 149 ++ .../external/sfml251-32/doc/html/modules.html | 55 + .../sfml251-32/doc/html/namespacemembers.html | 89 + .../doc/html/namespacemembers_type.html | 89 + .../sfml251-32/doc/html/namespaces.html | 58 + .../doc/html/namespacesf_1_1Glsl.html | 304 +++ .../external/sfml251-32/doc/html/nav_f.png | Bin 0 -> 153 bytes .../external/sfml251-32/doc/html/nav_g.png | Bin 0 -> 95 bytes .../external/sfml251-32/doc/html/nav_h.png | Bin 0 -> 98 bytes .../external/sfml251-32/doc/html/open.png | Bin 0 -> 123 bytes .../external/sfml251-32/doc/html/pages.html | 51 + .../external/sfml251-32/doc/html/splitbar.png | Bin 0 -> 314 bytes .../html/structsf_1_1BlendMode-members.html | 87 + .../doc/html/structsf_1_1BlendMode.html | 565 +++++ .../structsf_1_1ContextSettings-members.html | 73 + .../doc/html/structsf_1_1ContextSettings.html | 361 +++ ...1Event_1_1JoystickButtonEvent-members.html | 63 + ...uctsf_1_1Event_1_1JoystickButtonEvent.html | 120 + ...Event_1_1JoystickConnectEvent-members.html | 62 + ...ctsf_1_1Event_1_1JoystickConnectEvent.html | 99 + ...1_1Event_1_1JoystickMoveEvent-members.html | 64 + ...tructsf_1_1Event_1_1JoystickMoveEvent.html | 141 ++ ...structsf_1_1Event_1_1KeyEvent-members.html | 66 + .../html/structsf_1_1Event_1_1KeyEvent.html | 183 ++ ..._1_1Event_1_1MouseButtonEvent-members.html | 64 + ...structsf_1_1Event_1_1MouseButtonEvent.html | 141 ++ ...sf_1_1Event_1_1MouseMoveEvent-members.html | 63 + .../structsf_1_1Event_1_1MouseMoveEvent.html | 120 + ...f_1_1Event_1_1MouseWheelEvent-members.html | 64 + .../structsf_1_1Event_1_1MouseWheelEvent.html | 142 ++ ...vent_1_1MouseWheelScrollEvent-members.html | 65 + ...tsf_1_1Event_1_1MouseWheelScrollEvent.html | 162 ++ ...uctsf_1_1Event_1_1SensorEvent-members.html | 65 + .../structsf_1_1Event_1_1SensorEvent.html | 162 ++ ...tructsf_1_1Event_1_1SizeEvent-members.html | 63 + .../html/structsf_1_1Event_1_1SizeEvent.html | 120 + ...tructsf_1_1Event_1_1TextEvent-members.html | 62 + .../html/structsf_1_1Event_1_1TextEvent.html | 99 + ...ructsf_1_1Event_1_1TouchEvent-members.html | 64 + .../html/structsf_1_1Event_1_1TouchEvent.html | 141 ++ .../structsf_1_1Font_1_1Info-members.html | 62 + .../doc/html/structsf_1_1Font_1_1Info.html | 99 + ...1_1Joystick_1_1Identification-members.html | 65 + ...tructsf_1_1Joystick_1_1Identification.html | 141 ++ .../structsf_1_1Music_1_1Span-members.html | 65 + .../doc/html/structsf_1_1Music_1_1Span.html | 218 ++ ...uctsf_1_1Shader_1_1CurrentTextureType.html | 71 + ...sf_1_1SoundFileReader_1_1Info-members.html | 64 + .../structsf_1_1SoundFileReader_1_1Info.html | 141 ++ ...uctsf_1_1SoundStream_1_1Chunk-members.html | 63 + .../structsf_1_1SoundStream_1_1Chunk.html | 120 + .../external/sfml251-32/doc/html/sync_off.png | Bin 0 -> 853 bytes .../external/sfml251-32/doc/html/sync_on.png | Bin 0 -> 845 bytes .../external/sfml251-32/doc/html/tab_a.png | Bin 0 -> 142 bytes .../external/sfml251-32/doc/html/tab_b.png | Bin 0 -> 169 bytes .../external/sfml251-32/doc/html/tab_h.png | Bin 0 -> 177 bytes .../external/sfml251-32/doc/html/tab_s.png | Bin 0 -> 184 bytes .../external/sfml251-32/doc/html/tabs.css | 61 + .../external/sfml251-32/examples/ftp/Ftp.cpp | 206 ++ .../external/sfml251-32/examples/ftp/ftp.exe | Bin 0 -> 66048 bytes .../sfml251-32/examples/island/Island.cpp | 590 +++++ .../sfml251-32/examples/island/island.exe | Bin 0 -> 591872 bytes .../examples/island/resources/sansation.ttf | Bin 0 -> 28912 bytes .../examples/island/resources/terrain.frag | 11 + .../examples/island/resources/terrain.vert | 8 + .../sfml251-32/examples/joystick/Joystick.cpp | 238 ++ .../sfml251-32/examples/joystick/joystick.exe | Bin 0 -> 581632 bytes .../examples/joystick/resources/sansation.ttf | Bin 0 -> 28912 bytes .../sfml251-32/examples/opengl/OpenGL.cpp | 258 ++ .../sfml251-32/examples/opengl/opengl.exe | Bin 0 -> 623104 bytes .../examples/opengl/resources/background.jpg | Bin 0 -> 142959 bytes .../examples/opengl/resources/sansation.ttf | Bin 0 -> 28912 bytes .../examples/opengl/resources/texture.jpg | Bin 0 -> 20550 bytes .../sfml251-32/examples/pong/Pong.cpp | 242 ++ .../sfml251-32/examples/pong/pong.exe | Bin 0 -> 1429504 bytes .../examples/pong/resources/ball.wav | Bin 0 -> 15442 bytes .../examples/pong/resources/sansation.ttf | Bin 0 -> 28912 bytes .../sfml251-32/examples/shader/Effect.hpp | 88 + .../sfml251-32/examples/shader/Shader.cpp | 460 ++++ .../examples/shader/resources/background.jpg | Bin 0 -> 53507 bytes .../examples/shader/resources/billboard.frag | 11 + .../examples/shader/resources/billboard.geom | 56 + .../examples/shader/resources/billboard.vert | 5 + .../examples/shader/resources/blink.frag | 9 + .../examples/shader/resources/blur.frag | 20 + .../examples/shader/resources/devices.png | Bin 0 -> 51410 bytes .../examples/shader/resources/edge.frag | 32 + .../examples/shader/resources/logo.png | Bin 0 -> 8849 bytes .../examples/shader/resources/pixelate.frag | 9 + .../examples/shader/resources/sansation.ttf | Bin 0 -> 28912 bytes .../examples/shader/resources/sfml.png | Bin 0 -> 25973 bytes .../examples/shader/resources/storm.vert | 19 + .../shader/resources/text-background.png | Bin 0 -> 745 bytes .../examples/shader/resources/wave.vert | 15 + .../sfml251-32/examples/shader/shader.exe | Bin 0 -> 667136 bytes .../sfml251-32/examples/sockets/Sockets.cpp | 59 + .../sfml251-32/examples/sockets/TCP.cpp | 81 + .../sfml251-32/examples/sockets/UDP.cpp | 72 + .../sfml251-32/examples/sockets/sockets.exe | Bin 0 -> 33280 bytes .../examples/sound-capture/SoundCapture.cpp | 94 + .../examples/sound-capture/sound-capture.exe | Bin 0 -> 892928 bytes .../sfml251-32/examples/sound/Sound.cpp | 101 + .../examples/sound/resources/canary.wav | Bin 0 -> 63504 bytes .../examples/sound/resources/ding.flac | Bin 0 -> 61764 bytes .../examples/sound/resources/orchestral.ogg | Bin 0 -> 153776 bytes .../sfml251-32/examples/sound/sound.exe | Bin 0 -> 893952 bytes .../sfml251-32/examples/voip/Client.cpp | 141 ++ .../sfml251-32/examples/voip/Server.cpp | 200 ++ .../sfml251-32/examples/voip/VoIP.cpp | 50 + .../sfml251-32/examples/voip/voip.exe | Bin 0 -> 48640 bytes .../sfml251-32/examples/window/Window.cpp | 146 ++ .../sfml251-32/examples/window/window.exe | Bin 0 -> 95232 bytes .../sfml251-32/include/SFML/Audio.hpp | 56 + .../include/SFML/Audio/AlResource.hpp | 70 + .../sfml251-32/include/SFML/Audio/Export.hpp | 48 + .../include/SFML/Audio/InputSoundFile.hpp | 263 ++ .../include/SFML/Audio/Listener.hpp | 234 ++ .../sfml251-32/include/SFML/Audio/Music.hpp | 337 +++ .../include/SFML/Audio/OutputSoundFile.hpp | 133 ++ .../sfml251-32/include/SFML/Audio/Sound.hpp | 264 ++ .../include/SFML/Audio/SoundBuffer.hpp | 352 +++ .../SFML/Audio/SoundBufferRecorder.hpp | 144 ++ .../include/SFML/Audio/SoundFileFactory.hpp | 197 ++ .../include/SFML/Audio/SoundFileFactory.inl | 100 + .../include/SFML/Audio/SoundFileReader.hpp | 165 ++ .../include/SFML/Audio/SoundFileWriter.hpp | 125 + .../include/SFML/Audio/SoundRecorder.hpp | 408 ++++ .../include/SFML/Audio/SoundSource.hpp | 332 +++ .../include/SFML/Audio/SoundStream.hpp | 405 ++++ .../sfml251-32/include/SFML/Config.hpp | 236 ++ .../sfml251-32/include/SFML/GpuPreference.hpp | 74 + .../sfml251-32/include/SFML/Graphics.hpp | 68 + .../include/SFML/Graphics/BlendMode.hpp | 215 ++ .../include/SFML/Graphics/CircleShape.hpp | 154 ++ .../include/SFML/Graphics/Color.hpp | 275 +++ .../include/SFML/Graphics/ConvexShape.hpp | 153 ++ .../include/SFML/Graphics/Drawable.hpp | 126 + .../include/SFML/Graphics/Export.hpp | 48 + .../sfml251-32/include/SFML/Graphics/Font.hpp | 439 ++++ .../sfml251-32/include/SFML/Graphics/Glsl.hpp | 227 ++ .../sfml251-32/include/SFML/Graphics/Glsl.inl | 155 ++ .../include/SFML/Graphics/Glyph.hpp | 79 + .../include/SFML/Graphics/Image.hpp | 324 +++ .../include/SFML/Graphics/PrimitiveType.hpp | 58 + .../sfml251-32/include/SFML/Graphics/Rect.hpp | 254 ++ .../sfml251-32/include/SFML/Graphics/Rect.inl | 159 ++ .../include/SFML/Graphics/RectangleShape.hpp | 132 + .../include/SFML/Graphics/RenderStates.hpp | 174 ++ .../include/SFML/Graphics/RenderTarget.hpp | 510 ++++ .../include/SFML/Graphics/RenderTexture.hpp | 314 +++ .../include/SFML/Graphics/RenderWindow.hpp | 284 +++ .../include/SFML/Graphics/Shader.hpp | 875 +++++++ .../include/SFML/Graphics/Shape.hpp | 355 +++ .../include/SFML/Graphics/Sprite.hpp | 279 +++ .../sfml251-32/include/SFML/Graphics/Text.hpp | 513 ++++ .../include/SFML/Graphics/Texture.hpp | 733 ++++++ .../include/SFML/Graphics/Transform.hpp | 479 ++++ .../include/SFML/Graphics/Transformable.hpp | 429 ++++ .../include/SFML/Graphics/Vertex.hpp | 148 ++ .../include/SFML/Graphics/VertexArray.hpp | 223 ++ .../include/SFML/Graphics/VertexBuffer.hpp | 408 ++++ .../sfml251-32/include/SFML/Graphics/View.hpp | 343 +++ .../external/sfml251-32/include/SFML/Main.hpp | 43 + .../sfml251-32/include/SFML/Network.hpp | 53 + .../include/SFML/Network/Export.hpp | 48 + .../sfml251-32/include/SFML/Network/Ftp.hpp | 616 +++++ .../sfml251-32/include/SFML/Network/Http.hpp | 482 ++++ .../include/SFML/Network/IpAddress.hpp | 328 +++ .../include/SFML/Network/Packet.hpp | 532 +++++ .../include/SFML/Network/Socket.hpp | 219 ++ .../include/SFML/Network/SocketHandle.hpp | 57 + .../include/SFML/Network/SocketSelector.hpp | 263 ++ .../include/SFML/Network/TcpListener.hpp | 166 ++ .../include/SFML/Network/TcpSocket.hpp | 316 +++ .../include/SFML/Network/UdpSocket.hpp | 291 +++ .../sfml251-32/include/SFML/OpenGL.hpp | 78 + .../sfml251-32/include/SFML/System.hpp | 60 + .../sfml251-32/include/SFML/System/Clock.hpp | 117 + .../sfml251-32/include/SFML/System/Err.hpp | 80 + .../sfml251-32/include/SFML/System/Export.hpp | 48 + .../include/SFML/System/FileInputStream.hpp | 169 ++ .../include/SFML/System/InputStream.hpp | 152 ++ .../sfml251-32/include/SFML/System/Lock.hpp | 139 ++ .../include/SFML/System/MemoryInputStream.hpp | 148 ++ .../sfml251-32/include/SFML/System/Mutex.hpp | 148 ++ .../include/SFML/System/NativeActivity.hpp | 62 + .../include/SFML/System/NonCopyable.hpp | 129 + .../sfml251-32/include/SFML/System/Sleep.hpp | 52 + .../sfml251-32/include/SFML/System/String.hpp | 669 ++++++ .../sfml251-32/include/SFML/System/String.inl | 53 + .../sfml251-32/include/SFML/System/Thread.hpp | 282 +++ .../sfml251-32/include/SFML/System/Thread.inl | 90 + .../include/SFML/System/ThreadLocal.hpp | 103 + .../include/SFML/System/ThreadLocalPtr.hpp | 158 ++ .../include/SFML/System/ThreadLocalPtr.inl | 77 + .../sfml251-32/include/SFML/System/Time.hpp | 488 ++++ .../sfml251-32/include/SFML/System/Utf.hpp | 763 ++++++ .../sfml251-32/include/SFML/System/Utf.inl | 752 ++++++ .../include/SFML/System/Vector2.hpp | 301 +++ .../include/SFML/System/Vector2.inl | 161 ++ .../include/SFML/System/Vector3.hpp | 302 +++ .../include/SFML/System/Vector3.inl | 168 ++ .../sfml251-32/include/SFML/Window.hpp | 58 + .../include/SFML/Window/Clipboard.hpp | 119 + .../include/SFML/Window/Context.hpp | 195 ++ .../include/SFML/Window/ContextSettings.hpp | 149 ++ .../sfml251-32/include/SFML/Window/Cursor.hpp | 222 ++ .../sfml251-32/include/SFML/Window/Event.hpp | 284 +++ .../sfml251-32/include/SFML/Window/Export.hpp | 48 + .../include/SFML/Window/GlResource.hpp | 109 + .../include/SFML/Window/Joystick.hpp | 227 ++ .../include/SFML/Window/Keyboard.hpp | 232 ++ .../sfml251-32/include/SFML/Window/Mouse.hpp | 177 ++ .../sfml251-32/include/SFML/Window/Sensor.hpp | 150 ++ .../sfml251-32/include/SFML/Window/Touch.hpp | 137 ++ .../include/SFML/Window/VideoMode.hpp | 228 ++ .../sfml251-32/include/SFML/Window/Window.hpp | 622 +++++ .../include/SFML/Window/WindowHandle.hpp | 101 + .../include/SFML/Window/WindowStyle.hpp | 53 + .../lib/cmake/SFML/SFMLConfig.cmake | 148 ++ .../cmake/SFML/SFMLConfigDependencies.cmake | 86 + .../lib/cmake/SFML/SFMLConfigVersion.cmake | 46 + .../cmake/SFML/SFMLSharedTargets-debug.cmake | 69 + .../SFML/SFMLSharedTargets-release.cmake | 69 + .../lib/cmake/SFML/SFMLSharedTargets.cmake | 160 ++ .../cmake/SFML/SFMLStaticTargets-debug.cmake | 69 + .../SFML/SFMLStaticTargets-release.cmake | 69 + .../lib/cmake/SFML/SFMLStaticTargets.cmake | 167 ++ SQCSim2021/external/sfml251-32/lib/flac.lib | Bin 0 -> 512458 bytes .../external/sfml251-32/lib/freetype.lib | Bin 0 -> 858580 bytes SQCSim2021/external/sfml251-32/lib/ogg.lib | Bin 0 -> 22794 bytes .../external/sfml251-32/lib/openal32.lib | Bin 0 -> 34440 bytes .../external/sfml251-32/lib/sfml-audio-d.lib | Bin 0 -> 59372 bytes .../sfml251-32/lib/sfml-audio-s-d.lib | Bin 0 -> 3367878 bytes .../external/sfml251-32/lib/sfml-audio-s.lib | Bin 0 -> 1217000 bytes .../external/sfml251-32/lib/sfml-audio.lib | Bin 0 -> 59012 bytes .../sfml251-32/lib/sfml-graphics-d.lib | Bin 0 -> 164820 bytes .../sfml251-32/lib/sfml-graphics-s-d.lib | Bin 0 -> 5933526 bytes .../sfml251-32/lib/sfml-graphics-s.lib | Bin 0 -> 2430178 bytes .../external/sfml251-32/lib/sfml-graphics.lib | Bin 0 -> 163864 bytes .../external/sfml251-32/lib/sfml-main-d.lib | Bin 0 -> 19320 bytes .../external/sfml251-32/lib/sfml-main.lib | Bin 0 -> 1306 bytes .../sfml251-32/lib/sfml-network-d.lib | Bin 0 -> 72806 bytes .../sfml251-32/lib/sfml-network-s-d.lib | Bin 0 -> 2942654 bytes .../sfml251-32/lib/sfml-network-s.lib | Bin 0 -> 1157334 bytes .../external/sfml251-32/lib/sfml-network.lib | Bin 0 -> 72396 bytes .../external/sfml251-32/lib/sfml-system-d.lib | Bin 0 -> 42074 bytes .../sfml251-32/lib/sfml-system-s-d.lib | Bin 0 -> 1097046 bytes .../external/sfml251-32/lib/sfml-system-s.lib | Bin 0 -> 391334 bytes .../external/sfml251-32/lib/sfml-system.lib | Bin 0 -> 41790 bytes .../external/sfml251-32/lib/sfml-window-d.lib | Bin 0 -> 36906 bytes .../sfml251-32/lib/sfml-window-s-d.lib | Bin 0 -> 3203886 bytes .../external/sfml251-32/lib/sfml-window-s.lib | Bin 0 -> 1132996 bytes .../external/sfml251-32/lib/sfml-window.lib | Bin 0 -> 36666 bytes SQCSim2021/external/sfml251-32/lib/vorbis.lib | Bin 0 -> 852828 bytes .../external/sfml251-32/lib/vorbisenc.lib | Bin 0 -> 643788 bytes .../external/sfml251-32/lib/vorbisfile.lib | Bin 0 -> 34544 bytes SQCSim2021/external/sfml251-32/license.md | 20 + SQCSim2021/external/sfml251-32/readme.md | 39 + 694 files changed, 120490 insertions(+), 5 deletions(-) create mode 100644 SQCSim2021/external/sfml251-32/doc/html/AlResource_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Audio_2Export_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Audio_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/BlendMode_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/CircleShape_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Clipboard_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Clock_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Color_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Config_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/ContextSettings_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Context_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/ConvexShape_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Cursor_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Drawable_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Err_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Event_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/FileInputStream_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Font_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Ftp_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/GlResource_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Glsl_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Glyph_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/GpuPreference_8hpp.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/GpuPreference_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Graphics_2Export_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Graphics_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Http_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Image_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/InputSoundFile_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/InputStream_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/IpAddress_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Joystick_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Keyboard_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Listener_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Lock_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Main_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/MemoryInputStream_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Mouse_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Music_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Mutex_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/NativeActivity_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Network_2Export_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Network_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/NonCopyable_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/OpenGL_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/OutputSoundFile_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Packet_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/PrimitiveType_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Rect_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/RectangleShape_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/RenderStates_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/RenderTarget_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/RenderTexture_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/RenderWindow_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Sensor_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Shader_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Shape_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Sleep_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/SocketHandle_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/SocketSelector_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Socket_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/SoundBufferRecorder_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/SoundBuffer_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/SoundFileFactory_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/SoundFileReader_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/SoundFileWriter_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/SoundRecorder_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/SoundSource_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/SoundStream_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Sound_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Sprite_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/String_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/System_2Export_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/System_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/TcpListener_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/TcpSocket_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Text_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Texture_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/ThreadLocalPtr_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/ThreadLocal_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Thread_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Time_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Touch_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Transform_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Transformable_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/UdpSocket_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Utf_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Vector2_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Vector3_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/VertexArray_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/VertexBuffer_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Vertex_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/VideoMode_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/View_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/WindowHandle_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/WindowStyle_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Window_2Export_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Window_2Window_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/Window_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/annotated.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/bc_s.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/bdwn.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classes.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1AlResource-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1AlResource.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1AlResource.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1CircleShape-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1CircleShape.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1CircleShape.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Clipboard-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Clipboard.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Clock-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Clock.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Color-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Color.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Context-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Context.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Context.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1ConvexShape-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1ConvexShape.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1ConvexShape.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Cursor-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Cursor.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Cursor.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Drawable-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Drawable.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Drawable.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Event-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Event.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1FileInputStream-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1FileInputStream.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1FileInputStream.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Font-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Font.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Ftp-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Ftp.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Ftp.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Ftp_1_1DirectoryResponse-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Ftp_1_1DirectoryResponse.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Ftp_1_1DirectoryResponse.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Ftp_1_1ListingResponse-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Ftp_1_1ListingResponse.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Ftp_1_1ListingResponse.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Ftp_1_1Response-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Ftp_1_1Response.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Ftp_1_1Response.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1GlResource-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1GlResource.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1GlResource.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1GlResource_1_1TransientContextLock-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1GlResource_1_1TransientContextLock.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1GlResource_1_1TransientContextLock.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Glyph-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Glyph.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Http-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Http.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Http.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Http_1_1Request-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Http_1_1Request.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Http_1_1Response-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Http_1_1Response.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Image-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Image.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1InputSoundFile-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1InputSoundFile.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1InputSoundFile.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1InputStream-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1InputStream.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1InputStream.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1IpAddress-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1IpAddress.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Joystick-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Joystick.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Keyboard-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Keyboard.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Listener-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Listener.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Lock-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Lock.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Lock.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1MemoryInputStream-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1MemoryInputStream.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1MemoryInputStream.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Mouse-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Mouse.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Music-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Music.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Music.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Mutex-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Mutex.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Mutex.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1NonCopyable-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1NonCopyable.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1NonCopyable.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1OutputSoundFile-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1OutputSoundFile.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1OutputSoundFile.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Packet-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Packet.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Rect-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Rect.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1RectangleShape-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1RectangleShape.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1RectangleShape.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1RenderStates-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1RenderStates.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1RenderTarget-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1RenderTarget.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1RenderTarget.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1RenderTexture-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1RenderTexture.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1RenderTexture.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1RenderWindow-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1RenderWindow.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1RenderWindow.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Sensor-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Sensor.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Shader-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Shader.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Shader.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Shape-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Shape.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Shape.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Socket-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Socket.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Socket.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1SocketSelector-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1SocketSelector.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Sound-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Sound.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Sound.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1SoundBuffer-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1SoundBuffer.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1SoundBuffer.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1SoundBufferRecorder-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1SoundBufferRecorder.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1SoundBufferRecorder.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1SoundFileFactory-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1SoundFileFactory.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1SoundFileReader-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1SoundFileReader.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1SoundFileWriter-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1SoundFileWriter.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1SoundRecorder-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1SoundRecorder.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1SoundRecorder.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1SoundSource-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1SoundSource.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1SoundSource.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1SoundStream-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1SoundStream.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1SoundStream.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Sprite-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Sprite.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Sprite.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1String-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1String.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1TcpListener-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1TcpListener.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1TcpListener.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1TcpSocket-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1TcpSocket.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1TcpSocket.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Text-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Text.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Text.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Texture-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Texture.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Texture.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Thread-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Thread.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Thread.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1ThreadLocal-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1ThreadLocal.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1ThreadLocal.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1ThreadLocalPtr-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1ThreadLocalPtr.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1ThreadLocalPtr.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Time-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Time.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Touch-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Touch.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Transform-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Transform.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Transformable-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Transformable.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Transformable.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1UdpSocket-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1UdpSocket.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1UdpSocket.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Utf.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Utf_3_0116_01_4-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Utf_3_0116_01_4.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Utf_3_0132_01_4-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Utf_3_0132_01_4.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Utf_3_018_01_4-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Utf_3_018_01_4.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Vector2-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Vector2.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Vector3-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Vector3.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Vertex-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Vertex.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1VertexArray-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1VertexArray.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1VertexArray.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1VertexBuffer-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1VertexBuffer.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1VertexBuffer.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1VideoMode-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1VideoMode.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1View-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1View.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Window-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Window.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/classsf_1_1Window.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/closed.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/deprecated.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/dir_5cf786e58cbf7297a26339ae6e44357c.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/dir_83d50c0b1f1eceb6f182949162e90861.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/dir_89e9fb32471ae291b179a889144513db.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/dir_c0a853e81d6f1c1f0a3eb7a27dc24256.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/dir_d44c64559bbebec7f509842c48db8b23.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/dir_dd49ddb3ba8035e4a328f8c5f31cda7e.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/dir_e68e8157741866f444e17edd764ebbae.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/dir_e71ec51a9abd604c65f6abb639f6ea75.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/doc.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/doxygen.css create mode 100644 SQCSim2021/external/sfml251-32/doc/html/doxygen.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/dynsections.js create mode 100644 SQCSim2021/external/sfml251-32/doc/html/files.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/folderclosed.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/folderopen.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_0x7e.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_b.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_c.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_d.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_e.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_enum.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_eval.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_eval_b.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_eval_c.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_eval_d.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_eval_e.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_eval_f.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_eval_g.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_eval_h.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_eval_i.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_eval_j.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_eval_k.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_eval_l.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_eval_m.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_eval_n.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_eval_o.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_eval_p.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_eval_q.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_eval_r.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_eval_s.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_eval_t.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_eval_u.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_eval_v.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_eval_w.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_eval_x.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_eval_y.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_eval_z.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_f.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_func.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_func_0x7e.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_func_b.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_func_c.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_func_d.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_func_e.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_func_f.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_func_g.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_func_h.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_func_i.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_func_k.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_func_l.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_func_m.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_func_n.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_func_o.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_func_p.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_func_r.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_func_s.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_func_t.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_func_u.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_func_v.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_func_w.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_func_z.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_g.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_h.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_i.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_j.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_k.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_l.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_m.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_n.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_o.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_p.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_q.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_r.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_rela.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_s.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_t.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_type.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_u.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_v.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_vars.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_w.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_x.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_y.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/functions_z.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/globals.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/globals_defs.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/group__audio.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/group__graphics.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/group__network.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/group__system.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/group__window.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/hierarchy.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/index.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/jquery.js create mode 100644 SQCSim2021/external/sfml251-32/doc/html/mainpage_8hpp_source.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/menudata.js create mode 100644 SQCSim2021/external/sfml251-32/doc/html/modules.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/namespacemembers.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/namespacemembers_type.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/namespaces.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/namespacesf_1_1Glsl.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/nav_f.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/nav_g.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/nav_h.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/open.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/pages.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/splitbar.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1BlendMode-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1BlendMode.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1ContextSettings-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1ContextSettings.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Event_1_1JoystickButtonEvent-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Event_1_1JoystickButtonEvent.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Event_1_1JoystickConnectEvent-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Event_1_1JoystickConnectEvent.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Event_1_1JoystickMoveEvent-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Event_1_1JoystickMoveEvent.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Event_1_1KeyEvent-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Event_1_1KeyEvent.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Event_1_1MouseButtonEvent-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Event_1_1MouseButtonEvent.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Event_1_1MouseMoveEvent-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Event_1_1MouseMoveEvent.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Event_1_1MouseWheelEvent-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Event_1_1MouseWheelEvent.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Event_1_1MouseWheelScrollEvent-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Event_1_1MouseWheelScrollEvent.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Event_1_1SensorEvent-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Event_1_1SensorEvent.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Event_1_1SizeEvent-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Event_1_1SizeEvent.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Event_1_1TextEvent-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Event_1_1TextEvent.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Event_1_1TouchEvent-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Event_1_1TouchEvent.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Font_1_1Info-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Font_1_1Info.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Joystick_1_1Identification-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Joystick_1_1Identification.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Music_1_1Span-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Music_1_1Span.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1Shader_1_1CurrentTextureType.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1SoundFileReader_1_1Info-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1SoundFileReader_1_1Info.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1SoundStream_1_1Chunk-members.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/structsf_1_1SoundStream_1_1Chunk.html create mode 100644 SQCSim2021/external/sfml251-32/doc/html/sync_off.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/sync_on.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/tab_a.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/tab_b.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/tab_h.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/tab_s.png create mode 100644 SQCSim2021/external/sfml251-32/doc/html/tabs.css create mode 100644 SQCSim2021/external/sfml251-32/examples/ftp/Ftp.cpp create mode 100644 SQCSim2021/external/sfml251-32/examples/ftp/ftp.exe create mode 100644 SQCSim2021/external/sfml251-32/examples/island/Island.cpp create mode 100644 SQCSim2021/external/sfml251-32/examples/island/island.exe create mode 100644 SQCSim2021/external/sfml251-32/examples/island/resources/sansation.ttf create mode 100644 SQCSim2021/external/sfml251-32/examples/island/resources/terrain.frag create mode 100644 SQCSim2021/external/sfml251-32/examples/island/resources/terrain.vert create mode 100644 SQCSim2021/external/sfml251-32/examples/joystick/Joystick.cpp create mode 100644 SQCSim2021/external/sfml251-32/examples/joystick/joystick.exe create mode 100644 SQCSim2021/external/sfml251-32/examples/joystick/resources/sansation.ttf create mode 100644 SQCSim2021/external/sfml251-32/examples/opengl/OpenGL.cpp create mode 100644 SQCSim2021/external/sfml251-32/examples/opengl/opengl.exe create mode 100644 SQCSim2021/external/sfml251-32/examples/opengl/resources/background.jpg create mode 100644 SQCSim2021/external/sfml251-32/examples/opengl/resources/sansation.ttf create mode 100644 SQCSim2021/external/sfml251-32/examples/opengl/resources/texture.jpg create mode 100644 SQCSim2021/external/sfml251-32/examples/pong/Pong.cpp create mode 100644 SQCSim2021/external/sfml251-32/examples/pong/pong.exe create mode 100644 SQCSim2021/external/sfml251-32/examples/pong/resources/ball.wav create mode 100644 SQCSim2021/external/sfml251-32/examples/pong/resources/sansation.ttf create mode 100644 SQCSim2021/external/sfml251-32/examples/shader/Effect.hpp create mode 100644 SQCSim2021/external/sfml251-32/examples/shader/Shader.cpp create mode 100644 SQCSim2021/external/sfml251-32/examples/shader/resources/background.jpg create mode 100644 SQCSim2021/external/sfml251-32/examples/shader/resources/billboard.frag create mode 100644 SQCSim2021/external/sfml251-32/examples/shader/resources/billboard.geom create mode 100644 SQCSim2021/external/sfml251-32/examples/shader/resources/billboard.vert create mode 100644 SQCSim2021/external/sfml251-32/examples/shader/resources/blink.frag create mode 100644 SQCSim2021/external/sfml251-32/examples/shader/resources/blur.frag create mode 100644 SQCSim2021/external/sfml251-32/examples/shader/resources/devices.png create mode 100644 SQCSim2021/external/sfml251-32/examples/shader/resources/edge.frag create mode 100644 SQCSim2021/external/sfml251-32/examples/shader/resources/logo.png create mode 100644 SQCSim2021/external/sfml251-32/examples/shader/resources/pixelate.frag create mode 100644 SQCSim2021/external/sfml251-32/examples/shader/resources/sansation.ttf create mode 100644 SQCSim2021/external/sfml251-32/examples/shader/resources/sfml.png create mode 100644 SQCSim2021/external/sfml251-32/examples/shader/resources/storm.vert create mode 100644 SQCSim2021/external/sfml251-32/examples/shader/resources/text-background.png create mode 100644 SQCSim2021/external/sfml251-32/examples/shader/resources/wave.vert create mode 100644 SQCSim2021/external/sfml251-32/examples/shader/shader.exe create mode 100644 SQCSim2021/external/sfml251-32/examples/sockets/Sockets.cpp create mode 100644 SQCSim2021/external/sfml251-32/examples/sockets/TCP.cpp create mode 100644 SQCSim2021/external/sfml251-32/examples/sockets/UDP.cpp create mode 100644 SQCSim2021/external/sfml251-32/examples/sockets/sockets.exe create mode 100644 SQCSim2021/external/sfml251-32/examples/sound-capture/SoundCapture.cpp create mode 100644 SQCSim2021/external/sfml251-32/examples/sound-capture/sound-capture.exe create mode 100644 SQCSim2021/external/sfml251-32/examples/sound/Sound.cpp create mode 100644 SQCSim2021/external/sfml251-32/examples/sound/resources/canary.wav create mode 100644 SQCSim2021/external/sfml251-32/examples/sound/resources/ding.flac create mode 100644 SQCSim2021/external/sfml251-32/examples/sound/resources/orchestral.ogg create mode 100644 SQCSim2021/external/sfml251-32/examples/sound/sound.exe create mode 100644 SQCSim2021/external/sfml251-32/examples/voip/Client.cpp create mode 100644 SQCSim2021/external/sfml251-32/examples/voip/Server.cpp create mode 100644 SQCSim2021/external/sfml251-32/examples/voip/VoIP.cpp create mode 100644 SQCSim2021/external/sfml251-32/examples/voip/voip.exe create mode 100644 SQCSim2021/external/sfml251-32/examples/window/Window.cpp create mode 100644 SQCSim2021/external/sfml251-32/examples/window/window.exe create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Audio.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Audio/AlResource.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Audio/Export.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Audio/InputSoundFile.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Audio/Listener.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Audio/Music.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Audio/OutputSoundFile.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Audio/Sound.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Audio/SoundBuffer.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Audio/SoundBufferRecorder.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Audio/SoundFileFactory.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Audio/SoundFileFactory.inl create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Audio/SoundFileReader.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Audio/SoundFileWriter.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Audio/SoundRecorder.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Audio/SoundSource.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Audio/SoundStream.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Config.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/GpuPreference.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/BlendMode.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/CircleShape.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/Color.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/ConvexShape.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/Drawable.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/Export.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/Font.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/Glsl.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/Glsl.inl create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/Glyph.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/Image.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/PrimitiveType.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/Rect.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/Rect.inl create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/RectangleShape.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/RenderStates.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/RenderTarget.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/RenderTexture.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/RenderWindow.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/Shader.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/Shape.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/Sprite.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/Text.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/Texture.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/Transform.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/Transformable.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/Vertex.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/VertexArray.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/VertexBuffer.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Graphics/View.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Main.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Network.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Network/Export.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Network/Ftp.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Network/Http.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Network/IpAddress.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Network/Packet.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Network/Socket.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Network/SocketHandle.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Network/SocketSelector.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Network/TcpListener.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Network/TcpSocket.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Network/UdpSocket.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/OpenGL.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/System.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/System/Clock.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/System/Err.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/System/Export.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/System/FileInputStream.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/System/InputStream.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/System/Lock.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/System/MemoryInputStream.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/System/Mutex.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/System/NativeActivity.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/System/NonCopyable.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/System/Sleep.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/System/String.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/System/String.inl create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/System/Thread.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/System/Thread.inl create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/System/ThreadLocal.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/System/ThreadLocalPtr.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/System/ThreadLocalPtr.inl create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/System/Time.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/System/Utf.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/System/Utf.inl create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/System/Vector2.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/System/Vector2.inl create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/System/Vector3.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/System/Vector3.inl create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Window.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Window/Clipboard.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Window/Context.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Window/ContextSettings.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Window/Cursor.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Window/Event.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Window/Export.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Window/GlResource.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Window/Joystick.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Window/Keyboard.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Window/Mouse.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Window/Sensor.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Window/Touch.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Window/VideoMode.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Window/Window.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Window/WindowHandle.hpp create mode 100644 SQCSim2021/external/sfml251-32/include/SFML/Window/WindowStyle.hpp create mode 100644 SQCSim2021/external/sfml251-32/lib/cmake/SFML/SFMLConfig.cmake create mode 100644 SQCSim2021/external/sfml251-32/lib/cmake/SFML/SFMLConfigDependencies.cmake create mode 100644 SQCSim2021/external/sfml251-32/lib/cmake/SFML/SFMLConfigVersion.cmake create mode 100644 SQCSim2021/external/sfml251-32/lib/cmake/SFML/SFMLSharedTargets-debug.cmake create mode 100644 SQCSim2021/external/sfml251-32/lib/cmake/SFML/SFMLSharedTargets-release.cmake create mode 100644 SQCSim2021/external/sfml251-32/lib/cmake/SFML/SFMLSharedTargets.cmake create mode 100644 SQCSim2021/external/sfml251-32/lib/cmake/SFML/SFMLStaticTargets-debug.cmake create mode 100644 SQCSim2021/external/sfml251-32/lib/cmake/SFML/SFMLStaticTargets-release.cmake create mode 100644 SQCSim2021/external/sfml251-32/lib/cmake/SFML/SFMLStaticTargets.cmake create mode 100644 SQCSim2021/external/sfml251-32/lib/flac.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/freetype.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/ogg.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/openal32.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/sfml-audio-d.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/sfml-audio-s-d.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/sfml-audio-s.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/sfml-audio.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/sfml-graphics-d.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/sfml-graphics-s-d.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/sfml-graphics-s.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/sfml-graphics.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/sfml-main-d.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/sfml-main.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/sfml-network-d.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/sfml-network-s-d.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/sfml-network-s.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/sfml-network.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/sfml-system-d.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/sfml-system-s-d.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/sfml-system-s.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/sfml-system.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/sfml-window-d.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/sfml-window-s-d.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/sfml-window-s.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/sfml-window.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/vorbis.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/vorbisenc.lib create mode 100644 SQCSim2021/external/sfml251-32/lib/vorbisfile.lib create mode 100644 SQCSim2021/external/sfml251-32/license.md create mode 100644 SQCSim2021/external/sfml251-32/readme.md diff --git a/Debug/glew32.dll b/Debug/glew32.dll index cbce7da7692ddd7eb16880ec7b05e0faa8139c1a..cbe1d67b3c5e482ecb039d17fba4312ed0173f23 100644 GIT binary patch literal 389632 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�|GjPKc;nZ0Ju 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 literal 331776 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