From aaf76a3ff0d3e72e24dea03e4029b031af1ff9f8 Mon Sep 17 00:00:00 2001 From: Persson-dev Date: Wed, 16 Jul 2025 00:32:40 +0200 Subject: [PATCH] nice --- imgui.ini | 16 ++ include/td/Maths.h | 183 +++++++++++++ include/td/Types.h | 12 +- include/td/common/DataBuffer.h | 300 --------------------- include/td/common/VarInt.h | 58 ---- include/td/game/Game.h | 44 ++- include/td/game/Mobs.h | 260 ++++++++++++++++++ include/td/game/Team.h | 86 ++++++ include/td/game/Towers.h | 266 ++++++++++++++++++ include/td/game/World.h | 137 ++++++++++ include/td/game/WorldTypes.h | 119 ++++++++ include/td/input/Display.h | 43 +++ include/td/misc/Shapes.h | 96 +++++++ include/td/protocol/command/CommandData.h | 2 +- include/td/protocol/packet/PacketData.h | 20 +- include/td/protocol/packet/Packets.h | 8 +- include/td/render/Camera.h | 37 +++ include/td/render/OpenGL.h | 8 + include/td/render/Renderer.h | 47 ++++ include/td/render/loader/GLLoader.h | 86 ++++++ include/td/render/loader/WorldLoader.h | 25 ++ include/td/render/renderer/WorldRenderer.h | 25 ++ include/td/render/shader/EntityShader.h | 30 +++ include/td/render/shader/ShaderProgram.h | 43 +++ include/td/render/shader/WorldShader.h | 21 ++ src/main.cpp | 212 ++++++++++++++- src/td/Maths.cpp | 169 ++++++++++++ src/td/common/DataBuffer.cpp | 163 ----------- src/td/common/VarInt.cpp | 52 ---- src/td/game/World.cpp | 59 ++++ src/td/input/Display.cpp | 156 +++++++++++ src/td/render/Camera.cpp | 26 ++ src/td/render/Renderer.cpp | 26 ++ src/td/render/loader/GLLoader.cpp | 99 +++++++ src/td/render/loader/WorldLoader.cpp | 218 +++++++++++++++ src/td/render/renderer/WorldRenderer.cpp | 26 ++ src/td/render/shader/EntityShader.cpp | 123 +++++++++ src/td/render/shader/ShaderProgram.cpp | 145 ++++++++++ src/td/render/shader/WorldShader.cpp | 102 +++++++ test/tdmap.tdmap2 | Bin 0 -> 595 bytes xmake.lua | 14 +- 41 files changed, 2963 insertions(+), 599 deletions(-) create mode 100644 imgui.ini create mode 100644 include/td/Maths.h delete mode 100644 include/td/common/DataBuffer.h delete mode 100644 include/td/common/VarInt.h create mode 100644 include/td/game/Mobs.h create mode 100644 include/td/game/Team.h create mode 100644 include/td/game/Towers.h create mode 100644 include/td/game/World.h create mode 100644 include/td/game/WorldTypes.h create mode 100644 include/td/input/Display.h create mode 100644 include/td/misc/Shapes.h create mode 100644 include/td/render/Camera.h create mode 100644 include/td/render/OpenGL.h create mode 100644 include/td/render/Renderer.h create mode 100644 include/td/render/loader/GLLoader.h create mode 100644 include/td/render/loader/WorldLoader.h create mode 100644 include/td/render/renderer/WorldRenderer.h create mode 100644 include/td/render/shader/EntityShader.h create mode 100755 include/td/render/shader/ShaderProgram.h create mode 100644 include/td/render/shader/WorldShader.h create mode 100644 src/td/Maths.cpp delete mode 100644 src/td/common/DataBuffer.cpp delete mode 100644 src/td/common/VarInt.cpp create mode 100644 src/td/game/World.cpp create mode 100644 src/td/input/Display.cpp create mode 100644 src/td/render/Camera.cpp create mode 100644 src/td/render/Renderer.cpp create mode 100644 src/td/render/loader/GLLoader.cpp create mode 100644 src/td/render/loader/WorldLoader.cpp create mode 100644 src/td/render/renderer/WorldRenderer.cpp create mode 100644 src/td/render/shader/EntityShader.cpp create mode 100755 src/td/render/shader/ShaderProgram.cpp create mode 100644 src/td/render/shader/WorldShader.cpp create mode 100644 test/tdmap.tdmap2 diff --git a/imgui.ini b/imgui.ini new file mode 100644 index 0000000..c9168d1 --- /dev/null +++ b/imgui.ini @@ -0,0 +1,16 @@ +[Window][Debug##Default] +Pos=20,60 +Size=400,400 + +[Window][Dear ImGui Demo] +Pos=1116,220 +Size=927,695 + +[Window][Dear ImGui Debug Log] +Pos=60,60 +Size=670,354 + +[Window][Example: Console] +Pos=958,210 +Size=520,600 + diff --git a/include/td/Maths.h b/include/td/Maths.h new file mode 100644 index 0000000..61b2f59 --- /dev/null +++ b/include/td/Maths.h @@ -0,0 +1,183 @@ +#pragma once + +#include + +namespace td { + +static constexpr float PI = 3.141592653f; + +template +struct Vec2 { + union { + T x; + T r; + }; + + union { + T y; + T g; + }; + + constexpr Vec2(T X = 0, T Y = 0) : x(X), y(Y) {} +}; + +template +inline bool operator==(const Vec2& vec2, const Vec2& other) { + return vec2.x == other.x && vec2.y == other.y; +} + +template +struct Vec3 { + union { + T x; + T r; + }; + + union { + T y; + T g; + }; + + union { + T z; + T b; + }; + + constexpr Vec3(T X = 0, T Y = 0, T Z = 0) : x(X), y(Y), z(Z) {} +}; + +template +inline bool operator==(const Vec3& vec3, const Vec3& other) { + return vec3.x == other.x && vec3.y == other.y && vec3.z == other.z; +} + +template +struct Vec4 { + union { + T x; + T r; + }; + + union { + T y; + T g; + }; + + union { + T z; + T b; + }; + + union { + T w; + T a; + }; + + constexpr Vec4(Vec3 vec, T W = 1) : x(vec.x), y(vec.y), z(vec.z), w(W) {} + constexpr Vec4(T X = 0, T Y = 0, T Z = 0, T W = 0) : x(X), y(Y), z(Z), w(W) {} +}; + +template +inline bool operator==(const Vec4& vec4, const Vec4& other) { + return vec4.x == other.x && vec4.y == other.y && vec4.z == other.z && vec4.w = other.w; +} + +using Vec2i = Vec2; +using Vec2u = Vec2; +using Vec2f = Vec2; +using Vec2d = Vec2; + +using Vec3i = Vec3; +using Vec3u = Vec3; +using Vec3f = Vec3; +using Vec3d = Vec3; + +using Vec4i = Vec4; +using Vec4u = Vec4; +using Vec4f = Vec4; +using Vec4d = Vec4; + +using Color = Vec3; + +template +struct Mat4 { + static const std::size_t MATRIX_SIZE = 4; + + T x0, x1, x2, x3; + T y0, y1, y2, y3; + T z0, z1, z2, z3; + T w0, w1, w2, w3; + + T operator[](std::size_t offset) const { + return reinterpret_cast(this)[offset]; + } + + T& operator[](std::size_t offset) { + return reinterpret_cast(this)[offset]; + } + + T* data() { + return reinterpret_cast(this); + } + + const T* data() const { + return reinterpret_cast(this); + } + + T at(std::size_t row, std::size_t column) const { + return operator[](row * MATRIX_SIZE + column); + } + + T& at(std::size_t row, std::size_t column) { + return operator[](row * MATRIX_SIZE + column); + } +}; + +typedef Mat4 Mat4f; +typedef Mat4 Mat4i; +typedef Mat4 Mat4d; + +template +inline bool operator==(const Mat4& mat, const Mat4& other) { + return mat.x0 == other.x0 && mat.y0 == other.y0 && mat.z0 == other.z0 && mat.w0 == other.w0 && mat.x1 == other.x1 && + mat.y1 == other.y1 && mat.z1 == other.z1 && mat.w1 == other.w1 && mat.x2 == other.x2 && mat.y2 == other.y2 && + mat.z2 == other.z2 && mat.w2 == other.w2 && mat.x3 == other.x3 && mat.y3 == other.y3 && mat.z3 == other.z3 && + mat.w3 == other.w3; +} + +namespace maths { + +template +Mat4 Transpose(const Mat4& mat) { + Mat4 result; + + result.x1 = mat.y0; + result.x2 = mat.z0; + result.x3 = mat.w0; + result.y0 = mat.x1; + result.y2 = mat.z1; + result.y3 = mat.w1; + result.z0 = mat.x2; + result.z1 = mat.y2; + result.z3 = mat.w2; + result.w0 = mat.x3; + result.w1 = mat.y3; + result.w2 = mat.z3; + result.x0 = mat.x0; + result.y1 = mat.y1; + result.z2 = mat.z2; + result.w3 = mat.w3; + + return result; +} + +Mat4f Perspective(float fovY, float aspectRatio, float zNear, float zFar); +Mat4f Look(const Vec3f& eye, const Vec3f& center, const Vec3f& up); + +Mat4f Inverse(const Mat4f& mat); + +} // namespace maths + + + +} // namespace td diff --git a/include/td/Types.h b/include/td/Types.h index c293bf8..84cc15a 100644 --- a/include/td/Types.h +++ b/include/td/Types.h @@ -7,8 +7,9 @@ namespace td { using FpFloat = fpm::fixed_16_16; -enum class Team : std::uint8_t { - Blue = 0, +enum class TeamColor : std::int8_t { + None = -1, + Blue, Red, }; @@ -67,4 +68,11 @@ struct EntityCoords { using PeerID = std::uint16_t; +enum class Direction : std::uint8_t { + PositiveX = 1 << 0, + NegativeX = 1 << 1, + PositiveY = 1 << 2, + NegativeY = 1 << 3, +}; + } // namespace td diff --git a/include/td/common/DataBuffer.h b/include/td/common/DataBuffer.h deleted file mode 100644 index 2cc9eff..0000000 --- a/include/td/common/DataBuffer.h +++ /dev/null @@ -1,300 +0,0 @@ -#pragma once - -/** - * \file DataBuffer.h - * \brief File containing the td::DataBuffer class - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace td { - -/** - * \class DataBuffer - * \brief Class used to manipulate memory - */ -class DataBuffer { - private: - typedef std::vector Data; - Data m_Buffer; - std::size_t m_ReadOffset; - - public: - typedef Data::iterator iterator; - typedef Data::const_iterator const_iterator; - typedef Data::reference reference; - typedef Data::const_reference const_reference; - typedef Data::difference_type difference_type; - - DataBuffer(); - DataBuffer(const DataBuffer& other); - DataBuffer(const DataBuffer& other, difference_type offset); - DataBuffer(DataBuffer&& other); - DataBuffer(const std::string& str); - - DataBuffer& operator=(const DataBuffer& other); - DataBuffer& operator=(DataBuffer&& other); - - /** - * \brief Append data to the buffer - */ - template - void Append(const T& data) { - std::size_t size = sizeof(data); - std::size_t end_pos = m_Buffer.size(); - m_Buffer.resize(m_Buffer.size() + size); - std::memcpy(&m_Buffer[end_pos], &data, size); - } - - /** - * \brief Append data to the buffer - */ - template - DataBuffer& operator<<(const T& data) { - Append(data); - return *this; - } - - /** - * \brief Append a string to the buffer - * \warning Don't use it for binary data ! - * \param str The string to append - */ - DataBuffer& operator<<(const std::string& str); - - /** - * \brief Append data to the buffer from another const buffer - * \param data The buffer to append - */ - DataBuffer& operator<<(const DataBuffer& data); - - /** - * \brief Append a vector to the buffer by first writing the size - * \param data The vector to append - */ - template - DataBuffer& operator<<(const std::vector& data) { - *this << VarInt{data.size()}; - for (const auto& element : data) { - *this << element; - } - return *this; - } - - /** - * \brief Append an array to the buffer by first writing the size - * \param data The buffer to append - */ - template - DataBuffer& operator<<(const std::array& data) { - for (const auto& element : data) { - *this << element; - } - return *this; - } - - /** - * \brief Read some data from the buffer and assign to desired variable - */ - template - DataBuffer& operator>>(T& data) { - assert(m_ReadOffset + sizeof(T) <= GetSize()); - data = *(reinterpret_cast(&m_Buffer[m_ReadOffset])); - m_ReadOffset += sizeof(T); - return *this; - } - - /** - * \brief Read some data from the buffer and assign to the new buffer - * \param data The buffer to assign - */ - DataBuffer& operator>>(DataBuffer& data); - - /** - * \brief Read a string from the buffer - * \param str The string to assign in the new buffer - * \warning Don't use it for binary data ! - */ - DataBuffer& operator>>(std::string& str); - - /** - * \brief Read a vector (size + data) from the buffer - * \pre The vector is assumed to be empty - */ - template - DataBuffer& operator>>(std::vector& data) { - VarInt arraySize; - *this >> arraySize; - for (std::size_t i = 0; i < arraySize.GetValue(); i++) { - T newElement; - *this >> newElement; - data.push_back(newElement); - } - return *this; - } - - /** - * \brief Read an array from the buffer - */ - template - DataBuffer& operator>>(std::array& data) { - for (std::size_t i = 0; i < Size; i++) { - T newElement; - *this >> newElement; - data[i] = newElement; - } - return *this; - } - - /** - * \brief Write some data to the buffer - * \param buffer The buffer to write - * \param amount The amount of data to write - */ - void WriteSome(const char* buffer, std::size_t amount); - - /** - * \brief Write some data to the buffer - * \param buffer The buffer to write - * \param amount The amount of data to write - */ - void WriteSome(const std::uint8_t* buffer, std::size_t amount); - - /** - * \brief Read some data from the buffer - * \param buffer The buffer to Read - * \param amount The amount of data from the buffer - */ - void ReadSome(char* buffer, std::size_t amount); - - /** - * \brief Read some data from the buffer - * \param buffer The buffer to Read - * \param amount The amount of data from the buffer - */ - void ReadSome(std::uint8_t* buffer, std::size_t amount); - - /** - * \brief Read some data from the buffer - * \param buffer The buffer to Read - * \param amount The amount of data from the buffer - */ - void ReadSome(DataBuffer& buffer, std::size_t amount); - - /** - * \brief Resize the buffer - * \param size The new size of the buffer - */ - void Resize(std::size_t size) { - m_Buffer.resize(size); - } - - /** - * \brief Reserve some space in the buffer - * \param amount The amount of space to reserve - */ - void Reserve(std::size_t amount) { - m_Buffer.reserve(amount); - } - - - /** - * \brief Clear the buffer - */ - void Clear() { - m_Buffer.clear(); - m_ReadOffset = 0; - } - - /** - * \brief When the buffer has been read entirely - */ - bool IsFinished() const { - return m_ReadOffset >= m_Buffer.size(); - } - - /** - * \brief Get the buffer data - */ - std::uint8_t* data() { - return m_Buffer.data(); - } - - /** - * \brief Get the buffer data - */ - const std::uint8_t* data() const { - return m_Buffer.data(); - } - - /** - * \brief Get the read offset - */ - std::size_t GetReadOffset() const { - return m_ReadOffset; - } - - /** - * \brief Set the read offset - * \param pos The new read offset - */ - void SetReadOffset(std::size_t pos); - - /** - * \brief Get the size of the buffer - */ - std::size_t GetSize() const; - - /** - * \brief Get the remaining size of the buffer - */ - std::size_t GetRemaining() const; - - /** - * \brief Read a file into the buffer - * \param fileName The name of the file to read - */ - bool ReadFile(const std::string& fileName); - - /** - * \brief Write a file into the buffer - * \param fileName The name of the file to write to - */ - bool WriteFile(const std::string& fileName) const; - - /** - * \brief Allocate the buffer on the heap - * \warning Don't forget to free the data ! - */ - std::uint8_t* HeapAllocatedData() const { - std::uint8_t* newBuffer = new std::uint8_t[GetSize()]; - std::memcpy(newBuffer, data(), GetSize()); - return newBuffer; - } - - /** - * \brief Operator == to compare two DataBuffer - */ - bool operator==(const DataBuffer& other) const { - return m_Buffer == other.m_Buffer; - } - - iterator begin(); - iterator end(); - const_iterator begin() const; - const_iterator end() const; -}; - -/** - * \brief Operator << to write a DataBuffer to an ostream - */ -std::ostream& operator<<(std::ostream& os, const DataBuffer& buffer); - -} // namespace td diff --git a/include/td/common/VarInt.h b/include/td/common/VarInt.h deleted file mode 100644 index 12ac986..0000000 --- a/include/td/common/VarInt.h +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -/** - * \file VarInt.h - * \brief File containing the td::VarInt class - */ - -#include -#include - -namespace td { - -class DataBuffer; - -/** - * \class VarInt - * \brief Variable-length format such that smaller numbers use fewer bytes. - */ -class VarInt { - private: - std::uint64_t m_Value; - - public: - VarInt() : m_Value(0) {} - /** - * \brief Construct a variable integer from a value - * \param value The value of the variable integer - */ - VarInt(std::uint64_t value) : m_Value(value) {} - - /** - * \brief Get the value of the variable integer - */ - std::uint64_t GetValue() const { - return m_Value; - } - - /** - * \brief Get the length of the serialized variable integer - */ - std::size_t GetSerializedLength() const; - - /** - * \brief Serialize the variable integer - * \param out The buffer to write the serialized variable integer to - * \param var The variable integer to serialize - */ - friend DataBuffer& operator<<(DataBuffer& out, const VarInt& var); - - /** - * \brief Deserialize the variable integer - * \param in The buffer to read the serialized variable integer from - * \param var The variable integer to deserialize - */ - friend DataBuffer& operator>>(DataBuffer& in, VarInt& var); -}; - -} // namespace td diff --git a/include/td/game/Game.h b/include/td/game/Game.h index 0928249..dd5bac9 100644 --- a/include/td/game/Game.h +++ b/include/td/game/Game.h @@ -1,14 +1,46 @@ #pragma once -#include +#include namespace td { namespace game { -class Game { - private: - GameHistory m_History; +enum class GameState : std::uint8_t { + Lobby, + Game, + EndGame, + Disconnected, + Closed }; -} // namespace game -} // namespace td +typedef std::map PlayerList; + + +class Game { +protected: + World* m_World; + + GameState m_GameState = GameState::Lobby; + PlayerList m_Players; +public: + Game(World* world); + virtual ~Game(); + + virtual void Tick(std::uint64_t delta); + + GameState GetGameState() const { return m_GameState; } + void SetGameState(GameState gameState) { m_GameState = gameState; }; + + const World* GetWorld() const { return m_World; } + World* GetWorld() { return m_World; } + + const PlayerList& GetPlayers() const { return m_Players; } + PlayerList& GetPlayers() { return m_Players; } + + const Player* GetPlayerById(PlayerID id) const; + Player* GetPlayerById(PlayerID id); + +}; + +} // namespace game +} // namespace td diff --git a/include/td/game/Mobs.h b/include/td/game/Mobs.h new file mode 100644 index 0000000..3a9fe37 --- /dev/null +++ b/include/td/game/Mobs.h @@ -0,0 +1,260 @@ +#pragma once + +#include + +#include +#include +#include + +#include +#include + +namespace td { +namespace game { + +struct WalkableTile; + +enum class EffectType : std::uint8_t { + Slowness = 0, + Stun, + Fire, + Poison, + Heal, +}; + +enum class MobType : std::uint8_t { + Zombie = 0, + Spider, + Skeleton, + Pigman, + Creeper, + Silverfish, + Blaze, + Witch, + Slime, + Giant, + + MOB_COUNT +}; + +typedef std::uint32_t MobID; +typedef std::uint8_t MobLevel; +typedef std::vector TowerImmunities; +typedef std::vector EffectImmunities; + +class MobStats { +private: + float m_Damage; + float m_Speed; + Vec2f m_Size; + std::uint16_t m_MoneyCost; + std::uint16_t m_ExpCost; + std::uint16_t m_MaxLife; + std::uint16_t m_ExpReward; +public: + MobStats(float damage, float speed, Vec2f size, std::uint16_t moneyCost, + std::uint16_t expCost, std::uint16_t expReward, + std::uint16_t maxLife) : m_Damage(damage), m_Speed(speed), + m_Size(size), m_MoneyCost(moneyCost), m_ExpCost(expCost), + m_MaxLife(maxLife), m_ExpReward(expReward) { + } + + float GetDamage() const { return m_Damage; } + float GetMovementSpeed() const { return m_Speed; } + const Vec2f& GetSize() const { return m_Size; } + std::uint16_t GetMoneyCost() const { return m_MoneyCost; } + std::uint16_t GetExpCost() const { return m_ExpCost; } + std::uint16_t GetExpReward() const { return m_ExpReward; } + std::uint16_t GetMaxLife() const { return m_MaxLife; } +}; + +struct EffectDuration { + EffectType type; + float duration; // in seconds + Tower* tower; // the tower that gived the effect +}; + +const MobStats* GetMobStats(MobType type, std::uint8_t level); +const TowerImmunities& GetMobTowerImmunities(MobType type, std::uint8_t level); +const EffectImmunities& GetMobEffectImmunities(MobType type, std::uint8_t level); + +class Mob : public utils::shape::Rectangle { +protected: + float m_Health; +private: + MobID m_ID; + PlayerID m_Sender; + MobLevel m_Level; + Direction m_Direction; + std::vector m_Effects; + const Tower* m_LastDamage; // the last tower that damaged the mob + float m_HitCooldown; + + // utils::Timer m_EffectFireTimer; + // utils::Timer m_EffectPoisonTimer; + // utils::Timer m_EffectHealTimer; + + TeamCastle* m_CastleTarget; + // utils::CooldownTimer m_AttackTimer; + +public: + Mob(MobID id, MobLevel level, PlayerID sender) : m_Sender(sender), m_Level(level), + m_HitCooldown(0), m_CastleTarget(nullptr) { + + } + + virtual MobType GetType() const = 0; + + virtual void Tick(std::uint64_t delta, World* world); + + virtual bool OnDeath(World* world) { return true; } + + MobID GetMobID() const { return m_ID; } + const TowerImmunities& GetTowerImmunities() const { return GetMobTowerImmunities(GetType(), m_Level); } + const EffectImmunities& GetEffectImmunities() const { return GetMobEffectImmunities(GetType(), m_Level); } + PlayerID GetSender() const { return m_Sender; } + MobLevel GetLevel() const { return m_Level; } + const MobStats* GetStats() const { return GetMobStats(GetType(), m_Level); } + void SetHealth(float newHealth) { m_Health = newHealth; } + float GetHealth() const { return m_Health; } + bool IsDead() const { return m_Health <= 0; } + bool IsAlive() const { return m_Health > 0; } + const Tower* GetLastDamageTower() { return m_LastDamage; } + bool HasReachedEnemyCastle() { return m_CastleTarget != nullptr; } + + void Damage(float dmg, const Tower* damager) { + m_Health = std::max(0.0f, m_Health - dmg); + m_LastDamage = damager; + m_HitCooldown = 0.1; + } + + void Heal(float heal) { + m_Health = std::min(static_cast(GetStats()->GetMaxLife()), m_Health + heal); + } + + void SetMobReachedCastle(TeamCastle* castle) { m_CastleTarget = castle; } // used when mob is in front of the castle + + bool IsImmuneTo(TowerType type); + + bool IsImmuneTo(EffectType type); + void AddEffect(EffectType type, float durationSec, Tower* tower); + bool HasEffect(EffectType type); + + bool HasTakenDamage() { return m_HitCooldown > 0; } + + float GetTileX() { return GetCenterX() - static_cast(static_cast(GetCenterX())); } // returns a float between 0 and 1 excluded + float GetTileY() { return GetCenterY() - static_cast(static_cast(GetCenterY())); } // returns a float between 0 and 1 excluded + + Direction GetDirection() const { return m_Direction; } + void SetDirection(Direction dir) { m_Direction = dir; } +protected: + void InitMob() { + m_Health = static_cast(GetStats()->GetMaxLife()); + SetSize(GetStats()->GetSize().x, GetStats()->GetSize().y); + } +private: + void UpdateEffects(std::uint64_t delta, World* world); + void AttackCastle(std::uint64_t delta, World* world); + void Move(std::uint64_t delta, World* world); + void Walk(std::uint64_t delta, World* world); + void MoveBack(const TeamCastle& castle, World* world); + void ChangeDirection(const WalkableTile& tile, World* world); + bool IsTouchingCastle(const TeamCastle& castle) const; + EffectDuration& GetEffect(EffectType type); +}; + +typedef std::shared_ptr MobPtr; + +class Zombie : public Mob { +public: + Zombie(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } + + virtual MobType GetType() const { return MobType::Zombie; } +}; + +class Spider : public Mob { +public: + Spider(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } + + virtual MobType GetType() const { return MobType::Spider; } +}; + +class Skeleton : public Mob { +public: + Skeleton(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } + + virtual MobType GetType() const { return MobType::Skeleton; } +}; + +class PigMan : public Mob { +public: + PigMan(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } + + virtual MobType GetType() const { return MobType::Pigman; } +}; + +class Creeper : public Mob { +public: + Creeper(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } + + virtual MobType GetType() const { return MobType::Creeper; } +}; + +class Silverfish : public Mob { +public: + Silverfish(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } + + virtual MobType GetType() const { return MobType::Silverfish; } +}; + +class Blaze : public Mob { +public: + Blaze(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } + + virtual MobType GetType() const { return MobType::Blaze; } +}; + +class Witch : public Mob { +public: + Witch(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } + + virtual MobType GetType() const { return MobType::Witch; } +}; + +class Slime : public Mob { +public: + Slime(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } + + virtual MobType GetType() const { return MobType::Slime; } +}; + +class Giant : public Mob { +public: + Giant(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); } + + virtual MobType GetType() const { return MobType::Giant; } +}; + +namespace MobFactory { + +MobPtr CreateMob(MobID id, MobType type, std::uint8_t level, PlayerID sender); +std::string GetMobName(MobType type); + +} + +class MobListener { +public: + virtual void OnMobSpawn(Mob* mob) {} + virtual void OnMobDie(Mob* mob) {} + + virtual void OnMobDamage(Mob* target, float damage, Tower* damager) {} + + virtual void OnMobTouchCastle(Mob* damager, TeamCastle* enemyCastle) {} + virtual void OnMobCastleDamage(Mob* damager, TeamCastle* enemyCastle, float damage) {} +}; + +// typedef utils::ObjectNotifier MobNotifier; + +} // namespace game +} // namespace td + diff --git a/include/td/game/Team.h b/include/td/game/Team.h new file mode 100644 index 0000000..79e5547 --- /dev/null +++ b/include/td/game/Team.h @@ -0,0 +1,86 @@ +#pragma once + +#include + +#include + +#include +#include +#include + +namespace td { +namespace game { + +class Player; + +class Spawn : public utils::shape::Rectangle { +private: + Direction m_Direction; +public: + Spawn() { + SetWidth(5); + SetHeight(5); + } + + Direction GetDirection() const { return m_Direction; } + + void SetDirection(Direction direction) { m_Direction = direction; } +}; + +class Team; + +class TeamCastle : public utils::shape::Rectangle { +private: + const Team* m_Team; + float m_Life; +public: + static constexpr int CastleMaxLife = 1000; + + TeamCastle(const Team* team) : m_Team(team), m_Life(CastleMaxLife) { + SetWidth(5); + SetHeight(5); + } + + TeamCastle() : TeamCastle(nullptr) {} + + float GetLife() const { return m_Life; } + + const Team* GetTeam() const { return m_Team; } + void SetTeam(const Team* team) { m_Team = team; } + + void SetLife(float life) { m_Life = life; } + void Damage(float damage) { m_Life = std::max(0.0f, m_Life - damage); } + + void SetShape(utils::shape::Rectangle rect) { + SetCenter(rect.GetCenter()); + SetSize(rect.GetSize()); + } +}; + +class Team { +private: + std::vector m_Players; + TeamColor m_Color; + Spawn m_Spawn; + TeamCastle m_TeamCastle; +public: + Team(TeamColor color) : m_Color(color) {} + + void AddPlayer(Player* newPlayer); + void RemovePlayer(const Player* player); + + TeamColor GetColor() const; + + const Spawn& GetSpawn() const { return m_Spawn; } + Spawn& GetSpawn() { return m_Spawn; } + + const TeamCastle& GetCastle() const { return m_TeamCastle; } + TeamCastle& GetCastle() { return m_TeamCastle; } + + std::uint8_t GetPlayerCount() const; +}; + +typedef std::array TeamList; + +} // namespace game +} // namespace td diff --git a/include/td/game/Towers.h b/include/td/game/Towers.h new file mode 100644 index 0000000..af84e02 --- /dev/null +++ b/include/td/game/Towers.h @@ -0,0 +1,266 @@ +#pragma once + +#include +#include + +#include + +#include + +namespace td { +namespace game { + +class World; +class Mob; + +typedef std::shared_ptr MobPtr; + +enum class TowerType : std::uint8_t { + Archer = 0, + Ice, + Sorcerer, + Zeus, + Mage, + Artillery, + Quake, + Poison, + + Leach, + Turret, + Necromancer, + + TowerCount +}; + +enum class TowerSize : std::uint8_t { + Little = 3, // 3x3 + Big = 5, // 5x5 +}; + +enum class TowerPath : std::uint8_t { + Top = 0, + Base, // Base Path + Bottom +}; + +class TowerStats { +private: + float m_Rate; + float m_Damage; + std::uint8_t m_Range; +public: + TowerStats(float rate, float damage, std::uint8_t range) : m_Rate(rate), m_Damage(damage), + m_Range(range) { + } + + float GetDamageRate() const { return m_Rate; } + float GetDamage() const { return m_Damage; } + std::uint8_t GetRange() const { return m_Range; } +}; + +class TowerLevel { +private: + // 1, 2, 3, 4 + std::uint8_t m_Level : 3; + // 0 : base path 1 : top path (if there is bottom path) 2 : bottom path (if there is top path) + TowerPath m_Path : 2; +public: + TowerLevel() : m_Level(1), m_Path(TowerPath::Base) {} + TowerLevel(std::uint8_t level, TowerPath path) : m_Level(level), m_Path(path) {} + + std::uint8_t GetLevel() const { return m_Level; } + TowerPath GetPath() const { return m_Path; } + + void SetLevel(std::uint8_t level) { m_Level = level; } + void SetPath(TowerPath path) { m_Path = path; } + + // operator to sort maps + friend bool operator<(const TowerLevel& level, const TowerLevel& other) { + return level.GetLevel() + static_cast(level.GetPath()) * 4 < + other.GetLevel() + static_cast(other.GetPath()) * 4; + } +}; + +const TowerStats* GetTowerStats(TowerType type, TowerLevel level); + +typedef std::uint16_t TowerID; + +class Tower : public utils::shape::Circle { +private: + TowerID m_ID; + TowerType m_Type; + TowerLevel m_Level{}; + PlayerID m_Builder; +protected: + // utils::CooldownTimer m_Timer; +public: + Tower(TowerID id, TowerType type, std::int32_t x, std::int32_t y, PlayerID builder) : utils::shape::Circle(x + 0.5f, y + 0.5f, 0), m_ID(id), m_Type(type), m_Builder(builder) + // m_Timer(GetStats()->GetDamageRate() * 1000) + { // converting seconds to millis + SetRadius(GetStats()->GetRange()); + } + + virtual TowerType GetType() const = 0; + virtual TowerSize GetSize() const = 0; + virtual void Tick(std::uint64_t delta, World* world) = 0; + + void Upgrade(std::uint8_t level, TowerPath path) { + m_Level.SetLevel(level); + m_Level.SetPath(path); + // m_Timer.SetCooldown(GetStats()->GetDamageRate() * 1000); // converting seconds to millis + // m_Timer.Reset(); + SetRadius(GetStats()->GetRange()); + } + + std::uint16_t GetID() const { return m_ID; } + const TowerLevel& GetLevel() const { return m_Level; } + const TowerStats* GetStats() const { return GetTowerStats(m_Type, m_Level); } + PlayerID GetBuilder() const { return m_Builder; } + + bool IsMobInRange(MobPtr mob); +}; + +typedef std::shared_ptr TowerPtr; + +namespace TowerFactory { + +TowerPtr CreateTower(TowerType type, TowerID id, std::int32_t x, std::int32_t y, PlayerID builder); +std::string GetTowerName(TowerType type); + +} // namespace TowerFactory + + +class TowerInfo { +private: + std::string m_Name, m_Description; + bool m_IsBigTower; +public: + TowerInfo(std::string&& name, std::string&& description, bool big) : m_Name(std::move(name)), + m_Description(std::move(description)), m_IsBigTower(big) { + } + + const std::string& GetName() const { return m_Name; } + const std::string& GetDescription() const { return m_Description; } + + bool IsBigTower() const { return m_IsBigTower; } +}; + +const TowerInfo& GetTowerInfo(TowerType type); + +// ---------- Little Towers ---------- + +class LittleTower : public Tower { +public: + LittleTower(TowerID id, TowerType type, std::uint16_t x, std::uint16_t y, PlayerID builder) : Tower(id, type, x, y, builder) {} + + virtual TowerSize GetSize() const { return TowerSize::Little; } + + virtual TowerType GetType() const = 0; + virtual void Tick(std::uint64_t delta, World* world) = 0; +}; + +class ArcherTower : public LittleTower { +public: + ArcherTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {} + + constexpr static float ExplosionRadius = 1.5f; + constexpr static float FireDurationSec = 10.0f; + + virtual TowerType GetType() const { return TowerType::Archer; } + virtual void Tick(std::uint64_t delta, World* world); +}; + +class IceTower : public LittleTower { +public: + IceTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {} + + virtual TowerType GetType() const { return TowerType::Ice; } + virtual void Tick(std::uint64_t delta, World* world); +}; + +class MageTower : public LittleTower { +public: + MageTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {} + + virtual TowerType GetType() const { return TowerType::Mage; } + virtual void Tick(std::uint64_t delta, World* world); +}; + +class PoisonTower : public LittleTower { +public: + PoisonTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {} + + virtual TowerType GetType() const { return TowerType::Poison; } + virtual void Tick(std::uint64_t delta, World* world); +}; + +class QuakeTower : public LittleTower { +public: + QuakeTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {} + + virtual TowerType GetType() const { return TowerType::Quake; } + virtual void Tick(std::uint64_t delta, World* world); +}; + +class ArtilleryTower : public LittleTower { +public: + ArtilleryTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {} + + virtual TowerType GetType() const { return TowerType::Artillery; } + virtual void Tick(std::uint64_t delta, World* world); +}; + +class SorcererTower : public LittleTower { +public: + SorcererTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {} + + virtual TowerType GetType() const { return TowerType::Sorcerer; } + virtual void Tick(std::uint64_t delta, World* world); +}; + +class ZeusTower : public LittleTower { +public: + ZeusTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {} + + virtual TowerType GetType() const { return TowerType::Zeus; } + virtual void Tick(std::uint64_t delta, World* world); +}; + +// ---------- Big Towers ---------- + +class BigTower : public Tower { +public: + BigTower(TowerID id, TowerType type, std::uint16_t x, std::uint16_t y, PlayerID builder) : Tower(id, type, x, y, builder) {} + + virtual TowerSize GetSize() const { return TowerSize::Big; } + + virtual TowerType GetType() const = 0; + virtual void Tick(std::uint64_t delta, World* world) = 0; +}; + +class TurretTower : public BigTower { +public: + TurretTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : BigTower(id, GetType(), x, y, builder) {} + + virtual TowerType GetType() const { return TowerType::Turret; } + virtual void Tick(std::uint64_t delta, World* world); +}; + +class NecromancerTower : public BigTower { +public: + NecromancerTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : BigTower(id, GetType(), x, y, builder) {} + + virtual TowerType GetType() const { return TowerType::Necromancer; } + virtual void Tick(std::uint64_t delta, World* world); +}; + +class LeachTower : public BigTower { +public: + LeachTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : BigTower(id, GetType(), x, y, builder) {} + + virtual TowerType GetType() const { return TowerType::Leach; } + virtual void Tick(std::uint64_t delta, World* world); +}; + +} // namespace game +} // namespace td diff --git a/include/td/game/World.h b/include/td/game/World.h new file mode 100644 index 0000000..886cc4b --- /dev/null +++ b/include/td/game/World.h @@ -0,0 +1,137 @@ +#pragma once + +#include +#include + +namespace td { +namespace game { + +class World { + protected: + TowerTileColorPalette m_TowerPlacePalette; + Color m_WalkablePalette; + std::vector m_DecorationPalette; + Color m_Background; + + std::unordered_map m_Chunks; + + SpawnColorPalette m_SpawnColorPalette; + + TilePalette m_TilePalette; + + MobList m_Mobs; + + TowerList m_Towers; + + TeamList m_Teams; + + public: + World(); + + bool LoadMap(const protocol::pdata::WorldHeader& worldHeader); + bool LoadMap(const protocol::pdata::WorldData& worldData); + + bool LoadMapFromFile(const std::string& fileName); + bool SaveMap(const std::string& fileName) const; + + void Tick(std::uint64_t delta); + + void SpawnMobAt(MobID id, MobType type, std::uint8_t level, PlayerID sender, float x, float y, Direction dir); + + TowerPtr PlaceTowerAt(TowerID id, TowerType type, std::int32_t x, std::int32_t y, PlayerID builder); + TowerPtr RemoveTower(TowerID id); + + TilePtr GetTile(std::int32_t x, std::int32_t y) const; + + const TowerTileColorPalette& GetTowerTileColorPalette() const { + return m_TowerPlacePalette; + } + const Color& GetWalkableTileColor() const { + return m_WalkablePalette; + } + const std::vector& GetDecorationPalette() const { + return m_DecorationPalette; + } + const Color& GetBackgroundColor() const { + return m_Background; + } + + const TilePalette& GetTilePalette() const { + return m_TilePalette; + } + + TilePtr GetTilePtr(TileIndex index) const { + if (index == 0) + return nullptr; + return m_TilePalette.at(index - 1); + } + + bool CanPlaceLittleTower(const Vec2f& worldPos, PlayerID player) const; + bool CanPlaceBigTower(const Vec2f& worldPos, PlayerID player) const; + + TowerPtr GetTower(const Vec2f& position) const; // returns null if no tower is here + + const std::unordered_map& GetChunks() const { + return m_Chunks; + } + + const Color& GetSpawnColor(TeamColor color) const { + return m_SpawnColorPalette[static_cast(color)]; + } + const SpawnColorPalette& GetSpawnColors() const { + return m_SpawnColorPalette; + } + + const MobList& GetMobList() const { + return m_Mobs; + } + MobList& GetMobList() { + return m_Mobs; + } + + const Color* GetTileColor(TilePtr tile) const; + + Team& GetRedTeam() { + return m_Teams[static_cast(TeamColor::Red)]; + } + const Team& GetRedTeam() const { + return m_Teams[static_cast(TeamColor::Red)]; + } + + Team& GetBlueTeam() { + return m_Teams[static_cast(TeamColor::Blue)]; + } + const Team& GetBlueTeam() const { + return m_Teams[static_cast(TeamColor::Red)]; + } + + Team& GetTeam(TeamColor team) { + return m_Teams[static_cast(team)]; + } + const Team& GetTeam(TeamColor team) const { + return m_Teams[static_cast(team)]; + } + + const TeamList& GetTeams() const { + return m_Teams; + } + + const TowerList& GetTowers() const { + return m_Towers; + } + + TowerPtr GetTowerById(TowerID tower); + + const Player* GetPlayerById(PlayerID id) const; + + private: + void TickMobs(std::uint64_t delta); + void CleanDeadMobs(); +}; + + + + + +} // namespace game +} // namespace td diff --git a/include/td/game/WorldTypes.h b/include/td/game/WorldTypes.h new file mode 100644 index 0000000..8acdbd2 --- /dev/null +++ b/include/td/game/WorldTypes.h @@ -0,0 +1,119 @@ +#pragma once + +#include +#include +#include +#include + +namespace td { +namespace game { +struct ChunkCoord { + std::int16_t x, y; + + friend bool operator==(const td::game::ChunkCoord& first, const td::game::ChunkCoord& other) { + return first.x == other.x && first.y == other.y; + } +}; + + +class Game; + +enum class TileType : std::uint8_t { + None = 0, + Tower, + Walk, + Decoration, + /*Heal, + Lava, + Bedrock, + Freeze, + Ice,*/ +}; + +static constexpr Color BLACK{0, 0, 0}; +static constexpr Color WHITE{255, 255, 255}; + +static constexpr Color RED{255, 0, 0}; +static constexpr Color GREEN{0, 255, 0}; +static constexpr Color BLUE{0, 0, 255}; + +struct Tile { + virtual TileType GetType() const = 0; +}; + +struct TowerTile : Tile { + std::uint8_t color_palette_ref; + TeamColor team_owner; + + virtual TileType GetType() const { + return TileType::Tower; + } +}; + +struct WalkableTile : Tile { + Direction direction; + + virtual TileType GetType() const { + return TileType::Walk; + } +}; + +struct DecorationTile : Tile { + std::uint16_t color_palette_ref; + + virtual TileType GetType() const { + return TileType::Decoration; + } +}; + +typedef std::shared_ptr TilePtr; +typedef std::vector ChunkPalette; + +typedef std::shared_ptr WalkableTilePtr; + +typedef std::uint32_t TileIndex; + +// 32 x 32 area +struct Chunk { + enum { ChunkWidth = 32, ChunkHeight = 32, ChunkSize = ChunkWidth * ChunkHeight }; + typedef std::array ChunkData; + + // stores index of tile palette + ChunkData tiles{0}; + ChunkPalette palette; + + TileIndex GetTileIndex(std::uint16_t tileNumber) const { + TileIndex chunkPaletteIndex = tiles.at(tileNumber); + if (chunkPaletteIndex == 0) // index 0 means empty tile index 1 = first tile + return 0; + return palette.at(chunkPaletteIndex); + } +}; + +typedef std::shared_ptr ChunkPtr; + +typedef std::array TowerTileColorPalette; + +typedef std::vector TilePalette; + +typedef std::vector MobList; + +typedef std::array SpawnColorPalette; + +typedef std::vector TowerList; + + +} // namespace game + +} // namespace td + + + +namespace std { +template <> +struct hash { + std::size_t operator()(const td::game::ChunkCoord& key) const noexcept { + return std::hash()(key.x << 16 | key.y); + } +}; +} // namespace std \ No newline at end of file diff --git a/include/td/input/Display.h b/include/td/input/Display.h new file mode 100644 index 0000000..95f6b39 --- /dev/null +++ b/include/td/input/Display.h @@ -0,0 +1,43 @@ +#pragma once + +#include + +#include + +namespace td { + +class Display { + public: + Display(int a_Width, int a_Height, const std::string& a_Title); + ~Display(); + + void PollEvents(); + void Update(); + + bool IsCloseRequested() { + return m_ShouldClose; + } + + float GetAspectRatio() { + return m_AspectRatio; + } + + int GetWindowWidth() { + return m_LastWidth; + } + + int GetWindowHeight() { + return m_LastHeight; + } + + private: + SDL_Window* m_Window; + SDL_GLContext m_GLContext; + + int m_LastWidth, m_LastHeight; + float m_AspectRatio; + + bool m_ShouldClose; +}; + +} // namespace td \ No newline at end of file diff --git a/include/td/misc/Shapes.h b/include/td/misc/Shapes.h new file mode 100644 index 0000000..6a789f8 --- /dev/null +++ b/include/td/misc/Shapes.h @@ -0,0 +1,96 @@ +#pragma once + +#include + +namespace td { +namespace utils { +namespace shape { + +class Point { +private: + float m_X, m_Y; +public: + Point() : m_X(0), m_Y(0) {} + Point(float x, float y) : m_X(x), m_Y(y) {} + + float GetX() const { return m_X; } + float GetY() const { return m_Y; } + + void SetX(float x) { m_X = x; } + void SetY(float y) { m_Y = y; } + + float Distance(const Point& point) const; + float DistanceSquared(const Point& point) const; +}; + +class Circle; + +class Rectangle { +private: + Point m_Center; + float m_Width, m_Height; +public: + Rectangle() {} + + const Point& GetCenter() const { return m_Center; } + float GetCenterX() const { return m_Center.GetX(); } + float GetCenterY() const { return m_Center.GetY(); } + + float GetWidth() const { return m_Width; } + float GetHeight() const { return m_Height; } + + Point GetTopLeft() const { return { m_Center.GetX() - (m_Width / 2.0f), m_Center.GetY() - (m_Height / 2.0f) }; } + Point GetBottomRight() const { return { m_Center.GetX() + (m_Width / 2.0f), m_Center.GetY() + (m_Height / 2.0f) }; } + + void SetCenter(const Point& center) { m_Center = center; } + void SetCenterX(float x) { m_Center.SetX(x); } + void SetCenterY(float y) { m_Center.SetY(y); } + + void SetSize(float width, float height) { SetWidth(width); SetHeight(height); } + void SetSize(Point size) { SetSize(size.GetX(), size.GetY()); } + Point GetSize() { return { m_Width, m_Height }; } + + void SetWidth(float width) { m_Width = width; } + void SetHeight(float height) { m_Height = height; } + + bool CollidesWith(const Point& point) const; + bool CollidesWith(const Rectangle& rect) const; + bool CollidesWith(const Circle& circle) const; + + // distance from the closest side of the rectangle + float Distance(const Circle& circle) const; + float DistanceSquared(const Circle& circle) const; +}; + +class Circle { +private: + Point m_Center; + float m_Radius; +public: + Circle(float x, float y, float radius) : m_Center(x, y), m_Radius(radius) {} + Circle() : m_Radius(0) {} + + const Point& GetCenter() const { return m_Center; } + float GetCenterX() const { return m_Center.GetX(); } + float GetCenterY() const { return m_Center.GetY(); } + + float GetRadius() const { return m_Radius; } + + void SetCenter(const Point& center) { m_Center = center; } + void SetCenterX(float x) { m_Center.SetX(x); } + void SetCenterY(float y) { m_Center.SetY(y); } + + void SetRadius(float radius) { m_Radius = radius; } + + bool CollidesWith(const Point& point) const; + bool CollidesWith(const Rectangle& rect) const; + bool CollidesWith(const Circle& circle) const; + + // distance from the closest side of the rectangle + float Distance(const Rectangle& rect) const; + float DistanceSquared(const Rectangle& rect) const; +}; + +} // namespace shape +} // namespace utils +} // namespace td diff --git a/include/td/protocol/command/CommandData.h b/include/td/protocol/command/CommandData.h index ed3e850..bf01602 100644 --- a/include/td/protocol/command/CommandData.h +++ b/include/td/protocol/command/CommandData.h @@ -36,7 +36,7 @@ struct UseItem { struct TeamChange { sp::BitField m_Player; - sp::BitField m_NewTeam; + sp::BitField m_NewTeam; }; struct PlayerJoin { diff --git a/include/td/protocol/packet/PacketData.h b/include/td/protocol/packet/PacketData.h index 3bd37aa..de85032 100644 --- a/include/td/protocol/packet/PacketData.h +++ b/include/td/protocol/packet/PacketData.h @@ -4,6 +4,7 @@ #include #include #include +#include // Make it dynamic ? #define LOCKSTEP_BUFFER_SIZE 10 @@ -60,7 +61,6 @@ struct ChatMessage { // TODO: handle players joining in the first second struct BeginGame { - MapData m_Map; std::vector m_BlueTeam; std::vector m_RedTeam; // optional, used for players joining mid game @@ -72,6 +72,24 @@ struct LockSteps { Array m_LockSteps; }; +struct WorldHeader { + game::TowerTileColorPalette m_TowerPlacePalette; + Color m_WalkablePalette; + std::vector m_DecorationPalette; + Color m_Background; + + game::SpawnColorPalette m_SpawnColorPalette; + + game::TilePalette m_TilePalette; + + game::Spawn m_RedSpawn, m_BlueSpawn; + game::TeamCastle m_RedCastle, m_BlueCastle; +}; + +struct WorldData { + std::unordered_map m_Chunks; +}; + } // namespace pdata } // namespace protocol } // namespace td diff --git a/include/td/protocol/packet/Packets.h b/include/td/protocol/packet/Packets.h index 92822e1..4be3b2f 100644 --- a/include/td/protocol/packet/Packets.h +++ b/include/td/protocol/packet/Packets.h @@ -26,7 +26,9 @@ enum class PacketID : std::uint8_t { LoggingSuccess, PlayerJoin, PlayerLeave, - PlayerLogin + PlayerLogin, + WorldHeader, + WorldData, }; class PacketHandler; @@ -49,13 +51,15 @@ using LoggingSuccessPacket = PacketMessage; using PlayerLeavePacket = PacketMessage; using PlayerLoginPacket = PacketMessage; +using WorldHeaderPacket = PacketMessage; +using WorldDataPacket = PacketMessage; } // namespace packets using AllPackets = std::tuple; + packets::PlayerLeavePacket, packets::PlayerLoginPacket, packets::WorldHeaderPacket, packets::WorldDataPacket>; class PacketHandler : public sp::MessageHandler {}; diff --git a/include/td/render/Camera.h b/include/td/render/Camera.h new file mode 100644 index 0000000..1f621a1 --- /dev/null +++ b/include/td/render/Camera.h @@ -0,0 +1,37 @@ +#pragma once + +#include + +namespace td { +namespace render { + +class Camera { + private: + Mat4f m_ViewMatrix; + Mat4f m_ProjectionMatrix; + Mat4f m_InvViewMatrix; + Mat4f m_InvProjectionMatrix; + + float m_CamDistance = 25.0f; + Vec3f m_CamPos{0, m_CamDistance, 0}; + Vec2f m_CamLook{}; + + float m_Yaw = -PI / 2.0f; + float m_Pitch = -PI / 2.0f + 0.0000001f; + + public: + const Mat4f& GetViewMatrix() const { + return m_ViewMatrix; + } + + const Mat4f& GetProjectionMatrix() const { + return m_ProjectionMatrix; + } + + void UpdatePerspective(float a_AspectRatio); + + void SetCamPos(const Vec3f& a_NewPos); +}; + +} // namespace render +} // namespace td diff --git a/include/td/render/OpenGL.h b/include/td/render/OpenGL.h new file mode 100644 index 0000000..75fd640 --- /dev/null +++ b/include/td/render/OpenGL.h @@ -0,0 +1,8 @@ +#pragma once + +#ifdef TD_GL_LOADER_GLEW +#include +#else +#include +using namespace gl; +#endif diff --git a/include/td/render/Renderer.h b/include/td/render/Renderer.h new file mode 100644 index 0000000..407134c --- /dev/null +++ b/include/td/render/Renderer.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include + +namespace td { +namespace render { + +class Renderer { + protected: + Camera& m_Camera; + + public: + Renderer(Camera& a_Camera) : m_Camera(a_Camera) {} + virtual ~Renderer() {} + + virtual void Render() = 0; + + void Render(const GL::VertexArray& a_Vao); +}; + +class RenderPipeline { + private: + std::vector> m_Renderers; + + public: + RenderPipeline(); + ~RenderPipeline() = default; + + void AddRenderer(std::unique_ptr&& a_Renderer) { + m_Renderers.push_back(std::move(a_Renderer)); + } + + void Clear() { + m_Renderers.clear(); + } + + void Render() { + for (auto& renderer : m_Renderers) { + renderer->Render(); + } + } +}; + +} // namespace render +} // namespace td diff --git a/include/td/render/loader/GLLoader.h b/include/td/render/loader/GLLoader.h new file mode 100644 index 0000000..2447489 --- /dev/null +++ b/include/td/render/loader/GLLoader.h @@ -0,0 +1,86 @@ +#pragma once + +#include + +#include + +namespace td { +namespace GL { + +struct VertexAttribPointer { + unsigned int m_Index; + unsigned int m_Size; + unsigned int m_Offset; +}; + +class ElementBuffer : private sp::NonCopyable { + private: + unsigned int m_ID = 0; + std::size_t m_TriangleCount; + + public: + ElementBuffer(ElementBuffer&& other) { + std::swap(m_ID, other.m_ID); + m_TriangleCount = other.m_TriangleCount; + } + + explicit ElementBuffer(const std::vector& indicies); + ~ElementBuffer(); + + void Bind() const; + void Unbind() const; + + std::size_t GetTriangleCount() const { + return m_TriangleCount; + } +}; + +class VertexBuffer : private sp::NonCopyable { + private: + unsigned int m_ID = 0, m_DataStride; + std::vector m_VertexAttribs; + + public: + VertexBuffer(VertexBuffer&& other) { + std::swap(m_ID, other.m_ID); + m_VertexAttribs = std::move(other.m_VertexAttribs); + m_DataStride = other.m_DataStride; + } + + VertexBuffer(const std::vector& data, unsigned int stride); + ~VertexBuffer(); + + void Bind() const; + void Unbind() const; + void AddVertexAttribPointer(unsigned int index, unsigned int coordinateSize, unsigned int offset); + void BindVertexAttribs() const; +}; + +class VertexArray : private sp::NonCopyable { + private: + unsigned int m_ID = 0; + ElementBuffer m_ElementBuffer; + std::vector m_VertexBuffers; // use to destroy vbos when become unused + public: + VertexArray(VertexArray&& other) : m_ElementBuffer(std::move(other.m_ElementBuffer)) { + std::swap(m_ID, other.m_ID); + m_VertexBuffers = std::move(other.m_VertexBuffers); + } + + VertexArray(ElementBuffer&& indicies); + ~VertexArray(); + + std::size_t GetVertexCount() const { + return m_ElementBuffer.GetTriangleCount(); + } + + void BindVertexBuffer(VertexBuffer&& vbo); + void Bind() const; + void Unbind() const; + + private: + void BindElementArrayBuffer(); +}; + +} // namespace GL +} // namespace td diff --git a/include/td/render/loader/WorldLoader.h b/include/td/render/loader/WorldLoader.h new file mode 100644 index 0000000..b9d2482 --- /dev/null +++ b/include/td/render/loader/WorldLoader.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +namespace td { +namespace render { + +namespace WorldLoader { + +struct RenderData { + std::vector positions; + std::vector colors; +}; + +GL::VertexArray LoadMobModel(); +GL::VertexArray LoadWorldModel(const td::game::World* world); +GL::VertexArray LoadTileSelectModel(); +RenderData LoadTowerModel(game::TowerPtr tower); + +} // namespace WorldLoader + + +} // namespace render +} // namespace td diff --git a/include/td/render/renderer/WorldRenderer.h b/include/td/render/renderer/WorldRenderer.h new file mode 100644 index 0000000..61fd3c2 --- /dev/null +++ b/include/td/render/renderer/WorldRenderer.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include +#include + +namespace td { +namespace render { + +class WorldRenderer : public Renderer { + private: + const game::World& m_World; + shader::WorldShader m_Shader; + std::unique_ptr m_WorldVao; + + public: + WorldRenderer(Camera& a_Camera, const game::World& a_World); + virtual ~WorldRenderer(); + + virtual void Render() override; +}; + +} // namespace render +} // namespace td diff --git a/include/td/render/shader/EntityShader.h b/include/td/render/shader/EntityShader.h new file mode 100644 index 0000000..765d908 --- /dev/null +++ b/include/td/render/shader/EntityShader.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +namespace td { +namespace shader { + +class EntityShader : public ShaderProgram { + private: + unsigned int m_LocationProjectionMatrix = 0; + unsigned int m_LocationViewMatrix = 0; + unsigned int m_LocationPosition = 0; + unsigned int m_LocationColorEffect = 0; + + protected: + virtual void GetAllUniformLocation(); + + public: + EntityShader(); + + void LoadShader(); + + void SetColorEffect(const Vec3f& color); + void SetProjectionMatrix(const Mat4f& proj) const; + void SetViewMatrix(const Mat4f& view) const; + void SetModelPos(const Vec3f& pos) const; +}; + +} // namespace shader +} // namespace td diff --git a/include/td/render/shader/ShaderProgram.h b/include/td/render/shader/ShaderProgram.h new file mode 100755 index 0000000..23aef9d --- /dev/null +++ b/include/td/render/shader/ShaderProgram.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include + +namespace td { +namespace shader { + +class ShaderProgram { + public: + ShaderProgram(); + virtual ~ShaderProgram(); + + void Start() const; + void Stop() const; + + void LoadProgramFile(const std::string& vertexFile, const std::string& fragmentFile); + void LoadProgram(const std::string& vertexSource, const std::string& fragmentSource); + + protected: + virtual void GetAllUniformLocation() = 0; + int GetUniformLocation(const std::string& uniformName) const; + + void LoadFloat(unsigned int location, float value) const; + void LoadInt(unsigned int location, int value) const; + void LoadVector(unsigned int location, const Vec2f& vector) const; + void LoadVector(unsigned int location, const Vec3f& vector) const; + void LoadBoolean(unsigned int location, bool value) const; + void LoadMat4(unsigned int location, const Mat4f& mat) const; + void CleanUp() const; + + private: + unsigned int m_ProgramID; + unsigned int m_VertexShaderID; + unsigned int m_FragmentShaderID; + + unsigned int LoadShaderFromFile(const std::string& file, GLenum type); + unsigned int LoadShader(const std::string& source, GLenum type); +}; + +} // namespace shader +} // namespace td \ No newline at end of file diff --git a/include/td/render/shader/WorldShader.h b/include/td/render/shader/WorldShader.h new file mode 100644 index 0000000..f7d743e --- /dev/null +++ b/include/td/render/shader/WorldShader.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +namespace td { +namespace shader { + +class WorldShader : public ShaderProgram { +private: + unsigned int m_LocationProjection = 0, m_LocationView = 0; +protected: + void GetAllUniformLocation(); +public: + WorldShader(); + + void SetProjectionMatrix(const Mat4f& proj) const; + void SetViewMatrix(const Mat4f& view) const; +}; + +} // namespace shader +} // namespace td diff --git a/src/main.cpp b/src/main.cpp index 80fa251..5dd40b2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,18 +1,210 @@ #include -// #include -#include -#include -class Test : public td::protocol::CommandHandler {}; +#include +#include +#include + +#include +#include +#include + +namespace td { +namespace game { + +sp::DataBuffer& operator>>(sp::DataBuffer& buffer, game::TilePtr& tile) { + game::TileType tileType; + buffer >> tileType; + switch (tileType) { + case game::TileType::Tower: { + auto tilePtr = std::make_shared(); + buffer >> tilePtr->color_palette_ref >> tilePtr->team_owner; + tile = tilePtr; + break; + } + case game::TileType::Walk: { + auto tilePtr = std::make_shared(); + buffer >> tilePtr->direction; + tile = tilePtr; + break; + } + case game::TileType::Decoration: { + auto tilePtr = std::make_shared(); + buffer >> tilePtr->color_palette_ref; + tile = tilePtr; + break; + } + default: + break; + } + return buffer; +} +} // namespace game +} // namespace td + +namespace sp { +namespace details { + +template <> +void ReadMessage(DataBuffer& a_Buffer, td::protocol::pdata::WorldHeader& a_Header) { + a_Buffer >> a_Header.m_TowerPlacePalette >> a_Header.m_WalkablePalette; + + std::uint16_t decoPaletteSize; + a_Buffer >> decoPaletteSize; + + std::size_t decoPalletteSizeByte = decoPaletteSize * sizeof(td::Color); + + a_Header.m_DecorationPalette.resize(decoPaletteSize); + + memcpy(reinterpret_cast(a_Header.m_DecorationPalette.data()), a_Buffer.data() + a_Buffer.GetReadOffset(), + decoPalletteSizeByte); + + a_Buffer.SetReadOffset(a_Buffer.GetReadOffset() + decoPalletteSizeByte); + + a_Buffer >> a_Header.m_Background; + + td::utils::shape::Rectangle redCastle, blueCastle; + + a_Buffer >> a_Header.m_RedSpawn >> redCastle; + a_Buffer >> a_Header.m_BlueSpawn >> blueCastle; + + a_Header.m_RedCastle.SetShape(redCastle); + a_Header.m_BlueCastle.SetShape(blueCastle); + + std::uint64_t tilePaletteSize; + a_Buffer >> tilePaletteSize; + + a_Header.m_TilePalette.reserve(tilePaletteSize); + + for (std::uint64_t tileNumber = 0; tileNumber < tilePaletteSize; tileNumber++) { + td::game::TilePtr tile; + a_Buffer >> tile; + a_Header.m_TilePalette.push_back(tile); + } + + a_Buffer >> a_Header.m_SpawnColorPalette; +} + +typedef std::vector ChunkPackedData; + +const int BITS_IN_BYTE = 8; +const int BITS_IN_LONG = BITS_IN_BYTE * sizeof(std::uint64_t); + +static unsigned int countBits(unsigned int number) { + // log function in base 2 + // take only integer part + return static_cast(std::log2(number) + 1); +} + + +template <> +void ReadMessage(DataBuffer& a_Buffer, td::protocol::pdata::WorldData& a_WorldData) { + std::uint64_t chunkCount; + a_Buffer >> chunkCount; + + for (std::uint64_t chunkNumber = 0; chunkNumber < chunkCount; chunkNumber++) { + td::game::ChunkPtr chunk = std::make_shared(); + + td::game::ChunkCoord chunkCoords; + a_Buffer >> chunkCoords.x >> chunkCoords.y; + + std::uint64_t chunkPaletteSize; + // std::reverse(reinterpret_cast(&chunkPaletteSize), reinterpret_cast(&chunkPaletteSize) + 4); + a_Buffer >> chunkPaletteSize; + + td::game::ChunkPalette chunkPalette(chunkPaletteSize); + + memcpy(reinterpret_cast(chunkPalette.data()), a_Buffer.data() + a_Buffer.GetReadOffset(), + chunkPaletteSize * sizeof(td::game::ChunkPalette::value_type)); + a_Buffer.SetReadOffset(a_Buffer.GetReadOffset() + chunkPaletteSize * sizeof(td::game::ChunkPalette::value_type)); + + chunk->palette = chunkPalette; + + std::uint8_t bitsPerTile = countBits(chunkPaletteSize); + + // A bitmask that contains bitsPerTile set bits + td::game::Chunk::ChunkData::value_type individualValueMask = ((1 << bitsPerTile) - 1); + + ChunkPackedData chunkData(td::game::Chunk::ChunkSize / (BITS_IN_BYTE * sizeof(ChunkPackedData::value_type) / bitsPerTile), 0); + + memcpy(reinterpret_cast(chunkData.data()), a_Buffer.data() + a_Buffer.GetReadOffset(), + chunkData.size() * sizeof(ChunkPackedData::value_type)); + a_Buffer.SetReadOffset(a_Buffer.GetReadOffset() + chunkData.size() * sizeof(ChunkPackedData::value_type)); + + for (unsigned int tileNumber = 0; tileNumber < td::game::Chunk::ChunkSize; tileNumber++) { + std::size_t startLong = (tileNumber * bitsPerTile) / BITS_IN_LONG; + std::size_t startOffset = (tileNumber * bitsPerTile) % BITS_IN_LONG; + std::size_t endLong = ((tileNumber + 1) * bitsPerTile - 1) / BITS_IN_LONG; + + td::game::Chunk::ChunkData::value_type value; + if (startLong == endLong) { + value = (chunkData[startLong] >> startOffset); + } else { + int endOffset = BITS_IN_LONG - startOffset; + value = (chunkData[startLong] >> startOffset | chunkData[endLong] << endOffset); + } + value &= individualValueMask; + + chunk->tiles[tileNumber] = value; + } + a_WorldData.m_Chunks.insert({chunkCoords, chunk}); + } +} + +} // namespace details +} // namespace sp + +class WorldApply : public td::protocol::PacketHandler { + private: + td::game::World& m_World; + public: + WorldApply(td::game::World& a_World) : m_World(a_World) {} + + void Handle(const td::protocol::pdata::WorldHeader& a_Header) override { + m_World.LoadMap(a_Header); + } + void Handle(const td::protocol::pdata::WorldData& a_Data) override { + m_World.LoadMap(a_Data); + } +}; int main(int argc, char** argv) { - // Test visitor; - // td::protocol::packets::ChatMessage chat{{"coucou"}}; - // visitor.Check(chat); + sp::DataBuffer buffer; + buffer.ReadFile("test/tdmap.tdmap2"); + sp::DataBuffer buffer1 = sp::zlib::Decompress(buffer, 84); + buffer.SetReadOffset(buffer.GetReadOffset() + 83); + sp::DataBuffer buffer2 = sp::zlib::Decompress(buffer, 511); + + td::protocol::packets::WorldHeaderPacket header; + header.Read(buffer1); + + td::protocol::packets::WorldDataPacket data; + data.Read(buffer2); + + td::game::World w; + WorldApply wa(w); + + td::protocol::PacketDispatcher d; + d.RegisterHandler(td::protocol::PacketID::WorldData, &wa); + d.RegisterHandler(td::protocol::PacketID::WorldHeader, &wa); + + d.Dispatch(header); + d.Dispatch(data); + + td::Display display(1920, 1080, "Tower-Defense 2"); + + td::render::Camera cam; + cam.SetCamPos({77, 25, 13}); + cam.UpdatePerspective(display.GetAspectRatio()); + + td::render::RenderPipeline renderer; + renderer.AddRenderer(std::make_unique(cam, w)); + + while (!display.IsCloseRequested()) { + display.PollEvents(); + renderer.Render(); + display.Update(); + } - td::protocol::commands::UpgradeTowerCommand com{1, 2}; - std::cout << (unsigned)com.GetId() << std::endl; - td::protocol::CommandDispatcher disptacher; return 0; } diff --git a/src/td/Maths.cpp b/src/td/Maths.cpp new file mode 100644 index 0000000..97358af --- /dev/null +++ b/src/td/Maths.cpp @@ -0,0 +1,169 @@ +#include + +#include +#include + +namespace td { +namespace maths { + +template +T Length(const Vec3& vect) { + return std::sqrt(vect.x * vect.x + vect.y * vect.y + vect.z * vect.z); +} + +template +Vec3 Normalize(const Vec3& vect) { + T length = Length(vect); + + return {vect.x / length, vect.y / length, vect.z / length}; +} + +template +Vec4 Normalize(const Vec4& vect) { + T length = std::sqrt(vect.x * vect.x + vect.y * vect.y + vect.z * vect.z + vect.w * vect.w); + + return {vect.x / length, vect.y / length, vect.z / length, vect.w / length}; +} + +template +T Dot(const Vec3& vect, const Vec3& other) { + return vect.x * other.x + vect.y * other.y + vect.z * other.z; +} + +template +Vec3 Cross(const Vec3& vect, const Vec3& other) { + return { + vect.y * other.z - vect.z * other.y, + vect.z * other.x - vect.x * other.z, + vect.x * other.y - vect.y * other.x, + }; +} + +template +T Dot(const Vec4& vect, const Vec4& other) { + return vect.x * other.x + vect.y * other.y + vect.z * other.z + vect.w * other.w; +} + +template +T Distance(const Vec3& vect, const Vec3& other) { + return Length(vect - other); +} + +template +Vec4 Dot(const Mat4& mat, const Vec4& vect) { + return {mat.x0 * vect.x + mat.x1 * vect.y + mat.x2 * vect.z + mat.x3 * vect.w, + mat.y0 * vect.x + mat.y1 * vect.y + mat.y2 * vect.z + mat.y3 * vect.w, + mat.z0 * vect.x + mat.z1 * vect.y + mat.z2 * vect.z + mat.z3 * vect.w, + mat.w0 * vect.x + mat.w1 * vect.y + mat.w2 * vect.z + mat.w3 * vect.w}; +} + +template +Mat4 Dot(const Mat4& mat, const Mat4& other) { + Mat4 result{}; + + for (std::size_t i = 0; i < Mat4::MATRIX_SIZE; i++) { + for (std::size_t j = 0; j < Mat4::MATRIX_SIZE; j++) { + for (std::size_t k = 0; k < Mat4::MATRIX_SIZE; k++) { + result.at(i, j) += mat.at(i, k) * other.at(k, j); + } + } + } + + return result; +} + +template +Mat4 Identity() { + Mat4 result{}; + + result.x0 = static_cast(1); + result.y1 = static_cast(1); + result.z2 = static_cast(1); + result.w3 = static_cast(1); + + return result; +} + +Mat4f Perspective(float fovY, float aspectRatio, float zNear, float zFar) { + const float tanHalfFovy = std::tan(fovY / 2.0f); + + Mat4f result{}; + result.x0 = 1.0f / (aspectRatio * tanHalfFovy); + result.y1 = 1.0f / (tanHalfFovy); + result.z2 = -(zFar + zNear) / (zFar - zNear); + result.z3 = -1.0f; + result.w2 = -(2.0f * zFar * zNear) / (zFar - zNear); + + return result; +} + +Mat4f Look(const Vec3f& eye, const Vec3f& center, const Vec3f& up) { + const Vec3f f = Normalize(center); + const Vec3f s = Normalize(Cross(f, up)); + const Vec3f u = Cross(s, f); + + Mat4f result = Identity(); + result.x0 = s.x; + result.y0 = s.y; + result.z0 = s.z; + result.x1 = u.x; + result.y1 = u.y; + result.z1 = u.z; + result.x2 = -f.x; + result.y2 = -f.y; + result.z2 = -f.z; + result.w0 = -Dot(s, eye); + result.w1 = -Dot(u, eye); + result.w2 = Dot(f, eye); + + return result; +} + +Mat4f Inverse(const Mat4f& mat) { + float s0 = mat.at(0, 0) * mat.at(1, 1) - mat.at(1, 0) * mat.at(0, 1); + float s1 = mat.at(0, 0) * mat.at(1, 2) - mat.at(1, 0) * mat.at(0, 2); + float s2 = mat.at(0, 0) * mat.at(1, 3) - mat.at(1, 0) * mat.at(0, 3); + float s3 = mat.at(0, 1) * mat.at(1, 2) - mat.at(1, 1) * mat.at(0, 2); + float s4 = mat.at(0, 1) * mat.at(1, 3) - mat.at(1, 1) * mat.at(0, 3); + float s5 = mat.at(0, 2) * mat.at(1, 3) - mat.at(1, 2) * mat.at(0, 3); + + float c5 = mat.at(2, 2) * mat.at(3, 3) - mat.at(3, 2) * mat.at(2, 3); + float c4 = mat.at(2, 1) * mat.at(3, 3) - mat.at(3, 1) * mat.at(2, 3); + float c3 = mat.at(2, 1) * mat.at(3, 2) - mat.at(3, 1) * mat.at(2, 2); + float c2 = mat.at(2, 0) * mat.at(3, 3) - mat.at(3, 0) * mat.at(2, 3); + float c1 = mat.at(2, 0) * mat.at(3, 2) - mat.at(3, 0) * mat.at(2, 2); + float c0 = mat.at(2, 0) * mat.at(3, 1) - mat.at(3, 0) * mat.at(2, 1); + + float det = s0 * c5 - s1 * c4 + s2 * c3 + s3 * c2 - s4 * c1 + s5 * c0; + + assert(det != 0 && "Determinant equals 0 !"); + + float invdet = 1.0 / det; + + Mat4f result; + + result.at(0, 0) = (mat.at(1, 1) * c5 - mat.at(1, 2) * c4 + mat.at(1, 3) * c3) * invdet; + result.at(0, 1) = (-mat.at(0, 1) * c5 + mat.at(0, 2) * c4 - mat.at(0, 3) * c3) * invdet; + result.at(0, 2) = (mat.at(3, 1) * s5 - mat.at(3, 2) * s4 + mat.at(3, 3) * s3) * invdet; + result.at(0, 3) = (-mat.at(2, 1) * s5 + mat.at(2, 2) * s4 - mat.at(2, 3) * s3) * invdet; + + result.at(1, 0) = (-mat.at(1, 0) * c5 + mat.at(1, 2) * c2 - mat.at(1, 3) * c1) * invdet; + result.at(1, 1) = (mat.at(0, 0) * c5 - mat.at(0, 2) * c2 + mat.at(0, 3) * c1) * invdet; + result.at(1, 2) = (-mat.at(3, 0) * s5 + mat.at(3, 2) * s2 - mat.at(3, 3) * s1) * invdet; + result.at(1, 3) = (mat.at(2, 0) * s5 - mat.at(2, 2) * s2 + mat.at(2, 3) * s1) * invdet; + + result.at(2, 0) = (mat.at(1, 0) * c4 - mat.at(1, 1) * c2 + mat.at(1, 3) * c0) * invdet; + result.at(2, 1) = (-mat.at(0, 0) * c4 + mat.at(0, 1) * c2 - mat.at(0, 3) * c0) * invdet; + result.at(2, 2) = (mat.at(3, 0) * s4 - mat.at(3, 1) * s2 + mat.at(3, 3) * s0) * invdet; + result.at(2, 3) = (-mat.at(2, 0) * s4 + mat.at(2, 1) * s2 - mat.at(2, 3) * s0) * invdet; + + result.at(3, 0) = (-mat.at(1, 0) * c3 + mat.at(1, 1) * c1 - mat.at(1, 2) * c0) * invdet; + result.at(3, 1) = (mat.at(0, 0) * c3 - mat.at(0, 1) * c1 + mat.at(0, 2) * c0) * invdet; + result.at(3, 2) = (-mat.at(3, 0) * s3 + mat.at(3, 1) * s1 - mat.at(3, 2) * s0) * invdet; + result.at(3, 3) = (mat.at(2, 0) * s3 - mat.at(2, 1) * s1 + mat.at(2, 2) * s0) * invdet; + + return result; +} + +} // namespace maths +} // namespace td \ No newline at end of file diff --git a/src/td/common/DataBuffer.cpp b/src/td/common/DataBuffer.cpp deleted file mode 100644 index 9ed5062..0000000 --- a/src/td/common/DataBuffer.cpp +++ /dev/null @@ -1,163 +0,0 @@ -#include - -#include -#include -#include - -#include -#include - -namespace td { - -DataBuffer::DataBuffer() : m_ReadOffset(0) {} - -DataBuffer::DataBuffer(const DataBuffer& other) : m_Buffer(other.m_Buffer), m_ReadOffset(other.m_ReadOffset) {} - -DataBuffer::DataBuffer(DataBuffer&& other) : m_Buffer(std::move(other.m_Buffer)), m_ReadOffset(std::move(other.m_ReadOffset)) {} - -DataBuffer::DataBuffer(const std::string& str) : m_Buffer(str.begin(), str.end()), m_ReadOffset(0) {} - -DataBuffer::DataBuffer(const DataBuffer& other, Data::difference_type offset) : m_ReadOffset(0) { - m_Buffer.reserve(other.GetSize() - static_cast(offset)); - std::copy(other.m_Buffer.begin() + offset, other.m_Buffer.end(), std::back_inserter(m_Buffer)); -} - -DataBuffer& DataBuffer::operator<<(const std::string& str) { - std::size_t strlen = str.length() + 1; // including null character - Resize(GetSize() + strlen); - std::memcpy(m_Buffer.data() + GetSize() - strlen, str.data(), strlen); - return *this; -} - -DataBuffer& DataBuffer::operator<<(const DataBuffer& data) { - m_Buffer.insert(m_Buffer.end(), data.begin(), data.end()); - return *this; -} - -DataBuffer& DataBuffer::operator>>(std::string& str) { - std::size_t stringSize = strlen(reinterpret_cast(m_Buffer.data()) + m_ReadOffset) + 1; // including null character - str.resize(stringSize); - std::copy(m_Buffer.begin() + static_cast(m_ReadOffset), - m_Buffer.begin() + static_cast(m_ReadOffset + stringSize), str.begin()); - m_ReadOffset += stringSize; - str.resize(stringSize - 1); - return *this; -} - -DataBuffer& DataBuffer::operator>>(DataBuffer& data) { - data.Resize(GetSize() - m_ReadOffset); - std::copy(m_Buffer.begin() + static_cast(m_ReadOffset), m_Buffer.end(), data.begin()); - m_ReadOffset = m_Buffer.size(); - return *this; -} - -void DataBuffer::WriteSome(const char* buffer, std::size_t amount) { - std::size_t end_pos = m_Buffer.size(); - m_Buffer.resize(m_Buffer.size() + amount); - std::memcpy(m_Buffer.data() + end_pos, buffer, amount); -} - -void DataBuffer::WriteSome(const std::uint8_t* buffer, std::size_t amount) { - std::size_t end_pos = m_Buffer.size(); - m_Buffer.resize(m_Buffer.size() + amount); - std::memcpy(m_Buffer.data() + end_pos, buffer, amount); -} - -void DataBuffer::ReadSome(char* buffer, std::size_t amount) { - assert(m_ReadOffset + amount <= GetSize()); - std::copy_n(m_Buffer.begin() + static_cast(m_ReadOffset), amount, buffer); - m_ReadOffset += amount; -} - - -void DataBuffer::ReadSome(std::uint8_t* buffer, std::size_t amount) { - assert(m_ReadOffset + amount <= GetSize()); - std::copy_n(m_Buffer.begin() + static_cast(m_ReadOffset), amount, buffer); - m_ReadOffset += amount; -} - - -void DataBuffer::ReadSome(DataBuffer& buffer, std::size_t amount) { - assert(m_ReadOffset + amount <= GetSize()); - buffer.Resize(amount); - std::copy_n(m_Buffer.begin() + static_cast(m_ReadOffset), amount, buffer.begin()); - m_ReadOffset += amount; -} - - -DataBuffer& DataBuffer::operator=(const DataBuffer& other) { - m_Buffer = other.m_Buffer; - m_ReadOffset = other.m_ReadOffset; - return *this; -} - -DataBuffer& DataBuffer::operator=(DataBuffer&& other) { - m_Buffer = std::move(other.m_Buffer); - m_ReadOffset = std::move(other.m_ReadOffset); - return *this; -} - -void DataBuffer::SetReadOffset(std::size_t pos) { - assert(pos <= GetSize()); - m_ReadOffset = pos; -} - -std::size_t DataBuffer::GetSize() const { - return m_Buffer.size(); -} - -std::size_t DataBuffer::GetRemaining() const { - return m_Buffer.size() - m_ReadOffset; -} - -DataBuffer::iterator DataBuffer::begin() { - return m_Buffer.begin(); -} - -DataBuffer::iterator DataBuffer::end() { - return m_Buffer.end(); -} - -DataBuffer::const_iterator DataBuffer::begin() const { - return m_Buffer.begin(); -} - -DataBuffer::const_iterator DataBuffer::end() const { - return m_Buffer.end(); -} - -std::ostream& operator<<(std::ostream& os, const DataBuffer& buffer) { - for (unsigned char u : buffer) - os << std::hex << std::setfill('0') << std::setw(2) << static_cast(u) << " "; - os << std::dec; - return os; -} - -bool DataBuffer::ReadFile(const std::string& fileName) { - try { - std::ifstream file(fileName, std::istream::binary); - std::ostringstream ss; - ss << file.rdbuf(); - const std::string& s = ss.str(); - m_Buffer = DataBuffer::Data(s.begin(), s.end()); - m_ReadOffset = 0; - } catch (std::exception& e) { - utils::LOGE(utils::Format("[IO] Failed to read file %s ! reason : %s", fileName.c_str(), e.what())); - return false; - } - return m_Buffer.size() > 0; -} - -bool DataBuffer::WriteFile(const std::string& fileName) const { - try { - std::ofstream file(fileName, std::ostream::binary); - file.write(reinterpret_cast(m_Buffer.data()), static_cast(m_Buffer.size())); - file.flush(); - } catch (std::exception& e) { - utils::LOGE(utils::Format("[IO] Failed to read file %s ! reason : %s", fileName.c_str(), e.what())); - return false; - } - return true; -} - -} // namespace td diff --git a/src/td/common/VarInt.cpp b/src/td/common/VarInt.cpp deleted file mode 100644 index 0136f2f..0000000 --- a/src/td/common/VarInt.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include - -#include -#include - -namespace td { - -static constexpr int SEGMENT_BITS = 0x7F; -static constexpr int CONTINUE_BIT = 0x80; - -std::size_t VarInt::GetSerializedLength() const { - DataBuffer buffer; - buffer << *this; - return buffer.GetSize(); -} - -DataBuffer& operator<<(DataBuffer& out, const VarInt& var) { - std::uint64_t value = var.m_Value; - while (true) { - if ((value & ~static_cast(SEGMENT_BITS)) == 0) { - out << static_cast(value); - return out; - } - - out << static_cast((value & SEGMENT_BITS) | CONTINUE_BIT); - - value >>= 7; - } -} - -DataBuffer& operator>>(DataBuffer& in, VarInt& var) { - var.m_Value = 0; - unsigned int position = 0; - std::uint8_t currentByte; - - while (true) { - in.ReadSome(¤tByte, 1); - var.m_Value |= static_cast(currentByte & SEGMENT_BITS) << position; - - if ((currentByte & CONTINUE_BIT) == 0) - break; - - position += 7; - - if (position >= 8 * sizeof(var.m_Value)) - throw std::runtime_error("VarInt is too big"); - } - - return in; -} - -} // namespace td diff --git a/src/td/game/World.cpp b/src/td/game/World.cpp new file mode 100644 index 0000000..035bb7b --- /dev/null +++ b/src/td/game/World.cpp @@ -0,0 +1,59 @@ +#include + +namespace td { +namespace game { + +World::World() : m_Teams{Team{TeamColor::Red}, Team{TeamColor::Blue}} { + +} + +const Color* World::GetTileColor(TilePtr tile) const { + switch (tile->GetType()) { + case TileType::Tower: { + TowerTile* towerTile = dynamic_cast(tile.get()); + return &m_TowerPlacePalette[towerTile->color_palette_ref]; + } + case TileType::Walk: { + return &m_WalkablePalette; + } + case TileType::Decoration: { + DecorationTile* towerTile = dynamic_cast(tile.get()); + return &m_DecorationPalette[towerTile->color_palette_ref]; + break; + } + default: { + return nullptr; + } + } + return nullptr; +} + +bool World::LoadMap(const protocol::pdata::WorldHeader& a_WorldHeader) { + m_TowerPlacePalette = a_WorldHeader.m_TowerPlacePalette; + m_WalkablePalette = a_WorldHeader.m_WalkablePalette; + m_DecorationPalette = a_WorldHeader.m_DecorationPalette; + m_Background = a_WorldHeader.m_Background; + + GetRedTeam().GetSpawn() = a_WorldHeader.m_RedSpawn; + GetBlueTeam().GetSpawn() = a_WorldHeader.m_BlueSpawn; + + m_SpawnColorPalette = a_WorldHeader.m_SpawnColorPalette; + + GetRedTeam().GetCastle() = a_WorldHeader.m_RedCastle; + GetRedTeam().GetCastle().SetTeam(&GetRedTeam()); + + GetBlueTeam().GetCastle() = a_WorldHeader.m_BlueCastle; + GetBlueTeam().GetCastle().SetTeam(&GetBlueTeam()); + + m_TilePalette = a_WorldHeader.m_TilePalette; + + return true; +} + +bool World::LoadMap(const protocol::pdata::WorldData& a_WorldData) { + m_Chunks = a_WorldData.m_Chunks; + return true; +} + +} // namespace game +} // namespace td diff --git a/src/td/input/Display.cpp b/src/td/input/Display.cpp new file mode 100644 index 0000000..ec3a2cc --- /dev/null +++ b/src/td/input/Display.cpp @@ -0,0 +1,156 @@ +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace td { + +Display::Display(int a_Width, int a_Height, const std::string& a_Title) : + m_LastWidth(0), m_LastHeight(0), m_AspectRatio(1), m_ShouldClose(false) { + + m_Window = SDL_CreateWindow(a_Title.c_str(), a_Width, a_Height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); + + m_LastWidth = a_Width; + m_LastHeight = a_Height; + m_AspectRatio = (float)m_LastWidth / m_LastHeight; + + // Prepare and create context +#ifdef __ANDROID__ + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); +#else + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); +#endif + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); + + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + + m_GLContext = SDL_GL_CreateContext(m_Window); + + if (!m_GLContext) { + utils::LOGE(utils::Format("Could not create context! SDL error: %s", SDL_GetError())); + } + + int major, minor, mask; + int r, g, b, a, depth; + SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &mask); + SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major); + SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor); + + SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &r); + SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &g); + SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &b); + SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &a); + + SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depth); + + const char* mask_desc; + + if (mask & SDL_GL_CONTEXT_PROFILE_CORE) { + mask_desc = "core"; + } else if (mask & SDL_GL_CONTEXT_PROFILE_COMPATIBILITY) { + mask_desc = "compatibility"; + } else if (mask & SDL_GL_CONTEXT_PROFILE_ES) { + mask_desc = "es"; + } else { + mask_desc = "?"; + } + + utils::LOG(utils::Format( + "GL Context : %i.%i %s, Color : R:%i G:%i B:%i A:%i, Depth bits : %i", major, minor, mask_desc, r, g, b, a, depth)); + + SDL_GL_MakeCurrent(m_Window, m_GLContext); + + GLenum error = glewInit(); + if (error) { + utils::LOGE(utils::Format("Error initializing glew : %s", glewGetErrorString(error))); + } + + // WindowResizeEvent(WindowWidth, WindowHeight); + + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); + (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + // ImGui::StyleColorsLight(); + + // Setup scaling + float main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this + // requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (using io.ConfigDpiScaleFonts=true makes this unnecessary. We leave + // both here for documentation purpose) + + // Setup Platform/Renderer backends + ImGui_ImplSDL3_InitForOpenGL(m_Window, m_GLContext); + ImGui_ImplOpenGL3_Init("#version 330"); +} + + +void Display::PollEvents() { + SDL_Event event; + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_EVENT_QUIT: + case SDL_EVENT_WINDOW_CLOSE_REQUESTED: { + m_ShouldClose = true; + } + + case SDL_EVENT_WINDOW_RESIZED: { + m_LastWidth = event.window.data1; + m_LastHeight = event.window.data2; + m_AspectRatio = (float)m_LastWidth / m_LastHeight; + } + + default: + break; + } + ImGui_ImplSDL3_ProcessEvent(&event); + } + // Start the Dear ImGui frame + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplSDL3_NewFrame(); + ImGui::NewFrame(); +} + +void Display::Update() { + ImGui::Render(); + ImGuiIO& io = ImGui::GetIO(); + glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y); + // glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + SDL_GL_SwapWindow(m_Window); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + + +Display::~Display() { + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplSDL3_Shutdown(); + ImGui::DestroyContext(); + SDL_GL_DestroyContext(m_GLContext); + SDL_DestroyWindow(m_Window); + SDL_Quit(); +} + +} // namespace td diff --git a/src/td/render/Camera.cpp b/src/td/render/Camera.cpp new file mode 100644 index 0000000..e1bfe19 --- /dev/null +++ b/src/td/render/Camera.cpp @@ -0,0 +1,26 @@ +#include + +#include + +namespace td { +namespace render { + +void Camera::UpdatePerspective(float a_AspectRatio) { + m_ProjectionMatrix = maths::Perspective(80.0f / 180.0f * PI, a_AspectRatio, 0.1f, 160.0f); + m_InvProjectionMatrix = maths::Inverse(m_ProjectionMatrix); +} + +void Camera::SetCamPos(const Vec3f& a_NewPos) { + Vec3f front = { + std::cos(m_Yaw) * std::cos(m_Pitch), + std::sin(m_Pitch), + std::sin(m_Yaw) * std::cos(m_Pitch) + }; + + m_CamPos = a_NewPos; + m_ViewMatrix = maths::Look(m_CamPos, front, { 0, 1, 0 }); + m_InvViewMatrix = maths::Transpose(maths::Inverse(m_ViewMatrix)); // why transpose ? I don't know +} + +} // namespace render +} // namespace td diff --git a/src/td/render/Renderer.cpp b/src/td/render/Renderer.cpp new file mode 100644 index 0000000..cf811eb --- /dev/null +++ b/src/td/render/Renderer.cpp @@ -0,0 +1,26 @@ +#include + +#include + +namespace td { +namespace render { + +void Renderer::Render(const GL::VertexArray& a_Vao) { + a_Vao.Bind(); + glDrawArrays(GL_TRIANGLES, 0, a_Vao.GetVertexCount()); + // glDrawElements(GL_TRIANGLES, a_Vao.GetVertexCount(), GL_UNSIGNED_INT, nullptr); + a_Vao.Unbind(); +} + +RenderPipeline::RenderPipeline() { + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthFunc(GL_LESS); + glFrontFace(GL_CCW); +} + +} // namespace render +} // namespace td diff --git a/src/td/render/loader/GLLoader.cpp b/src/td/render/loader/GLLoader.cpp new file mode 100644 index 0000000..48b388d --- /dev/null +++ b/src/td/render/loader/GLLoader.cpp @@ -0,0 +1,99 @@ +#include + +#include + +namespace td { +namespace GL { + +VertexArray::~VertexArray() { + if (m_ID != 0) + glDeleteVertexArrays(1, &m_ID); +} + +VertexArray::VertexArray(ElementBuffer&& indicies) : m_ElementBuffer(std::move(indicies)) { + glGenVertexArrays(1, &m_ID); + Bind(); + BindElementArrayBuffer(); + // Unbind(); +} + +void VertexArray::Bind() const { + glBindVertexArray(m_ID); +} + +void VertexArray::Unbind() const { + glBindVertexArray(0); +} + +void VertexArray::BindVertexBuffer(VertexBuffer&& VertexBuffer) { + VertexBuffer.Bind(); + VertexBuffer.BindVertexAttribs(); + m_VertexBuffers.push_back(std::move(VertexBuffer)); +} + +void VertexArray::BindElementArrayBuffer() { + m_ElementBuffer.Bind(); +} + +VertexBuffer::~VertexBuffer() { + if (m_ID != 0) + glDeleteBuffers(1, &m_ID); +} + +VertexBuffer::VertexBuffer(const std::vector& data, unsigned int stride) : m_DataStride(stride) { + glGenBuffers(1, &m_ID); + Bind(); + glBufferData(GL_ARRAY_BUFFER, static_cast(data.size() * sizeof(float)), nullptr, GL_STATIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, static_cast(data.size() * sizeof(float)), data.data()); + Unbind(); +} + +void VertexBuffer::Bind() const { + glBindBuffer(GL_ARRAY_BUFFER, m_ID); +} + +void VertexBuffer::Unbind() const { + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +void VertexBuffer::AddVertexAttribPointer(unsigned int index, unsigned int coordinateSize, unsigned int offset) { + VertexAttribPointer pointer { + .m_Index = index, + .m_Size = coordinateSize, + .m_Offset = offset + }; + m_VertexAttribs.push_back(pointer); +} + +void VertexBuffer::BindVertexAttribs() const { + for (const VertexAttribPointer& pointer : m_VertexAttribs) { + glVertexAttribPointer(pointer.m_Index, static_cast(pointer.m_Size), GL_FLOAT, false, m_DataStride * sizeof(float), + reinterpret_cast(static_cast(pointer.m_Offset))); + glEnableVertexAttribArray(pointer.m_Index); + } +} + +ElementBuffer::ElementBuffer(const std::vector& indicies) { + m_TriangleCount = indicies.size(); + glGenBuffers(1, &m_ID); + Bind(); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, static_cast(indicies.size() * sizeof(unsigned int)), nullptr, GL_STATIC_DRAW); + glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, static_cast(indicies.size() * sizeof(unsigned int)), indicies.data()); + Unbind(); +} + +ElementBuffer::~ElementBuffer() { + if (m_ID != 0) + glDeleteBuffers(1, &m_ID); +} + +void ElementBuffer::Bind() const { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ID); +} + +void ElementBuffer::Unbind() const { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} + +} // namespace GL +} // namespace td \ No newline at end of file diff --git a/src/td/render/loader/WorldLoader.cpp b/src/td/render/loader/WorldLoader.cpp new file mode 100644 index 0000000..9854c4c --- /dev/null +++ b/src/td/render/loader/WorldLoader.cpp @@ -0,0 +1,218 @@ +#include + +#include +#include + +#include + +namespace td { +namespace render { + +namespace WorldLoader { + +const static int POSITION_VERTEX_SIZE = 3; +const static int TEXTURE_VERTEX_SIZE = 2; + +GL::VertexArray LoadWorldModel(const td::game::World* world) { + std::vector positions; + std::vector colors; + + for (const auto& chunkInfo : world->GetChunks()) { + const td::game::ChunkCoord& coords = chunkInfo.first; + td::game::ChunkPtr chunk = chunkInfo.second; + + std::int32_t chunkX = coords.x * td::game::Chunk::ChunkWidth; + std::int32_t chunkY = coords.y * td::game::Chunk::ChunkHeight; + + for (int tileY = 0; tileY < td::game::Chunk::ChunkHeight; tileY++) { + for (int tileX = 0; tileX < td::game::Chunk::ChunkWidth; tileX++) { + int tileNumber = tileY * td::game::Chunk::ChunkWidth + tileX; + td::game::TileIndex tileIndex = chunk->GetTileIndex(tileNumber); + td::game::TilePtr tile = world->GetTilePtr(tileIndex); + + if (tile == nullptr) + continue; + + positions.insert( + positions.end(), {static_cast(chunkX + tileX + 1), 0, static_cast(chunkY + tileY), + static_cast(chunkX + tileX), 0, static_cast(chunkY + tileY), + static_cast(chunkX + tileX), 0, static_cast(chunkY + tileY + 1), + + static_cast(chunkX + tileX + 1), 0, static_cast(chunkY + tileY), + static_cast(chunkX + tileX), 0, static_cast(chunkY + tileY + 1), + static_cast(chunkX + tileX + 1), 0, static_cast(chunkY + tileY + 1)}); + + const td::Color* tileColor = world->GetTileColor(tile); + + for (int i = 0; i < 6; i++) { + int color = 255; + color |= tileColor->r << 24; + color |= tileColor->g << 16; + color |= tileColor->b << 8; + + int newColorIndex = colors.size(); + colors.push_back(0); + + memcpy(colors.data() + newColorIndex, &color, 1 * sizeof(int)); + } + } + } + } + + for (int spawnColor = 0; spawnColor < 2; spawnColor++) { + const game::Spawn& spawn = world->GetTeam(TeamColor(spawnColor)).GetSpawn(); + float fromX = spawn.GetTopLeft().GetX(), toX = spawn.GetBottomRight().GetX(); + float fromY = spawn.GetTopLeft().GetY(), toY = spawn.GetBottomRight().GetY(); + + positions.insert(positions.end(), {fromX, 0, fromY, fromX, 0, toY, toX, 0, fromY, fromX, 0, toY, toX, 0, toY, toX, 0, fromY}); + + for (int i = 0; i < 6; i++) { + int color = 255; + color |= world->GetSpawnColor(TeamColor(spawnColor)).r << 24; + color |= world->GetSpawnColor(TeamColor(spawnColor)).g << 16; + color |= world->GetSpawnColor(TeamColor(spawnColor)).b << 8; + + int newColorIndex = colors.size(); + colors.push_back(0); + + memcpy(colors.data() + newColorIndex, &color, 1 * sizeof(int)); + } + } + + for (int castleColor = 0; castleColor < 2; castleColor++) { + const game::TeamCastle& castle = world->GetTeam(TeamColor(castleColor)).GetCastle(); + float fromX = castle.GetTopLeft().GetX(), toX = castle.GetBottomRight().GetX(); + float fromY = castle.GetTopLeft().GetY(), toY = castle.GetBottomRight().GetY(); + + positions.insert(positions.end(), {fromX, 0, fromY, fromX, 0, toY, toX, 0, fromY, fromX, 0, toY, toX, 0, toY, toX, 0, fromY}); + + for (int i = 0; i < 6; i++) { + int color = 255; + color |= world->GetSpawnColor(TeamColor(castleColor)).r << 24; + color |= world->GetSpawnColor(TeamColor(castleColor)).g << 16; + color |= world->GetSpawnColor(TeamColor(castleColor)).b << 8; + + int newColorIndex = colors.size(); + colors.push_back(0); + + memcpy(colors.data() + newColorIndex, &color, 1 * sizeof(int)); + } + } + + GL::VertexBuffer positionVBO(positions, POSITION_VERTEX_SIZE); + positionVBO.AddVertexAttribPointer(0, POSITION_VERTEX_SIZE, 0); + GL::VertexBuffer colorVBO(colors, 1); + colorVBO.AddVertexAttribPointer(1, 1, 0); + + std::vector indexes(positions.size() / 3, 0); + for (size_t i = 0; i < indexes.size(); i++) { + indexes[i] = i + 1; + } + + GL::ElementBuffer indexVBO(indexes); + + GL::VertexArray worldVao(std::move(indexVBO)); // each pos = 3 vertecies + worldVao.Bind(); + worldVao.BindVertexBuffer(std::move(positionVBO)); + worldVao.BindVertexBuffer(std::move(colorVBO)); + worldVao.Unbind(); + return worldVao; +} + +GL::VertexArray LoadTileSelectModel() { + std::vector positions = { + -0.5f, + -0.5f, + -1.0f, + + 0.5f, + -0.5f, + -1.0f, + + 0.0f, + 0.5f, + -1.0f, + + 1, + .01, + 1, + + 0, + .01, + 1, + + 0, + 1, + 1, + }; + + int color = 255 << 24 | 255 << 16 | 255 << 8 | 150; + float colorFloat; + + memcpy(reinterpret_cast(&colorFloat), &color, sizeof(float)); + + std::vector colors(6, colorFloat); + + GL::VertexBuffer positionVBO(positions, POSITION_VERTEX_SIZE); + positionVBO.AddVertexAttribPointer(0, POSITION_VERTEX_SIZE, 0); + GL::VertexBuffer colorVBO(colors, 1); + colorVBO.AddVertexAttribPointer(1, 1, 0); + + std::vector indexes(positions.size() / 3, 0); + for (size_t i = 0; i < indexes.size(); i++) { + indexes[i] = i + 1; + } + GL::ElementBuffer indexVBO(indexes); + + GL::VertexArray tileSelectVao(std::move(indexVBO)); + tileSelectVao.Bind(); + tileSelectVao.BindVertexBuffer(std::move(positionVBO)); + tileSelectVao.BindVertexBuffer(std::move(colorVBO)); + tileSelectVao.Unbind(); + + return tileSelectVao; +} + +RenderData LoadTowerModel(game::TowerPtr tower) { + RenderData renderData; + + float towerX, towerDX; + float towerY, towerDY; + + if (tower->GetSize() == game::TowerSize::Little) { + towerX = tower->GetCenterX() - 1.5f; + towerDX = tower->GetCenterX() + 1.5f; + + towerY = tower->GetCenterY() - 1.5f; + towerDY = tower->GetCenterY() + 1.5f; + } else { + towerX = tower->GetCenterX() - 2.5f; + towerDX = tower->GetCenterX() + 2.5f; + + towerY = tower->GetCenterY() - 2.5f; + towerDY = tower->GetCenterY() + 2.5f; + } + std::vector positions = {towerDX, 0.001, towerY, towerX, 0.001, towerY, towerX, 0.001, towerDY, towerDX, 0.001, towerY, + towerX, 0.001, towerDY, towerDX, 0.001, towerDY}; + + renderData.positions = positions; + + std::uint8_t towerType = static_cast(tower->GetType()); + std::uint8_t r = 10 * towerType + 40, g = 5 * towerType + 30, b = 10 * towerType + 20; + + float colorFloat; + int color = r << 24 | g << 16 | b << 8 | 255; + memcpy(&colorFloat, &color, sizeof(int)); + + std::vector colors(6, colorFloat); + renderData.colors = colors; + + return renderData; +} + + +} // namespace WorldLoader + + +} // namespace render +} // namespace td diff --git a/src/td/render/renderer/WorldRenderer.cpp b/src/td/render/renderer/WorldRenderer.cpp new file mode 100644 index 0000000..998c02a --- /dev/null +++ b/src/td/render/renderer/WorldRenderer.cpp @@ -0,0 +1,26 @@ +#include + +#include + +#include + +namespace td { +namespace render { + +WorldRenderer::WorldRenderer(Camera& a_Camera, const game::World& a_World) : Renderer(a_Camera), m_World(a_World){ + m_WorldVao = std::make_unique(std::move(WorldLoader::LoadWorldModel(&a_World))); +} + +WorldRenderer::~WorldRenderer() {} + +void WorldRenderer::Render() { + m_Shader.Start(); + m_Shader.SetProjectionMatrix(m_Camera.GetProjectionMatrix()); + m_Shader.SetViewMatrix(m_Camera.GetViewMatrix()); + Renderer::Render(*m_WorldVao); + ImGui::ShowDemoWindow(); +} + + +} // namespace render +} // namespace td diff --git a/src/td/render/shader/EntityShader.cpp b/src/td/render/shader/EntityShader.cpp new file mode 100644 index 0000000..4b4dd06 --- /dev/null +++ b/src/td/render/shader/EntityShader.cpp @@ -0,0 +1,123 @@ +#include + +namespace td { +namespace shader { + +// TODO: update ES shaders + +#ifdef __ANDROID__ +static const char vertexSource[] = +R"(#version 300 es + +precision mediump float; + +layout(location = 0) in vec3 position; +layout(location = 1) in int color; + +uniform mat4 viewMatrix; +uniform mat4 projectionMatrix; +uniform vec3 modelPosition; + +flat out int pass_color; + +void main(void){ + pass_color = color; + gl_Position = projectionMatrix * viewMatrix * vec4(position + modelPosition, 1.0); +} +)"; + +static const char fragmentSource[] = +R"(#version 300 es + +precision mediump float; + +flat in int pass_color; + +out vec4 out_color; + +uniform vec3 ColorEffect; + +void main(void){ + + float r = float(pass_color >> 24 & 0xFF) / 255.0; + float g = float(pass_color >> 16 & 0xFF) / 255.0; + float b = float(pass_color >> 8 & 0xFF) / 255.0; + float a = float(pass_color & 0xFF) / 255.0; + vec3 intermediate_color = vec3(r, g, b) * ColorEffect; + out_color = vec4(intermediate_color, a); + +} +)"; +#else +static const char vertexSource[] = R"( +#version 330 + +layout(location = 0) in vec3 position; +layout(location = 1) in vec2 textureCoords; + +uniform mat4 viewMatrix; +uniform mat4 projectionMatrix; +uniform vec3 modelPosition; + +out vec2 pass_textureCoords; + +void main(void){ + pass_textureCoords = textureCoords; + gl_Position = projectionMatrix * viewMatrix * vec4(position + modelPosition, 1.0); +} +)"; + +static const char fragmentSource[] = R"( +#version 330 + +in vec2 pass_textureCoords; + +out vec4 out_color; + +uniform vec3 ColorEffect; +uniform sampler2D textureSampler; + +void main(void){ + + vec4 color = vec4(ColorEffect, 1.0) * texture(textureSampler, pass_textureCoords); + + if (color.a <= 0.1) + discard; + + out_color = color; + +} +)"; +#endif + +EntityShader::EntityShader() : ShaderProgram() {} + +void EntityShader::LoadShader() { + ShaderProgram::LoadProgram(vertexSource, fragmentSource); +} + +void EntityShader::GetAllUniformLocation() { + m_LocationColorEffect = static_cast(GetUniformLocation("ColorEffect")); + m_LocationViewMatrix = static_cast(GetUniformLocation("viewMatrix")); + m_LocationPosition = static_cast(GetUniformLocation("modelPosition")); + m_LocationProjectionMatrix = static_cast(GetUniformLocation("projectionMatrix")); +} + +void EntityShader::SetColorEffect(const Vec3f& color) { + LoadVector(m_LocationColorEffect, color); +} + +void EntityShader::SetProjectionMatrix(const Mat4f& proj) const { + LoadMat4(m_LocationProjectionMatrix, proj); +} + +void EntityShader::SetViewMatrix(const Mat4f& view) const { + LoadMat4(m_LocationViewMatrix, view); +} + +void EntityShader::SetModelPos(const Vec3f& pos) const { + LoadVector(m_LocationPosition, pos); +} + +} // namespace shader +} // namespace td \ No newline at end of file diff --git a/src/td/render/shader/ShaderProgram.cpp b/src/td/render/shader/ShaderProgram.cpp new file mode 100755 index 0000000..5d89462 --- /dev/null +++ b/src/td/render/shader/ShaderProgram.cpp @@ -0,0 +1,145 @@ +/* + * ShaderProgram.cpp + * + * Created on: 31 janv. 2020 + * Author: simon + */ + +#include +#include +#include + +#include +#include +#include +#include + +namespace td { +namespace shader { + +ShaderProgram::ShaderProgram() : + m_ProgramID(0), m_VertexShaderID(0), m_FragmentShaderID(0) { +} + +ShaderProgram::~ShaderProgram() { + CleanUp(); +} + +void ShaderProgram::Start() const { + glUseProgram(m_ProgramID); +} + +void ShaderProgram::Stop() const { + glUseProgram(0); +} + +int ShaderProgram::GetUniformLocation(const std::string& uniformName) const { + const int location = glGetUniformLocation(m_ProgramID, uniformName.c_str()); + if (location == -1) { + utils::LOGD(utils::Format("Warning ! Uniform variable %s not found !", uniformName.c_str())); + } + return location; +} + +void ShaderProgram::LoadFloat(unsigned int location, float value) const { + glUniform1f(static_cast(location), value); +} + +void ShaderProgram::LoadInt(unsigned int location, int value) const { + glUniform1i(static_cast(location), value); +} + +void ShaderProgram::LoadVector(unsigned int location, + const Vec2f& vector) const { + glUniform2f(static_cast(location), vector.x, vector.y); +} + +void ShaderProgram::LoadVector(unsigned int location, + const Vec3f& vector) const { + glUniform3f(static_cast(location), vector.x, vector.y, vector.z); +} + +void ShaderProgram::LoadBoolean(unsigned int location, bool value) const { + glUniform1i(static_cast(location), value); +} + +void ShaderProgram::LoadMat4(unsigned int location, const Mat4f& mat) const { + glUniformMatrix4fv(static_cast(location), 1, false, reinterpret_cast(&mat)); +} + +void ShaderProgram::CleanUp() const { + Stop(); + glDetachShader(m_ProgramID, m_VertexShaderID); + glDetachShader(m_ProgramID, m_FragmentShaderID); + glDeleteShader(m_VertexShaderID); + glDeleteShader(m_FragmentShaderID); + glDeleteProgram(m_ProgramID); +} + +void ShaderProgram::LoadProgramFile(const std::string& vertexFile, + const std::string& fragmentFile) { + m_VertexShaderID = static_cast(LoadShaderFromFile(vertexFile, GL_VERTEX_SHADER)); + m_FragmentShaderID = static_cast(LoadShaderFromFile(fragmentFile, GL_FRAGMENT_SHADER)); + m_ProgramID = glCreateProgram(); + glAttachShader(m_ProgramID, m_VertexShaderID); + glAttachShader(m_ProgramID, m_FragmentShaderID); + glLinkProgram(m_ProgramID); + glValidateProgram(m_ProgramID); + GetAllUniformLocation(); +} + +void ShaderProgram::LoadProgram(const std::string& vertexSource, + const std::string& fragmentSource) { + m_VertexShaderID = static_cast(LoadShader(vertexSource, GL_VERTEX_SHADER)); + m_FragmentShaderID = static_cast(LoadShader(fragmentSource, GL_FRAGMENT_SHADER)); + m_ProgramID = glCreateProgram(); + glAttachShader(m_ProgramID, m_VertexShaderID); + glAttachShader(m_ProgramID, m_FragmentShaderID); + glLinkProgram(m_ProgramID); + glValidateProgram(m_ProgramID); + GetAllUniformLocation(); +} + +unsigned int ShaderProgram::LoadShader(const std::string& source, GLenum type) { + unsigned int shaderID = glCreateShader(type); + + const char* c_str = source.c_str(); + int* null = 0; + glShaderSource(shaderID, 1, &c_str, null); // @suppress("Function cannot be resolved") + glCompileShader(shaderID); + GLint compilesuccessful; + glGetShaderiv(shaderID, GL_COMPILE_STATUS, &compilesuccessful); + if (compilesuccessful == false) { + GLsizei size; + glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &size); + std::vector shaderError(static_cast(size)); + glGetShaderInfoLog(shaderID, size, &size, shaderError.data()); + + utils::LOGE("Could not compile shader !"); + + utils::LOGE(shaderError.data()); + + utils::LOGD(utils::Format("\nShader source : \n" + "------------------------------------------------------------------------------------------------------------------------------------\n" + "%s\n" + "------------------------------------------------------------------------------------------------------------------------------------\n" + , source.c_str())); + } + return shaderID; +} + +unsigned int ShaderProgram::LoadShaderFromFile(const std::string& file, GLenum type) { + std::stringstream stream; + std::ifstream fileStream(file); + + if (fileStream) { + stream << fileStream.rdbuf(); + } else { + return 0; + } + + return LoadShader(stream.str(), type); +} + +} // namespace shader +} // namespace td \ No newline at end of file diff --git a/src/td/render/shader/WorldShader.cpp b/src/td/render/shader/WorldShader.cpp new file mode 100644 index 0000000..469882d --- /dev/null +++ b/src/td/render/shader/WorldShader.cpp @@ -0,0 +1,102 @@ +#include + +namespace td { +namespace shader { + +// TODO: GLES Shaders + +#ifdef __ANDROID__ +static const char vertexSource[] = +R"(#version 300 es + +precision mediump float; + +layout(location = 0) in vec3 position; +layout(location = 1) in int color; + +uniform mat4 viewMatrix; +uniform mat4 projectionMatrix; + +flat out int pass_color; + +void main(void){ + pass_color = color; + gl_Position = projectionMatrix * viewMatrix * vec4(position, 1.0); +} +)"; + +static const char fragmentSource[] = +R"(#version 300 es + +precision mediump float; + +flat in int pass_color; + +out vec4 out_color; + +void main(void){ + + float r = float(pass_color >> 24 & 0xFF) / 255.0; + float g = float(pass_color >> 16 & 0xFF) / 255.0; + float b = float(pass_color >> 8 & 0xFF) / 255.0; + float a = float(pass_color & 0xFF) / 255.0; + out_color = vec4(r, g, b, a); + +} +)"; +#else +static const char vertexSource[] = R"( +#version 330 + +layout(location = 0) in vec3 position; +layout(location = 1) in int color; + +uniform mat4 viewMatrix; +uniform mat4 projectionMatrix; + +flat out int pass_color; + +void main(void){ + pass_color = color; + gl_Position = projectionMatrix * viewMatrix * vec4(position, 1.0); +} +)"; + +static const char fragmentSource[] = R"( +#version 330 + +flat in int pass_color; + +out vec4 out_color; + +void main(void){ + + float r = float(pass_color >> 24 & 0xFF) / 255.0; + float g = float(pass_color >> 16 & 0xFF) / 255.0; + float b = float(pass_color >> 8 & 0xFF) / 255.0; + float a = float(pass_color & 0xFF) / 255.0; + out_color = vec4(r, g, b, a); + +} +)"; +#endif + +WorldShader::WorldShader() : ShaderProgram() { + ShaderProgram::LoadProgram(vertexSource, fragmentSource); +} + +void WorldShader::GetAllUniformLocation() { + m_LocationProjection = static_cast(GetUniformLocation("projectionMatrix")); + m_LocationView = static_cast(GetUniformLocation("viewMatrix")); +} + +void WorldShader::SetProjectionMatrix(const Mat4f& proj) const { + LoadMat4(m_LocationProjection, proj); +} + +void WorldShader::SetViewMatrix(const Mat4f& view) const { + LoadMat4(m_LocationView, view); +} + +} // namespace shader +} // namespace td \ No newline at end of file diff --git a/test/tdmap.tdmap2 b/test/tdmap.tdmap2 new file mode 100644 index 0000000000000000000000000000000000000000..798a860df68b2a187e39b8af964fb7967bd5402e GIT binary patch literal 595 zcmV-Z0<8Uhc$_<9%r)Od^TMYxnGll+>=2 zKR*_d_LoGA>flNlL~thqmpK?qTIK#Lm%Fnecqi_s&brQl=gxcZ6?BrF*n)q+PqNAn zIT4OAfEY8}I~%Uu3-b(jI|sX+DbMIs`~TZox|Yjr%_Umo^3QA8_)}8Xik=mp4fK5y zN%{{q4@=rF=P~|V{cQ0-vMDFvJn(%6+raA;9hUY=nUo)mdV`p+G4o4lcu(vF>?ag> zoVt8{6aKQaXp_C$Q2qu^r~1UEmuc|F%rs8B-0<68!*`#<{~Z&aeDHcjenN1U0~1*L zID1UpvG4D>@Qn8|i;SjvMV^EI7i*sp6%_h2zlil&ycc{&c~+nGYo8YKh<;0^g>3$= zn7YMj@6Q}!ozELb@N0a=^tQnt1FQZOd=Ye$U?+@&Zjv|7TKvxFgTo}s6ALa*8{P(E zzfCL$y=L#PS<2wgar*n`xpL;V>AT^SIvI hy7D